U8G2中文字库裁剪与压缩:嵌入式点阵字库实战指南
简介这份资源面向使用 Arduino 与 u8g2 库进行中文显示开发的嵌入式爱好者与工程师解决 u8g2 默认字库不含中文字形、需要自行生成定制字体的实际问题。压缩包内包含 GUITool 字体取模工具与 u8g2-master 源码库两部分核心内容前者用于将中文字符转换为 u8g2 可识别的点阵字模数据后者提供库文件与示例参考便于在项目中直接调用自定义中文字体。资源包整体约 54.68MB文件类型以工具程序与源码文件为主结构清晰适合按需取用。目前已有 2242 人学习下载读者可借助这套资料完成从取模、生成字库到在 Arduino 工程中加载显示中文的完整流程减少自行摸索字库格式与配置参数的时间对 OLED、LCD 等小屏中文界面开发具有直接参考价值。1. 从 U8G2ChineseFont.zip 说起嵌入式中文点阵字库到底怎么落地如果你玩过 OLED 或 LCD 单色屏大概率绕不开 U8G2 这个图形库。它轻量、跨平台、驱动覆盖广但真正让国内开发者头疼的从来不是画线画圆而是在只有几十 KB RAM 的单片机上显示中文。U8G2ChineseFont.zip 这类资源包本质上就是一套已经裁剪好的中文字库文件加配套取模配置解决的就是“屏幕能亮、英文能显示、中文一写就乱码或编译爆 Flash”的经典问题。它适合做小家电面板、工控仪表、穿戴设备、电子价签的嵌入式工程师也适合刚接触 U8G2、被字库格式劝退的新手。核心矛盾只有一个中文字形数据太大而单片机资源太小必须靠取模裁剪和按需加载来平衡。2. 中文字库在 U8G2 里的三种存法选错了后面全是坑2.1 全字库、子集字库和动态加载的取舍U8G2 原生支持的中文字库调用方式常见做法有三种。第一种是全字库编译进 Flash比如把 GB2312 一二级字库全部转成 C 数组优点是调用简单缺点是 Flash 占用动辄几百 KB 到 1 MB 以上STM32F103C8T6 这种 64 KB Flash 的芯片直接出局。第二种是子集字库只提取项目实际用到的几十到几百个汉字生成一个自定义小字库这是 U8G2ChineseFont.zip 这类资源最典型的用法。第三种是外部 Flash 动态加载字库放在 W25Q64 这类 SPI Flash 里运行时按需读取字形点阵RAM 和内部 Flash 都省但需要自己写读取和缓存逻辑。选型判断很简单如果你的 MCU Flash 小于 128 KB优先子集字库如果产品需要显示用户输入或大量动态文本考虑外部 Flash 方案如果只是固定显示几十个菜单词子集字库加U8G2_USE_FONT宏裁剪就够了。我一般会先统计 UI 上所有可能出现的汉字去重后通常不超过 300 个这样生成的字库体积可以压到 20 KB 以内。2.2 用 bdfconv 把 TTF 转成 U8G2 可用的 C 数组U8G2 官方工具链里有一个bdfconv工具配合otf2bdf可以把 TTF/OTF 字体转成 U8G2 能识别的.bdf再转成 C 数组。下面是我在 Linux 下常用的命令流程Windows 下可以用 WSL 或 MSYS2 跑同样的命令。# 第一步把 TTF 转成 BDF指定字号 16 像素 otf2bdf -p 16 -o myfont16.bdf SourceHanSansCN-Regular.ttf # 第二步用 bdfconv 生成 U8G2 格式的 C 文件 # -f 1 表示输出格式为 U8G2 字体 # -m 0 表示不生成映射表使用 Unicode 直接索引 # -n 指定字体变量名 # -o 输出文件名 # -b 0 表示从 0 开始编码 # -e 1 表示编码范围结束标志 bdfconv -f 1 -m 0 -n u8g2_font_myfont16 -o myfont16.c myfont16.bdf # 第三步如果只需要部分汉字用 -u 指定 Unicode 列表文件 # unicode_list.txt 每行一个十六进制码点例如 4E2D 表示“中” bdfconv -f 1 -m 0 -n u8g2_font_subset16 -o subset16.c -u unicode_list.txt myfont16.bdf这里的关键参数是-m。-m 0表示不做映射表U8G2 会按 Unicode 码点直接计算偏移适合码点连续的 GB2312 子集如果码点跳跃很大建议用-m 1生成映射表虽然多占一点空间但查找速度稳定。-u指定的 Unicode 列表文件就是你把 UI 上所有汉字去重后转成十六进制码点的结果可以用 Python 脚本从字符串里自动提取。2.3 在 U8G2 工程里注册自定义字库并调用生成 C 文件后把它加入工程然后在调用u8g2_DrawStr之前设置字体。下面是一个 STM32 HAL U8G2 的典型片段。// 声明外部字库变量变量名要和 bdfconv -n 参数一致 extern const uint8_t u8g2_font_subset16[]; // 初始化 U8G2 后设置自定义中文字库 u8g2_Setup_ssd1306_i2c_128x64_noname_f( u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay); u8g2_InitDisplay(u8g2); u8g2_SetPowerSave(u8g2, 0); // 关键一步把字体设置为自定义子集字库 u8g2_SetFont(u8g2, u8g2_font_subset16); // 绘制中文注意字符串要用 UTF-8 编码 u8g2_DrawUTF8(u8g2, 0, 20, 温度); u8g2_DrawUTF8(u8g2, 0, 40, 湿度); u8g2_SendBuffer(u8g2);逻辑说明u8g2_SetFont只是切换当前字体指针真正绘制中文要用u8g2_DrawUTF8而不是u8g2_DrawStr因为DrawStr按单字节处理遇到 UTF-8 多字节编码会直接乱码。参数上u8g2_font_subset16这个变量名必须和bdfconv -n指定的一致否则链接阶段会报未定义符号。另外注意u8g2_Setup_ssd1306_i2c_128x64_noname_f里的_f后缀表示全缓冲模式RAM 占用 1 KB如果 RAM 紧张可以换成_1或_2分页模式但分页模式下DrawUTF8的坐标计算会更麻烦。3. 裁剪与压缩把中文字库塞进 64 KB Flash 的实操参数3.1 按 Unicode 码点生成最小子集子集字库的核心是“只取所需”。我一般用下面这个 Python 脚本从 UI 字符串里提取所有不重复汉字输出 bdfconv 需要的 Unicode 列表文件。# extract_unicode.py # 从文本文件中提取所有汉字输出十六进制 Unicode 码点列表 import re def extract_chinese_chars(text): # 匹配 CJK 统一汉字基本区 pattern re.compile(r[\u4e00-\u9fff]) return set(pattern.findall(text)) def main(): with open(ui_strings.txt, r, encodingutf-8) as f: content f.read() chars extract_chinese_chars(content) # 排序后输出每行一个码点格式如 4E2D with open(unicode_list.txt, w, encodingutf-8) as f: for ch in sorted(chars): f.write(f{ord(ch):04X}\n) print(f共提取 {len(chars)} 个不重复汉字) if __name__ __main__: main()逻辑说明ui_strings.txt里放你界面上所有可能出现的字符串一行一个或直接一段文本都行。脚本用正则匹配\u4e00-\u9fff范围内的字符去重后按码点排序输出。参数上04X表示至少 4 位十六进制不足补零这是 bdfconv 能识别的格式。如果你的项目还包含标点符号和数字需要额外匹配\u3000-\u303f和\uff00-\uffef区段。3.2 用 RLE 压缩点阵数据再省一半空间即使裁剪到 300 个汉字16x16 点阵每个字 32 字节总共也要 9.6 KB加上 U8G2 的字体结构体开销差不多 12 KB。如果 Flash 还是紧张可以对点阵数据做游程编码RLE。U8G2 本身不直接支持 RLE 字库但你可以自己写一个解压函数在绘制前把字形解到 RAM 缓冲区。// 简单的 RLE 解压每个字节的高 4 位是重复次数低 4 位是数据 // 注意这是自定义格式需要配合你自己的取模脚本生成 void rle_decode(const uint8_t *src, uint8_t *dst, uint16_t dst_len) { uint16_t di 0; while (di dst_len) { uint8_t count (*src 4) 1; // 重复次数 1-16 uint8_t value *src 0x0F; // 实际数据这里只用了低 4 位 for (uint8_t i 0; i count di dst_len; i) { dst[di] value; } src; } }逻辑说明这个 RLE 方案把每个字节拆成“重复次数 数据”适合点阵里大量连续 0x00 或 0xFF 的情况。参数上高 4 位最大表示 16 次重复低 4 位表示数据值实际使用中如果数据超过 4 位范围需要改成整字节压缩方案。解压后的数据要按 U8G2 的字形格式重新组装这部分需要你根据u8g2_font_decode的源码逻辑来适配属于进阶操作新手建议先用未压缩子集字库跑通。3.3 外部 SPI Flash 方案的分区与读取参数当子集字库也放不下时外部 Flash 是唯一出路。以 W25Q64 为例8 MB 空间可以放完整 GB2312 字库还有富余。我的做法是把字库按 4 KB 扇区对齐存放每个汉字 32 字节一个扇区存 128 个汉字。读取时根据 Unicode 码点计算扇区地址和偏移。// 假设字库从外部 Flash 的 0x10000 地址开始存放 // 每个汉字 32 字节按 Unicode 码点顺序排列 #define FONT_BASE_ADDR 0x10000 #define FONT_CHAR_SIZE 32 void read_font_from_flash(uint16_t unicode, uint8_t *buf) { // 计算偏移只处理 CJK 基本区码点减去 0x4E00 uint32_t offset (unicode - 0x4E00) * FONT_CHAR_SIZE; uint32_t addr FONT_BASE_ADDR offset; // W25Q64 读取函数每次最多读一页 256 字节 w25q64_read(addr, buf, FONT_CHAR_SIZE); }逻辑说明FONT_BASE_ADDR是字库在外部 Flash 中的起始地址需要根据你的分区表调整。FONT_CHAR_SIZE是单个汉字点阵的字节数16x16 是 32 字节24x24 是 72 字节。w25q64_read是底层 SPI 读取函数注意 W25Q64 的页读取边界是 256 字节跨页读取需要分两次。这个方案省内部 Flash但每次绘制中文都要读 SPI刷新率会下降适合静态显示场景。4. 避坑与排查中文显示翻车的五个血泪现场4.1 现象中文显示为方块或问号原因字体没有正确设置或者调用了u8g2_DrawStr而不是u8g2_DrawUTF8。DrawStr按单字节处理UTF-8 编码的汉字会被拆成多个无效字节。解决确认u8g2_SetFont设置的是中文字库并且绘制函数用u8g2_DrawUTF8。另外检查源文件编码是否为 UTF-8Keil 默认 GBK 编码会导致字符串常量本身就不对。4.2 现象编译报错 “undefined reference to u8g2_font_xxx”原因bdfconv 生成的 C 文件没有加入工程或者变量名和-n参数不一致。解决检查生成的.c文件是否在编译列表里检查extern声明和实际变量名是否完全一致包括大小写。如果用的是 C 工程记得加extern C包裹。4.3 现象显示一半汉字后面的乱码原因子集字库的 Unicode 列表不完整或者 bdfconv 的-m映射模式选错。如果用了-m 0但码点不连续U8G2 会按错误偏移查找。解决重新生成 Unicode 列表确保覆盖所有用到的汉字如果码点跳跃大改用-m 1生成映射表。另外检查unicode_list.txt里是否有重复或格式错误的行。4.4 现象Flash 占用远超预期原因bdfconv 默认会生成完整的字体结构体包括所有字形的偏移表即使子集很小偏移表也可能占几百字节。另外如果-m 1映射表过大也会增加体积。解决用arm-none-eabi-size查看各段大小确认字库确实在.rodata段。如果偏移表太大可以手动裁剪只保留实际用到的码点范围。我一般会把字库单独放一个.c文件方便用size命令单独统计。4.5 现象外部 Flash 方案读取速度慢界面卡顿原因每次绘制都直接读 SPI Flash没有缓存机制。解决在 RAM 里开一个字形缓存比如缓存最近用到的 16 个汉字点阵用简单的 LRU 淘汰。缓存大小根据 RAM 余量决定16 个 16x16 汉字占 512 字节对大多数 MCU 可以接受。另外把 SPI 时钟提高到 40 MHz 以上W25Q64 支持 80 MHz 读取但要注意 PCB 走线质量。5. 进阶技巧用 U8G2 的字体裁剪宏和运行时切换做多语言界面U8G2 本身提供了一组编译期裁剪宏比如U8G2_USE_FONT可以在u8g2.h里禁用不需要的字体减少 Flash 占用。但很多人不知道的是你可以在运行时动态切换字体指针实现中英文混排或多语言界面。具体做法是定义多个字体变量在界面切换时调用u8g2_SetFont切换。// 定义中英文字体指针 extern const uint8_t u8g2_font_subset16[]; extern const uint8_t u8g2_font_helvB12_tf[]; // 根据语言设置切换字体 void set_language_font(u8g2_t *u8g2, uint8_t lang) { if (lang LANG_CN) { u8g2_SetFont(u8g2, u8g2_font_subset16); } else { u8g2_SetFont(u8g2, u8g2_font_helvB12_tf); } } // 绘制时统一用 DrawUTF8英文也能正常显示 u8g2_DrawUTF8(u8g2, 0, 20, 温度: 25C);逻辑说明u8g2_font_helvB12_tf是 U8G2 自带的英文字体_tf后缀表示包含完整 ASCII 字符集。DrawUTF8对纯 ASCII 字符串同样有效所以中英文混排不需要切换绘制函数。参数上LANG_CN和LANG_EN是你自己定义的宏根据界面状态或用户设置来切换。这个技巧在需要支持多国语言的产品里很实用代价是同时链接两套字体Flash 占用增加但比全字库方案还是省很多。另一个进阶点是字库的按需加载与释放。如果你的系统有外部 RAM 或足够大的内部 RAM可以把整个子集字库在启动时从外部 Flash 读到 RAM 里之后绘制直接读 RAM速度提升明显。代价是启动时间增加几百毫秒以及 RAM 占用。我一般会在产品启动时做一个进度条把字库加载和界面初始化并行处理用户感知不到延迟。最后说一个我踩过的坑不要用u8g2_DrawGlyph直接绘制中文。这个函数按字形索引查找而中文字库的字形索引和 Unicode 码点不是一一对应关系除非你完全清楚 bdfconv 生成的索引表结构否则很容易画错字。老老实实用u8g2_DrawUTF8让它内部去处理编码转换和字形查找。这个习惯帮我省了至少两天调试时间。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

