Featured image of post STM32笔记 Day 3 OLED

STM32笔记 Day 3 OLED

大一新生STM32学习笔记

OLED(Organic Light-Emitting Display, 有机自发光显示)是非常常见的显示技术,其通过自发光的有机二极管材料实现显示,与使用恒定背光的LCD(Liquid Crystal Display,液晶显示)相区分。现在绝大多数中端及以上手机全部使用OLED屏幕,其具备厚度薄、视角广、功耗低、对比度高、响应速度快等等优势。

单片机使用的OLED模块最常见的型号是SSD1306,分辨率128×64,具有I²C(Inter-Integrated Circuit,集成电路间通信总线)和SPI(Serial Peripheral Interface,串行外设接口)两个协议的版本,今天以前者为例(因为我买到的初学者套装送的是前者)。两个协议需要了解的是,I²C速率较低,资源占用少,只用两根线传输,通常用于OLED、传感器等便携模块;而SPI速率高,资源占用多,要用四根线传输,通常用于屏幕、存储器等高速大型模块。对于STM32功能层开发,其他更加深入的原理我们完全几乎不需要了解,因为虽然底层驱动不同(网上可以轻易获取),但上层接口和使用方式是完全相同的。

接线

对于I²C版本的SSD1306,除了VCCGND以外,有SCL(Serial Clock,时钟线)和SDA(Serial Data,数据线)两个引脚。STM32的I²C通用默认引脚是SCL对应PB6SDA对应PB7,因此需要这样接线:

STM32 ──────── SSD1306
3.3V ─────── VCC
PB6 ───────> SCL
PB7 ───────> SDA
GND ───────── GND

导入驱动与基本点阵原理

根据协议和模块版本不同,对应的OLED驱动实现和文件名称可能也不同,但总体分为OLED.holedfont.hBMP.h三个头文件和oled.c一个C源文件。OLED.h负责声明所有功能层接口,oledfont.h负责声明要输出的英文和中文以及其16进制点阵数组,BMP.h负责声明要输出的BMP图像的16进制点阵数组,oled.c则是驱动的核心底层逻辑(这我们反而不需要关心)。

OLED屏显示需要的唯一数据就是16进制点阵数组,每个16进制数代表一列的八个像素,即一个字节。例如十六进制数0x48,其二进制为01001000,则代表一个八像素的纵列第二和第五个像素亮,其余像素暗,依次类推:

1
2
{0x20,0x24,0x24,0x24,0xFE,0x23,0x22,0x20,0x20,0xFF,0x20,0x22,0x2C,0xA0,0x20,0x00},
{0x00,0x08,0x48,0x84,0x7F,0x02,0x41,0x40,0x20,0x13,0x0C,0x14,0x22,0x41,0xF8,0x00}

因此,上面这个二维数组就是2个16列,每列8像素的点阵。前6列在上方,后6列紧接着正下方,故构成的本质上是16列16像素的点阵,即我们常见的一个16×16汉字,这个字是“我”。

SSD1306的分辨率128×64。以上面的逻辑,每列8像素为一个16进制数字(1字节),我们可以把每列8像素的横向集合定义成新的行,这样的行称为页(page)。那么我们就可以归纳出一个新的坐标系,这个坐标系是128×8的,正是我们在接口中实际使用的字节坐标系。

对于一个英文字母或标点,是8×8点阵,翻译成字节坐标系就占据一个8×1的区域。因此,横向排布英文字母或标点的坐标,从左上角第一个字母(0,0)开始依次向右8列,第2个字母(8,0),第3个字母(16,0)直到第16个字母(120,0);纵向排布则就是一页一个。横坐标保持8的整数倍,纵坐标保持整数,如果填满英文字母或标点,右下角最大坐标即为(120,8)

对于一个汉字,是16×16点阵,翻译成字节坐标系就占据一个16×2的区域,同理,横向排布16列一个,纵向排布2页一个。横坐标保持8的整数倍,纵坐标保持2的整数倍,如果填满汉字,右下角最大坐标即为(112,8)

对于图像,如果要填满整个屏幕,就是一个长度为128×8=1024的16进制点阵大数组,当然接口也支持在特定坐标绘制局部图像。将BMP图像、汉字、英文字母或标点转换为16进制点阵数组的工具称为字模转换器,Windows平台上,PCtoLCD2002是最经典的。

接口使用

接口中使用的坐标系都是字节坐标系,而非像素坐标系。

基本

1
2
void OLED_Init(void);
void OLED_Clear(void);

这两个函数负责初始化和清屏,不多赘述。

显示英文字母或标点

1
void OLED_ShowString(uint8_t x, uint8_t y, uint8_t *p, uint8_t Char_Size);

前两个参数是初始坐标,第三个是需要显示的字符串;第四个字体大小(有些版本的驱动不存在这个参数),只有当其为16时会使用8×16(字节坐标系8×2)字体,否则其他任意值都输出默认的8×8(字节坐标系8×1)字体。

显示汉字

需在oledfont.h中填入需要显示的汉字的点阵数据,这里以“你好”为例:

1
2
3
4
5
6
7
char Hzk[][32]={
    {0x00,0x80,0x60,0xF8,0x07,0x40,0x20,0x18,0x0F,0x08,0xC8,0x08,0x08,0x28,0x18,0x00},
    {0x01,0x00,0x00,0xFF,0x00,0x10,0x0C,0x03,0x40,0x80,0x7F,0x00,0x01,0x06,0x18,0x00},/*"你",0*/

    {0x10,0x10,0xF0,0x1F,0x10,0xF0,0x00,0x80,0x82,0x82,0xE2,0x92,0x8A,0x86,0x80,0x00},
    {0x40,0x22,0x15,0x08,0x16,0x61,0x00,0x00,0x40,0x80,0x7F,0x00,0x00,0x00,0x00,0x00},/*"好",1*/
};

函数用法:

1
void OLED_ShowCHinese(uint8_t x, uint8_t y, uint8_t no);

前两个参数是初始坐标,第三个是需要显示的汉字在Hzk中的索引,例如显示“你好”:

1
2
OLED_ShowCHinese(0,0,0); // 你
OLED_ShowCHinese(16,0,1); // 好 

显示图像

和汉字一样,也需要把图像的点阵数据提前填入BMP.hBMP1数组中,函数用法:

1
void OLED_DrawBMP(unsigned char x0, unsigned char y0, unsigned char x1, unsigned char y1, unsigned char BMP[]);

前两个参数是初始坐标,后两个参数是图像范围,第五个则是图像点阵数组,实际使用时需要用extern字段引入头文件里定义的BMP1,例如铺满全屏:

1
2
extern unsigned char BMP1[];
OLED_DrawBMP(0,0,128,8,BMP1);

以上所述所有文件、函数、变量名可能因协议与驱动而异,但功能都几乎相同,可以举一反三。

You never know what will happen next.
使用 Hugo 构建
主题 StackJimmy 设计