文章目录
- 前言
- 一、软件模拟IIC协议
- 1.开启IIC协议
- 2.结束IIC协议
- 3.传输数据
- 二、OLED的操作
- 1.传输数据的准备
- 2.写入命令
- 3.写入数据
- 4.初始化函数
- 5.设置光标
- 6.显示字符
- 7.显示字符串
- 8.清屏
- 9.显示汉字
- 10.显示图片
- 11.显示动图
- 三、完整代码
- 总结
前言
这一章主要是上一节没有讲完的项目的一个编写,上一章我们说道单片机的输入和输出,包括四种单片机与外设之间的数据交换使用到的方式,这一章就利用前面说到的这几种方式来编写一个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 GPIOBvoid 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 0xAFvoid 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上,这样就能很好的观察其中的变化了。
下一章准备介绍一下显示屏的一些操作,比如显示进度条,显示动图或者其他内容。