前言

这一章主要是上一节没有讲完的项目的一个编写,上一章我们说道单片机的输入和输出,包括四种单片机与外设之间的数据交换使用到的方式,这一章就利用前面说到的这几种方式来编写一个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. 继承,super,重写,多态,抽象,接口

    继承,super,重写,多态,抽象,接口 继承 extends 用于表示两个类之间的继承关系,继承是OOP的四大特性之一,他允许一个类(称之为子类或派送类) 继承另一个类(称之为父类或基类)的变量和方 ...

  2. NOIP模拟54

    我觉得,不改变也很好. 前言 这题太难了,场上竟然无人切题..(听说别的学校切题的人不少.. T1 选择 解题思路 范围比较小,并且每个边的度也比较小,因此考虑 树形DP+状压 . 大概就是对于每一个 ...

  3. windows报错

    如果说你dns没有权威的话1.先去long.com上面右键属性把"区域传送给所有服务器打勾"2.右键属性,在名称分析器中,输入要添加为辅助dns的服务器的ip显示解析成功就可以了

  4. Java BigInteger类和BigDecimal()类

    BigInteger类 BigInteger 支持任意精度的整数,可以准确的表达任何大小的整数而不丢失精准度 BigInteger 位于 java.math包中 BigInteger()括号里必须是字 ...

  5. redis主从复制篇

    我们知道要避免单点故障,即保证高可用,便需要冗余(副本)方式提供集群服务. 而Redis 提供了主从库模式,以保证数据副本的一致,主从库之间采用的是读写分离的方式. 主从复制概述 主从复制,是指将一台 ...

  6. Lecture3

    Smiling & Weeping ---- 蝴蝶在双翼里藏匿夏的脉络 妄图在绿意中品鉴隆冬 第三章 Git分支管理 3.1 分支的简介 Git最重要的运用场景是多人协同开发,但是如何能保证每 ...

  7. sqlUtil

    package com.cmbchina.monitor.utils;import com.alibaba.druid.sql.ast.SQLStatement;import com.alibaba. ...

  8. MYSQL8-快速生成表结构(用于生成文档)

    各种工具都有,没有特别趁手的.不如自己用sql处理. SELECT column_name AS CODE, CASE WHEN column_comment IS NULL OR TRIM(colu ...

  9. Kubernetes(K8s)最新版搭建

    Kubernetes简单介绍 Kubernetes意为舵手,简称K8s. 前身是Google的Borg.所以一开源就吸引了一大批注意力. 因为谷歌,所以墙.在国内搭建K8s非常头疼. 下面我就来介绍一 ...

  10. 【读论文】LLaMA: Open and Efficient Foundation Language Models

    论文:LLaMA: Open and Efficient Foundation Language Models 模型代码:https://github.com/facebookresearch/lla ...