03 OLED显示屏实现

文章目录

  • 前言
  • 一、软件模拟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协议之前需要发送一个开始信号的,这个开始信号的时序图如下:

3.png

可以看到左边是起始条件,我们要开启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.传输数据

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

3.png

看中间的位置,这个位置就是传输一位数据时的时序图,那么我们就可以根据这个图写出传输一个字节的数据了,这里需要注意一下,我们传输一条字节数据时是需要先传递高位数据,在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响应是什么呢?

我们可以看这个时序图:

3.jpg

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

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

二、OLED的操作

1.传输数据的准备

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

1.开启IIC协议

2.向OLED传入从地址

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

4.开始传输数据

5.结束IIC协议

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

4.png

这个就是时序图,在右边的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 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上,这样就能很好的观察其中的变化了。

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/3456.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端项目中使用插件prettier/jscodeshift/json-stringify-pretty-compact格式化代码或json数据

同学们可以私信我加入学习群&#xff01; 正文开始 前言一、json代码格式化-选型二、json-stringify-pretty-compact简单试用三、prettier在前端使用四、查看prettier支持的语言和插件五、使用prettier格式化vue代码最终效果如图&#xff1a; ![在这里插入图片描述](https://im…

LLM应用实战:当KBQA集成LLM(二)

1. 背景 又两周过去了&#xff0c;本qiang~依然奋斗在上周提到的项目KBQA集成LLM&#xff0c;感兴趣的可通过传送门查阅先前的文章《LLM应用实战&#xff1a;当KBQA集成LLM》。 本次又有什么更新呢&#xff1f;主要是针对上次提到的缺点进行优化改进。主要包含如下方面&#…

多客圈子交友系统 uniapp+thinkphp6适配小程序/H5/app/api全开源,多款插件自选,支持个性定制!

网上交友的优点包括&#xff1a; 1. 方便&#xff1a;网上交友可以随时随地进行&#xff0c;不受时间和空间的限制&#xff0c;方便且高效。 2. 匿名性&#xff1a;网上交友可以实现匿名性&#xff0c;用户可以匿名地搜索、聊天或交换信息&#xff0c;保护个人隐私和安全。 3.…

COOIS 生产订单显示系统增强

需求说明&#xff1a;订单系统显示页面新增批量打印功能 增强点&#xff1a;CL_COIS_DISP_LIST_NAVIGATION -->TOOLBAR方法中新增隐式增强添加自定义打印按钮 增强点&#xff1a;BADI-->WORKORDER_INFOSYSTEM新增增强实施 实现位置&#xff1a;IF_EX_WORKORDER_INFOSYS…

制造型企业 如何实现便捷的机台文件统一管理?

机台文件统一管理&#xff0c;这是生产制造型企业都需要去做的&#xff0c;机台文件需要统一管理的原因主要包括以下几点&#xff1a; 1、提高效率&#xff1a;统一管理可以简化文件的访问和使用过程&#xff0c;提高工作效率&#xff0c;尤其是在需要频繁访问或更新机台文件的…

MySQL中什么情况下会出现索引失效?如何排查索引失效?

目录 1-引言&#xff1a;什么是MySQL的索引失效&#xff1f;(What、Why)1-1 索引失效定义1-2 为什么排查索引失效 2- 索引失效的原因及排查&#xff08;How&#xff09;2-1 索引失效的情况① 索引列参与计算② 对索引列进行函数操作③ 查询中使用了 OR 两边有范围查询 > 或 …

USB设备的音频类UAC

一、UAC简介 UAC&#xff08;USB Audio Class&#xff09;是USB设备的音频类&#xff0c;它定义了USB音频设备与主机计算机通信的方式。UAC标准是USB规范的一部分&#xff0c;并受到各种操作系统&#xff08;包括Windows、macOS和Linux&#xff09;的支持。 UAC是基于libusb,实…

图像在神经网络中的预处理与后处理的原理和作用(最详细版本)

1. 问题引出及内容介绍 相信大家在学习与图像任务相关的神经网络时&#xff0c;经常会见到这样一个预处理方式。 self.to_tensor_norm transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))]) 具体原理及作用稍后解释&…

java8 Stream流常用方法(持续更新中...)

