简介:Arduino的u8g2字库压缩包,聚焦OLED屏幕显示需求,面向正在学习硬件控制和嵌入式界面设计的开发者,尤其适合从零开始接触屏幕驱动的初学者。压缩包大小约52.36MB,已有2491人学习下载,解压后放入Arduino库目录即可被IDE直接识别,免去复杂配置。u8g2作为u8glib的第二版,在设备兼容性、内存管理和图形能力上大幅增强,覆盖超过200种显示控制器,支持点、线、矩形、圆、文本、位图、滚动动画,以及自定义字体和多语言渲染。借助绘图函数,可在SSD1306、SH1106等常见OLED屏上快速搭建信息看板、温度计、菜单系统或物联网终端;通过实操还能理解缓冲区刷新、光标定位、字体切换等底层机制,为后续复杂显示项目打下扎实基础。资源应用场景广泛,适用于智能家居、可穿戴设备和教学实验,是Arduino开发者进行屏幕界面开发的高性价比工具。 很多玩Arduino的朋友只要碰过OLED屏,基本都会遇到同一个坎:英文、数字、符号显示得清清楚楚,一换中文就变成方块、问号或者干脆白屏。我最早在ST7920这种自带中文字库的LCD上没吃过这亏,后来换了SSD1306 OLED,用u8g2库驱动,才发现事情没那么简单。u8g2是目前Arduino生态里最常用的单色屏驱动库,但它本身不会内置完整的中文字形,想显示中文,就得给它塞一份字库。网上最常见的资源叫“u8g2.zip字库”,下载解压、放进工程、改一行字体名,中文就出来了。这篇文章就围绕这套流程,把u8g2.zip字库的构成、放置方法、代码接入和踩坑点一次说清楚。适合被中文显示卡住的新手,也适合想自己定制一套字体的朋友。
1. 先搞懂u8g2.zip字库里到底有什么
1.1 为什么Arduino默认显示不了中文
这要从显示链路说起。OLED屏的驱动芯片(比如SSD1306)本身只认像素,它不区分“这是个汉字”还是“这是个方块”。你调用u8g2的drawStr画中文时,库函数不知道“中”字的笔画长什么样,它只能去字库里查。Arduino标准库里没有任何自带中文字形,u8g2库虽然是显示驱动库,但官方只内置了少量西文字体和一部分Unifont字符,常见汉字并不全。所以,硬件侧没有点阵、库侧没有字形、文件侧没有字库,三层都缺,自然只能显示乱码。
另外,主流Arduino开发板的Flash都很小。Uno只有32KB,一块全量中文点阵字库动辄几百KB,就算想内置也放不下。所以u8g2的默认设计是:字体以C语言数组形式编译进固件,你往工程里塞了多少个字,就要付出多少Flash开销。
1.2 字库文件的本质:点阵、索引和偏移表
u8g2里的一个中文字体,本质上就是一张“编码到点阵”的查找表。比如16x16的汉字,每个字需要32字节来记录256个像素点,0代表不点亮,1代表点亮。你屏幕上看到的每一个汉字,其实都是程序按这张表逐点画出来的。
具体到代码层面,u8g2的字体文件一般由几段组成:位图数据(bitmap)、位图偏移表(bmp_offsets)、编码区间表(encoding)。显示字符串时,流程是这样的:先把UTF-8序列解码成Unicode码点,再在编码区间表里找到这个字符落在哪个区间,接着通过偏移表定位到位图数据中的存储位置,最后读取点阵画到缓冲区。如果拿房子来类比,编码区间表是小区楼号,偏移表是单元门牌,位图数据是每个房间的户型图。
1.3 解压一个u8g2.zip,通常看到哪些文件
网上流传的u8g2.zip字库包有两种。第一种是纯字体资源包,解压后通常是两个文件:u8g2_font_xxx.c和u8g2_font_xxx.h,有时还会带一个example.ino演示文件。这种包需要在Arduino工程里手动引入,不是用IDE的“Add .ZIP Library”装的。第二种是打包成完整库的形式,里面有library.properties,这种情况下可以直接用“Add .ZIP Library”安装,但本质还是把字体文件编译进工程。
我建议拿到任何zip先解压看结构,再决定放哪。最怕的是明明下载的是字体资源包,却把它当Arduino库去安装,结果IDE没反应,白白浪费一晚上。
2. 获取或生成自己的中文字库:两种路线
2.1 路线A:直接使用现成zip字库包
如果你对字体没有特殊要求,用别人打包好的现成字库最快。下载后和“手工搬文件”两步:
- 在sketch目录下新建一个
src文件夹。 - 把zip里的
.c和.h文件复制进去。 - 在代码里include这个头文件,并使用里面声明的字体变量。
放置方式要注意,Arduino IDE对同级的.c文件有时不会自动编译,放进src子目录后编译系统会默认把它纳入编译,这是最稳妥的放法。如果你希望多个工程共用这份字库,也可以在libraries目录下手动新建一个文件夹,比如Arduino/libraries/u8g2_font_chinese/,把文件放进去。以后在代码里写成#include <u8g2_font_xxx.h>就行。
这里顺便回应一个很多人在搜的问题:Arduino安装库的位置怎么改。默认的libraries目录在文档/Arduino/libraries下,如果磁盘或项目要求特殊,可以手动把整个字库文件夹放到任意路径,然后用#include "完整路径"引用。不过个人不建议这么干,因为路径一旦换了机器就失效,维护成本很高。放在src目录里跟随sketch走,才是最省心的。
2.2 路线B:本地生成,BDF字体加bdfconv转换
想定制字体,比如想用思源黑体、想只用自己界面里的几十个汉字,就需要自己生成。u8g2的字体转换链是:TTF/OTF转BDF,BDF再转C数组。
第一步,用FontForge打开想用的TTF字体,设置一个点阵尺寸,比如16像素或12像素,然后“生成BDF字体”。BDF是一种很老但很清晰的文本点阵格式,u8g2的转换工具只吃这种格式。
第二步,在u8g2源码目录的tools/font下找到bdfconv工具,用命令行转换。参考命令大致是这个样子:
./bdfconv -f 32-255 -u -n u8g2_font_custom16 -o u8g2_font_custom16.c custom16.bdf不同版本的参数可能略有差异,运行前用./bdfconv -h看帮助确认。-f指定要保留的编码范围,-u启用UTF-8映射,-n指定生成后的字体变量名,-o指定输出文件路径。转换完成后,得到的.c文件可以直接放进工程的src目录。
2.3 控制字库体积:常用字子集的思路
先把账算清楚:16x16汉字一个大概32字节,1000字就是32KB,接近Uno整个Flash容量。所以Uno这类小Flash板子,根本没法做全字库。哪怕只放3500个常用字,也要112KB左右,明显超了。
解决办法是做子集。先把界面文案全部写出来,比如“温度”、“湿度”、“时间”、“星期”,去重后统计一遍,大部分场景不会超过100个不同的汉字。100个汉字配16x16点阵,只要3.2KB,Uno绰绰有余。所以我的经验是:先列文案,再生成字库,永远不要为了“万一以后要用”而把整本新华字典塞进去。真要做大而全,可以直接换ESP32这类Flash在4MB以上的板子,没必要在Uno上死磕。
3. 把字库接入Arduino工程:目录与代码细节
3.1 文件放哪里,IDE才会自动编译
如果你把字体资源放在sketch文件同级,Arduino IDE并不会保证一定编译它,容易出现“undefined reference”的链接错误。推荐用下面这种结构:
MyProject/ MyProject.ino src/ u8g2_font_custom16.c u8g2_font_custom16.h在ino里这样引用:
#include "src/u8g2_font_custom16.h"如果文件放在libraries目录下,则写成:
#include <u8g2_font_custom16.h>两种方式本质上都是让连接器找得到字体结构体定义,选一种即可,别同名字体文件出现两份,否则会链接冲突。
3.2 setFont与drawUTF8的正确组合
接入代码并不复杂,核心就三行:
u8g2.setFont(u8g2_font_custom16); u8g2.setFontMode(1); u8g2.setFontDirection(0);设置完字体后,显示中文时不要用drawStr,要用drawUTF8:
u8g2.firstPage(); do { u8g2.drawUTF8(0, 16, "你好,世界"); } while (u8g2.nextPage());drawStr把字符串当单字节的ASCII/Latin-1处理,遇到中文的UTF-8多字节序列就会逐个显示乱码。drawUTF8会把字符串按UTF-8解码,再映射到字体表里的Unicode区间,中文才能正常出来。所以一旦发现“半正常半乱码”,先看看是不是用错了函数。
同时确认Arduino IDE保存文件时用的是UTF-8编码,不要把源文件存成GBK或ANSI,否则代码里肉眼看着正确,编译后的字节流却是错的。
3.3 多字体、坐标对齐和页面刷新
做过界面的朋友都知道,全用一套中文字体会让数字很难看,尤其是时间、温度数字。我习惯这样搭配:标题和单位用中文字库,数字和大号字符用西文字库,需要时随时setFont切换。u8g2里切换字体只是换一个指针,开销很小,loop里频繁切换也没问题。
坐标对齐是另一个高频坑。drawUTF8的y坐标默认是基线,不是字顶。所以第一个字符往往显示在预期位置偏下的地方。要让它贴顶,可以用u8g2.setFontPosTop();要恢复默认,用u8g2.setFontPosBaseline()。多行文本的行距可以直接用u8g2.getFontAscent()加getFontDescent()估算,比写死坐标靠谱。
页面刷新推荐用firstPage/nextPage这套写法,它适用于页缓冲模式,也能在全缓冲模式正常工作。不要在循环里反复clearBuffer又sendBuffer,容易闪屏和浪费CPU。
4. 常见问题与排查技巧实录
4.1 满屏乱码,第一反应先查这三处
| 现象 | 可能原因 | 解决办法 |
|---|---|---|
| 满屏方块 | 字符根本不在字体子集里 | 换字符集更大的字体,或补生成字模 |
| 前面正常后面乱 | 用了drawStr而不是drawUTF8 | 换成drawUTF8 |
| 全部乱码但英文正常 | 源文件编码不是UTF-8 | IDE里另存为UTF-8编码 |
| 字体没变化 | setFont变量名写错 | 对照.h里声明的完整名字 |
我的血泪教训是:下载字体zip后,先打开.h文件确认字体变量名。很多人习惯性用文件名猜测,结果setFont里写错一个字符,编译还不报错,画面却毫无反应,排查半天才发现是名字写岔了。
4.2 编译报undefined reference与stray character
报undefined reference to u8g2_font_xxx,基本就是.c文件没有被编译进工程。优先检查文件是否在src子目录。也见过有人把.c文件改成.cpp后缀,结果内部语法的旧风格和新C++标准不兼容,出现莫名报错,所以别乱改后缀。
报stray '\xxx' in program则是源文件里出现了非法字节,多半是字符串编码不对。用记事本或IDE重新保存为UTF-8无BOM格式,能解决大部分这类问题。
4.3 RAM爆了、屏幕闪、刷新慢怎么办
全缓冲模式下,128x64的OLED约需要1KB RAM作为帧缓冲,Uno只有2KB RAM,再叠加其他变量很容易溢出。症状是编译能过,运行起来花屏或反复重启。解决方向有两个:一是把屏幕构造函数里的大写F改成1,比如U8G2_SSD1306_128X64_NONAME_1_SW_I2C,用页缓冲模式,缓冲降到128字节;二是压缩业务逻辑,尽量减少全局数组。
刷新慢多半和I2C速率有关。SSD1306默认I2C时钟可能只有100kHz,如果屏幕支持,可以试试在begin后设置更高频率,或改用SPI版本。SPI的速度优势明显,但会多占两三根引脚,看你的硬件余量。
4.4 在模拟器上验证:u8g2模拟器的价值与局限
如果你暂时没有屏幕,或者不想反复烧录,u8g2自带PC模拟器工程,可以在电脑上预演显示效果。它对确认坐标、对齐、翻页节奏很有用。但要注意,模拟器里的字体源文件仍然是.c数组,它不会自带中文字体。你依然要把之前在Arduino工程里用的字体文件复制到模拟器工程中。模拟器验证通过不代表真机一定没问题,因为不同单片机平台的字节对齐、字库变量声明可能有差异,最后必须回真机跑一遍。
5. 实操心得:先把字库“瘦身”,再谈全能
5.1 3500常用字足够覆盖绝大多数场景
做产品原型或小工具时,我从来不追求全量字库。先把界面上的所有文案集中到一个txt文件里,用脚本去重,统计出来通常只有几十到几百个不同汉字。像“年月日时分秒星期一二三四五六日”这类时间场景,大概不到50个字,12像素字模随便放。
如果你用Python,可以这样提取字符集:
text = open("ui.txt", encoding="utf-8").read() uniq = sorted(set(text)) open("chars.txt", "w", encoding="utf-8").write("".join(uniq))得到chars.txt后,再用工具把这份字表对应到BDF里保留的字形范围,最后一次性转成u8g2字体C文件。这样生成的字体,既小又干净,编译起来也快。
5.2 中文字符串的内存与Flash问题
AVR平台下,字符串常量默认放在Flash。u8g2的部分绘制接口对Flash字符串的处理有一些历史坑,不同版本表现不一样。我的习惯是:较短的显示文本直接用字符串字面量传给drawUTF8,复杂一点的先放到char buf[]里再调用。如果文本很长,建议按屏拆分,不要一次性组一条大字符串。
另外,不要在setup里反复重建字符串,能放全局常量就不要在loop里拼。Arduino Uno这种板子RAM少,随便几个数组就可能溢出,有条件就先把串口打印调试关掉,能省不少内存。
5.3 我建议的工作流
按下面这套顺序做,基本能少走很多弯路:
- 确定MCU,查Flash和RAM:Flash决定能塞多少字,RAM决定能不能用全缓冲。
- 列界面文案:所有要显示的中文先写出来,去重统计。
- 选字体:小屏我用12像素或16像素的方块字,大屏用20像素起,但字越大Flash消耗越快。
- 生成字库:推荐bdfconv命令行方案,写成一个脚本,以后更新文案重新生成一次就行。
- 代码接入:用src目录结构,include加setFont加drawUTF8。
- 真机验证:先显示一个“中”字,正常以后再铺量。
最后分享一个小技巧:在ESP32这类大Flash板子上,我会做一个“中文字库+数字大字体”的组合。标题、单位、状态提示用中文字库,数字和英文字母单独用一套大号西文字体,显示时间温度时既清晰又不占额外字节。这个组合我在多个项目里实测下来,稳定、省心、显示效果也最舒服。
本文还有配套的精品资源,点击获取