文章目录
- 1 概述
- 2 代码简述
- 2.1 OLED 对象
- 2.2 OLEDProtocol 对象
- 2.3 OLEDFont 对象
- 3 成果展示
1 概述
-
1)代码仓库:这里尝试了两种面向对象的方式,不足之处敬请指正。
- OOP 方式:https://gitee.com/luyaocf/demo-jlc_stm32f407_oop.git
- Class In C:https://gitee.com/luyaocf/demo-jlc_stm32f407_cic.git
本文主要以 Class In C 的方式记录,虽然它占用 RAM 高,但它更像 OOP。
-
2)OLED 要显示中文汉字,有以下几点:
-
(1)首先是 OLED 的显示驱动,如 OLED 屏幕初始化,设置显示位置,数据写入方式等
-
(2)然后是通信协议,如采用 SPI 或 I2C,使用软件模拟简单,使用硬件实现更快。
-
(3)字库,一般汉字达到可辨认的最小规格应该为 16x16 像素,而就算最小支持的 GB2312 编码的也有几千个汉字,几百 KB 大小点阵数据,一般我们使用片外存储器件,如 w25qxx。(如果只是显示固定的几个汉字,那么存储到片上 FLASH 即可。)
-
(4)编码,汉字是有编码的,如果你的源文件编码为 UTF-8,而使用的字库为 GBK 的,两者没有转换关系,只能通过映射表来转换。(Linux 下有 iconv 库,单片机没那么富裕的空间,当然也可以像我一样同时生成 GBK 与 Unicode 字库,那么源文件编码无论是 UTF-8 或 GBK 都可以简单使用)
-
附录:点阵字库大小
2 代码简述
2.1 OLED 对象
- 1)我们创建一个 OLED 对象,其内部持有两个对象的指针:
- OLEDProtocol 对象:用来实现写点阵数据的协议;
- OLEDFont 对象:用来根据汉字获取点阵字库。
另外,OLED 对象通过 size 来指定显示汉字大小;通过 mode 来配置汉字阴显或阳显;还具有很多函数,用来初始化、显示数字、字符串、清屏、画点等功能。
- 2)显存缓冲区
这里我们建立一个显存缓冲区,与 OLED 内部显存对应起来,有两个目的:首先可以方便地获取当前显示的数据;其次通过一次刷新整个屏幕来避免频繁写入。
/**
* Physically, OLED Graphic bits is [128][64](x * y).
* Logically, OLED Graphic Memory is 8 * 128(page * segment). 128 * 8 is okay, except that it's
* not convenient to display.
*/
uint8_t OLED_GRAPHIC_MEMORY[8][128];
- 3)OLED 功能函数如下:
void (*init) (OLED *this);
void (*clear) (OLED *this);
void (*full) (OLED *this);
void (*refresh)(OLED *this);
uint8_t (*showChar) (OLED *this, uint8_t x, uint8_t y, char chr);
void (*showString) (OLED *this, uint8_t x, uint8_t y, char *str);
void (*showNum) (OLED *this, uint8_t x, uint8_t y, uint32_t num, uint8_t len);
void (*showSignedNum) (OLED *this, uint8_t x, uint8_t y, int32_t num, uint8_t len);
void (*showHexNum) (OLED *this, uint8_t x, uint8_t y, uint32_t num, uint8_t len);
void (*showBinNum) (OLED *this, uint8_t x, uint8_t y, uint32_t num, uint8_t len);
void (*showFloatNum) (OLED *this, uint8_t x, uint8_t y, float num, uint8_t len);
uint8_t (*showChinese) (OLED *this, uint8_t x, uint8_t y, char *cn);
void (*showImage) (OLED *this, uint8_t x, uint8_t y, uint8_t width, uint8_t height, const uint8_t *dotMatrix);
void (*printf) (OLED *this, uint8_t x, uint8_t y, char *format, ...);
void (*drawPoint) (uint8_t x, uint8_t y, uint8_t point);
2.2 OLEDProtocol 对象
- 1)OLEDProtocol 是一个抽象类,由 OLED 支持的各种协议实现类继承:
- OLEDProtocolI2C 类:通过 I2C 协议读写 OLED 数据
- OLEDProtocolSPI 类:通过 SPI 协议读写 OLED 数据
- 还可以通过 OLEDProtocol6800、OLEDProtocol8080 协议实现类自行扩展。
- 2)以 OLEDProtocolI2C 对象为例:
- (1)继承 OLEDProtocol 类,需要实现父类的 write_byte() 函数
- (2)持有一个 I2C 对象,而这个 I2C 对象可以是实现模拟 I2C 协议的 I2CSimulate 类,也可以是实现硬件 I2C 协议的 I2CHardware 类
- (3)OLEDProtocolI2C 不用关心底层的 I2C 实现,它只要调用父类 I2C 的 send()、read() 函数来读写数据即可。
2.3 OLEDFont 对象
- 1)OLEDFont 不是抽象类,但它持有了抽象类 Storage 的指针。Storage 是存储抽象类,由各种可存储的各种外设设备类继承:
- StorageFlash 类:可以理解为片上 FLASH,适用于小规模的点阵字库存储,如 ASCII 码的点阵数据。
- W25Qxx 类:典型的如 W25Q64 8MB FLASH,已经可以满足大多数显示的需求
- 还可以自行扩展 SDCardStorage 类,用来实现 SDCard 的字库数据存储。
-
2)OLEDFont 调用抽象类 Storage 的 read() 函数来获取点阵,各个实现 Storage 类的存储设备各自实现 read() 函数来提供点阵数据。
-
3)另一点值得一提的是,OLEDFont 需要指定编码(UTF-8、GB2312、GBK):
- (1)UTF-8 编码:UTF-8 是编码方式不是字符集,需要首先转换为 Unicode 字符集编码,然后我们根据字符编码以及字库基地址计算出该汉字的存储位置,最后到存储设备对应的位置读取点阵数据
- (2)GB2312 编码和 GBK 编码:这两者既是编码又是字符集,所以直接计算点阵数据位置即可。(由于 GBK 没有完全兼容 GB2312,所以这里我分开处理,实际使用中根据显示要求择一即可。)
- (3)ASCII 编码:判断当前字符小于 0x7F 单独处理即可。
3 成果展示
- 这里以嘉立创天空星 STM32F407 开发板为基础,因为它带有 SDCard 槽,可以方便地将自己制作的字库复制到板上的 W25Q64 中去(字库的制作及复制见下一章)。
下方图片使用 UTF-8 编码,可以看到 ASCII、汉字(包括 12、16、24 尺寸,以及正反显示)、图片都正常显示。
下方图片使用 GBK 编码测试字库,平均选择了 GBK 编码的几个区。第一行字体尺寸为 12x12 像素,有的字已经模糊得无法辨认了;第二行为 16x16 像素,显示效果刚刚好;第三行字体尺寸为 24x24 像素,清晰但不美观;