商业地产租赁合同关键条款拆解:租金、押金与装修期避坑指南

商业地产租赁合同关键条款拆解:租金、押金与装修期避坑指南

简介:房屋租赁合同书模板是一份面向出租方、承租方及法务行政人员的专业合同范本,适用于商场商铺、写字楼等房屋租赁场景。模板依据《中华人民共和国民法典》及相关法规拟定,对合同主体信息、租赁房屋详情、租赁用途、租赁期限、租金结算与支…

2026/10/15 11:57:51 阅读更多 →
PHPStan 2.0 升级全指南:从 1.x 平滑迁移的配置变更与扩展开发适配

PHPStan 2.0 升级全指南:从 1.x 平滑迁移的配置变更与扩展开发适配

开发工具后端 【免费下载链接】environment Provides functionality that helps writing PHP code that has runtime-specific execution paths 项目地址: https://gitcode.com/gh_mirrors/en/environment 点击查看 免费下载 PHPStan 2.0 是 PHP 静态分析工具的一次…

2026/10/15 11:57:51 阅读更多 →
OpenShell:统一Shell环境管理器的模块化实践

OpenShell:统一Shell环境管理器的模块化实践

在终端里泡了十多年,我试过从原生的 Terminal 一路折腾到各种第三方工具,Zsh、Fish、Starship、Tmux 拼拼凑凑用了一大堆,配置散落在好几个文件里,换台电脑就得重新调半天。后来接触到 OpenShell,整个思路才换了个方向…

