u8g2 与 TouchGFX 在 STM32 上的实战对比:从点阵屏到彩色 TFT,如何选型不踩坑?
你有没有遇到过这样的场景?
项目刚立项,老板说:“做个带屏幕的控制器,界面要简洁明了。”
你心里一盘算:用个 OLED 屏吧,便宜又省事。可下一秒产品经理甩来一张 UI 设计稿——圆角按钮、渐变背景、滑动动画……这哪是“简洁”,分明是迷你版安卓!
于是问题来了:该上轻量级图形库快速收工,还是直接上高端框架一步到位?
在 STM32 平台上,这个问题往往归结为一个经典抉择:u8g2 还是 TouchGFX?
它们代表了嵌入式 GUI 的两个极端——一个是“螺蛳壳里做道场”的极致精简派,另一个是“堆料也要流畅”的性能豪华派。本文不讲虚的,带你从驱动原理、内存布局、刷新机制到开发流程,一层层拆开看清楚:什么时候该用哪个,为什么这么选,以及踩过的那些坑该怎么绕过去。
从一块 128x64 OLED 开始说起
假设你现在手上是一块经典的 SSD1306 驱动的单色 OLED 屏,主控是 STM32F103C8T6(72MHz,20KB SRAM),没有外部存储,也不打算上 RTOS。
这时候你要显示温度、状态图标和简单菜单,你会选什么?
答案几乎是唯一的:u8g2。
它不是最炫的,但它是这种场景下最靠谱的。
u8g2 到底做了什么?
别被名字误导,“u8g2” 听起来像底层驱动,其实它是个完整的图形绘制引擎。它的核心任务只有一个:把你想画的东西,变成能发给屏幕的一串字节流。
由于大多数单色屏(比如 SSD1306)内部没有帧缓存,或者只有行缓冲,所以 u8g2 必须自己模拟一个“画布”。常见模式有两种:
- Page Buffer:每次只处理屏幕的一小页(如 8 行),节省 RAM。
- Full Buffer:整屏内容先画在内存中,再一次性刷出去,避免闪烁。
以 128x64 单色屏为例,全缓冲只需要128 * 64 / 8 = 1KB内存。这点开销对于现代 MCU 来说完全可以接受。
更重要的是,整个过程完全由 CPU 软件实现,不依赖 DMA、GPU 或任何专用外设。这意味着哪怕是最便宜的 F1 系列芯片也能跑起来。
它的优势在哪?
- 极低资源占用:最小配置下不到 1KB RAM + 几 KB Flash。
- 零依赖运行:裸机系统直跑,连 malloc 都不需要。
- 跨平台统一接口:换块 SH1106?改一行初始化即可。
- 字体系统强大:支持自定义点阵、压缩中文库,还能反显、旋转。
但代价也很明显:
- 所有绘图操作都是 CPU 密集型;
- 没有脏区域检测,更新局部内容也得重绘整页;
- 动画容易闪,刷新率难稳定;
- 不支持触摸集成,输入得自己轮询。
换句话说,它是为“信息展示”而生,不是为“交互体验”设计的。
当 UI 变得复杂时,我们该怎么办?
现在设想升级需求:客户想要彩色屏幕、触摸操作、滑动菜单、实时曲线图表。原来的 128x64 OLED 显然不够看了,换成 320x240 的 TFT LCD,带电容触摸。
硬件也得跟上:换到 STM32F429ZGT6,主频 180MHz,带 LTDC 显示控制器和 FSMC 接口,外挂 8MB SDRAM。
此时如果还想着用 u8g2 去画彩色图?恭喜你,将亲手把自己拖入性能地狱。
CPU 得手动计算每个像素颜色、处理透明混合、管理双缓冲切换……帧率可能连 10fps 都不到。
这时候就得请出真正的主角:TouchGFX。
TouchGFX 是怎么“作弊”的?
准确地说,TouchGFX 的秘诀在于两个字:借力。
它充分利用了 STM32 高端型号上的三大神器:
- LTDC(LCD-TFT Display Controller):硬件级图层合成,直接输出 RGB 信号;
- DMA2D(Graphics Accelerator):专用于图像搬运、填充、混合的协处理器;
- 外部 SDRAM:存放帧缓冲区,解放片上 SRAM。
这样一来,原本需要 CPU 亲自下场干的苦力活,全都交给了这些外设。
举个例子:你想把一张 PNG 图标从资源区复制到屏幕上某个位置,并加上半透明效果。
传统做法:
for (y = 0; y < height; y++) for (x = 0; x < width; x++) dest[...] = alpha_blend(src[...], bg[...]);这段代码跑在 CPU 上,耗时且占资源。
而 TouchGFX 会调用:
dma2d->blitAlpha(src, dest, area);一句话触发硬件加速,CPU 几乎不参与运算,DMA2D 自己搞定一切。
再加上双缓冲 + VSYNC 同步翻页,彻底告别画面撕裂。
它不只是个绘图库,而是一整套生态
TouchGFX 的真正杀伤力还不止于此。
它提供了一个完整的MVP 架构 + 可视化设计器(Designer),让你可以用拖拽方式搭建界面,像做网页一样做嵌入式 UI。
- 设计师负责布局、配色、动画;
- 工程师专注业务逻辑,在 Presenter 中响应事件;
- 所有资源(图片、字体、字符串)编译成二进制包,运行时加载。
而且这一切都和 STM32CubeMX 深度集成:你只要勾选 TouchGFX,它就自动帮你生成 LTDC、SDRAM、I²C 触摸等初始化代码。
开发效率提升不止一个数量级。
实战对比:同样是“刷新一行文字”,两者差别有多大?
让我们来看一个具体场景:在一个循环中每隔 1 秒更新一次时间文本。
u8g2 方案(SSD1306 + F103)
void loop() { u8g2_ClearBuffer(&u8g2); u8g2_SetFont(&u8g2, u8g2_font_timB12_tf); char buf[20]; sprintf(buf, "Time: %02d:%02d", hour, min); u8g2_DrawStr(&u8g2, 10, 30, buf); u8g2_SendBuffer(&u8g2); // 分页发送,约耗时 8~12ms HAL_Delay(1000); }关键点分析:
ClearBuffer()清空 1KB 缓冲 → 全内存写操作;DrawStr()字符逐个查表渲染 → 多次循环;SendBuffer()通过 SPI 发送约 1024 字节数据 → 占用总线;- 整体刷新期间 CPU 被重度占用,无法处理其他任务;
- 若同时有按键轮询或通信任务,响应延迟明显。
更糟的是,即使只改了一个数字,也是全屏重绘,SPI 流量一点没省。
TouchGFX 方案(TFT + F429)
void TimePresenter::updateTime(int h, int m) { Unicode::snprintf(timeTextBuffer, 40, "Time: %02d:%02d", h, m); view.updateTimeText(timeTextBuffer); // 触发局部重绘 }后台发生了什么?
- 框架检测到文本控件内容变化;
- 计算“脏区域”(dirty region)——仅包含该文本框矩形;
- 使用 DMA2D 将新文本绘制到前台缓冲对应区域;
- 下一个 VSYNC 到来时,LTDC 自动切换图层指针;
- 整个过程 CPU 仅参与事件调度,负载极低。
实测结果:同样的更新频率下,CPU 占用率下降 70% 以上,且动画平滑无闪烁。
关键差异总结:五个维度硬核对比
| 维度 | u8g2 | TouchGFX |
|---|---|---|
| 适用屏幕 | 单色 OLED/LCD(≤128x64) | 彩色 TFT(≥240x320) |
| 内存模型 | 片上 SRAM 缓冲(1~2KB) | 外部 SDRAM 存储帧缓冲(>300KB) |
| 渲染方式 | CPU 软件绘制,逐像素操作 | DMA2D 硬件加速,块传输 |
| 刷新机制 | 全页/全屏刷新,易闪烁 | 脏区域局部刷新,VSYNC 同步 |
| 开发效率 | 手写代码为主,调试靠打印 | Designer 拖拽 + 自动生成代码 |
💡经验法则:
如果你的 UI 更新面积超过屏幕 30%,且频率 >5Hz,就应该认真考虑 TouchGFX 了。
踩过的坑与避坑指南
u8g2 常见陷阱
❌ 频繁调用sendBuffer()导致 SPI 总线拥塞
现象:UI 卡顿,UART 接收丢包。
原因:SPI 传输期间阻塞其他中断。
解决方案:
- 改用硬件 SPI + DMA 传输(需修改回调函数);
- 减少刷新频率,合并多次更新;
- 使用u8g2_UpdateDisplayPart()实现部分页刷新。
❌ 中文字库太大,Flash 溢出
典型错误:加载完整 GBK 字库,占用 >100KB Flash。
建议做法:
- 使用 BDF 转换工具 生成子集化字库;
- 只保留项目所需字符(如数字+常用汉字);
- 或采用动态加载机制,从外部 Flash 读取。
TouchGFX 高手才知道的事
✅ 帧缓冲地址必须对齐
DMA2D 要求源/目标地址按 32 位对齐。若分配不当会导致 HardFault。
正确姿势:
__attribute__((aligned(32))) uint16_t frameBuffer[2][320*240];✅ 合理设置优先级,防止触摸卡顿
FreeRTOS 中应确保:
- GUI 任务优先级 > 应用逻辑;
- 触摸中断优先级 > 其他非关键中断;
- 使用HAL_LTDCH->Instance->SRCR |= LTDC_SRCR_VBR强制垂直空白期更新。
✅ 控件太多怎么办?用容器和隐藏机制
不要一次性创建所有页面!使用Container和setVisible(false)动态管理内存。
否则启动时报Out of memory in widget tree就晚了。
如何做出正确的技术选型?
别再问“哪个更好”,而是要问:“我的项目到底需要什么?”
下面是我在多个产品迭代中总结出的决策树:
选择 u8g2,如果你:
- 屏幕 ≤1.3 英寸,分辨率 ≤128x64;
- MCU 没有 LTDC/DMA2D 外设(如 F1/F3/L0);
- SRAM < 64KB,无外部存储;
- UI 更新频率 ≤1Hz;
- 开发周期紧张,原型验证优先;
- 成本敏感,BOM 要压到最低。
✅ 典型应用:手持仪表、温湿度计、蓝牙配置助手。
选择 TouchGFX,如果你:
- 屏幕 ≥2.4 英寸,分辨率 ≥240x320;
- 使用 F429/F767/H743 等高端型号;
- 外挂 SDRAM(至少 4MB);
- 需要触摸交互、动画过渡、图表显示;
- 产品面向终端用户,体验要求高;
- 团队中有 UI 设计人员参与。
✅ 典型应用:医疗设备面板、工业 HMI、智能家居中枢、车载后装屏。
特殊策略:高低搭配,软硬兼施
有意思的是,有些产品线采用了“双轨制”:
- 基础版:STM32F1 + u8g2 + OLED,功能简化,售价低;
- 旗舰版:STM32H7 + TouchGFX + TFT,全功能加持。
共用同一套业务逻辑代码,仅 UI 层分离。既能覆盖不同市场,又能控制研发成本。
最后一点思考:GUI 技术演进的方向在哪里?
有人说 LVGL 正在取代 u8g2,也有人说 Embedded Wizard 要挑战 TouchGFX。但在我看来,没有银弹,只有权衡。
未来的趋势可能是:
- 轻量 GUI 开始吸收硬件加速思想:LVGL 已支持 DMA2D 加速;
- AI 辅助 UI 生成:输入描述自动生成界面草图;
- 远程预览与热更新:设计师不用烧板就能看到真实效果;
- 低功耗显示技术结合:比如用 ePaper + u8g2 做超长待机终端。
但无论如何变化,有一点不会变:合适的才是最好的。
就像你不会拿法拉利去越野,也不会开着拖拉机参加 F1。
如果你正在纠结要不要上 TouchGFX,不妨先问自己三个问题:
- 我的屏幕是不是彩色的?
- 用户会不会频繁点击或滑动?
- 未来会不会加动画或图表?
只要有两个“是”,那就别犹豫了——早点上车,少走弯路。
毕竟,没人愿意对着一块“PPT 式”卡顿界面说:“这是我们精心设计的用户体验。”