1、卡顿现象的本质:从视觉暂留到刷新率
做嵌入式图形开发,最头疼的问题是什么?我猜很多人会脱口而出——卡顿。屏幕一闪一闪的,画面撕裂得像被撕开的报纸,用户一划就骂娘。这问题我入行第三年就撞上了,当时在一个工业手持设备项目里,LCD刷新的撕裂问题差点让产品延期三个月。后来我才明白,要根治卡顿,得先搞清楚屏幕到底是怎么“画”出画面的。
1.1 视觉暂留:人眼是怎么“骗”自己的?
先聊个基础概念。你想想看,电影每秒24帧,为什么我们看着是连续的?
因为人眼有个特性叫视觉暂留。说白了,图像在视网膜上消失后,大脑还会“留”它一小会儿,大约几十毫秒。只要下一帧在这个时间内补上来,大脑就自动脑补成连续运动。
关键数字:视觉暂留时间大约 30~50ms。这意味着刷新间隔超过 50ms,人眼就能感知到“闪”或“跳”。
我在一个电子墨水屏项目里就吃过这个亏。当时觉得刷新率低点没关系,结果用户反馈“翻页像幻灯片”。后来一算,刷新间隔 80ms,远超视觉暂留窗口。嗯,这锅得自己背。
1.2 刷新率:屏幕到底在忙什么?
屏幕刷新不是瞬间完成的。它从左到右、从上到下,一行一行地扫过去。这个过程叫光栅扫描。
拿常见的 60Hz LCD 来说:
- 每秒刷新 60 次,每次约 16.67ms
- 每帧又分成 前肩、同步脉冲、后肩、有效显示区 四个阶段
- 只有在有效显示区,像素数据才会被真正写入屏幕
我习惯把刷新周期比作“画布被一行一行地涂色”。涂到一半的时候,如果你突然换画布——嗯,撕裂就来了。
| 刷新率 | 每帧时间 | 视觉感受 | 常见场景 |
|---|---|---|---|
| 30 Hz | 33.33 ms | 明显卡顿,能感知闪烁 | 早期低端屏、电子纸 |
| 60 Hz | 16.67 ms | 基本流畅,快速运动有撕裂 | 大多数嵌入式 LCD |
| 120 Hz | 8.33 ms | 非常流畅,撕裂不易察觉 | 高端手机、游戏屏 |
我的经验:嵌入式设备别盲目追高刷。60Hz 配双缓冲,比 120Hz 单缓冲体验好得多。功耗和流畅度要一起算账。
1.3 撕裂:为什么画面会“劈叉”?
撕裂(Tearing)是卡顿最典型的“脸面”。
为什么会这样?
假设你的 MCU 在后台往帧缓冲区里画新画面,而屏幕控制器正在读取同一个缓冲区去刷新。如果 MCU 在屏幕扫到一半时把缓冲区内容改了——上半屏还是旧画面,下半屏已经变成新画面了。结果就是一条横贯屏幕的“撕裂线”。
我曾经在一个智能家居面板项目里,用单缓冲跑动画。用户旋转旋钮时,进度条像被刀切了一样。测试妹子当场说:“这产品是不是坏了?”——我当时真想找个地缝钻进去。
避坑指南:我曾经以为只要提高 MCU 刷图速度就能避免撕裂。结果速度越快,撕裂线越明显。因为 MCU 写入越快,越容易在屏幕刷新中途“抢”到缓冲区。撕裂不是速度问题,是同步问题。
1.4 卡顿:不只是撕裂那么简单
撕裂是“视觉分裂”,卡顿是“视觉停顿”。两者经常一起出现,但本质不同。
- 撕裂:缓冲区竞争导致的一帧内画面不完整
- 卡顿:帧率不稳定,某几帧耗时过长,导致画面“跳帧”或“重复上一帧”
我见过一个典型的案例:某团队用 FreeRTOS + LTDC 驱动做仪表盘,任务调度一忙,刷图函数被抢占,帧间隔从 16ms 突然跳到 40ms。用户看到的就是指针“抖一下再跳一下”。
说白了,卡顿的本质就是刷新节奏被打乱。要么是缓冲区没准备好,要么是 CPU 被其他任务拖住了。
1.5 一张图看懂:从写入到显示的全链路
下面这张图是我自己总结的,把整个刷新链路画清楚了。你看一遍就能明白问题出在哪。
1.6 小结:根治卡顿,先看清敌人
这一章我们聊了三件事:
- 视觉暂留是人眼的“缓存”,刷新间隔超过 50ms 就会露馅
- 刷新率决定了每帧的时间窗口,60Hz 下只有 16.67ms 可用
- 撕裂和卡顿本质都是缓冲区读写节奏错乱
我个人习惯,在开始任何图形项目前,先把刷新链路画一遍。哪里可能抢缓冲区,哪里可能被任务打断,一目了然。下一章我们会深入双缓冲的具体实现——但先把这些基础扎牢,后面才不会翻车。
一个小练习:拿示波器抓一下你板子的 VSYNC 和 MCU 写缓冲区的 GPIO 波形。看看写入是否落在有效显示区内。如果是——嗯,你已经找到撕裂的元凶了。