前言

这一章主要是上一节没有讲完的项目的一个编写,上一章我们说道单片机的输入和输出,包括四种单片机与外设之间的数据交换使用到的方式,这一章就利用前面说到的这几种方式来编写一个OLED显示屏的驱动。

一、软件模拟IIC协议

这使用的OLED屏幕是用的4引脚IIC协议的,在编写相关代码之前我们需要先了解一下什么是IIC协议,在单片机中,IIC通常指的是Inter-Integrated Circuit,也称为I2C(Inter-Integrated Circuit)总线,它是一种用于在集成电路之间进行通信的串行通信协议。

I2C总线由飞利浦公司(现在的恩智浦半导体公司)开发,用于在数字集成电路之间进行通信。它通常用于连接微控制器和外部设备,如传感器、存储器、扩展模块等。

I2C总线使用两根线进行通信:串行数据线(SDA)和串行时钟线(SCL)。通过这两根线,多个设备可以在同一总线上进行通信。每个设备都有一个唯一的地址,因此可以通过地址来选择要通信的特定设备。

I2C总线采用主从结构,主设备负责发起通信并控制总线,而从设备则响应主设备的命令。这种通信协议简单而灵活,适用于连接多种类型的设备。

简单来说IIC协议就是一种串行通讯协议,我们只需要对两根数据线进行操作就可以了,在32中有对应的IIC的函数库,我们可以直接调用来实现,但是那个内容我安排在后面了,我们实现先学习上一节用输入和输出来进行模拟的方式,学习完后我们才可以用一些传感器来观察效果并实现更高级的内容。

1.开启IIC协议

我们在使用IIC协议之前需要发送一个开始信号的,这个开始信号的时序图如下:

可以看到左边是起始条件,我们要开启IIC协议必须得先发送其实条件,这里的代码很容易就可以编写,这里我用了一下宏定义和位操作:

void IIC_Start(void){
GPIO_SetBits(GPIO, SDA);
GPIO_SetBits(GPIO, SCL);
GPIO_ResetBits(GPIO, SDA);
GPIO_ResetBits(GPIO, SCL);
}

这个函数就可以让IIC协议开始,当开始后我们就可以参数数据和一些命令了。

2.结束IIC协议

当一条IIC协议传输完成后需要发送一条协议结束信号,这个信号的写法也很简单,代码如下:

void IIC_Stop(void){
GPIO_ResetBits(GPIO, SDA);
GPIO_SetBits(GPIO, SCL);
GPIO_SetBits(GPIO, SDA);
}

这样就可以结束上一次的IIC数据传输。

3.传输数据

这个的时序图也可以参考上面的时序图

看中间的位置,这个位置就是传输一位数据时的时序图,那么我们就可以根据这个图写出传输一个字节的数据了,这里需要注意一下,我们传输一条字节数据时是需要先传递高位数据,在SCL从高电平到低电平的时候才会将数据传递过去,代码如下:

void IIC_WriteBit(u8 byte){
u8 i;
for (i = 0; i < 8; i++){
if (byte & 0x80){
GPIO_SetBits(GPIO, SDA);
}
else{
GPIO_ResetBits(GPIO, SDA);
}
byte <<= 1;
GPIO_SetBits(GPIO, SCL);
GPIO_ResetBits(GPIO, SCL);
}
// 跳过一个ACK信号
GPIO_SetBits(GPIO, SCL);
GPIO_ResetBits(GPIO, SCL);
}

这里就是向IIC传递一个字节数据的函数,最后这里写了一个跳过ACK响应,这个ACK响应是什么呢?

我们可以看这个时序图:

可以看到WORD数据后有一个ACK信号,这个ACK信号是响应信号,就是当我们数据传递到IIC设备后,它如果接受到数据后就会发送一个接受信号,这个就是接受信号,我们不需要这个信号,所以直接忽略即可。

好了,模拟IIC的操作就是这些了,下面我们就介绍一下OLED的操作了。

二、OLED的操作

1.传输数据的准备

在用IIC协议向OLED传输数据前需要做的操作有下面这几步,我们只有操作好了才能正确的传输命令或者数据:

1.开启IIC协议

2.向OLED传入从地址

3.传输是写入的是命令还是数据

4.开始传输数据

5.结束IIC协议

在传输数据或者命令之前要先向OLED传入它的地址,相当于一个片选,当选中了才可以进行操作,每个OLED的地址都是固定的,都是为0x78,如果要更改需要联系厂家

这个就是时序图,在右边的SSD1306 Slave Address就是它的地址,我们就将这个先传入即可,在下面的写入命令会有这个代码。

然后就是传入是数据还是命令,在words这个数据块中可以告诉你该如何传入,第一个Co不用管,默认0皆可,然后第二个是选择命令还是数据,1是数据,0是命令,所以就可以得到下面的内容,当我们要写命令的时候,传入完地址后再传入0x00。如果要写数据得到的时候,要传入0x40

2.写入命令

知道了上面的过程后我们就可以写写入命令的函数了:

void write_command(u8 cmd){
IIC_Start(); // 开启IIC协议
IIC_Write(0x78); // 传入地址
IIC_Write(0x00); // 传入0x00代表要传入命令
IIC_Write(cmd); // 传入命令
IIC_Stop(); // 结束IIC协议
}

