文章目录
SSD1306 介绍
SSD1306 是一款 OLED 驱动芯片,可以驱动 64*128px 的OLED屏幕。允许主机使用 I2C、6800、8080、4线SPI、3线SPI 方式进行通信,为 BS[2:0] 设置不同的电平以实现通信方式的选择,本文介绍的是采用硬件 I2C 的通信方式,。
I2C 通信流程
上图所示为 I2C 通信,将连续的 Control byte 与 Data byte 称为一组有效信息,可以看到,发出地址字节后,主机必须发送至少1次的有效信息。
I2C 通信地址
SSD1306 的 I2C 地址中 [1:7] 位为 0111100 或 0111101,第1位代表进行读或写操作,由于 OLED 为一输出设备,故第1位应固定为0,所以SSD1306的两种 I2C 地址位 0x78 和 0x7A。
有效信息
一个 Control byte 与一个 Data byte 构成一组有效信息,其中 Control byte 为:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
Co | D/C# | 0 | 0 | 0 | 0 | 0 | 0 |
-
Co位被设置为逻辑“0”,表示其后会传输一位 Data byte
-
D/C位决定将后一位 Data byte 解释为命令或数据。如果D/C位是“0”,则将 Data byte 定义为命令。若为“1”,则将 Data byte 定义为数据。
使用硬件 I2C 通信
SSD1306 的一次有效通信中应至少包含一组有效信息,故可简化为,每次与 SSD1306 通信均只发送一组有效信息。则这次通信中共发送三个字节,分别是 地址 、 Control byte 、 Data byte。故可以使用 HAL 库中提供的 HAL_I2C_Mem_Write 方法,将 Control byte 作为寄存器地址发送,并再发送一字节的数据。其中地址固定为 0x78,Control byte 为 0x40 或 0x00。
SSD1306 结构
SSD1306 将 OLED 屏幕划分成了 8 页,每页中右 8 行、128列,如下图所示:
寻址模式 (内容更新模式)
由于一次有效通信只能写入一字节的数据,只允许更新某一页中某一列共8位的显示内容。倘若在每次更新前指定更新位置,则存在一定的浪费,SSD1306 提供了三种寻址模式,可以连续更新显示内容。
页寻址
页寻址是默认的寻址模式,它具有以下特性:
- 列地址指针每次自动加1,如果列地址指针到达最后一个地址,将会自动复位到开始的地址;
- 页地址不会自动增加,必须手动设置新的页地址;
在这个模式下,设置页地址需要通过以下一个字节实现
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 作用 |
---|---|---|---|---|---|---|---|---|
1 | 0 | 1 | 1 | 0 | X2 | X1 | X0 | 设置页地址 |
发送以上字节,则页地址被设置为 X[2:0],
在这个模式下,设置列地址需要通过以下两个字节实现
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 作用 |
---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | X3 | X2 | X1 | X0 | 设置列地址低四位 |
0 | 0 | 0 | 0 | X7 | X6 | X5 | X4 | 设置列地址高四位 |
连续发送以上两个字节,则列地址被设置为 X[7:0]
水平寻址模式
在水平寻址模式下,列地址指针自动加1,如果列地址指针到达最后一个地址,将会自动复位到开始的地址,同时页地址指针也自动加1。
垂直寻址模式
在垂直寻址模式下,页地址指针自动加1,如果页地址指针到达最后一个地址,将会自动复位到开始的地址,同时列地址指针也自动加1。
在水平和垂直寻址模式下,需要设置页的起始和结束位置、列的起始和结束位置,需发送以下字节:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 作用 |
---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 开始设置列地址 |
* | A6 | A5 | A4 | A3 | A2 | A1 | A0 | 设置起始列地址为 A[6:0],默认为0 |
* | B6 | B5 | B4 | B3 | B2 | B1 | B0 | 设置结束列地址为 B[6:0],默认为127 |
0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 开始设置页地址 |
* | * | * | * | * | A2 | A1 | A0 | 设置起始页地址为 A[2:0],默认为0 |
* | * | * | * | * | B2 | B1 | B0 | 设置结束页地址为 B[2:0],默认为7 |
* 表示此位置对结果不产生影响
设置寻址模式
设置寻址模式需要发送以下字节:
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 作用 |
---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 开始设置寻址模式 |
* | * | * | * | * | * | A1 | A0 | 00b 水平寻址模式;01b 垂直寻址模式;10b 页寻址模式 |
SSD1306 初始化
SSD1306 上电后应首先进行初始化,我将会选择初始化中几个重要步骤进行解释:
关闭显示器
上电第一步应关闭显示器,以避免初始化过程中屏幕上显示寄存器中存留的内容,需发送以下字节
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 作用 |
---|---|---|---|---|---|---|---|---|
1 | 0 | 1 | 0 | 1 | 1 | 1 | X | X=0 显示关;X=1 显示开 |
设置对比度
使用以下字节设置对比度
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 作用 |
---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 开始设置对比度 |
A7 | A6 | A5 | A4 | A3 | A2 | A1 | A0 | 设置对比度为A[7:0],默认为0x7F |
设置整体显示状态
使用以下字节设置显示状态
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | 作用 |
---|---|---|---|---|---|---|---|---|
1 | 0 | 1 | 0 | 0 | 1 | 0 | X | X=0 显示RAM中的内容;X=1 不显示RAM中的内容 |
其他命令需阅读datasheet
https://wenku.baidu.com/view/6d6497a53d1ec5da50e2524de518964bce84d233.html
详细流程
初始化包括很多环节的设置,由于不能确定 MCU 上电时 SSD1306 的状态,所以即使我们需要的状态时默认状态,还是建议重新进行设定。
详细代码如下,可以参照注释进行分析。
OLED_Write_Byte(CmdReg, OLED_OFF); //关闭显示器
OLED_Write_Byte(CmdReg, 0x20); //设置内存寻址模式Set Memory Addressing Mode
// 00,水平寻址模式 01,垂直寻址模式 02,页面寻址模式(默认)
OLED_Write_Byte(CmdReg, 0x01);
OLED_Write_Byte(CmdReg, 0x81); //设置对比度
OLED_Write_Byte(CmdReg, 0xff); //对比度,数值越大对比度越高
OLED_Write_Byte(CmdReg, 0xc8); //扫描方向 不上下翻转Com scan direction
OLED_Write_Byte(CmdReg, 0xa1); //设置段重新映射 不左右翻转set segment remap
OLED_Write_Byte(CmdReg, 0xa8); //设置多路复用比(1-64)
OLED_Write_Byte(CmdReg, 0x3f); //设定值1/32 1/32 duty
OLED_Write_Byte(CmdReg, 0xd3); //设置显示偏移 set display offset
OLED_Write_Byte(CmdReg, 0x00); //
OLED_Write_Byte(CmdReg, 0xd5); //设置osc分区 set osc division
OLED_Write_Byte(CmdReg, 0x80); //
OLED_Write_Byte(CmdReg, 0xd8); //关闭区域颜色模式 set area color mode off
OLED_Write_Byte(CmdReg, 0x05); //
OLED_Write_Byte(CmdReg, 0xd9); //设置预充电期 Set Pre-Charge Period
OLED_Write_Byte(CmdReg, 0xf1); //
OLED_Write_Byte(CmdReg, 0xda); //设置com引脚配置 set com pin configuartion
OLED_Write_Byte(CmdReg, 0x12); //
OLED_Write_Byte(CmdReg, 0xdb); //设置vcomh set Vcomh
OLED_Write_Byte(CmdReg, 0x30); //
OLED_Write_Byte(CmdReg, 0x8d); //设置电源泵启用 set charge pump enable
OLED_Write_Byte(CmdReg, 0x14); //
OLED_Write_Byte(CmdReg, 0xa4); //设置全局显示 bit0,1白,0黑
#ifdef OLED_INVERSE_COLOR
OLED_Write_Byte(CmdReg, 0xa7); //反相显示
#endif
#ifndef OLED_INVERSE_COLOR
OLED_Write_Byte(CmdReg, 0xa6); //正常显示
#endif
OLED_Fill(0x00); //清屏
OLED_Write_Byte(CmdReg, OLED_ON); //打开oled面板 turn on oled panel
SSD1306 驱动
由于 SSD1306 在更新时会将同一页中本列的所有数据都进行更新,当需要显示的数据并没有占满整页的高度时,可能会擦除掉本页中其他行的图像。所以,建立一个缓冲区十分的有必要。
我编写了一个带有缓冲区的驱动,具有以下功能:
- 绘制图象时不受 page 限制,可以于屏幕上的任何坐标绘制。
- 图像布尔运算功能,实现图像的加减。
- 数据暂存缓冲区,统一进行通信,实现局部刷新。
- 计算通信较优方案,使得I2C通信次数较少。