2022.7.3更新:分享一下文件自取
链接:https://pan.baidu.com/s/1oEu-XXo5MCHzxKD0NWpjbA
提取码:4a42
以下为原文:
前几天买了个0.96寸的OLED显示屏,商家说支持I2C和SPI通信,就想着应该比较简单,于是兴冲冲准备大干一场。然而发现好像并没有想象中那么简单,还是有很多要点需要了解,下面细说。
首先拿到SSD1306后,接上GND,VCC,SCL,SDA四条线后,屏幕是不会自动亮起的,需要CPU写入数据驱动它显示。查阅SSD1306的手册,我们可以发现与其通信的方式。在这里我们使用的是I2C通信方式:
由于一开始我们需要初始化SSD1306,因此我们需要先写入一些控制字来命令SSD1306的工作模式。鉴于指令集网上的中文手册中已有许多资料,这里不再赘述,只简单介绍一些用到的指令。
OLED_WR_Byte(0xAE,OLED_CMD);//开启OLED屏显示
以上是开启OLED屏关闭的命令,0xAE是控制字,OLED_CMD是写入命令。以下同理,便不再赘述
OLED_WR_Byte(0x00,OLED_CMD);//设置列起始地址的高位
OLED_WR_Byte(0x10,OLED_CMD);//设置列起始地址的低位
OLED_WR_Byte(0xB0,OLED_CMD);//设置目标显示位置页的起始地址
这三行代码用来控制显示的位置。这里就必须提一下SSD1306的内存地址模式,因为不同的模式下写入相同的数据输出的图像不同。SSD1306中有三种不同的内存地址模式:页地址模式,水平地址模式,垂直地址模式。
可以看到,这是128*64的显示屏,其中横向分为128段(列),竖向分为8页,每页有8行。选择页地址模式后,数据的填充如下所示:
在用户定义的地址写入数据后,列地址会自动加1,写完最后一列后,列地址指针会重置为列开始地址,而页地址不会变。至此,我们就很好理解上述的三行命令,其中0x00和0x10设置了列起始地址为0,而0xb0则设置了页起始地址为0。
OLED_WR_Byte(0x81,OLED_CMD); //设置显示的对比度(00H~FFH)
OLED_WR_Byte(0xFF,OLED_CMD);//对比度为256
OLED_WR_Byte(0xA1,OLED_CMD);/设置Segment重映射
OLED_WR_Byte(0xA6,OLED_CMD);//设置正常显示
OLED_WR_Byte(0xA8,OLED_CMD);//设置复用率(16~63)
OLED_WR_Byte(0x3F,OLED_CMD);//设置复用率为63
OLED_WR_Byte(0xC8,OLED_CMD);//设置COM输出扫描方向
OLED_WR_Byte(0xD3,OLED_CMD);//设置显示偏移
OLED_WR_Byte(0x00,OLED_CMD);//偏移量为0
OLED_WR_Byte(0xD5,OLED_CMD);//设置时钟分频率(1~16),振荡器频率
OLED_WR_Byte(0x80,OLED_CMD);//0~3位为时钟分频率(1),4~7位为振荡器频率(1000)
OLED_WR_Byte(0xD8,OLED_CMD);//set area color mode off
OLED_WR_Byte(0x05,OLED_CMD);//
OLED_WR_Byte(0xD9,OLED_CMD);//设置重充电周期
OLED_WR_Byte(0xF1,OLED_CMD);//
OLED_WR_Byte(0xDA,OLED_CMD);//设置COM引脚硬件配置
OLED_WR_Byte(0x12,OLED_CMD);//
OLED_WR_Byte(0xDB,OLED_CMD);//设置Vcomh取消选择水平
OLED_WR_Byte(0x30,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//set charge pump enable
OLED_WR_Byte(0x14,OLED_CMD);//
OLED_WR_Byte(0xAF,OLED_CMD);//打开OLED显示
以上我们就把OLED初始化完成了。接下来我们可以开始写显示函数了。
void OLED_Set_Pos(unsigned char x, unsigned char y)
{ OLED_WR_Byte(0xb0+y,OLED_CMD);//设置目标显示位置页起始地址
OLED_WR_Byte(((x&0xf0)>>4)|0x10,OLED_CMD);//设置列起始地址高位
OLED_WR_Byte((x&0x0f),OLED_CMD);//设置列起始地址低位
} //起始列为x
这里我们先写一个设置坐标的函数,方便后面调用。
1.显示一个字符的函数如下:
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size)
{ //8x16规格
unsigned char c=0,i=0;
c=chr-' ';//得到偏移后的值
//' '为ASCII码第32个
if(x>Max_Column-1)
{
x=0;y=y+2;//如果x大于最大列,则置0,y+2?
}
if(Char_Size ==16)
{
OLED_Set_Pos(x,y);//起始列为x,起始页为y
for(i=0;i<8;i++)
{
OLED_WR_Byte(F8X16[c*16+i],OLED_DATA);//第c*16行前半行的数据
}
OLED_Set_Pos(x,y+1);//换下一页
for(i=0;i<8;i++)
{
OLED_WR_Byte(F8X16[c*16+i+8],OLED_DATA);//后半行数据
}
}
else {
OLED_Set_Pos(x,y);
for(i=0;i<6;i++)
OLED_WR_Byte(F6x8[c][i],OLED_DATA);
}
}
由于规格为8*16,因此我们需要16个16进制数来表示一个字符,表的一部分如下:
其中最后一个十进制数为所在数组中的第几行,即字符减去偏移量32后的十进制序号。
上图为SSD1306页地址模式下单个字符的写入模式。
2.显示一个汉字的函数如下:
void OLED_ShowCHinese(u8 x,u8 y,u8 no)
{ //16*16
u8 t,adder=0;
OLED_Set_Pos(x,y);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no][t],OLED_DATA);//字模数组
adder+=1;
}
OLED_Set_Pos(x,y+1);
for(t=0;t<16;t++)
{
OLED_WR_Byte(Hzk[2*no+1][t],OLED_DATA);
adder+=1;
}
}
可以看到,显示汉字与显示字符的方式大同小异,只是汉字为16*16的规格。
利用取模工具生成汉字的字模,再利用以上函数,就可以显示汉字。
3.如果要显示BMP图片,也需要用取模工具取模,再利用函数即可实现。
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;
if(y1%8==0) y=y1/8;
else y=y1/8+1;
for(y=y0;y
以上基本就可以实现SSD1306的显示功能,当然,有很多汉字和图片需要自己用取模工具(例如PCtoLCD2002)去取模。
以上就是SSD1306的显示功能的实现过程,不足之处,还望担待。