3. 字模提取技术:从像素到字节的魔法

做嵌入式GUI,绕不开一个字——「点」。说白了,屏幕上的每个汉字、每个字母,最终都是一堆点的亮灭组合。而字模提取,就是把我们看得懂的字符,翻译成单片机吃得下的二进制数据。

我记得刚入行那会儿,为了在12864上显示一个「嵌」字,硬是手算了一下午的点阵数据。后来才发现,有工具能帮我干这活。今天我就把字模提取这点事,掰开了跟你聊聊。

3.1 常用字模提取软件:PCtoLCD2002 与它的朋友们

市面上字模提取软件不少,但我用得最多的,还是 PCtoLCD2002。这软件体积小、功能全,而且免费。你想想看,一个不到2MB的工具,能搞定几乎所有常见字体的取模工作,多省心。

除了它,还有几个值得一提的:

软件名称 特点 适用场景
PCtoLCD2002 支持多种取模方式、字体丰富、界面直观 通用嵌入式GUI开发
FontForge 开源、支持字体编辑与导出 需要自定义字体的场景
BDF2C 命令行工具、适合批量处理 自动化构建流程
自写脚本 完全可控、可定制 特殊格式需求

我个人习惯用 PCtoLCD2002 做原型验证,等确定方案后,再用脚本批量生成正式数据。这样既快又稳。

3.2 手动提取字模步骤:一步步来

工具虽好,但原理得懂。我曾经在调试一个OLED驱动时,发现显示出来的字全是镜像的。排查了半天,原来是取模方向搞反了。所以,手动走一遍流程,能帮你理解工具背后在干什么。

手动提取字模,大致分这几步:

  1. 确定点阵尺寸——比如16×16、24×24、32×32。尺寸越大,字越精细,但占用的存储也越多。
  2. 绘制字符网格——在纸上或软件里画一个点阵网格,把字符填进去。有笔画的格子涂黑,没笔画的留白。
  3. 确定取模方向——这是关键。横向取模还是纵向取模?从上到下还是从左到右?方向错了,显示就乱套。
  4. 逐行/逐列读取数据——按你选的方向,把每个字节的8个bit拼起来。涂黑的格子为1,留白的为0。
  5. 生成十六进制数组——把二进制数据转成0x??的形式,方便在C代码里使用。
我的小技巧: 手动取模时,先用小尺寸(比如8×8)练手。等流程走通了,再上大尺寸。这样出错了也容易排查。

3.3 字模数据格式:横向取模 vs 纵向取模

这是新手最容易踩坑的地方。我见过太多人,取模方向选错了,折腾半天显示不出来。说白了,横向取模和纵向取模,就是数据排列顺序不同。

横向取模(行扫描)

按行从左到右扫描,每8个点组成一个字节。如果一行超过8个点,就拆成多个字节。这种方式适合大多数LCD驱动芯片,比如SSD1306、ST7735。

举个例子,一个8×8的「A」字,横向取模的结果可能是:

// 横向取模:从左到右,从上到下
// 行0: 00111100  -> 0x3C
// 行1: 01100110  -> 0x66
// 行2: 01100110  -> 0x66
// 行3: 01111110  -> 0x7E
// 行4: 01100110  -> 0x66
// 行5: 01100110  -> 0x66
// 行6: 01100110  -> 0x66
// 行7: 00000000  -> 0x00

uint8_t font_A_horizontal[8] = {
    0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00
};

纵向取模(列扫描)

按列从上到下扫描,每8个点组成一个字节。如果一列超过8个点,就拆成多个字节。这种方式在某些老式驱动或特殊屏上会用到。

同样的「A」字,纵向取模的结果是:

// 纵向取模:从上到下,从左到右
// 列0: 00111100  -> 0x3C
// 列1: 01000010  -> 0x42
// 列2: 01000010  -> 0x42
// 列3: 01000010  -> 0x42
// 列4: 00111100  -> 0x3C
// 列5: 00000000  -> 0x00
// 列6: 00000000  -> 0x00
// 列7: 00000000  -> 0x00

uint8_t font_A_vertical[8] = {
    0x3C, 0x42, 0x42, 0x42, 0x3C, 0x00, 0x00, 0x00
};
注意: 同一个字符,横向和纵向取模出来的数据完全不同。如果你发现显示出来的字是乱的,先检查取模方向是否匹配你的驱动芯片。

3.4 取模方向与字节序:细节决定成败

除了横向/纵向,还有两个细节容易被忽略:

  • 高位在前 vs 低位在前——比如0x3C,二进制是00111100。如果高位在前,最左边的点对应bit7;低位在前,最左边的点对应bit0。这个顺序错了,字会左右镜像。
  • 字节顺序——对于16×16以上的字,一行或一列会拆成多个字节。这些字节在数组里的排列顺序,也决定了显示效果。

我建议你在项目初期,就定好一套统一的取模规范。比如:横向取模、高位在前、字节顺序从左到右。然后所有字模都按这个规范生成,省得后面出问题。

3.5 知识体系总览

下面这张图,把字模提取的核心逻辑串起来了。你可以把它当作一个快速参考。

字模提取技术知识体系 字模提取 常用工具 手动提取步骤 数据格式 细节 PCtoLCD2002 FontForge 确定尺寸 → 绘制网格 → 取模 横向取模 纵向取模 字节序/位序 核心对比:横向取模 vs 纵向取模 横向:按行扫描,每8点一个字节,适合SSD1306等常见驱动 纵向:按列扫描,每8点一个字节,适合某些特殊屏或老式驱动

3.6 避坑指南:我踩过的那些坑

做字模提取这么多年,我总结了几条血泪教训:

  • 取模方向一定要跟驱动芯片匹配。 我曾经在SSD1306上用了纵向取模的数据,结果显示出来的字全是乱的。查了半天,才发现驱动默认是横向取模。
  • 注意字节内的位序。 有些工具默认高位在前,有些默认低位在前。如果你发现字是镜像的,先检查这个。
  • 大尺寸字模要分段测试。 32×32以上的字,数据量大了容易出错。我习惯先测8×8,再测16×16,最后上大尺寸。这样出问题了能快速定位。
  • 工具生成的数据,最好人工校验一遍。 尤其是第一个字和最后一个字,容易出边界问题。

一句话总结: 字模提取不难,但细节多。选对工具、定好规范、多做校验,就能少走弯路。