这里写目录标题
一、前言
0.96寸的OLED模块通常是使用 SSD1306 驱动芯片的。这个芯片非常常见,支持I2C或SPI通信等通信方式,通常用于低功耗显示应用,1306芯片手册。
二、OLED介绍
从1306芯片的芯片手册8.1能看到支持很多种通信方式,使用最多的是I2C和SPI方式,也就造就市面上OLED的两种主流型号,4针I2C和7针OLED

GND -> GND
SDA -> I2C数据线(STM32的一个I2C数据引脚,例如PB7)
SCL -> I2C时钟线(STM32的I2C时钟引脚,例如PB6)
2.1.2 SPI接口
7针OLED使用SPI接口如下图
VCC -> 3.3V
GND -> GND
SCL -> SPI时钟引脚
SDA -> SPI MOSI引脚
RES -> 重置引脚(可选,一般连接到一个GPIO口)
DC -> 数据/命令控制引脚(可选)
CS -> 片选引脚(可选)
2.2 OLED原理描述
- 0.96寸OLED显示区域为128*64个像素点,横坐标为X轴一共128个点,纵坐标为Y轴一共64个点,通过点亮对应的像素点达到显示不同图案的效果
/**
* 坐标轴定义:
* 左上角为(0, 0)点
* 横向向右为X轴,取值范围:0~127
* 纵向向下为Y轴,取值范围:0~63
*
* 0 X轴 127
* .------------------------------->
* 0 |
* |
* |
* |
* Y轴 |
* |
* |
* |
* 63 |
* v
*
*/
三、驱动流程
因为市面上大部分的0.96寸OLED都是4针I2C驱动,我们以4针版本讲解如何驱动显示文字,图案。选择STM32f103为例子
1. I2C初始化
I2C协议的原理我不具体描述,我其他博客模拟I2C使用有详细描述过I2C的使用,这里我们需要注意的是使用OLED屏幕作为I2C的从机,我们需要知道从机的地址和指令。
从数据手册能看出来从机地址为0x78(0b0111100)****或者0x7A(0b0111101),地址取决于SA0的数值,SA0的数值通过1306的芯片的D/C引脚接VCC或者GND来决定,根据我使用的情况来看大部分I2C 0.96寸OLED的从机地址都是0x78
后续所有对OLED进行命令的操作都是通过对OLED的从机地址写入指令
#define OLED_CMD 0 //??
#define OLED_DATA 1 //??
void OLED_WR_Byte(u8 dat,u8 cmd)
{
IIC_Start(); //IIC????
IIC_Send_Byte(0x78); //????
IIC_Wait_Ack(); //??ACK
if(cmd) IIC_Send_Byte(0x40); //???
else IIC_Send_Byte(0x00); //???
IIC_Wait_Ack(); //??ACK
IIC_Send_Byte(dat); //????/??
IIC_Wait_Ack(); //??ACK
IIC_Stop(); //IIC??
}
2.OLED初始化
OLED的初始化主要是1306库中的函数去初始化,实现I2C后,按顺序填充CMD,再清屏
下面的代码可以参考下
#include "ssd1306.h"
void OLED_Init(void)
{
delay_ms(500);
OLED_WR_Byte(0xAE,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0x10,OLED_CMD);
OLED_WR_Byte(0x40,OLED_CMD);
OLED_WR_Byte(0x81,OLED_CMD);
OLED_WR_Byte(0xCF,OLED_CMD);
OLED_WR_Byte(0xA1,OLED_CMD);
OLED_WR_Byte(0xC8,OLED_CMD);
OLED_WR_Byte(0xA6,OLED_CMD);
OLED_WR_Byte(0xA8,OLED_CMD);
OLED_WR_Byte(0x3F,OLED_CMD);
OLED_WR_Byte(0xD3,OLED_CMD);
OLED_WR_Byte(0x00,OLED_CMD);
OLED_WR_Byte(0xD5,OLED_CMD);
OLED_WR_Byte(0x80,OLED_CMD);
OLED_WR_Byte(0xD9,OLED_CMD);
OLED_WR_Byte(0xF1,OLED_CMD);
OLED_WR_Byte(0xDA,OLED_CMD);
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);
OLED_WR_Byte(0x40,OLED_CMD);
OLED_WR_Byte(0x20,OLED_CMD);
OLED_WR_Byte(0x02,OLED_CMD);
OLED_WR_Byte(0x8D,OLED_CMD);
OLED_WR_Byte(0x14,OLED_CMD);
OLED_WR_Byte(0xA4,OLED_CMD);
OLED_WR_Byte(0xA6,OLED_CMD);
OLED_WR_Byte(0xAF,OLED_CMD);
OLED_Clear();
OLED_SetCursorAddrese(0,0);
}
3.显示内容
3.1 显示文本字符串
/*在(0, 0)位置显示字符'A',字体大小为8*16点阵*/
OLED_ShowChar(0, 0, 'A', OLED_8X16);
/*在(16, 0)位置显示字符串"Hello World!",字体大小为8*16点阵*/
OLED_ShowString(16, 0, "Hello World!", OLED_8X16);
/*在(0, 18)位置显示字符'A',字体大小为6*8点阵*/
OLED_ShowChar(0, 18, 'A', OLED_6X8);
/*在(16, 18)位置显示字符串"Hello World!",字体大小为6*8点阵*/
OLED_ShowString(16, 18, "Hello World!", OLED_6X8);
/*在(0, 28)位置显示数字12345,长度为5,字体大小为6*8点阵*/
OLED_ShowNum(0, 28, 12345, 5, OLED_6X8);
/*在(40, 28)位置显示有符号数字-66,长度为2,字体大小为6*8点阵*/
OLED_ShowSignedNum(40, 28, -66, 2, OLED_6X8);
3.2 显示汉字
显示汉字需要有对应的汉字字库,通常可以使用16x16或24x24的点阵字库,16x16的字库是较为常见的,能适应0.96寸OLED屏幕的分辨率。你可以从一些开源库中获取汉字字库,或者自己编写程序来生成。
我们以16*16字库为例,这个字库是最常用的。
使用PCtoLCD2002软件去完成汉字的取模,这里我们选16*16的字体,生成的字模型定义成数组,调用
OLED_ShowCHinese(0,0,0)去显示,第一、二个参数是起始显示的x、y的位置,第三个参数是显示数组中第几个字
以下面举例的取模为例,第三个参数应该是0和1.
const char hanzi[4][32]
{0x10,0x60,0x02,0x0C,0xC0,0x02,0x1E,0xE2,0x02,0x02,0x02,0xE2,0x1E,0x00,0x00,0x00},
{0x04,0x04,0x7C,0x03,0x80,0x80,0x40,0x20,0x13,0x0C,0x13,0x20,0x40,0x80,0x80,0x00},/*"汉",0*/
/* (16 X 16 , 宋体 )*/
{0x10,0x0C,0x04,0x24,0x24,0x24,0x25,0x26,0xA4,0x64,0x24,0x04,0x04,0x14,0x0C,0x00},
{0x02,0x02,0x02,0x02,0x02,0x42,0x82,0x7F,0x02,0x02,0x02,0x02,0x02,0x02,0x02,0x00},/*"字",1*/
/* (16 X 16 , 宋体 )*/
3.3 显示图片
在0.96寸OLED屏幕上显示图片,通常需要将图片转换成适合屏幕分辨率(通常是128x64像素)的点阵数据,并通过程序将这些数据传输到OLED屏幕上进行显示。由于OLED是单色显示,因此图片需要转换成二值图像(黑白图像)
具体分三步进行:
3.3.1 图片转换为bmp单色格式
-
画图板转换(这里做原理介绍,如果是使用建议直接下拉用方式2)
有两种方式一种是使用电脑自带的画图板进行转换,
重设完成后另存为单色bmp格式即可,但是有一个缺点,如果原来的图片亮度比较高可能会生成的单色bmp图片在取模软件上
取模后的数据无法准确显示,所以方法2是使用软件去生成bmp图片,可以调整亮度对比度后生成 -
使用img2lcd软件去生产单色bmp图片
注意红线框起来的地方的设置,这里我找了一张黑色占比较多的图片,直接生成单色图片会很模糊,所以我们可以调整亮度和对比度后再生成,这是直接使用画图板生成bmp无法做到的,也是我推荐这种方式生成单色BMP的原因。
下图是默认参数下的单色bmp图片,丢失了中间部分黄色的细节
下图是调整了亮度和对比度的结果还原了中间过亮区域的细节,调整到满意的效果后保存单色BMP文件到PCtoLCD软件里面的
图片模式去对图片取模。
3.3.2 对单色bmp图片进行取模
使用 PCtoLCD软件对bmp图片进行取模,先设置成图片模式,再调整参数,导入图片
按照下图设置好图形模式,然后导入图片生成
//
unsigned char oled_img[] =
{
0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,
0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFC,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xF8,0x0C,0x0C,0xCC,0x0C,0x0C,0xCC,0x0C,0x0C,0xCC,0x0C,0xCC,0x0C,
0x0C,0xCC,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0xFC,0xF8,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x1F,0xFF,0xFF,0xFF,
0xFF,0xFB,0xF1,0xF1,0xF1,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0xFF,0x00,0x00,0x7F,0x00,0x00,0x7F,0x00,0x00,0x7F,0x00,0x7F,0x00,
0x00,0x7F,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0x1F,0x1F,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0x00,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x03,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,
0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x06,0x07,0x03,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0x0F,0x1F,0x1F,0x1F,0x1F,0x1F,0x1F,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,
0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x1F,0x1F,0x1F,0x1F,0x1F,0x0F,0x07,
};
/***********功能描述:显示显示BMP图片128×64起始点坐标(x,y),x的范围0~127,y为页的范围0~7*****************/
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<y1;y++)
{
OLED_Set_Pos(x0,y);
for(x=x0;x<x1;x++)
{
OLED_WR_Byte(BMP[j++],OLED_DATA);
}
}
}
/*
函数功能:向OLED写入一个字节
参数:
dat:写入的数据/命令
cmd:1为写数据 0为写命令
*/
void OLED_WR_Byte(u8 dat,u8 cmd)
{
IIC_Start(); //IIC开始信号
IIC_Send_Byte(0x78); //传输地址
IIC_Wait_Ack(); //接收ACK
if(cmd) IIC_Send_Byte(0x40); //写数据
else IIC_Send_Byte(0x00); //写命令
IIC_Wait_Ack(); //接收ACK
IIC_Send_Byte(dat); //发送数据/命令
IIC_Wait_Ack(); //接收ACK
IIC_Stop(); //IIC停止
四、综合代码及效果视频
下面是综合代码基于stm32f103的代码片供参考,显示汉使用OLED_DisplayChinese接口,显示字符串使用OLED_DisplayString接口,显示图片使用OLED_DrawBMP接口去实现,如果需要实现表情包类的动图,则需要循环显示图片了见附录效果。
需要完整代码和取模软的评论留下邮箱我打包发你
int main( void )
{
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
uart_init(115200);
delay_init();
IIC_Init();
OLED_Init(); //OLED???
delay_ms(100);
//OLED_DisplayChinese(24,0,0,4); // OLED显示汉字
//OLED_DisplayString(88,0,16,":");
//OLED_DisplayChinese(24,3,4,2);
//OLED_DisplayString(56,3,16,":"); // OLED显示字符串
//OLED_DisplayString(88,3,16,"mA");
while(1)
{
OLED_DrawBMP(0,0,54,8,tupian);
}
}
附录GIF动图效果
OLED_GIF