这样就完成了一次写入命令。

3.写入数据

写入数据的函数和上面的函数一样,只不过就是传入地址后传入的内容不同:

void write_data(u8 data){
IIC_Start();
IIC_Write(0x78);
IIC_Write(0x40);
IIC_Write(data);
IIC_Stop();
}

这样就可以完成一次数据的传送。

这些基础功能讲完了,后面的操作是需要结合命令列表来进行操作了。

4.初始化函数

首先我们要操作需要先进行初始化,这里初始化的功能不会全部说明,大家如果有问题可以查一下OLED的数据手册即可。

初始化传入的命令步骤如下:

  1. 关闭显示屏

  2. 设置显示时钟分频比/振荡器频率

  3. 设置多路复用率

  4. 显示开始行

  5. 设置左右方向

  6. 设置上下方向

  7. 设置COM引脚硬件配置

  8. 设置对比度

  9. 设置预充电周期

  10. 设置VCOMH取消选择级别

  11. 设置整个显示打开/关闭

  12. 设置正常/倒转显示

  13. 设置充电泵

  14. 开启显示

这个内容需要查看手册来慢慢写,我这里就不一个一个介绍了,我这写好了,你们可以直接复制和粘贴

#define CMD 0x00
#define DATA 0x40
#define CLOSESHOW 0xAE
#define SETCLS 0xD5
#define RATE 0xA8
#define OFFSET 0xD3
#define START 0x40
#define LEFTRIG 0xA1
#define UPDOWN 0xC8
#define HARD 0xDA
#define RATION 0x81
#define CYCLE 0xD9
#define VCOM 0xDB
#define SHOWUP 0xA4
#define BEN 0x8D
#define SHOWOK 0xAF
void OLED_Init(){
write_command(CLOSESHOW);
write_command(SETCLS);
write_command(0x80);
write_command(RATE);
write_command(0x3F);
write_command(OFFSET);
write_command(0x00);
write_command(START);
write_command(LEFTRIG);
write_command(UPDOWN);
write_command(HARD);
write_command(0x12);
write_command(RATION);
write_command(0xCF);
write_command(CYCLE);
write_command(0xF1);
write_command(VCOM);
write_command(0x30);
write_command(SHOWUP);
write_command(0xA6);
write_command(BEN);
write_command(0x14);
write_command(SHOWOK);
}

5.设置光标

在屏幕中有一个光标,只不过我们在初始化的时候就把这个光标给隐藏了,但是你要有这个概念,设置光标也是需要使用命令来进行操作,这里也需要参考一下命令手册,但是,我这已经写好了,直接用就可以了:

void set_currsor(u8 row, u8 col){
write_command(0xB0 |row);
write_command(0x10 | ((col & 0xF0) >> 4));
write_command(0x00 | (col & 0x0F));
}

row就是光标所在的行,col是光标所在的列。

6.显示字符

要让OLED显示字符需要使用到取摸3工具取出来,因为我们显示其实就是让那些液晶块亮,哪些不亮,所以需要取摸,这里我没找到Linux下的取摸工具,等后面我有时间后我写一个,然后下面是我之前取好的ASCII后32个字符对应的字摸,大家可以直接拿来用:

