1. 初识刷新率:什么是FPS?为什么60帧是黄金标准?TouchGFX的渲染流水线概览
各位同学,咱们开始上课。
做嵌入式UI,尤其是用TouchGFX,你迟早会碰到一个绕不开的话题——刷新率。说白了,就是你的屏幕画面每秒能翻新多少次。我见过不少项目,功能逻辑写得飞起,结果一上机,界面卡得像幻灯片。用户点个按钮,半秒后才看到反馈。嗯,这种体验,基本等于产品宣判死刑。
所以第一章,咱们先把刷新率这个概念掰开揉碎。搞清楚它是什么,为什么60帧成了行业默认的“黄金标准”,以及TouchGFX到底是怎么把一帧画面送到屏幕上的。
1.1 什么是FPS?
FPS,全称Frames Per Second,每秒传输帧数。在UI领域,它直接决定了你看到的动画是否流畅。
你可以把FPS想象成翻书动画。一本书有100页,你1秒钟翻60页,看起来动作就连贯。如果你1秒钟只翻10页,中间的黑页停顿就会非常明显,这就是卡顿。
在TouchGFX里,FPS就是MCU从开始绘制一帧,到这一帧完全显示在屏幕上的次数。我习惯用下面这个公式来估算:
FPS = 1 / (单帧渲染时间 + 等待时间)
举个例子:
- 如果你的单帧渲染时间是10ms,等待时间也是10ms,那FPS = 1 / 0.02 = 50 FPS。
- 如果渲染时间变成30ms,FPS就掉到33左右。
你看,渲染时间稍微一涨,帧率就断崖式下跌。这就是为什么调优的核心,就是压缩那该死的单帧渲染时间。
核心概念: FPS不是越高越好,但低于30 FPS,人眼就能明显感知到卡顿。60 FPS是“流畅”的基准线。
1.2 为什么60帧是黄金标准?
这个问题,我当年刚入行时也问过。为什么不是30?为什么不是120?
答案其实跟人眼的生理特性有关,也跟显示器的历史有关。
第一,人眼的视觉暂留效应。
人眼对连续画面的感知,有一个临界点。当画面刷新频率超过每秒50-60次时,大脑就会认为这是一个连续运动的画面,而不是一帧一帧的静态图。低于这个频率,闪烁感就出来了。我最早做VGA驱动的时候,把刷新率降到50Hz,屏幕就开始闪,眼睛看一会儿就酸得不行。
第二,与交流电频率的历史渊源。
早期的CRT显示器,刷新率跟电网频率挂钩。50Hz(欧洲)或60Hz(美国)是最容易实现的。这个标准一直延续到了液晶屏时代。虽然现在的LCD没有物理闪烁了,但60Hz作为默认的垂直同步频率,已经成了整个显示生态的基石。
第三,性能与功耗的平衡点。
在嵌入式领域,尤其是电池供电的设备上,60帧是一个性价比极高的选择。你想想看:
- 30帧:功耗低,但动画明显掉帧,滑动列表有拖影。
- 60帧:功耗适中,动画丝滑,用户感知极好。
- 120帧:功耗翻倍,但人眼在手机小屏上的感知提升有限,MCU的发热量却可能失控。
| 帧率 | 单帧时间 | 流畅度 | 典型场景 |
|---|---|---|---|
| 30 FPS | 33.3 ms | 可接受,有卡顿感 | 低功耗手表、静态菜单 |
| 60 FPS | 16.6 ms | 流畅,黄金标准 | 智能家居、车载仪表 |
| 120 FPS | 8.3 ms | 极流畅,但代价高 | 高端手机、游戏机 |
我的经验: 在TouchGFX项目中,我一般把目标锁定在55-60 FPS。不要死磕60.0,只要稳定在55以上,用户基本感觉不出区别。但一旦掉到45以下,就必须介入调优了。
1.3 TouchGFX的渲染流水线概览
好了,知道了目标,我们得看看TouchGFX是怎么工作的。只有理解了流水线,你才知道瓶颈在哪。
TouchGFX的渲染,本质上是一个“生产-消费”模型。MCU负责生产帧,显示控制器负责消费帧。中间通过帧缓冲(Frame Buffer)来衔接。
我把它拆成四个阶段:
- 应用逻辑阶段: 处理用户输入、更新模型、触发回调。这个阶段不涉及像素操作,但会决定下一帧要画什么。
- 绘制阶段: TouchGFX引擎遍历所有可见的Widget,调用它们的
draw()方法。这个阶段会操作像素,把文字、图片、形状写入帧缓冲。 - 传输阶段: 帧缓冲里的数据,通过LTDC(或DSI、RGB接口)逐行发送给显示面板。这个阶段由硬件DMA完成,基本不占CPU。
- 垂直同步阶段: 等待显示控制器发送VSYNC信号。这个信号告诉MCU:“上一帧已经显示完了,你可以开始画下一帧了。”
下面这张图,是我自己画的TouchGFX渲染流水线,你一看就明白:
从这张图你能看到,整个流水线是串行的。也就是说,MCU必须等VSYNC信号来了,才能开始画下一帧。如果你在绘制阶段花了20ms,那留给应用逻辑和等待的时间就只剩-3.4ms了——这显然不可能,所以帧率就会掉。
避坑指南: 我曾经在一个项目中,把大量计算放在了绘制阶段里。结果每次draw()都要做浮点运算,单帧时间直接飙到40ms。后来我把计算挪到了应用逻辑阶段,利用空闲时间预计算,绘制阶段只做像素填充,帧率瞬间回到了58 FPS。记住:绘制阶段不要做任何非必要的计算!
1.4 帧缓冲与双缓冲机制
聊流水线,就不得不提帧缓冲。TouchGFX支持两种模式:单缓冲和双缓冲。
- 单缓冲: MCU直接在正在显示的帧缓冲上画图。优点是省内存,缺点是会出现“撕裂”——屏幕上半部分是新帧,下半部分还是旧帧。
- 双缓冲: 两个帧缓冲。一个用于显示(Front Buffer),一个用于绘制(Back Buffer)。绘制完成后,通过切换指针来交换。彻底消除撕裂。
我个人的建议是:只要内存够,无脑上双缓冲。撕裂现象在静态界面不明显,但一旦有滑动、旋转动画,撕裂感会非常掉价。
| 特性 | 单缓冲 | 双缓冲 |
|---|---|---|
| 内存占用 | 1个Frame Buffer | 2个Frame Buffer |
| 撕裂风险 | 高 | 无 |
| 实现复杂度 | 低 | 中 |
| 推荐场景 | 内存极受限、静态UI | 动画丰富、追求品质 |
小技巧: 在TouchGFX Designer中,你可以在项目设置里选择“Double Buffer”模式。如果你的MCU有足够大的SRAM或SDRAM,我强烈建议开启。如果内存吃紧,可以考虑使用局部刷新(Partial Buffer)策略,只刷新变化区域,也能有效降低内存需求。
1.5 本章小结
这一章,我们搞清楚了三个核心问题:
- FPS 就是每秒画面刷新次数,它直接决定了用户体验。
- 60帧 是性能与功耗的黄金平衡点,也是嵌入式UI的默认目标。
- TouchGFX的渲染流水线 分为应用逻辑、绘制、传输、VSYNC四个阶段,其中绘制阶段是调优的重灾区。