- D-6 目录规范化:docs 根目录散落文档归入 UI设计/子系统设计/架构设计/审查记录/ 环境与部署/网络 等子目录,去掉目录名空格,重命名 嵌入式C语言代码规范_V1.0.md - D-7 三档一致性门禁:新增 scripts/check_doc_links.py(链接存在性)、 scripts/gen_task_table.py(FreeRTOS 任务表数值)、scripts/check_mem_numbers.py (内存/Flash 体积),均支持 --check/--write/--diff,pre-commit 钩子同步注册 - D-5 文档索引:新建 docs/README.md,新建 docs/文档规范.md 约束目录命名与链接写法 - D-1 芯片手册瘦身:删除 1069 张无引用孤儿图片(77.06 MB), docs/芯片手册 120 MB → 41 MB,已引用图片零断链 - 同步 C 源码注释中的文档路径引用(D-6 重组后),构建 0 错误 0 警告
8.8 KiB
UI 中文字体生成与配置
适用范围:STM32F4-Base 远程单色屏(160×160,1bpp,
LV_COLOR_FORMAT_I1)的 LVGL 中文显示。 目标:把工程实际用到的汉字生成子集化 1bpp 位图字体,挂到LV_FONT_DEFAULT,使菜单/标签正常显示中文并推流到客户端。
1. 背景
- LVGL 内置字体(Montserrat 等)不含中文;
LV_FONT_SIMSUN_16_CJK等全字库体积过大,不适合 160×160 小屏。 - 本工程显示是 1bpp 单色:像素 1=亮(白)/0=暗(黑),与客户端
mono_to_png的1→白语义一致。 - 方案:用
lv_font_conv把系统中文字体(宋体 SimSun)按"实际用到的汉字 + ASCII"子集,生成 1bpp 的 LVGL 字体.c。
生成产物:Drivers/BSP/UI/ui_font_cn_12.c(变量名 ui_font_cn_12)。
2. 工具准备
2.1 工具与环境确认
| 工具 | 用途 | 安装 |
|---|---|---|
| Node.js (≥ v14) | 运行 lv_font_conv |
已具备 |
lv_font_conv |
TTF/OTF → LVGL 字体 .c(官方离线转换器) |
npm i -g lv_font_conv |
Python fontTools |
make_font.py 读取 .ttc 集合时抽取单字重(仅在字库为 .ttc 时需要) |
pip install fonttools |
安装完成后验证可用:lv_font_conv -h 能打印用法即成功。
Python 解释器(统一入口):用仓库内
.venv(已装 fontTools,见 AGENTS.md「Python 环境」):& '.venv\Scripts\python.exe' tools/font_gen/make_font.pyPATH 默认
python是 msys64 的(无 fonttools),直接跑会失败。.venv缺失/换机时重建:& 'C:\ProgramData\anaconda3\python.exe' -m venv .venv; & '.venv\Scripts\python.exe' -m pip install -r requirements.txt。extract_cn.py纯标准库,任意 python3 均可。
3. 提取字符集(只取会显示的汉字)
已落库脚本 tools/font_gen/extract_cn.py:扫描 Drivers/BSP/UI/*.c/.h 与 App/task/ui_task.c/.h 中字符串字面量里的非 ASCII 字符,跳过 /* */、// 注释与字符常量(避免把注释里的汉字也打进字体、徒增体积),输出 <仓库根>/ui_cn_chars.txt。脚本自动定位仓库根,可在任意目录运行。
执行命令(在仓库根目录):
python tools/font_gen/extract_cn.py
输出示例:
scanned 13 files; unique non-ASCII chars: 78
-> D:\Code\DTU Code\STM32F4-Base\ui_cn_chars.txt
产物 ui_cn_chars.txt 为本工程实际显示的汉字清单(当前 122 字,含 ▶/,等符号),可留作字符集记录。
4. 生成字体
已落库脚本 tools/font_gen/make_font.py:自动定位 lv_font_conv(全局安装或 PATH)、自动处理宋体 .ttc 的 face0 抽取,直接生成 Drivers/BSP/UI/ui_font_cn_12.c。
前置依赖(仅需一次,统一装进仓库 .venv):
npm i -g lv_font_conv # TTF/OTF -> LVGL 字体 离线转换器
.venv\Scripts\python.exe -m pip install fonttools # 仅当使用 .ttc 集合字体(如宋体)时需要;已含于 requirements.txt
若
npm i -g lv_font_conv报代理错误,先清代理:npm config delete proxy/npm config delete https-proxy。
执行命令(在仓库根目录,用 §2.1 的 .venv 解释器):
& '.venv\Scripts\python.exe' tools/font_gen/make_font.py # 默认:宋体, 12px, 1bpp
& '.venv\Scripts\python.exe' tools/font_gen/make_font.py --bpp 2 # 宋体细笔画发虚时改用 2bpp
& '.venv\Scripts\python.exe' tools/font_gen/make_font.py --font C:/path/to/font.ttf # 换其他字体
输出示例:
extracted SimSun regular (face0) -> C:\Users\...\Temp\simsun_regular.ttf
RUN: lv_font_conv.cmd --font ... --size 12 --bpp 1 -r 0x20-0x7E --symbols ... --no-compress --format lvgl --lv-font-name ui_font_cn_12 -o ...\ui_font_cn_12.c
已合并箭头 U+25B6:位图+10 字节,glyph_id=206
已合并 U+FF0C ',':1 字节位图,glyph_id=207
OK 43614 bytes -> ...\Drivers\BSP\UI\ui_font_cn_12.c
▶ 箭头自动合并:宋体(
simsun.ttc)没有 U+25B6(▶)字形,lv_font_conv会静默跳过 → 真机显示成方框。make_font.py在生成后自动调用merge_arrow_glyph(),把从 Segoe UI Symbol(seguisym.ttf,Windows 自带)预提取的箭头字形(位图 10 字节 +glyph_dsc+cmap)补回ui_font_cn_12.c。该合并幂等(已存在则跳过),所以重生成字体后箭头不会丢。换用自带 ▶ 的字体(如黑体/Segoe)时可忽略此步。
低码位符号自动合并:
lv_font_conv的稀疏 cmap 从符号集最小码位起算,低于该起点的符号被静默丢弃(实测 U+3001/U+FF0C 等:U+FF0C 全角逗号 < 0x4E2D 范围起点时丢失,页脚"第 X 页,共 Y 页"的逗号长期渲染为空白)。make_font.py的merge_extra_symbols()对每个此类符号单独生成迷你字体(真实宋体位图/度量),再并入主字体(位图 +glyph_dsc+ 单码位cmap),同样幂等。新增此类符号时把它加入脚本的EXTRA_SYMBOLS列表即可。
生成后覆盖率断言:
make_font.py在合并后调用verify_coverage()——解析生成字体的 cmap,校验ui_cn_chars.txt的全部码位都进了字体,缺一个即以非零退出码报错(列出U+XXXX)。把 lv_font_conv 的静默丢字(低码位、源字体缺字形)从"上线后才发现空白"变成"生成期硬错误"。
参数说明:
--bpp 1:单色无抗锯齿;体积最小。注意 lv_font_conv 在 1bpp 下斜线/细笔画偶发"丢像素",宋体尤甚。--bpp 2/3:字形带灰度,渲染到 1bpp 屏时按LV_DRAW_SW_I1_LUM_THRESHOLD阈值化,笔画更连贯;子集仅几十~上百字,体积仍很小。--no-compress:关闭内置压缩,规避LV_FONT_COMPRESSED未启用导致的兼容问题。--lv-font-name ui_font_cn_12:生成的lv_font_t变量名,须与下面LV_FONT_DECLARE一致。
5. 配置接入
5.1 声明与设为默认字体(Lib/lv_conf.h)
该文件当前为 UTF-8(带 BOM)。历史上它曾为 GBK,且工作区版本因某次整文件文本重写导致中文注释被 U+FFFD 不可逆损坏(HEAD 中的 GBK 版本仍干净)。
损坏恢复:若二次损坏,不要用整文件文本重写。正确做法:
git show HEAD:Lib/lv_conf.h以gbk解码得到干净中文 → 叠加第 5.1 节的字体配置改动 → 重新写为utf-8-sig(UTF-8 带 BOM)。这样中文可完整恢复,不会引入U+FFFD。
/* 自定义中文 1bpp 字体:由 lv_font_conv 生成(ASCII + 本工程所需汉字子集) */
#define LV_FONT_CUSTOM_DECLARE LV_FONT_DECLARE(ui_font_cn_12)
/* 默认字体:中文 1bpp 子集字体(已含 ASCII),菜单/标签均使用它 */
#define LV_FONT_DEFAULT &ui_font_cn_12
字体
.c自身通过#ifndef UI_FONT_CN_12自包含,编译进工程即生效;无需额外#include。
5.2 加入 Keil 工程
MDK-ARM/STM32F407-Demo.uvprojx 的 Drivers/BSP/UI 组追加(纯 ASCII 插入):
<File>
<FileName>ui_font_cn_12.c</FileName>
<FileType>1</FileType>
<FilePath>..\Drivers\BSP\UI\ui_font_cn_12.c</FilePath>
</File>
6. 编译验证
@build # 即 MDK-ARM\build.bat,调用 UV4 全量重建
要求:0 Error(s), 0 Warning(s)。重新生成/修改字体或 lv_conf.h 后务必重建验证。当前已验证通过,ui_font_cn_12.c 正常编入。
7. 注意事项
- 1bpp 细笔画发虚:宋体在 1bpp 下可能丢像素。若上板/客户端中文偏淡,把第 4 节
--bpp改为2重生成即可(子集小,体积可接受)。 - 字符集变更:以后新增中文菜单项,须重跑第 3、4 节(重新提取
ui_cn_chars.txt并生成),否则新字显示为方框。 - 编码:UI 源文件须为 UTF-8(带 BOM),LVGL 按 UTF-8 解码中文串;字体
.c为纯 ASCII,无编码风险。当前 Keil 已加 Misc Controls 抑制项,无需额外--locale=english。 - 字体体积:12px + 1bpp + 122 字(含 ASCII/符号,共 217 字形)≈ 44.4KB(RO-data);改 bpp 2 约翻倍,仍在 F407 Flash 余量内。
- 换字体:黑体用
simhei.ttf(单文件免抽取);其余字体同理替换--font路径后重生成。
8. 文件清单
| 文件 | 说明 |
|---|---|
Drivers/BSP/UI/ui_font_cn_12.c |
生成的中文 1bpp 字体(变量 ui_font_cn_12) |
Lib/lv_conf.h |
字体声明与默认字体配置 |
MDK-ARM/STM32F407-Demo.uvprojx |
UI 组包含该字体 .c |
ui_cn_chars.txt(工程根) |
实际显示汉字清单记录(可删) |
tools/font_gen/extract_cn.py |
提取字符集脚本(见第 3 节) |
tools/font_gen/make_font.py |
生成字体脚本,自动定位 lv_font_conv 并处理 .ttc(见第 4 节) |