头图

1 概述

  • 1)代码仓库:这里尝试了两种面向对象的方式,不足之处敬请指正。

  • 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 像素,清晰但不美观;


送南阳马生序
7 声望1 粉丝

余之业有不精、德有不成,非天质之卑,则心不若他之专耳,岂他人之过哉!