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上,这样就能很好的观察其中的变化了。
下一章准备介绍一下显示屏的一些操作,比如显示进度条,显示动图或者其他内容。
03 OLED显示屏实现的更多相关文章
- [TPYBoard - Micropython之会python就能做硬件 6] 学习使用OLED显示屏
转载请注明:@小五义 http://www.cnblogs.com/xiaowuyi 欢迎加入讨论群 64770604 一.实验器材 1.TPYBoard板子一块 2.数据线一条 ...
- Galaxy S10使用几乎零黑边框的OLED显示屏
2019年的首波安卓旗舰中,目前关于三星Galaxy S10的爆料是最多的,在销量连续萎缩后,外界对手机一哥的“发力之作”充满期待. 据TheElec报道,Galaxy S10正面使用的是一块几乎零黑 ...
- Arduino和C51开发OLED显示屏
技术:51单片机.Arduino.OLED显示屏.U8glib 概述 OLED显示屏常常用作为智能产品的显示设备,本文简单介绍OLED显示屏的使用方法. 详细 代码下载:http://www.de ...
- 0.96寸OLED显示屏驱动手册(SSD1306)
MCU IIC接口 IIC通信接口由从地址位SA0,IIC总线数据信号SDA(输出SDAout/D2和输入SDAin /D1)和IIC总线时钟信号SCL(D0).不管是数据线还是时钟线都需要连接上拉电 ...
- 基于I2C总线的0.96寸OLED显示屏驱动
资料未整理,先占位置,以后补充
- 基于FPGA的OLED真彩色动态图像显示的实现
源:基于FPGA的OLED真彩色动态图像显示的实现 作为第3代显示器,有机电致发光器件(Organic Light Emitting Diode,OLED)由于其主动发光.响应快.高亮度.全视角.直流 ...
- MicroPython之TPYBoard v102开发板控制OLED显示中文
转载请以链接形式注明文章来源,公众号:MicroPython玩家汇 0x00前言 之前看到一篇文章是关于TPYBoardv102控制OLED屏显示的,看到之后就想尝试一下使用OLED屏来显示中文.最近 ...
- MicroPython实例之TPYBoard开发板控制OLED显示中文
0x00 前言 之前看到一篇文章是关于TPYBoard v102控制OLED屏显示的,看到之后就想尝试一下使用OLED屏来显示中文.最近利用空余时间搞定了这个实验,特此将实验过程及源码分享出来,方便以 ...
- Arduino和C51开发LCD1602显示屏
技术:51单片机.Arduino.LCD1602 概述 本文介绍了LCD1602显示屏,并在LCD1602上显示字符串,对LCD1602常见的问题的解决和开发方法也做了简单介绍. 详细 代码下载: ...
- 玩转X-CTR100 l STM32F4 l OLED显示-SSD1306无字库
我造轮子,你造车,创客一起造起来!塔克创新资讯[塔克社区 www.xtark.cn ][塔克博客 www.cnblogs.com/xtark/ ] OLED显示屏具有自发光特性,不需要背光, ...
随机推荐
- 继承,super,重写,多态,抽象,接口
继承,super,重写,多态,抽象,接口 继承 extends 用于表示两个类之间的继承关系,继承是OOP的四大特性之一,他允许一个类(称之为子类或派送类) 继承另一个类(称之为父类或基类)的变量和方 ...
- NOIP模拟54
我觉得,不改变也很好. 前言 这题太难了,场上竟然无人切题..(听说别的学校切题的人不少.. T1 选择 解题思路 范围比较小,并且每个边的度也比较小,因此考虑 树形DP+状压 . 大概就是对于每一个 ...
- windows报错
如果说你dns没有权威的话1.先去long.com上面右键属性把"区域传送给所有服务器打勾"2.右键属性,在名称分析器中,输入要添加为辅助dns的服务器的ip显示解析成功就可以了
- Java BigInteger类和BigDecimal()类
BigInteger类 BigInteger 支持任意精度的整数,可以准确的表达任何大小的整数而不丢失精准度 BigInteger 位于 java.math包中 BigInteger()括号里必须是字 ...
- redis主从复制篇
我们知道要避免单点故障,即保证高可用,便需要冗余(副本)方式提供集群服务. 而Redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式. 主从复制概述 主从复制,是指将一台 ...
- Lecture3
Smiling & Weeping ---- 蝴蝶在双翼里藏匿夏的脉络 妄图在绿意中品鉴隆冬 第三章 Git分支管理 3.1 分支的简介 Git最重要的运用场景是多人协同开发,但是如何能保证每 ...
- sqlUtil
package com.cmbchina.monitor.utils;import com.alibaba.druid.sql.ast.SQLStatement;import com.alibaba. ...
- MYSQL8-快速生成表结构(用于生成文档)
各种工具都有,没有特别趁手的.不如自己用sql处理. SELECT column_name AS CODE, CASE WHEN column_comment IS NULL OR TRIM(colu ...
- Kubernetes(K8s)最新版搭建
Kubernetes简单介绍 Kubernetes意为舵手,简称K8s. 前身是Google的Borg.所以一开源就吸引了一大批注意力. 因为谷歌,所以墙.在国内搭建K8s非常头疼. 下面我就来介绍一 ...
- 【读论文】LLaMA: Open and Efficient Foundation Language Models
论文:LLaMA: Open and Efficient Foundation Language Models 模型代码:https://github.com/facebookresearch/lla ...