OLED,即有机发光二极管(Organic Light-Emitting Diode),又称为有机电激光显示(Organic Electroluminesence Display, OELD)。OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。
OLED跟LCD不同,LCD需要背光才能点亮,而OLED不需要背光,因为它是自发光。因为技术的原因,目前OLED不能做得很大,目前常见的OLED尺寸有0.96、1.3、1.54和2.42寸。本节实验将会使用2.42寸、分辨率为128*64的OLED做实验。
通常OLED内部都会一块OLED的驱动芯片,驱动芯片的作用是接收用户发送的数据,将数据进行管理,最后将数据显示到OLED上。常见的OLED驱动芯片有SSD1306、SSD1309等,操作OLED实际就是操作OLED的驱动芯片。本次实验用的驱动芯片是SSD1309。
引脚 | 作用 |
---|---|
CS | 片选信号,低电平有效 |
RES | 复位信号 |
D/C | 数据、命令引脚。低电平为写命令、高电平为写数据 |
R/W | MCU接口的读写控制输入引脚 |
E | MCU接口输入 |
D7~D0 | 并行传输接口 |
BS2~BS0 | OLED通信接口选择 |
SSD1309支持并行通信和串行通信,具体的通信方法选择是通过BS2~BS0引脚进行选择。
BS[2:0] | 接口 |
---|---|
000 | 4线SPI |
001 | 3线SPI |
010 | I2C |
110 | 8-bit 8080接口 |
100 | 8-bit 6800接口 |
将BS[2:0]设置为000即可进入4线SPI模式,在4线SPI模式下,需要7根通信引脚
引脚 | 作用 |
---|---|
RES | 复位信号 |
D/C | 写数据、写命令 |
CS | 片选 |
D0 | 用作SCL |
D1 | 用作SDA |
VCC | 电源 |
GND | 地线 |
将BS[2:0]设置为001即可进入3线SPI模式,在4线SPI模式下,需要6根通信引脚
引脚 | 作用 |
---|---|
RES | 复位信号 |
CS | 片选 |
D0 | 用作SCL |
D1 | 用作SDA |
VCC | 电源 |
GND | 地线 |
3线SPI模式跟4线SPI模式非常相似,主要区别是3线SPI模式下没有D/C信号线,D/C信号是以数据的形式发送出去。
将BS[2:0]设置为010即可进入I2C模式,在I2C模式下,需要5根通信引脚
引脚 | 作用 |
---|---|
RES | 复位信号 |
SCL | 时钟线 |
SDA | 数据线 |
VCC | 电源 |
GND | 地线 |
凡是涉及到I2C通信,从机设备都要确定好地址,SSD1309的地址设置如下
A6 | A5 | A4 | A3 | A2 | A1 | A0 | R/W |
---|---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 1 | 0 | SA0 | 1/0 |
SA0为地址选择信号,由D/C信号线决定,也就是说一个I2C总线上可以接2个OLED设备。
使用I2C通信需要注意控制字节的配置字。控制字节的高6位是D/C信号,bit为0代表发送命令,bit为1代表发送数据。
控制字节的高7位是设置是否需要连续发送显示数据,其目的是让OLED快速刷屏。我们通过上图的通信协议知道,当我们每次需要发送一个字节时,都需要开启一个完整的I2C通信,如果需要大量数据刷屏时就很浪费时间,当Co为0且D/C为1时,则开启连续发送数据模式,只需启动一个I2C通信,就可以连续发送数据,直到停止。
将BS[2:0]设置为110即可进入8080模式,在8080模式下,需要15根通信引脚
引脚 | 作用 |
---|---|
RES | 复位信号 |
CS | 片选 |
D/C | 写命令、写数据信号 |
RD | 读数据信号,低电平有效 |
WR | 写数据,低电平有效 |
D7~D0 | 并行数据口 |
VCC | 电源 |
GND | 地线 |
将BS[2:0]设置为100即可进入8080模式,在6800模式下,需要15根通信引脚
引脚 | 作用 |
---|---|
RES | 复位信号 |
CS | 片选 |
D/C | 写命令、写数据信号 |
RW | 读写信号 |
D7~D0 | 并行数据口 |
E | 使能信号 |
VCC | 电源 |
GND | 地线 |
OLED本身是没有存储显示数据的功能,这个功能是由SSD1309提供,SSD1309内部提供了一块显存区域,其分辨率为128 * 64,这个分辨率也就是OLED的分辨率。意思是这块OLED最多可以显示128 * 64位的数据,也就是1024个字节,长度最大为128,宽度最大为64。
SSD1309内部的显存大致如下分布。
SSD1309内部的显存可以划分成128 * 64个格子,每个格子代表一个bit。SSD1309又将这些格子进一步划分,按照8列为一组,分成了8页,每页大小为128 * 8个格子,也就是128字节为一页。
我们拿其中一页作为例子,来说一下在SSD1309内部数据是怎样存放的。我们需要知道的是在SSD1309内部,所有的数据都是按照列来存放,假设我们在PAGE0的(0,0)这个位置写入一个数据0xAC,那么其存储格式如下。最低位在前,高位在后。
针对SSD1309的显存存储结构,MCU每次修改数据的时候都需要将SSD1309的显存数据读出来,确保旧的数据跟新的数据没有冲突,但是这就有一个问题,每次修改数据的数据都需要读显存数据,这样就很浪费时间。所以在实际应用中,一般都会在MCU中定义一个128*64bit大小的二维数组,这个数组可以看做是SSD1309的外部显存,每次需要更新数据时,都会更新这个外部显存,最后再通过命令把整个显存数据刷新到SSD1309内部。
由于SSD1309内部是没有字库的,所以我们需要自己制作字库文件,这样OLED才能显示图片、字符等。字库的制作软件PCtoLCD2002,软件大家可以自行到网上进行下载。
我们需要制作常用的ASCII码字符,需要制作的字符如下,第一个ASCII码为空格
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
在生成字符前先选择字符的高度和宽度,然后将需要生成的字符复制到输入框内,点击生成字模,最后点击保存字模,生成后的字模就会保存到文件中。
本节例程中用到的OLED是128 * 64分辨率的OLED,通信协议是4线SPI协议。
void oled_write_byte(unsigned char byte,unsigned char type)
{
unsigned char i;
GPIO_ResetBits(OLED_CS_PIN_PORT,OLED_CS_PIN); // CS 拉低
if(OLED_WRITE_CMD == type)
GPIO_ResetBits(OLED_DC_PIN_PORT,OLED_DC_PIN); // 写命令 拉低DC
else
GPIO_SetBits(OLED_DC_PIN_PORT,OLED_DC_PIN); // 写数据 拉高DC
for(i = 0;i < 8;i++) // 传输数据
{
GPIO_ResetBits(OLED_SCL_PIN_PORT,OLED_SCL_PIN); // 拉低SCL
if(byte & 0x80)
GPIO_SetBits(OLED_SDA_PIN_PORT,OLED_SDA_PIN);
else
GPIO_ResetBits(OLED_SDA_PIN_PORT,OLED_SDA_PIN);
GPIO_SetBits(OLED_SCL_PIN_PORT,OLED_SCL_PIN); // 拉高SCL
byte <<= 1; // 数据左移一位
}
GPIO_SetBits(OLED_CS_PIN_PORT,OLED_CS_PIN); // 拉高CS
GPIO_SetBits(OLED_DC_PIN_PORT,OLED_DC_PIN); // 拉高DC
}
在开始传输数据前,要先把CS信号拉低,代表选中OLED,然后根据是写命令还是写数据,改变D/C信号电平,在SCL的上升沿的时候把数据按照高位在前,低位在后依次发送给OLED,最后拉高CS和D/C信号。
程序的实现都是按照数据手册的时序要求进行设计。
void oled_drawpoint(unsigned char x,unsigned char y,unsigned char t)
{
u8 i,m,n;
i=y/8; // 判断是第几页
m=y%8; // 判断是第几位
n=1<<m;
if(t){OLED_GRAM[x][i]|=n;} // 如果是1就直接写数据
else
{ // 如果是0,就清0
OLED_GRAM[x][i]=~OLED_GRAM[x][i];
OLED_GRAM[x][i]|=n;
OLED_GRAM[x][i]=~OLED_GRAM[x][i];
}
}
在往GRAM写数据前,先判断该点位于GRAM的页数,然后再判断该点是第几个bit,最后再根据是否需要点亮该bit进行写1或者清0操作。
static unsigned char OLED_GRAM[128][8];
//更新显存到OLED
void oled_refresh(void)
{
u8 i,n;
for(i=0;i<8;i++)
{
oled_write_byte(0xb0+i,OLED_WRITE_CMD); //设置页
oled_write_byte(0x00,OLED_WRITE_CMD); //设置低列起始地址
oled_write_byte(0x10,OLED_WRITE_CMD); //设置高列起始地址
for(n=0;n<128;n++)
oled_write_byte(OLED_GRAM[n][i],OLED_WRITE_DATA);
}
}
程序会先定义一个128*8的二维数据,数据类型为unsigned char类型,也就是1024个字节,用户更新完数据后,再将这个GRAM刷新到SSD1309内部的GRAM中,从而实现了更新OLED的目的。
由于例程过大,请在百度网盘下载
链接:https://pan.baidu.com/s/1ZjxPKjeIG1PyOVn7OHIjeQ
提取码:z59g
因本人水平有限,错误和疏漏之处在所难免,欢迎指正。