2026/10/15 11:57:51 阅读更多 →

最新新闻

安装VSCode插件与安装Vue项目:用TaoToken统一Key打通本地开发链路

安装VSCode插件与安装Vue项目:用TaoToken统一Key打通本地开发链路

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 13:24:55 阅读更多 →
Zabbix 4.2集成Orabbix监控Oracle 12c:安装部署全指南

Zabbix 4.2集成Orabbix监控Oracle 12c:安装部署全指南

有人可能会问:监控Oracle数据库又不是非要用Orabbix,Zabbix自己也有原生模板,为什么还得额外搭一套东西?这个问题的答案,等你真正拿Zabbix自带的Oracle模板去接一套Oracle 12c RAC的时候就明白了——光补齐那些采集项的…

2026/10/15 13:24:55 阅读更多 →
MQTT客户端从选型到排障:QoS、会话、遗嘱与心跳实战指南

MQTT客户端从选型到排障:QoS、会话、遗嘱与心跳实战指南

简介:这是一套围绕 MQTT 协议的自写测试客户端资源,面向物联网开发者、嵌入式工程师及协议学习者,讲解客户端与 Broker 建立连接、发布订阅消息及 QoS 服务质量保障机制,适合初步接触 MQTT 或需要自测通信链路的工程场景。压缩包内…

