1、SSD1306
SSD1306是一个单芯片的CMOS OLED/PLED驱动器,有着操纵微小的发光二极管组成的矩阵面板而展示图像的控制系统。包含着128
个segments
和64
个commons
,是为共阴OLED 面板设计的IC芯片。
以上就是SSD1306
文档的第一段话的大概意思,OLED 12864
是一个密度较大的LED点阵,和点阵模块的学习一样,都是在学习一个驱动器,但这个比MAX2719
牛逼的多。
它有五种与MCU交互的方式,但主要用到的只有SPI
和I2C
两种,我现在手上也只有I2C
的,但这个只是数据传输的方式而已,目的都是给这个昵称叫SSD1306
的好友发同样的信息,只要连接得到,用微信和QQ都是OK的。
2、I2C
直接跳到I2C
部分。
2.1 I2C写模式
SSD1306是不用读模式的,所以单独写个I2C写模式
的驱动就可以操作这个OLED屏了。
2.2.1 i2c起始信号
主机初始化I2C必须要发个起始信号给从机。起始信号的产生必须是在SCL
为高电平的情况下,下拉SDA
产生下降沿。
/**
* 起始信号
*/
void i2cStart(){
// 上拉SCL
SET_SCL();
// 拉低SDA,产生起始信号
CLR_SDA();
// 然后上拉回去,准备发信息
SET_SDA();
}
/**
* 终止信号
*/
void i2cStop(){
// 下拉SDA
CLR_SDA();
// SCL保持高电平
SET_SCL();
// 上拉SDA产生终止信号
SET_SDA();
}
2.2.2 从机地址
从机地址是由七个位组成的,如上图框起来的高七位,这里说明了SSD1306的从地址高六位是固定的,只有第七位受SA0
控制,如果这个位为逻辑0,地址则是0x78
,如果为逻辑1则是0x7A
。一般情况下看一下背面就可以了,厂家一般会默认选择搞成0x78
。
这样一来,理论上一个MCU是可以控制两个OLED实现"双屏"的。但我没有试过。然后回去看从机地址最后紧跟着的一个R/W#
位,表示读写模式,其中写模式对应的是逻辑"0"。
2.2.3 i2c的应答位
每次发送了一个字节的数据都会有一个时钟用于接收应答位,地址位+读写位就是一个字节,所以也是会有一个应答位的。但是说明文档里面也说了,不用读模式的情况下,应答位只要根据下图的模式把它忽略掉就可以,反正没什么意义。
/**
* 处理应答位
*/
void i2cIgnoreAck(){
// 上拉SCL,传输数据
SET_SCL();
// 下拉SCL,为下一次数据传输或终止信号服务
CLR_SCL();
}
2.2.4 i2c发送数据
/**
* 发送一个位
*/
void i2cSendBit(unsigned char bit){
// 产生信息
if(bit==0){
CLR_SDA();
}
else{
SET_SDA();
}
// 发送信息
SET_SCL();
// 下拉SCL,为下一次数据传输和终止信号服务
}
/**
* 发送一个字节
*/
void i2cSendByte(unsigned char dat){
unsigned char i;
// 下拉SCL
CLR_SCL();
for( i = 0; i < 8; i++ ){
i2cSendBit((dat<<i)&0x80);
}
}
2.2.5 Command Decoder
这个是SSD1306负责分析接口传输的信息的解析器,发送完地址和读写位之后,还要跟着发出一位Co
位和一个D/C#
位,再接着六个逻辑0填满一个字节。
对Co位,如果它为逻辑0,则会认为接下来要传输的信息只包含字节数据。而对于D/C#,当该位为逻辑0时会被认为本次传输的信息是指令信息,反之则是数据信息。数据信息是会存进GDDRAM
的,SDD1306包含着128*64
个比特的缓存区,刚好可以控制128*64个LED,也就是一整屏的数据。
每写完一次数据,GDDRAM
的指针就会自动后移一次,比如在屏幕的左边开始写入数据,则不用为每一个数据都设置坐标指令信息,它会像光标一样自动后移的,甚者,它还能自动换行,如果需要的话。
3、SSD1306工作机制
3.1 FR同步信号
传说中的抗锯齿?一开始显示第一帧的图像的速度是取决于单片机的,毕竟单片机数据写的慢了,SSD1306也只能"粉刷"式显示,即一行一行或一列一列刷新下去。如果单片机能够在一个OLED把缓冲的帧数据更新到屏幕的时间内完成一次写入,那就会被归类于快机
。如果不行,那就给它刷新两帧的时间,并且归类于慢机
,还不行的话它也没辙了。
对于快机
,它应该要在FR同步信号
产生上升沿的时候开始把帧数据写入ram中,并且它下次产生上升沿之前完成一帧的读写,然后给停下来,它产生上升沿时再写,这样可以确保帧数据写入时不会影响到屏幕刷新的帧画面。
而对于慢机
,则要在第一次FR
上升沿时进行写入,而在第三次上升沿之前写完一帧数据。不然屏幕就得告诉它:自求多福
。
3.2 SSD1306复位
RES#
引脚为低电平时,SSD1306就会复位成以下的状态:
显示(display)状态关闭。 初始化为 128 x 64 显示模式。 SEG0
、COM0
映射的两个零地址,并且指针也复位到这两个地址。串口交互寄存器的数据清空。 COM输出的方向为缺省的扫描方向。 对比度寄存器的值设为 7FH
。切换为缺省的展示模式( A4F
指令)。
2.2.8 Segment/Common驱动器
Segment
翻译下来就是段
,这个段驱动器将OLED的面板切割成128个段,每个段都有一个电流源去驱动OLED面板。电流源的强度可以用一个八位的寄存器在0~100uA
的范围内进行调整,也就是说,电流的大小被化分成256
个等级,也可以对应着256个亮度等级。
然而一个发光二极管要工作,光有电流是没用的,负载直接与电流源连接是不会工作的。电流、负载有了,差个电压,就是Common驱动器了。
在抽象的地方想,Segment
和Common
可以想象成一个直角坐标系,每一个坐标点都会对应一个像素点(或发光二极管),只要是Segment和Common对应的坐标上的负载,就是点亮的二极管(或有颜色的像素点)。
3.4 图像数据缓存区(GDDRAM)
这个中文名是我起的,毕竟好理解一点。正名叫图形显示数据随机存储器
(机翻Graphic Display Data RAM)。我们给SSD1306写入的帧数据都应该写入这个随机存储器中。
segment(SEG
)和common(COM
)从这里映射出来的,但这两个东西映射的都是一个个比特,而存储器是按字存储的东西,最小的存储单元也应该是字节才对。于是就引出了一个新的概念:页(Page)
。每一个页由8个COM
组成,即128 x 64 比特
的矩阵映射成了 128 x 8 字节(或页)
的矩阵。
再来看OLED面板:
一个128 x 64的矩阵面板被划分成了127个段
,每一个段都有公共的8个页
,每一页都包含着8个公共端(COM)
。每当有一个字节的数据被写入缓冲区的某一页时,页内八个公共端都会上电,然后段指针寄存器所指向的对应列就会从地位到高位写入该字节的数据。
如上图,假设有一个字节的数据要写入PAGE2
页,并且当前的"光标"就在SEG1
上,那么该数据就会在这个PAGE2
内的SEG1
列自上向下写入。
SSD1306也支持自下向上写入,有两种方法:第一种方法就是坐标重新反方向重映射(re-mapping
,后面会说),这样SEG0
就会在最右边且COM0
在最下面;第二种方法就是直接把屏幕倒过来。
4、点亮OLED
SSD1306的文档看到这里就差不多已经是可以停下来了,因为文档后面的内容都是一些指令的说明。指令的话,还是用到什么再查什么比较好。
4.1 指令集
4.1.1 基础指令
Hex | 指令名 | 指令N | 备注 |
---|---|---|---|
81H | 设置对比度控制 | A[] | 实际取值是A[] + 1 |
A4H/A5H | 设置全局显示 | A5H强制全局显示 | |
A6H/A7H | 设置反白显示 | A7H即比特值为0时显示 | |
AEH/AFH | 设置显示开关 | AEH为关闭显示 |
4.1.2 滚动指令
Hex | 指令名 | 指令 | 备注 |
---|---|---|---|
26H/27H | 连续水平滚动 | AEF[]、BCD[2:0] | Hex左/右滚,AEF空字节,BD始末页地址,C帧率 |
29H/2AH | 水平和竖直连续滚动 | ABCD[2:0]、E[5:0] | Hex左/右滚+竖直,指令同上,E是竖直滚动偏移 |
NA | 没有单独连续竖直滚动的指令 | ||
2EG/2FH | 滚动开关 | 启动/关闭滚动 | |
A3H | 垂直滚动区域 | A[5:0]、B[6:0] | A是固定区域的顶行号,B是行数 |
4.1.3 地址设置指令
Hex | 指令名 | 指令 | 备注 |
---|---|---|---|
00H~0F | 设置起始列地址的低位 | 只在页地址模式有效 | |
10H~1F | 设置起始列地址的高位 | 只在页地址模式有效 | |
20H | 设置存储地址的模式 | A[1:0] | 水平、竖直、页、[无效],四种,复位页模式 |
21H | 设置列地址 | AB[6:0] | 设置列地址的始末区域,在水平/竖直模式有效 |
22H | 设置页地址区域 | AB[2:0] | 设置页地址的始末区域,在页地址模式有效 |
B0H~B7H | 设置页地址 | 只在页地址模式有效,刚好对应八页 |
4.1.4 硬件配置指令
Hex | 指令名 | 指令 | 备注 |
---|---|---|---|
40H~7FH | 设置显示的起始行 | 总共64行 | |
A0H/A1H | 设置段(SEG)重映射 | A1H是列翻转 | |
C0H/C8H | 设置COM输出扫描方向 | C8H是行反转 | |
注意两个同时反转才是把屏幕倒过来 | |||
A8H | 设置多路复用率 | A[5:0] | 起始也就是显示几行,最低设置15,对应16行 |
D3H | 设置显示偏移 | A[5:0] | 图像竖直向上偏移,复位是00H |
DAH | 设置COM引脚硬件配置 | A[5:4] | 复位是12H,加上重映射总共八种玩法,太花了 |
4.1.5 其它的配置
Hex | 指令名 | 指令 | 备注 |
---|---|---|---|
D5H | 设置时钟倍频和振荡频率 | A[] | 高位振荡频率低位分频,复位80H |
D9H | 设置预充电周期 | A[] | 同步亮度的,复位22H |
DBH | 设置COM的反压 | A[6:4] | 复位是20H |
8DH | 电荷泵设置 | A[2] | 复位是0B,启用电荷泵需要配置成1B |
OLED面板的驱动电压就是靠这个泵上去的 | |||
E3H | NOP | 空指令 |
整理好了,需要用的时候参考这个表格就可以做大部分配置了。
4.2 写指令
前面的SSD1306复位
的内容提到,OLED默认是关掉显示的,所以要点亮这个OLED屏,得给它写入开显示屏的指令。所以首先得封装一个发送指令的函数:
/**
* 发送一个指令
*/
void oledSendCmd(unsigned char cmd){
// 启动I2C
i2cStart();
// 把地址和R/W#位一起发过去
i2cSendByte(I2C_ADDR_W);
i2cIgnoreAck();
// 告诉指令解析器,接下来的是一个指令
i2cSendByte(0x00);
i2cIgnoreAck();
i2cSendByte(cmd);
i2cIgnoreAck();
i2cStop();
}
4.2 点亮OLED
最简单的点亮方法就是直接启动和点亮OLED面板
两个指令。
// 启动OLED面板
oledSendCmd(0x8D);
oledSendCmd(0x14);
// 点亮OLED
oledSendCmd(0xAF);
这样的话亮的其实也不平均,反正什么数据都没写入,干脆就把整个屏点亮!
// 全局显示
oledSendCmd(0xA5);
5、 显示一张图片
默认是页模式,所以全屏图片的数组数据应该是128x8 = 1024
长度。
/**
* 显示一张128x8的图片
*/
void oledShowFullImg(){
unsigned int i = 0;
unsigned char j, k;
for( j = 0; j < 8; j++ ){
oledSetPos(0, j);
for( k = 0; k < 128; k++ ){
oledSendDat(~yize_image[i++]);
}
}
}
main.c
#include "delay.h"
#include "oled.h"
// 这里是图片数组,但因为太多了,放到末尾了
/**
* 显示一张128x8的图片
*/
void oledShowFullImg(){
unsigned int i = 0;
unsigned char j, k;
for( j = 0; j < 8; j++ ){
oledSetPos(0, j);
for( k = 0; k < 128; k++ ){
oledSendDat(~image[i++]);
}
}
}
int main(void){
delay_init();
oledInit();
delay_ms(800);
// 屏幕倒转
oledSendCmd(0xA1);
oledSendCmd(0xC8);
oledSendCmd(0x8D);
oledSendCmd(0x14);
oledSendCmd(0xAF);
oledShowFullImg();
while(1);
}
5.1 图片数组数据
unsigned char image[1024]={
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,127,31,15,7,7,39,63,63,63,63,63,63,63,63,63,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,127,31,15,7,7,39,63,63,63,63,63,63,63,63,63,255,255,255,255,255,255,255,255,255,255,255,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,255,255,255,255,255,255,255,15,15,15,143,143,143,143,143,143,15,15,15,143,143,143,143,143,143,15,15,15,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,231,199,195,1,0,0,228,230,231,231,231,231,7,7,3,225,224,224,228,230,231,7,7,7,255,255,255,255,255,255,231,199,195,1,0,0,228,230,231,231,231,231,7,7,3,225,224,224,228,230,231,7,7,7,255,255,255,255,255,255,255,255,255,255,255,255,255,127,63,31,15,3,1,0,16,252,126,63,127,127,255,255,255,255,255,255,255,255,255,255,255,255,127,64,64,64,68,68,68,68,68,68,0,0,0,68,68,68,68,68,68,64,64,64,127,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,0,0,0,24,24,24,24,24,24,0,0,0,24,24,24,24,24,24,0,0,0,255,255,255,255,255,255,255,255,255,0,0,0,24,24,24,24,24,24,0,0,0,24,24,24,24,24,24,0,0,0,255,255,255,255,255,223,159,15,135,131,195,225,240,240,248,252,254,255,0,0,0,255,254,252,248,248,240,225,195,131,135,143,207,255,255,255,255,255,0,0,128,198,70,70,102,230,230,38,32,32,96,230,230,224,192,2,14,30,0,0,192,255,255,255,255,255,255,255,255,
255,255,255,255,255,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,255,255,255,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,231,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,128,128,128,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,231,193,224,248,254,255,240,192,192,199,207,207,206,204,204,206,207,199,192,224,241,254,248,240,224,227,231,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
};