03 OLED显示屏实现
前言
这一章主要是上一节没有讲完的项目的一个编写,上一章我们说道单片机的输入和输出,包括四种单片机与外设之间的数据交换使用到的方式,这一章就利用前面说到的这几种方式来编写一个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的数据手册即可。
初始化传入的命令步骤如下:
-
关闭显示屏
-
设置显示时钟分频比/振荡器频率
-
设置多路复用率
-
显示开始行
-
设置左右方向
-
设置上下方向
-
设置COM引脚硬件配置
-
设置对比度
-
设置预充电周期
-
设置VCOMH取消选择级别
-
设置整个显示打开/关闭
-
设置正常/倒转显示
-
设置充电泵
-
开启显示
这个内容需要查看手册来慢慢写,我这里就不一个一个介绍了,我这写好了,你们可以直接复制和粘贴
#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上,这样就能很好的观察其中的变化了。
下一章准备介绍一下显示屏的一些操作,比如显示进度条,显示动图或者其他内容。
本文作者:Lavender·edgar
本文链接:https://www.cnblogs.com/Lavender-edgar/p/18158900
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步