static unsigned char ASCII1[][16] = {
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, {0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x00,0x00,0x00,0x00},/*"!",0*/ {0x00,0x10,0x0C,0x02,0x10,0x0C,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*""",1*/ {0x00,0x40,0xC0,0x78,0x40,0xC0,0x78,0x00,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x00},/*"#",2*/ {0x00,0x70,0x88,0x88,0xFC,0x08,0x30,0x00,0x00,0x18,0x20,0x20,0xFF,0x21,0x1E,0x00},/*"$",3*/ {0xF0,0x08,0xF0,0x80,0x60,0x18,0x00,0x00,0x00,0x31,0x0C,0x03,0x1E,0x21,0x1E,0x00},/*"%",4*/ {0x00,0xF0,0x08,0x88,0x70,0x00,0x00,0x00,0x1E,0x21,0x23,0x2C,0x19,0x27,0x21,0x10},/*"&",5*/ {0x00,0x12,0x0E,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"'",6*/ {0x00,0x00,0x00,0xE0,0x18,0x04,0x02,0x00,0x00,0x00,0x00,0x07,0x18,0x20,0x40,0x00},/*"(",7*/ {0x00,0x02,0x04,0x18,0xE0,0x00,0x00,0x00,0x00,0x40,0x20,0x18,0x07,0x00,0x00,0x00},/*")",8*/ {0x40,0x40,0x80,0xF0,0x80,0x40,0x40,0x00,0x02,0x02,0x01,0x0F,0x01,0x02,0x02,0x00},/*"*",9*/ {0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x0F,0x01,0x01,0x01},/*"+",10*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x70,0x00,0x00,0x00,0x00,0x00},/*",",11*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00},/*"-",12*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00,0x00,0x00},/*".",13*/ {0x00,0x00,0x00,0x00,0xC0,0x38,0x04,0x00,0x00,0x60,0x18,0x07,0x00,0x00,0x00,0x00},/*"/",14*/ {0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},/*"0",0*/ {0x00,0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00},/*"1",1*/ {0x00,0x70,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},/*"2",2*/ {0x00,0x30,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x18,0x20,0x21,0x21,0x22,0x1C,0x00},/*"3",3*/ {0x00,0x00,0x80,0x40,0x30,0xF8,0x00,0x00,0x00,0x06,0x05,0x24,0x24,0x3F,0x24,0x24},/*"4",4*/ {0x00,0xF8,0x88,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x20,0x20,0x20,0x11,0x0E,0x00},/*"5",5*/ {0x00,0xE0,0x10,0x88,0x88,0x90,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x20,0x1F,0x00},/*"6",6*/ {0x00,0x18,0x08,0x08,0x88,0x68,0x18,0x00,0x00,0x00,0x00,0x3E,0x01,0x00,0x00,0x00},/*"7",7*/ {0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},/*"8",8*/ {0x00,0xF0,0x08,0x08,0x08,0x10,0xE0,0x00,0x00,0x01,0x12,0x22,0x22,0x11,0x0F,0x00},/*"9",9*/ {0x00,0x00,0x00,0xC0,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00,0x00},/*":",0*/ {0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x00},/*";",1*/ {0x00,0x00,0x80,0x40,0x20,0x10,0x08,0x00,0x00,0x01,0x02,0x04,0x08,0x10,0x20,0x00},/*"<",2*/ {0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x02,0x02,0x02,0x02,0x02,0x02,0x00},/*"=",3*/ {0x00,0x08,0x10,0x20,0x40,0x80,0x00,0x00,0x00,0x20,0x10,0x08,0x04,0x02,0x01,0x00},/*">",4*/ {0x00,0x70,0x48,0x08,0x08,0x88,0x70,0x00,0x00,0x00,0x00,0x30,0x37,0x00,0x00,0x00},/*"?",5*/ {0xC0,0x30,0xC8,0x28,0xE8,0x10,0xE0,0x00,0x07,0x18,0x27,0x28,0x2F,0x28,0x17,0x00},/*"@",6*/ {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00,0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20},/*"A",0*/ {0x08,0xF8,0x88,0x88,0x88,0x70,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x11,0x0E,0x00},/*"B",1*/ {0xC0,0x30,0x08,0x08,0x08,0x08,0x38,0x00,0x07,0x18,0x20,0x20,0x20,0x10,0x08,0x00},/*"C",2*/ {0x08,0xF8,0x08,0x08,0x08,0x10,0xE0,0x00,0x20,0x3F,0x20,0x20,0x20,0x10,0x0F,0x00},/*"D",3*/ {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x20,0x23,0x20,0x18,0x00},/*"E",4*/ {0x08,0xF8,0x88,0x88,0xE8,0x08,0x10,0x00,0x20,0x3F,0x20,0x00,0x03,0x00,0x00,0x00},/*"F",5*/ {0xC0,0x30,0x08,0x08,0x08,0x38,0x00,0x00,0x07,0x18,0x20,0x20,0x22,0x1E,0x02,0x00},/*"G",6*/ {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x20,0x3F,0x21,0x01,0x01,0x21,0x3F,0x20},/*"H",7*/ {0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"I",8*/ {0x00,0x00,0x08,0x08,0xF8,0x08,0x08,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00,0x00},/*"J",9*/ {0x08,0xF8,0x88,0xC0,0x28,0x18,0x08,0x00,0x20,0x3F,0x20,0x01,0x26,0x38,0x20,0x00},/*"K",10*/ {0x08,0xF8,0x08,0x00,0x00,0x00,0x00,0x00,0x20,0x3F,0x20,0x20,0x20,0x20,0x30,0x00},/*"L",11*/ {0x08,0xF8,0xF8,0x00,0xF8,0xF8,0x08,0x00,0x20,0x3F,0x01,0x3E,0x01,0x3F,0x20,0x00},/*"M",12*/ {0x08,0xF8,0x30,0xC0,0x00,0x08,0xF8,0x08,0x20,0x3F,0x20,0x00,0x07,0x18,0x3F,0x00},/*"N",13*/ {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x20,0x20,0x20,0x10,0x0F,0x00},/*"O",14*/ {0x08,0xF8,0x08,0x08,0x08,0x08,0xF0,0x00,0x20,0x3F,0x21,0x01,0x01,0x01,0x00,0x00},/*"P",15*/ {0xE0,0x10,0x08,0x08,0x08,0x10,0xE0,0x00,0x0F,0x10,0x28,0x28,0x30,0x50,0x4F,0x00},/*"Q",16*/ {0x08,0xF8,0x88,0x88,0x88,0x88,0x70,0x00,0x20,0x3F,0x20,0x00,0x03,0x0C,0x30,0x20},/*"R",17*/ {0x00,0x70,0x88,0x08,0x08,0x08,0x38,0x00,0x00,0x38,0x20,0x21,0x21,0x22,0x1C,0x00},/*"S",18*/ {0x18,0x08,0x08,0xF8,0x08,0x08,0x18,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},/*"T",19*/ {0x08,0xF8,0x08,0x00,0x00,0x08,0xF8,0x08,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},/*"U",20*/ {0x08,0x78,0x88,0x00,0x00,0xC8,0x38,0x08,0x00,0x00,0x07,0x38,0x0E,0x01,0x00,0x00},/*"V",21*/ {0x08,0xF8,0x00,0xF8,0x00,0xF8,0x08,0x00,0x00,0x03,0x3E,0x01,0x3E,0x03,0x00,0x00},/*"W",22*/ {0x08,0x18,0x68,0x80,0x80,0x68,0x18,0x08,0x20,0x30,0x2C,0x03,0x03,0x2C,0x30,0x20},/*"X",23*/ {0x08,0x38,0xC8,0x00,0xC8,0x38,0x08,0x00,0x00,0x00,0x20,0x3F,0x20,0x00,0x00,0x00},/*"Y",24*/ {0x10,0x08,0x08,0x08,0xC8,0x38,0x08,0x00,0x20,0x38,0x26,0x21,0x20,0x20,0x18,0x00},/*"Z",25*/ {0x00,0x00,0x00,0xFE,0x02,0x02,0x02,0x00,0x00,0x00,0x00,0x7F,0x40,0x40,0x40,0x00},/*"[",0*/ {0x00,0x04,0x38,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x38,0xC0,0x00},/*"\",1*/ {0x00,0x02,0x02,0x02,0xFE,0x00,0x00,0x00,0x00,0x40,0x40,0x40,0x7F,0x00,0x00,0x00},/*"]",2*/ {0x00,0x00,0x04,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"^",3*/ {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},/*"_",4*/ {0x00,0x02,0x02,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"`",5*/ {0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x00,0x19,0x24,0x24,0x12,0x3F,0x20,0x00},/*"a",0*/ {0x10,0xF0,0x00,0x80,0x80,0x00,0x00,0x00,0x00,0x3F,0x11,0x20,0x20,0x11,0x0E,0x00},/*"b",1*/ {0x00,0x00,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0x0E,0x11,0x20,0x20,0x20,0x11,0x00},/*"c",2*/ {0x00,0x00,0x80,0x80,0x80,0x90,0xF0,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},/*"d",3*/ {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x24,0x24,0x24,0x24,0x17,0x00},/*"e",4*/ {0x00,0x80,0x80,0xE0,0x90,0x90,0x20,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"f",5*/ {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x6B,0x94,0x94,0x94,0x93,0x60,0x00},/*"g",6*/ {0x10,0xF0,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},/*"h",7*/ {0x00,0x80,0x98,0x98,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"i",8*/ {0x00,0x00,0x00,0x80,0x98,0x98,0x00,0x00,0x00,0xC0,0x80,0x80,0x80,0x7F,0x00,0x00},/*"j",9*/ {0x10,0xF0,0x00,0x00,0x80,0x80,0x80,0x00,0x20,0x3F,0x24,0x06,0x29,0x30,0x20,0x00},/*"k",10*/ {0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00,0x00},/*"l",11*/ {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x20,0x3F,0x20,0x00,0x3F,0x20,0x00,0x3F},/*"m",12*/ {0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x20,0x3F,0x21,0x00,0x00,0x20,0x3F,0x20},/*"n",13*/ {0x00,0x00,0x80,0x80,0x80,0x80,0x00,0x00,0x00,0x1F,0x20,0x20,0x20,0x20,0x1F,0x00},/*"o",14*/ {0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x00,0x80,0xFF,0x91,0x20,0x20,0x11,0x0E,0x00},/*"p",15*/ {0x00,0x00,0x00,0x80,0x80,0x00,0x80,0x00,0x00,0x0E,0x11,0x20,0x20,0x91,0xFF,0x80},/*"q",16*/ {0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x20,0x20,0x3F,0x21,0x20,0x00,0x01,0x00},/*"r",17*/ {0x00,0x00,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x33,0x24,0x24,0x24,0x24,0x19,0x00},/*"s",18*/ {0x00,0x80,0x80,0xE0,0x80,0x80,0x00,0x00,0x00,0x00,0x00,0x1F,0x20,0x20,0x10,0x00},/*"t",19*/ {0x80,0x80,0x00,0x00,0x00,0x80,0x80,0x00,0x00,0x1F,0x20,0x20,0x20,0x10,0x3F,0x20},/*"u",20*/ {0x80,0x80,0x80,0x00,0x80,0x80,0x80,0x00,0x00,0x03,0x0C,0x30,0x0C,0x03,0x00,0x00},/*"v",21*/ {0x80,0x80,0x00,0x80,0x80,0x00,0x80,0x80,0x01,0x0E,0x30,0x0C,0x07,0x38,0x06,0x01},/*"w",22*/ {0x00,0x80,0x80,0x80,0x00,0x80,0x80,0x00,0x00,0x20,0x31,0x0E,0x2E,0x31,0x20,0x00},/*"x",23*/ {0x80,0x80,0x80,0x00,0x00,0x80,0x80,0x80,0x00,0x81,0x86,0x78,0x18,0x06,0x01,0x00},/*"y",24*/ {0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00,0x00,0x21,0x30,0x2C,0x22,0x21,0x30,0x00},/*"z",25*/
}; static unsigned char number1[][16] = {
{0x00,0xE0,0x10,0x08,0x08,0x10,0xE0,0x00,0x00,0x0F,0x10,0x20,0x20,0x10,0x0F,0x00},/*"0",0*/ {0x00,0x00,0x10,0x10,0xF8,0x00,0x00,0x00,0x00,0x00,0x20,0x20,0x3F,0x20,0x20,0x00},/*"1",1*/ {0x00,0x70,0x08,0x08,0x08,0x08,0xF0,0x00,0x00,0x30,0x28,0x24,0x22,0x21,0x30,0x00},/*"2",2*/ {0x00,0x30,0x08,0x08,0x08,0x88,0x70,0x00,0x00,0x18,0x20,0x21,0x21,0x22,0x1C,0x00},/*"3",3*/ {0x00,0x00,0x80,0x40,0x30,0xF8,0x00,0x00,0x00,0x06,0x05,0x24,0x24,0x3F,0x24,0x24},/*"4",4*/ {0x00,0xF8,0x88,0x88,0x88,0x08,0x08,0x00,0x00,0x19,0x20,0x20,0x20,0x11,0x0E,0x00},/*"5",5*/ {0x00,0xE0,0x10,0x88,0x88,0x90,0x00,0x00,0x00,0x0F,0x11,0x20,0x20,0x20,0x1F,0x00},/*"6",6*/ {0x00,0x18,0x08,0x08,0x88,0x68,0x18,0x00,0x00,0x00,0x00,0x3E,0x01,0x00,0x00,0x00},/*"7",7*/ {0x00,0x70,0x88,0x08,0x08,0x88,0x70,0x00,0x00,0x1C,0x22,0x21,0x21,0x22,0x1C,0x00},/*"8",8*/ {0x00,0xF0,0x08,0x08,0x08,0x10,0xE0,0x00,0x00,0x01,0x12,0x22,0x22,0x11,0x0F,0x00},/*"9",9*/
};

然后根据这个字摸,我们可以显示字符了,这里写一个显示字符的函数:

void show_char(u8 row, u8 col, u8 charr){
u8 i; // 遍历变量
set_currsor(row, col + 8); // 设置显示位置
for (i = 0; i < 8; i++){
IIC_Write(ASCII1[charr - 32][i]);
}
set_currsor(row + 1, col + 8); // 设置下一行显示的位置
for (i = 0; i < 8; i++){
IIC_Write(ASCII1[charr - 32][i + 8]);
}
}

字摸的长度是16位,而我们显示的这个ASCII是8*8大小的,我们在第一次设置光标的时候要加上8是为了错过前面的一个字符,然后在显示字符的时候选择的是逐列式。

也就是先将第一个字节的字摸先显示,然后转到下一列再显示第二个字摸,依次下去,这样如果只有一半,也就是只有4*8,还需要转到下一行补上那4行才可以变成8*8。

在写这个函数的时候,我发现了一个好玩的,我这如果让列不跳过一个字符,而是跳过一位,那这个字符就会扭动,虽然没有什么用,但是感觉很有趣,代码如下,大家可以来试试:

// 扭动字符显示
// 只能显示一个,显示多个就不流畅了
void show_Twistchar(u8 row, u8 col, u8 word){
u8 i;
set_currsor(row, col * 8);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i]);
}
set_currsor(row + 1, col * 8);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i + 8]);
}
// 下面这些可以删掉,然后在主函数调用的时候调用两遍,要一样的字符
delay_us(5);
set_currsor(row, col * 8 + 1);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i]);
}
set_currsor(row + 1, col * 8 + 1);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i + 8]);
}
}

7.显示字符串

这个就得看大家对于串操作的熟练程度了,就是用到对字符串的操作,但是呢,这个需要考虑到换行,代码如下:

void show_string(u8 row, u8 col, u8* str){
u8 i, j = 0;
for (i = 0; str[i] != '\0'; i++, j++){
if (j >= 16){
row += 1;
col = 0;
j = 0;
}
show_char(row, col + j, str[i]);
}
}

这里我用一个变量作为判断条件,因为一行有128个像素点,我们显示8*8的字符,那一行只能显示16个8*8的字符,所以当j变量到了16个字符,我们就让行换一行,列清零,这样就可以实现换行操作了。

8.清屏

这个操作很简单,就是将每一行和每一列的所有像素点全部变为0x00就可以了,代码如下:

void clear(){
u8 i, j;
for (i = 0; i < 8; i++){
set_currsor(i, 0);
for (j = 0; j < 128; j++){
write_data(0x00);
}
}
}

9.显示汉字

这个也是需要使用取摸得到,然后每个汉字是16*16的点阵,我们只需要显示即可:

// 现实汉字
void show_hz(u8 row, u8 col, u8 len){
u8 i, j, x, y;
y = row;
x = col;
for (j = 0; j < len * 2; j += 2, y+= 2){
if ((x + y) * 8 >= 128){
y = 0;
row = 2;
}
set_currsor(row, (x + y) * 8);
for (i = 0; i < 16; i++){
write_data(hz[j][i]);
}
set_currsor(row + 1, (x + y) * 8);
for (i = 0; i < 16; i++){
write_data(hz[j + 1][i]);
}
}
}

这个也是可以自动换行的,但在使用前需要将字摸放入到hz这个数组中,这个数组的格式如下:

static unsigned char hz[][16] = {
{0x20,0x24,0x24,0x24,0xFE,0x23,0x22,0x20,0x20,0xFF,0x20,0x22,0x2C,0xA0,0x20,0x00},
{0x00,0x08,0x48,0x84,0x7F,0x02,0x41,0x40,0x20,0x13,0x0C,0x14,0x22,0x41,0xF8,0x00},/*"我",0*/ {0x00,0x80,0x60,0xF8,0x07,0x00,0xF8,0x01,0x06,0x00,0x04,0x04,0x04,0xFC,0x00,0x00},
{0x01,0x00,0x00,0xFF,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x40,0x80,0x7F,0x00,0x00},/*"们",1*/ {0x20,0x24,0x24,0xA4,0x7F,0x24,0x34,0x28,0x26,0x20,0xFE,0x02,0x22,0xDA,0x06,0x00},
{0x04,0x02,0xFF,0x49,0x49,0x49,0x49,0xFF,0x00,0x00,0xFF,0x08,0x10,0x08,0x07,0x00},/*"都",2*/ {0x04,0x04,0x04,0x84,0xE4,0x3C,0x27,0x24,0x24,0x24,0x24,0xE4,0x04,0x04,0x04,0x00},
{0x04,0x02,0x01,0x00,0xFF,0x09,0x09,0x09,0x09,0x49,0x89,0x7F,0x00,0x00,0x00,0x00},/*"有",3*/ {0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"一",4*/ {0x80,0x80,0x40,0x20,0x10,0x08,0x04,0xC3,0x04,0x08,0x10,0x20,0x40,0x80,0x80,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},/*"个",5*/ {0x40,0x44,0x24,0x14,0x7F,0x94,0x24,0x00,0x24,0x14,0x7F,0x14,0x24,0x44,0x40,0x00},
{0x00,0x80,0x88,0x84,0x42,0x45,0x29,0x11,0x11,0x09,0x05,0x03,0x00,0x00,0x00,0x00},/*"梦",6*/ {0x00,0x08,0x88,0x68,0xFF,0x48,0x88,0x00,0xFE,0x52,0x52,0x52,0xFE,0x00,0x00,0x00},
{0x40,0x31,0x00,0x00,0x3B,0x40,0x40,0x44,0x5B,0x42,0x42,0x72,0x03,0x08,0x30,0x00},/*"想",7*/ {0x00,0x08,0x88,0x68,0xFF,0x48,0x88,0x00,0xFE,0x52,0x52,0x52,0xFE,0x00,0x00,0x00},
{0x40,0x31,0x00,0x00,0x3B,0x40,0x40,0x44,0x5B,0x42,0x42,0x72,0x03,0x08,0x30,0x00},/*"想",7*/ };

这里面有一些测试的汉字,大家可以测试一下。

10.显示图片

显示图片的代码我没有写,其实和显示字符和汉字一样,然后用取摸工具取出,然后看一下行有多少,列有多少,然后显示即可,这里我后面写完后会放入gitee上的,github的话因为我最近资金不足,所以没工具,懂得都懂。

11.显示动图

这个其实和GIF的原理一样,图片上覆盖图片,这样就会让图片动起来。

三、完整代码

我这个项目是分文件编写的,分为oled.c函数原型,oled.h函数声明和头文件引入,oledfont.h这个是字符库,sys.c是延时函数和系统操作,gpio.h是gpio口的初始化函数的声明

// gpio.c
#include "gpio.h" void MX_GPIOInit(){
GPIO_InitTypeDef GPIO_InitStruct = {0}; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Pin = CLK;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIO, &GPIO_InitStruct); GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_OD;
GPIO_InitStruct.GPIO_Pin = SDA;
GPIO_Init(GPIO, &GPIO_InitStruct);
}
// gpio.h
#ifndef __GPIO_H
#define __GPIO_H #include "stm32f10x.h" #define CLK GPIO_Pin_8
#define SDA GPIO_Pin_9
#define GPIO GPIOB void MX_GPIOInit(); #endif
// oled.c
#include "oled.h" // I2C协议开始
void IIC_Start(){
GPIO_SetBits(GPIO, SDA);
GPIO_SetBits(GPIO, CLK);
GPIO_ResetBits(GPIO, SDA);
GPIO_ResetBits(GPIO, CLK);
} // IIC协议结束
void IIC_Stop(){
GPIO_ResetBits(GPIO, SDA);
GPIO_SetBits(GPIO, CLK);
GPIO_SetBits(GPIO, SDA);
} // 通过IIC写入数据
void IIC_Write(u8 data){
u8 i;
for (i = 0; i < 8; i++){ if (data & 0x80){
GPIO_SetBits(GPIO, SDA);
}
else{
GPIO_ResetBits(GPIO, SDA);
}
GPIO_SetBits(GPIO, CLK);
GPIO_ResetBits(GPIOB, CLK);
data <<= 1;
// GPIO_ResetBits(GPIO, CLK);
// GPIO_SetBits(GPIO, CLK);
}
GPIO_SetBits(GPIO, CLK);
GPIO_ResetBits(GPIO, CLK);
} // 写入命令
void write_command(u8 cmd){
IIC_Start();
IIC_Write(ADDR);
IIC_Write(0x00);
IIC_Write(cmd);
IIC_Stop();
} // 写入数据
void write_data(u8 data){
IIC_Start();
IIC_Write(ADDR);
IIC_Write(0x40);
IIC_Write(data);
IIC_Stop();
} // 设置光标
void set_currsor(u8 row, u8 col){
write_command(0xB0 |row);
write_command(0x10 | ((col & 0xF0) >> 4));
write_command(0x00 | (col & 0x0F));
} // 扭动字符显示
// 只能显示一个,显示多个就不流畅了
void show_Twistchar(u8 row, u8 col, u8 word){
u8 i;
set_currsor(row, col * 8);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i]);
}
set_currsor(row + 1, col * 8);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i + 8]);
}
delay_us(5);
set_currsor(row, col * 8 + 1);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i]);
}
set_currsor(row + 1, col * 8 + 1);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i + 8]);
}
} // 显示一个字符
void show_char(u8 row, u8 col, u8 word){
u8 i;
set_currsor(row * 2, col * 8);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i]);
}
set_currsor(row * 2 + 1, col * 8);
for (i = 0; i < 8; i++){
write_data(ASCII1[word - 32][i + 8]);
}
} // 显示字符串
void show_string(u8 row, u8 col, u8* str){
u8 i, j = 0;
for (i = 0; str[i] != '\0'; i++, j++){
if (j >= 16){
row += 1;
col = 0;
j = 0;
}
show_char(row, col + j, str[i]);
}
} // 现实汉字
void show_hz(u8 row, u8 col, u8 len){
u8 i, j, x, y;
y = row;
x = col;
for (j = 0; j < len * 2; j += 2, y+= 2){
// x = (x + j) * 8;
if ((x + y) * 8 >= 128){
y = 0;
row = 2;
}
set_currsor(row, (x + y) * 8);
for (i = 0; i < 16; i++){
write_data(hz[j][i]);
}
set_currsor(row + 1, (x + y) * 8);
for (i = 0; i < 16; i++){
write_data(hz[j + 1][i]);
}
}
} // 清屏
void clear(){
u8 i, j;
for (i = 0; i < 8; i++){
set_currsor(i, 0);
for (j = 0; j < 128; j++){
write_data(0x00);
}
}
} // 初始化函数
void OLED_Init(){
u32 i, j;
for (i = 0; i < 1000; i++){
for (j = 0; j < 1000; j++);
}
write_command(CLOSESHOW);
write_command(SETCLS);
write_command(0x80);
write_command(RATE);
write_command(0x3F);
write_command(OFFSET);
write_command(0x00);
write_command(START);
write_command(LEFTRIG);
write_command(UPDOWN);
write_command(HARD);
write_command(0x12);
write_command(RATION);
write_command(0xCF);
write_command(CYCLE);
write_command(0xF1);
write_command(VCOM);
write_command(0x30);
write_command(SHOWUP);
write_command(0xA6);
write_command(BEN);
write_command(0x14);
write_command(SHOWOK);
clear();
}
// oled.h
#ifndef __OLED_H
#define __OLED_H #include "stm32f10x.h"
#include "gpio.h"
#include "oledfont.h"
#include "sys.h" #define ADDR 0x78 #define CMD 0x00
#define DATA 0x40
#define CLOSESHOW 0xAE
#define SETCLS 0xD5
#define RATE 0xA8
#define OFFSET 0xD3
#define START 0x40
#define LEFTRIG 0xA1
#define UPDOWN 0xC8
#define HARD 0xDA
#define RATION 0x81
#define CYCLE 0xD9
#define VCOM 0xDB
#define SHOWUP 0xA4
#define BEN 0x8D
#define SHOWOK 0xAF void OLED_Init();
void clear();
void show_char(u8 row, u8 col, u8 word);
void set_currsor(u8 row, u8 col);
void show_Twistchar(u8 row, u8 col, u8 word);
void show_string(u8 row, u8 col, u8* str);
void show_hz(u8 row, u8 col, u8 len); #endif
// sys.c
#include "sys.h" void delay_us(u32 time){
u32 temp;
SysTick -> LOAD = 9 * time;
SysTick -> CTRL = 0x01;
SysTick -> VAL = 0;
do{
temp = SysTick -> CTRL;
}while((temp & 0x01) && (!(temp & (1 << 16))));
SysTick -> CTRL = 0;
SysTick -> VAL = 0;
}
// sys.h
#ifndef __SYS_H
#define __SYS_H #include "stm32f10x.h" void delay_us(u32 time); #endif

总结

学会了这个显示屏如何显示后后面我们可以对传感器进行操作了,到时候传感器接收到的数据就可以直接显示在OLED上,这样就能很好的观察其中的变化了。

下一章准备介绍一下显示屏的一些操作,比如显示进度条,显示动图或者其他内容。

03 OLED显示屏实现的更多相关文章

  1. [TPYBoard - Micropython之会python就能做硬件 6] 学习使用OLED显示屏

    转载请注明:@小五义 http://www.cnblogs.com/xiaowuyi            欢迎加入讨论群 64770604 一.实验器材 1.TPYBoard板子一块 2.数据线一条 ...

  2. Galaxy S10使用几乎零黑边框的OLED显示屏

    2019年的首波安卓旗舰中,目前关于三星Galaxy S10的爆料是最多的,在销量连续萎缩后,外界对手机一哥的“发力之作”充满期待. 据TheElec报道,Galaxy S10正面使用的是一块几乎零黑 ...

  3. Arduino和C51开发OLED显示屏

    技术:51单片机.Arduino.OLED显示屏.U8glib   概述 OLED显示屏常常用作为智能产品的显示设备,本文简单介绍OLED显示屏的使用方法. 详细 代码下载:http://www.de ...

  4. 0.96寸OLED显示屏驱动手册(SSD1306)

    MCU IIC接口 IIC通信接口由从地址位SA0,IIC总线数据信号SDA(输出SDAout/D2和输入SDAin /D1)和IIC总线时钟信号SCL(D0).不管是数据线还是时钟线都需要连接上拉电 ...

  5. 基于I2C总线的0.96寸OLED显示屏驱动

    资料未整理,先占位置,以后补充

  6. 基于FPGA的OLED真彩色动态图像显示的实现

    源:基于FPGA的OLED真彩色动态图像显示的实现 作为第3代显示器,有机电致发光器件(Organic Light Emitting Diode,OLED)由于其主动发光.响应快.高亮度.全视角.直流 ...

  7. MicroPython之TPYBoard v102开发板控制OLED显示中文

    转载请以链接形式注明文章来源,公众号:MicroPython玩家汇 0x00前言 之前看到一篇文章是关于TPYBoardv102控制OLED屏显示的,看到之后就想尝试一下使用OLED屏来显示中文.最近 ...

  8. MicroPython实例之TPYBoard开发板控制OLED显示中文

    0x00 前言 之前看到一篇文章是关于TPYBoard v102控制OLED屏显示的,看到之后就想尝试一下使用OLED屏来显示中文.最近利用空余时间搞定了这个实验,特此将实验过程及源码分享出来,方便以 ...

  9. Arduino和C51开发LCD1602显示屏

    技术:51单片机.Arduino.LCD1602   概述 本文介绍了LCD1602显示屏,并在LCD1602上显示字符串,对LCD1602常见的问题的解决和开发方法也做了简单介绍. 详细 代码下载: ...

  10. 玩转X-CTR100 l STM32F4 l OLED显示-SSD1306无字库

    我造轮子,你造车,创客一起造起来!塔克创新资讯[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ]      OLED显示屏具有自发光特性,不需要背光, ...

随机推荐

  1. Dijkstra(迪杰斯特拉)算法

    Dijkstra是什么算法 Dijkstra是典型最短路径算法,用于计算一个节点到其他节点的最短路径.该算法使用的是贪心策略:每次都找出剩余顶点中与源点距离最近的一个顶点. 什么是最短路径问题  给定 ...

  2. MySQL学习笔记-SQL实践1

    SQL实践1 借着学校的数据库实验,来对之前学习的SQL语言进行实践和总结. 实验环境: macOS 13.2 (22D49) mysql Ver 8.0.32 for macos13.0 on ar ...

  3. 绘图与可视化--pandas中的绘图函数

    matplotlib API函数都位于matplotlib.pyplot模块中. 本节代码中引入的约定为:import matplotlib.pyplot as plt numpy库也会用到,约定:i ...

  4. 01-布局扩展-BFC完成圣杯布局

    <!DOCTYPE html>   <html lang="en">   <head>   <meta charset="UTF ...

  5. 算法学习笔记(45): 快速沃尔什变换 FWT

    遗憾的是 math 里面一直没有很好的讲这个东西--所以这次细致说说. FWT 的本质 类似于多项式卷积中,利用 ntt 变换使得卷积 \(\to\) 点乘,fwt 也是类似的应用. 定义某种位运算 ...

  6. vm ware 虚拟WIN XP 时卡

    主机是I7 9700,SN750 NVME 1T SSD, 32G 内存,VM WARE 14,理论上虚拟WIN XP 不会卡,但实际情况就是很卡. 虚拟WIN7 ,WIN 10,UBUNTU LIN ...

  7. 比较 SpringSecurity 和 Shiro

    相比 Spring Security, Shiro 在保持强大功能的同时,使用简单性和灵活性. SpringSecurity: 即使是一个一个简单的请求, 最少得经过它的 8 个Filter.Spri ...

  8. CS后门源码特征分析与IDS入侵检测

    CS后门源码特征分析与IDS入侵检测考核作业 上线x64 getshell 抓心跳包,对特征字符解密Uqd3 用java的checksum8算法得到93,说明是x64的木马 public class ...

  9. ConvertLatOrLonFilter-经纬度格式转换-保留6位

    ConvertLatOrLonFilter-经纬度格式转换-保留6位 /** * 转换经纬度 * 小数点最后最多为6位 * @param latOrLon * @return */ private S ...

  10. insert into 表名 set

    insert into 表名 set CREATE TABLE `tbl_str` ( `id` INT DEFAULT NULL, `Str` VARCHAR(30) DEFAULT NULL ) ...