2026/10/15 13:24:55 阅读更多 →
Node.js浮点精度与%.4f格式化:从原理到工程实践

Node.js浮点精度与%.4f格式化:从原理到工程实践

1. 从C语言的%.4f说起:Node.js里第一次怀疑人生1.1 你搜索的「04f」到底是什么在 C 语言的 printf 里,printf("%.4f", value)的含义是:把 value 按定点小数输出,小数点后固定保留 4 位,写出来就是3.1416、2.…

2026/10/15 13:24:55 阅读更多 →
会写梯形图就能搞定产线?先弄清单点指令与系统集成的真实差距

会写梯形图就能搞定产线?先弄清单点指令与系统集成的真实差距

很多人以为工业自动化就是坐在电脑前敲几行梯形图代码。不少初入行的朋友常有一种错觉:只要把点位启保停背熟,软件仿真跑得顺畅,自己就已经是一名合格的自动化工程师了。 直到真正走进车间现场,面对轰鸣的伺服电机、急停报警的机械…

2026/10/15 13:24:55 阅读更多 →
context-mode:一条命令实现多项目开发上下文的无缝切换

context-mode:一条命令实现多项目开发上下文的无缝切换

说实话,这个项目的源头特别朴素:我在同时维护两个项目,一个是Java后端的微服务,一个是Python数据分析脚本。每天最崩溃的不是写代码,而是切换上下文——cd到对应目录、激活虚拟环境、设置环境变量、加载一堆alias、还得…

2026/10/15 13:23:54 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →