1、双缓冲基础概念:屏幕刷新率、撕裂效应与单缓冲的痛点

各位同学好,我是老吴。做嵌入式UI这么多年,我踩过最多的坑,就是显示缓冲区的配置问题。很多开发者一上来就开双缓冲,但连为什么要用双缓冲都说不清楚。今天这一章,我们先打好基础。

说白了,双缓冲不是什么高深技术。它就是一块「后台画板」。你在这块画板上画完,再整块翻到屏幕上。就这么简单。但为什么需要它?这得从屏幕的刷新机制说起。

1.1 屏幕刷新率——你的屏幕在「闪」

屏幕刷新率,单位是 Hz。60Hz 就是一秒闪 60 次,每次闪叫一帧。每次闪的时候,屏幕从左上角到右下角,一行一行地把像素刷出来。

嗯,这里要注意:屏幕不是瞬间刷完的。它需要时间。比如 60Hz 的屏幕,一帧大约 16.67ms。在这 16.67ms 里,屏幕正在「画」这一帧。如果你在它画到一半的时候,往显存里写了新数据——好戏就来了。

核心概念:刷新率决定了屏幕更新一帧的速度。刷新率越高,人眼感觉越流畅。但高刷新率对 MCU 的带宽要求也更高。

刷新率 每帧时间 常见场景
30 Hz 33.33 ms 低功耗手表、电子价签
60 Hz 16.67 ms 大部分嵌入式屏幕
120 Hz 8.33 ms 高端 HMI、游戏屏

我个人习惯,在项目初期先确认屏幕的刷新率。因为这会直接影响你选 MCU 的 RAM 大小和 DMA 带宽。你想想看,如果 MCU 刷一帧要 20ms,屏幕刷新率却是 60Hz,那必然掉帧。

1.2 撕裂效应——屏幕「劈叉」了

撕裂效应,英文叫 tearing。你肯定见过:屏幕滚动时,画面上下错位,像被撕开了一样。为什么会这样?

我举个例子。你在写一个动画,每 10ms 更新一次画面。屏幕每 16.67ms 刷新一次。假设屏幕刚刷到一半,你往显存里写入了新数据。屏幕下半部分读到的,是新一帧的数据;上半部分还是旧数据。结果就是——上半张脸在笑,下半张脸在哭。

避坑指南:我曾经在一个工业 HMI 项目里,因为没处理撕裂,客户投诉说屏幕「花屏」。排查了两天才发现,不是硬件坏了,是缓冲区竞争导致的撕裂。从那以后,我所有项目都强制加双缓冲或垂直同步。

撕裂的严重程度取决于两个因素:

  • 写入速度:你往显存写数据越快,撕裂的「裂缝」越窄,但出现频率更高。
  • 刷新周期:屏幕刷新率越低,每帧时间越长,撕裂窗口越大。

说白了,单缓冲就是一块显存,CPU 和屏幕控制器同时读写它。不打架才怪。

1.3 单缓冲的痛点——为什么它不够用

单缓冲,就是整个系统只有一块帧缓冲区。CPU 往里面画,屏幕从里面读。听起来简单,但问题一堆。

痛点一:撕裂

上面已经讲了。只要 CPU 写入和屏幕读取不同步,撕裂就必然出现。除非你保证 CPU 在屏幕的消隐区间(VBlank)内写完。但现实是,你的动画逻辑、触摸响应、网络通信,哪个能保证准时写完?

痛点二:闪烁

闪烁和撕裂不一样。闪烁是因为你清空了显存,但还没画完新内容。屏幕读到了「半成品」——一块空白或乱码。下一帧你又画完了,显示正常。再下一帧又清空……人眼看到的就是一闪一闪。

我记得早期做单片机游戏机时,用单缓冲画小球。每次更新前先清屏,再画球。结果球在屏幕上「抖」得厉害。后来加了双缓冲,世界清净了。

痛点三:性能瓶颈

单缓冲下,CPU 必须等屏幕刷完才能写下一帧。否则就会撕裂。这意味着你的帧率被屏幕刷新率锁死了。你想跑 30fps?屏幕是 60Hz,你只能跑 60fps 或 30fps,中间值很难稳定。

小技巧:如果你实在资源紧张只能用单缓冲,可以尝试在 VBlank 中断里更新显存。但前提是你的绘制时间必须小于 VBlank 时间。对于 60Hz 屏幕,VBlank 大约 1-2ms。你确定你的 LVGL 绘制能在 1ms 内完成?

1.4 一张图看懂:单缓冲 vs 双缓冲

下面这张 SVG 图,我画了单缓冲和双缓冲的核心流程对比。你看一眼就明白了。

单缓冲 vs 双缓冲 核心流程对比 单缓冲模式 CPU 写入 屏幕读取 冲突 结果:撕裂 + 闪烁 同一块显存,读写打架 双缓冲模式 前台缓冲 (屏幕读取) 后台缓冲 (CPU 写入) 交换 结果:无撕裂 + 无闪烁 读写分离,各用各的缓冲区 核心差异 单缓冲:一块显存,读写竞争 → 撕裂/闪烁 双缓冲优势 两块显存,读写分离 → 画面完整、流畅、无撕裂

1.5 什么时候必须上双缓冲?

不是所有场景都需要双缓冲。我列几个判断标准:

  • 有动画:只要 UI 里有滑动、旋转、渐变,单缓冲基本扛不住。
  • 有触摸交互:用户手指滑动时,画面必须实时响应。撕裂会让人感觉「卡顿」。
  • 屏幕分辨率高:320x240 以上,单缓冲的撕裂窗口就很大了。
  • MCU 性能弱:越弱的 MCU,绘制时间越长,越容易和屏幕刷新撞车。

我的经验:在 STM32F429 + 480x272 屏幕的项目里,单缓冲下 LVGL 跑动画,撕裂率大约 30%。就是每三帧就有一帧撕裂。用户肉眼可见。换成双缓冲后,撕裂率直接降到 0%。代价是多占用了 480×272×2 = 261KB 的 RAM。值不值?我觉得值。

1.6 本章小结

这一章我们聊了三件事:

  • 屏幕刷新率决定了每帧的时间窗口。
  • 撕裂效应是读写竞争的结果,单缓冲无法避免。
  • 单缓冲的痛点:撕裂、闪烁、性能瓶颈。

双缓冲不是银弹,但它解决了 90% 的显示问题。下一章,我会带你手把手配置 LVGL 的双缓冲驱动。到时候你会看到,代码其实没几行,但背后的原理,就是今天讲的这些。

嗯,先消化一下。有问题随时在群里问我。