2026/9/7 6:29:15

U8g2库实战指南:从Arduino OLED驱动到中文显示与仿真

U8g2库实战指南:从Arduino OLED驱动到中文显示与仿真 简介这套Arduino u8g2图形库资源为需要驱动OLED、LCD和电子纸显示屏的开发者提供了完整的库源码与示例工程。压缩包内含160个文件以96个C语言源文件构成核心绘图引擎53个示例程序展示不同屏幕驱动芯片的初始化与调用方式另有头文件、配置文件及许可说明整体只有7.44MB便于快速部署。u8g2库是经典u8g库的升级版兼容超过两百种显示屏支持高效的位图绘制、多语言与自定义字体以及矩形、圆形、线条等基本图形操作并具备旋转、镜像变换功能其页面模式能明显降低内存占用适合资源有限的Arduino控制器。包内示例覆盖常见OLED和LCD型号演示了主要绘图函数的用法结构清晰可方便地移植到实际项目中。目前已有2366人学习下载无论新手想快速点亮屏幕还是进阶者希望研究底层渲染机制这份资源都能提供扎实的参考价值。 前阵子一个做智能小车项目的朋友问我Arduino Uno 接了一块 0.96 寸 OLED想同时显示速度、方向角度和障碍物距离用什么库最省事我说你直接上 U8g2 吧别再去啃 SSD1306 的数据手册了。很多 Arduino 玩家都走过这条路先点亮一块屏幕然后被驱动、字体、刷新率这些东西反复折腾最后老老实实回到 U8g2 这个库上面。这篇我就把 U8g2 从为什么选、怎么装、怎么写代码、显示中文、到排查问题一次讲透不管你是刚点亮第一块 OLED还是想把界面做得更顺眼都能找到能直接抄的东西。1. 先搞清楚 U8g2 这个库到底解决了什么问题1.1 一个库覆盖市面上绝大多数单色屏U8g2 是德国开发者 Oliver Kraus 维护的开源单色图形库目前几乎是 Arduino 社区显示方案的默认答案。它不是只支持某一块屏而是把所有常见的单色 OLED/LCD 驱动芯片都收编了。我列几个比较常见的驱动芯片常见屏幕典型分辨率SSD13060.96 寸 OLED128x64 / 128x32SH11061.3 寸 OLED128x64SSD13092.42 寸 OLED128x64ST7920经典 12864 LCD128x64PCD8544Nokia 5110 屏84x48ST7565绘图 LCD128x64你只要记住一个 API 风格换屏幕的时候改一下构造函数前缀就能跑起来。比如我现在用的屏幕是 SSD1306如果哪天换成 SH1106 控制的 1.3 寸屏代码里只需要把U8G2_SSD1306_128X64_NONAME_F_HW_I2C改成U8G2_SH1106_128X64_NONAME_F_HW_I2C其他绘图代码一行都不用动。这种解耦能力是自己封装驱动时很难快速做到的。1.2 双缓冲机制U8g2 不闪屏的秘密U8g2 构造函数里有个F或1的标识很多人不知道这是什么意思。这其实是缓冲模式F全称 Full Buffer完整帧缓冲。所有绘图指令先画到内存里的一块缓冲区等你说sendBuffer()时一次性推到屏幕上。1是 Page Buffer页面缓冲。只留一页比如 128x8 像素的内存绘图时分页往屏幕送。拿生活类比完整帧缓冲就像你先在草稿纸上画完整张画再贴到墙上观众看到的是最终结果页面缓冲则是你直接拿笔往墙上分区画画完上半区再画下半区观众能看到过程。所以 F 模式画面更干净不闪不撕裂代价是吃内存。128x64 分辨率全缓冲需要 1024 字节 SRAMArduino Uno 总共只有 2KB用 F 模式会非常紧Nano 也一样。ESP32、STM32F103C8T6 这种内存大的板子随便用 F。还有一个容易忽略的对比对象U8g2 的兄弟库 U8x8。U8x8 只能显示文本和简单字符不能画点线圆但速度极快、内存占用极小。如果你只需要显示两行数字U8x8 更合适只要你想画进度条、波形图或者做图形界面老老实实用 U8g2。1.3 为什么不建议自己手写驱动刷过 SSD1306 数据手册的同学应该知道初始化序列、翻页寻址、显存分割这些内容自己踩一遍少说一两天而且很容易出现基于某一块屏写出来的驱动换屏就废的情况。U8g2 把这些芯片差异全部封装好了内部还有软件 I2C、硬件 I2C、SPI 多种接口选择你要做的只是选对构造函数。我见过不少项目号称“尽量少依赖库”结果显示模块拖了整个项目进度。U8g2 本身开源、免费、社区维护活跃放心依赖没毛病。2. 安装与库目录改造2.1 用库管理器一次性装好在 Arduino IDE 里选择工具 - 管理库搜索框输入U8g2找到作者 Oliver 的U8g2直接安装就行。这里提醒一句如果你的项目里之前装过老版本的 U8glib最好把老库先删掉或者注意两者的区别它们在函数命名上有不少重叠同时保留容易报重复定义。安装完成后在文件 - 示例 - U8g2下能看到官方示例列表比如full_buffer目录里的GraphicsTest。打开这个示例直接编译上传是验证板子和屏幕能不能正常工作的最快方式。我第一次接触 U8g2 就是靠这个示例确认硬件没接错的。2.2 库不想装在 C 盘位置怎么改很多人问 Arduino 安装库怎么改位置。Arduino IDE 默认会把库放在用户目录下比如 Windows 的C:\Users\你的用户名\Documents\Arduino\libraries。C 盘空间吃紧或者你想把整个开发环境迁到 D 盘时可以这样改打开文件 - 首选项 - 设置找到项目文件夹位置也就是 Sketchbook 位置把它改到D:\ArduinoSketchbook重启 IDE。之后再通过库管理器安装库IDE 就会把库放到D:\ArduinoSketchbook\libraries下面。这是一个全局改动对后装的任何库都生效比单个库手动挪来挪去干净得多。如果是手动安装源码包从 GitHub 下载 zip解压后把文件夹名字改干净比如去掉-master后缀放到上面说的libraries目录里重启 IDE 即可。改位置这条经验我是在做多用户开发的时候踩出来的换了电脑路径不对导致库一直识别不到后来统一约定把 sketchbook 路径固定下来问题再没出现过。2.3 不同主控板要注意什么库管理器装的是同一套 U8g2但不同板子的 I2C 引脚不同。Arduino Uno/Nano 默认 SDA 是 A4、SCL 是 A5ESP32 开发板默认 SDA 是 GPIO21、SCL 是 GPIO22ESP8266比如 Wemos D1 mini默认 SDA 是 GPIO4D2、SCL 是 GPIO5D1。用硬件 I2C 的构造函数时系统宏SDA和SCL会根据所选板子自动替换所以写代码时直接写SCL, SDA一般没问题但接线的时候一定要查一下板子引脚定义别按 Uno 的图去接 ESP32。3. 一个能直接抄的 OLED 显示实例3.1 I2C 接线四根线的事以最常见的 0.96 寸 SSD1306 I2C 屏为例模块上一般有 4 个引脚VCC、GND、SDA、SCL。Arduino Uno 接线是VCC - 3.3V 或 5V模块上有稳压电路的用 5V 也没事没有稳压的保险起见用 3.3VGND - GNDSDA - A4SCL - A5一些模块还有 RES 复位脚如果构造函数里把复位脚写成了U8X8_PIN_NONE那 RES 可以悬空如果构造函数里填了某个引脚编号就必须把 RES 接到对应引脚上。我习惯直接用U8X8_PIN_NONE让库自己处理少一根线少一个错误源。3.2 构造函数到底怎么选U8g2 构造函数全名很长很多人一看就懵。拆开看其实有规律U8G2_SSD1306_128X64_NONAME_F_HW_I2CSSD1306屏幕的主控芯片按实际情况选128X64屏幕分辨率NONAME代表屏厂商的命名通常 0.96 寸的通用 SSD1306 屏选 NONAME 就行F完整帧缓冲HW_I2C使用硬件 I2C还有SW_I2C软件模拟 I2C、HW_SPI等构造函数最后面的三个参数U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, SCL, SDA);U8G2_R0是屏幕旋转方向还有R1、R2、R3分别对应 0 度、90 度、180 度、270 度旋转。U8X8_PIN_NONE是复位脚不使用。后面的SCL, SDA指定硬件 I2C 引脚一般直接写这两个宏。3.3 静态显示从初始化到输出第一行字看一个最简示例#include Arduino.h #include Wire.h #include U8g2lib.h U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, SCL, SDA); void setup(void) { u8g2.begin(); u8g2.setFont(u8g2_font_ncenB14_tr); u8g2.clearBuffer(); u8g2.drawStr(0, 24, Hello U8g2!); u8g2.drawFrame(4, 34, 120, 22); u8g2.sendBuffer(); } void loop(void) { }关键就几行begin()完成屏幕初始化setFont()切换字体clearBuffer()清空缓冲区drawStr()在指定坐标画字符串sendBuffer()把缓冲区内容真正推送到屏幕。这里最低级的错误就是画完东西忘了调sendBuffer()屏幕会一直是全空或者旧内容。我第一次用的时候就犯过画了半天调试输出全是黑的最后发现缓冲区确实画了就是没送出去。从那以后我养成了先写sendBuffer()再往里填绘图代码的习惯。3.4 动态刷新别用 delay学会用时间戳如果要在屏幕上做计数器、进度条这类动态效果不能每画一帧就delay(100)否则其他逻辑全部被卡住。正确的做法是维护一个时间戳#include Arduino.h #include Wire.h #include U8g2lib.h U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, U8X8_PIN_NONE, SCL, SDA); void setup(void) { u8g2.begin(); u8g2.setFont(u8g2_font_ncenB14_tr); } void loop(void) { static uint32_t lastTime 0; static int counter 0; static int percent 0; if (millis() - lastTime 500) { lastTime millis(); counter; percent (percent 3) % 100; } uint8_t barWidth percent * 116 / 100; u8g2.clearBuffer(); u8g2.drawStr(4, 20, Demo); u8g2.drawStr(60, 20, String(counter).c_str()); u8g2.drawFrame(4, 32, 120, 14); if (barWidth 0) { u8g2.drawBox(6, 34, barWidth, 10); } u8g2.sendBuffer(); }这个逻辑是只有当millis()间隔超过 500 毫秒时才更新计数和进度条但每轮循环都会刷新画面。这样一来计数逻辑和显示逻辑分开了后续想加按键检测、传感器读取都不受显示刷新拖累。3.5 提速400kHz I2C 不是玄学SSD1306 支持最高 400kHz 的 I2C 时钟而 Arduino 默认通常是 100kHz。在setup()里加一句Wire.setClock(400000);实测刷新一帧全屏从原来大约 30ms 降到十几毫秒肉眼能明显感觉到流畅。不过如果接线比较长、线上干扰大400kHz 下可能出现花屏这种情况降回 100kHz 更稳。先跑 100kHz 确认屏幕稳定再提速是稳妥顺序。4. U8g2 的进阶玩法中文显示与在线仿真4.1 自带字体不支持中文怎么办U8g2 内置的字体非常多数字、字母、图标都有但默认字体里没有 CJK 中文。想要在 OLED 上显示“你好”这类中文得先有自己的中文字体数组。常用做法是拿文泉驿点阵字体转出 U8g2 能用的 C 语言字体文件。流程是下载 BDF 格式的中文字体比如文泉驿微米黑、文泉驿点阵宋体打开 U8g2 源码仓库tools/font/bdfconv目录下的转换工具用命令行把 BDF 转成 C 数组命令行大致是这样的bdfconv -v -f -n u8g2_font_wqy12_t_chinese -m 32-127,0x4e00-0x9fff wqy-microhei-12.bdf -o wqy12_t_chinese.c参数里-n是给新字体取名字-m指定要包含的字符范围-o是输出文件。生成的.c文件放到项目目录里Arduino IDE 会自动编译进去。代码里这样用extern const uint8_t u8g2_font_wqy12_t_chinese[] U8G2_FONT_SECTION(u8g2_font_wqy12_t_chinese); u8g2.setFont(u8g2_font_wqy12_t_chinese); u8g2.drawUTF8(0, 20, 你好U8g2);注意这里用的是drawUTF8不是drawStr因为中文字符串是 UTF-8 编码。Arduino IDE 默认源码文件就是 UTF-8 编码所以直接写中文没问题。有个坑必须提醒全量中文转出来的字体文件很大动辄几百 KBArduino Uno 的 Flash 根本放不下。我的习惯是先用脚本把项目里出现的中文字符收集出来再和常用字表合并去重只保留需要的那几百个字。这样 12 像素中文字体压缩到几十 KBUno 勉强能跑ESP32 就毫无压力。4.2 没有硬件也能调 U8g2在线仿真热词里有“u8g2 模拟器”和“wokwi 仿真平台 arduino”这两个确实是好东西。Wokwi 是一个浏览器里的硬件仿真平台支持 Arduino Uno、ESP32 等板卡也支持 SSD1306 OLED。在 Wokwi 新建一个 Arduino Uno 项目添加 SSD1306 OLED在项目设置里引入 Arduino 库 U8g2然后把上面的示例代码贴进去就能直接跑。我经常在没有屏幕在身边的时候用 Wokwi 验证绘图坐标和刷新逻辑确认没问题再拿到实机上跑省掉了很多来回烧录的时间。U8g2 官方仓库的tools目录里其实也有一个模拟器但要在 PC 上编译运行需要配 SDL 环境比 Wokwi 麻烦不少。对多数人来说Wokwi 在线跑一遍 U8g2 就够用了。4.3 屏幕自动换行没有得自己处理U8g2 的drawStr和drawUTF8都不自动换行画到屏幕右边界就直接截断。做多行文本时需要自己维护行号和每行的像素宽度。12px 字体的行高一般 14~16 像素第二行起始 y 坐标要比第一行多 16 左右。一个简单的思路是封装一个drawMultiLine()函数按\n分割字符串逐行绘制。说实话这个功能不算难但很多新手第一次遇到时都会觉得“咦怎么不换行”。5. 高频问题排查速查表5.1 现象、原因、解决现象可能原因解决办法屏幕完全不亮接线错误、供电不足检查 VCC/GND先用 5V 供电试试用 I2C 扫描程序确认设备地址屏幕上只有白点或花屏I2C 地址错误、总线速度过高常见地址是 0x3C也有 0x3D把 Wire 时钟降到 100kHz显示内容闪烁、有残影用了页面缓冲模式或刷新频率不固定换成F模式用millis()固定刷新间隔中文显示乱码字体没生成正确或用了drawStr确认字体名称一致改用drawUTF8编译报 RAM 不足故意选了完整帧缓冲换成_1_页面缓冲模式或换 ESP32 等大内存板卡屏幕亮度很低对比度参数默认偏暗在begin()后调用u8g2.setContrast(180)试试I2C 扫描程序网上到处都是核心就几行Wire.begin()然后循环扫描地址。把所有可能地址都扫一遍输出到串口立刻就能知道屏幕挂在哪个地址上。很多模块背面会直接标出地址但改过地址的模块就得靠扫描确定。5.2 我踩过的两个大坑第一个坑是 Uno F 模式。当时项目要显示动态曲线我图省事选了_F_HW_I2C编译能过上传之后一跑就随机重启。查了半天纳米级的内存余量经不起字符串拼接的临时变量最后换成_1_页面缓冲模式才稳定。后来换到 ESP32 上我才重新用回 F 模式。建议 Uno/Nano 用户默认选_1_模式别挑战 2KB SRAM 的极限。第二个坑是 Wemos D1 mini 上的接线。我把 SDA 接到了 D2、SCL 接到 D1但代码里写的是U8G2_SSD1306_128X64_NONAME_F_SW_I2C软件 I2C没有指定引脚默认用的还是硬件宏对应的引脚结果屏幕不亮。软件 I2C 构造函数后面可以传自定义引脚比如u8g2(U8G2_R0, U8X8_PIN_NONE, /* clock*/ D1, /* data*/ D2)但一定要和实际接线对应上。很多“屏幕不亮”的案例最后查出来都是软硬件引脚对不上。STM32F103C8T6 用 Arduino 框架跑 U8g2 也是同理先确认你的 I2C 引脚映射硬件 I2C1 默认 PB6/PB7如果你用的是 PB8/PB9就需要在代码里指定或者用软件 I2C。5.3 连续刷新时的主题切换技巧做界面切换时如果不先clearBuffer()就画新界面旧内容会残留。虽然 F 模式下sendBuffer()会覆盖整屏但clearBuffer()仍然建议每次刷新开头都调一次。另外如果想做局部更新减少刷新时间可以先只擦除需要改变的区域比如用一个填满背景色的drawBox覆盖旧内容再画新的文字。这个技巧在 ESP32 上做仪表盘界面时非常有用。6. 几个我至今还在用的使用习惯先说第一个习惯新建项目永远是先跑官方GraphicsTest示例再改自己的代码。这个示例把所有基本绘图函数都跑了一遍屏幕不会显示、旋转方向不对、刷新闪屏之类的问题一眼就能看出来。很多人拿到新屏直接粘贴自己的业务代码出了问题就分不清是硬件还是代码的问题。第二个习惯是显示和逻辑分离。我把所有 U8g2 绘图代码封装到updateDisplay()函数里主循环只负责采集数据和修改状态。这样一来如果传感器读取阻塞了屏幕内容不会跟着乱掉想改界面布局也只需要动一个函数。第三个习惯是串口日志先于屏幕调试。很多人一上来就只靠屏幕输出调试信息屏幕正常还好屏幕一出问题就完全抓瞎。我在代码里会同时往Serial.println和屏幕写日志串口能告诉我程序跑到哪一步了屏幕能告诉我显示效果是否符合预期。两边对照问题定位速度快得多。最后再分享一个小技巧U8g2 的字体选择对画面质感影响很大。u8g2_font_ncenB14_tr适合标题u8g2_font_6x10_tf适合小号正文u8g2_font_courB18_tn适合做成大数字显示。我做过一个电子钟用大数字字体加setContrast调亮度显示效果比默认字体好一大截。字体列表在 U8g2 源码的tools/font/build/single_font_all.txt里能查到挑一个顺眼的慢慢试。本文还有配套的精品资源点击获取