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)来衔接。

我把它拆成四个阶段:

  1. 应用逻辑阶段: 处理用户输入、更新模型、触发回调。这个阶段不涉及像素操作,但会决定下一帧要画什么。
  2. 绘制阶段: TouchGFX引擎遍历所有可见的Widget,调用它们的draw()方法。这个阶段会操作像素,把文字、图片、形状写入帧缓冲。
  3. 传输阶段: 帧缓冲里的数据,通过LTDC(或DSI、RGB接口)逐行发送给显示面板。这个阶段由硬件DMA完成,基本不占CPU。
  4. 垂直同步阶段: 等待显示控制器发送VSYNC信号。这个信号告诉MCU:“上一帧已经显示完了,你可以开始画下一帧了。”

下面这张图,是我自己画的TouchGFX渲染流水线,你一看就明白:

TouchGFX渲染流水线 TouchGFX 渲染流水线(单帧周期) 应用逻辑 输入处理 / 模型更新 绘制阶段 Widget::draw() 写帧缓冲 传输阶段 DMA / LTDC 送显 VSYNC 等待同步 VSYNC 中断触发下一帧 💡 关键点: 绘制阶段是CPU密集区,也是调优的主战场。传输阶段由硬件完成,一般不是瓶颈。

从这张图你能看到,整个流水线是串行的。也就是说,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四个阶段,其中绘制阶段是调优的重灾区。