java8 Stream流常用方法 1.过滤数据中年龄大于等于十八的学生2.获取对象中其中的一个字段并添加到集合(以学生姓名&#xff08;name&#xff09;为例)3.获取对象中其中的一个字段并转为其他数据类型最后添加到集合(以学生性别&#xff08;sex&#xff09;为例&#xff0c;将Str…

Apache Doris 2.x 版本【保姆级】安装+使用教程

Doris简介 Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff0c;仅需亚秒级响应时间即可返回海量数据下的查询结果&#xff0c;不仅可以支持高并发的点查询场景&#xff0c;也能支持高吞吐的复杂分析场景。基于…

【论文速读】|大语言模型(LLM)智能体可以自主利用1-day漏洞

本次分享论文&#xff1a; LLM Agents can Autonomously Exploit One-day Vulnerabilities 基本信息 原文作者&#xff1a;Richard Fang, Rohan Bindu, Akul Gupta, Daniel Kang 作者单位&#xff1a;无详细信息提供 关键词&#xff1a;大语言模型, 网络安全, 1-day漏洞, …

Redisson分布式锁 --- 源码分析

1.获取一把锁 RLock lock redissonClient.getLock("订单lock"); 2.业务代码加锁 lock.lock(); 2.1 lock.tryAcquire Long ttl tryAcquire(leaseTime, unit, threadId); 2.2 lua脚本: tryLockInnerAsync方法 如果获取锁失败&#xff0c;返回的结果是这个key的剩…

MMSeg搭建模型的坑

Input type(torch.suda.FloatTensor) and weight type (torch.FloatTensor) should be same 自己搭建模型的时候&#xff0c;经常会遇到二者不匹配&#xff0c;以这种情况为例&#xff0c;是因为部分模型没有加载到CUDA上面造成的。 注意搭建模型的时候&#xff0c;所有层都应…

【氮化镓】液态Ga在GaN(0001)和(0001̅)表面上的三维有序排列随温度的变化

文章标题是《Temperature dependence of liquid-gallium ordering on the surface of epitaxially grown GaN》&#xff0c;作者是Takuo Sasaki等人&#xff0c;发表在《Applied Physics Express》上。文章主要研究了在分子束外延(MBE)条件下&#xff0c;液态镓(Ga)在GaN(0001)…

WCH RISC CH32V303RCT6 单片机的SDI Printf 虚拟串口功能 类似RTT打印功能 简单分析

参考&#xff1a; 有关于 SDI printf 更多的信息和资料吗&#xff1f; 关于 CH32 系列 MCU SDI 虚拟串口功能的使用 【CH32X035 评估板测评】 教你使用 SDI 接口重定向 printf 0.前言 有段时间没有看CH32V单片机的开发了&#xff0c;今天帮新来的同事调试时候看到debug.c里面有…

java-spring 06 图灵 getBean方法和 doGetBean方法

01.一般的流程是&#xff0c;这里是从上一章的preInstantiateSingleton方法顺序过来的。 getBean() -> doGetBean() -> createBean() -> doCreateBean() -> createBeanInstance() -> populateBean() -> initializeBean() 02.getBean方法&#xff0c;一般就…

项目十:学会python爬虫数据保存(小白圆满级)

前言 上篇我们学会的文本文件、csv文件和excel文件的相关基础知识和操作&#xff0c;这一次我们再来了解一下四个文件操作方式 存储方法 HTML文件 将数据保存为HTML格式&#xff0c;可以直接在浏览器中查看。 使用字符串拼接将数据保存为HTML格式。 代码案例 # 创建数据…

Cookie、Session以及Token的区别

Cookei、Session以及Token总的来说都是为了实现客户端访问服务器数据而利用的一种手段&#xff0c;可以把服务器数据看成是密码箱&#xff0c;而它们是三种不同的钥匙。 一、定义 1.Cookie 客户端第一次访问服务器时&#xff0c;服务器返回cookie给客户端A&#xff0c;客户端…

ABTest如何计算最小样本量-工具篇

如果是比例类指标&#xff0c;有一个可以快速计算最小样本量的工具&#xff1a; https://www.evanmiller.org/ab-testing/sample-size.html 计算样本量有4个要输入的参数&#xff1a;①一类错误概率&#xff0c;②二类错误概率 &#xff08;一般是取固定取值&#xff09;&…

第 394 场 LeetCode 周赛题解

A 统计特殊字母的数量 I 哈希&#xff1a;遍历然后枚举 class Solution {public:int numberOfSpecialChars(string word) {unordered_map<char, int> m;for (auto ch : word)m[ch] 1;int res 0;for (char ch a; ch < z; ch)if (m.count(ch) && m.count(A …