1. TouchGFX 字体基础:字体在嵌入式 GUI 中的角色

做嵌入式 GUI 开发,说白了就是在跟「像素」打交道。而字体,就是把这些像素组织成可读文字的那套规则。我刚开始接触 TouchGFX 时,觉得字体不就是把 PC 上的 .ttf 文件丢进去编译吗?后来踩了几个坑才明白——嵌入式环境下的字体处理,远比想象中复杂。

1.1 字体在嵌入式 GUI 中的角色

你想想看,一个嵌入式设备上,用户第一眼看到的是什么?不是炫酷的动画,不是流畅的滑动,而是屏幕上的文字。字体直接决定了产品的专业度和用户体验。

  • 信息传递:菜单、状态、数值,全靠字体呈现
  • 品牌识别:定制字体能让产品更有辨识度
  • 资源消耗:字体数据占用的 Flash 和 RAM,往往超出你的预期

核心观点:在嵌入式系统中,字体不是「拿来就用」的素材,而是需要精心裁剪和优化的资源。选错字体或压缩策略,轻则界面卡顿,重则 Flash 爆满。

我在一个工业手持设备项目里,就因为用了全套 Unicode 字库,导致固件体积直接翻倍。后来才学会——嵌入式字体,够用就好,别贪多。

1.2 TouchGFX 字体引擎架构

TouchGFX 的字体引擎,说白了就是一套「从字形数据到屏幕像素」的流水线。我画了张图,帮你理清它的内部结构。

TouchGFX 字体引擎架构 字体文件输入 .ttf / .otf 字体解析器 字形轮廓提取 栅格化引擎 像素映射 字形缓存 (Glyph Cache) LRU 淘汰策略 文本渲染器 布局 & 绘制 帧缓冲区 最终像素输出 关键说明 1. 字体文件输入后,解析器提取字形轮廓(矢量数据) 2. 栅格化引擎将矢量轮廓转为像素位图 3. 缓存层管理已栅格化的字形,避免重复计算

这个架构里,我最想强调的就是字形缓存。为什么?因为栅格化一个汉字,在低端 MCU 上可能要花几毫秒。如果不做缓存,滚动列表时每一帧都在重新栅格化,帧率直接崩掉。

我的经验:TouchGFX 默认的缓存策略是 LRU(最近最少使用)。如果你的界面频繁切换页面,建议手动调整缓存大小。太小了命中率低,太大了浪费 RAM。一般我建议设为界面可见字符数的 1.5 倍。

1.3 字体文件格式与 TouchGFX 的兼容性

TouchGFX 支持两种主流字体格式:TrueType (.ttf) 和 OpenType (.otf)。但支持归支持,实际用起来有不少坑。

特性 .ttf (TrueType) .otf (OpenType)
轮廓技术 二次贝塞尔曲线 三次贝塞尔曲线 (CFF)
文件体积 通常较小 可能更大(含更多特性)
TouchGFX 支持度 完全支持 支持,但部分特性被忽略
常见问题 较少 连字、变体可能失效
推荐场景 嵌入式首选 需要特殊字形时

避坑指南:我曾经在一个项目里用了 .otf 字体,结果某些特殊字符(比如带变音符号的字母)在 TouchGFX 里显示成了方框。排查了两天才发现——TouchGFX 的字体转换工具不支持 OpenType 的 GSUB 特性。从那以后,我所有嵌入式项目都只用 .ttf。

1.4 字体转换流程

TouchGFX 不能直接使用 .ttf 或 .otf 文件。你需要通过 TouchGFX Font Converter 工具,把它们转成专用的二进制格式。流程是这样的:

  1. 选择字体文件:确保是 .ttf 格式,避免 .otf 的兼容性问题
  2. 指定字符集:只包含界面用到的字符,别贪多
  3. 设置字号:TouchGFX 不支持运行时缩放,字号必须在转换时固定
  4. 选择渲染模式:灰度抗锯齿还是单色?灰度效果好但占 Flash 多
  5. 生成 .ttf 文件:TouchGFX 专用的字体二进制文件

关键参数对比:

  • 灰度抗锯齿:每个像素 4 位(16 级灰度),效果好,体积大
  • 单色:每个像素 1 位,边缘锯齿明显,体积小
  • 亚像素渲染:需要 LCD 子像素排列支持,慎用

我个人习惯在开发阶段用灰度抗锯齿,方便调试。到了量产阶段,如果 Flash 吃紧,再切回单色。你想想看,一个 16x16 的汉字,灰度模式要 128 字节,单色只要 32 字节。界面里如果有 200 个汉字,差距就是 19KB 对 6KB。

1.5 字体资源管理策略

嵌入式系统的 Flash 和 RAM 都是稀缺资源。字体管理得好不好,直接决定了你的产品能不能塞进目标芯片。

策略 适用场景 优点 缺点
全字库嵌入 Flash > 16MB 的设备 无需运行时加载 占用大量 Flash
子集化嵌入 界面字符固定 体积小,效率高 不支持动态文本
外部存储加载 Flash 紧张,有 SPI Flash 按需加载 读取延迟
运行时栅格化 RAM 充足,CPU 性能好 灵活 CPU 开销大

我的建议:对于大多数 STM32 项目,子集化嵌入是最稳妥的方案。先用工具扫描整个 UI 用到的字符,生成最小字库。如果后续需要添加新字符,重新跑一遍转换流程就行。