帧率与刷新率:LVGL的帧率概念、屏幕刷新率与动画流畅度的关系

做嵌入式UI,尤其是用LVGL做动画的时候,有两个词你肯定绕不开:帧率和刷新率。

很多人把它们混为一谈,觉得“屏幕刷新率60Hz,那动画就是60帧”。其实不是这么回事。我刚开始做项目时也踩过这个坑,以为硬件参数到位了,动画就一定流畅。结果呢?动画一跑起来,肉眼可见的卡顿。后来一查,LVGL的帧率和屏幕的刷新率根本就是两码事。

今天我们就来把这层窗户纸捅破。

1. 屏幕刷新率:硬件的“底牌”

屏幕刷新率,单位是Hz,指的是屏幕每秒钟更新画面的次数。常见的LCD屏是60Hz,也就是每16.67ms刷新一次。高刷屏有90Hz、120Hz,甚至更高。

这个参数是硬件决定的,你改不了。它就像一条高速公路的限速——最高就跑这么快。

关键点:屏幕刷新率是LVGL动画流畅度的上限。你的动画帧率再高,如果屏幕只有60Hz,那用户实际看到的画面最多也就是60fps。

2. LVGL的帧率:软件能跑多快

LVGL的帧率,单位是fps(Frames Per Second),指的是LVGL的绘图引擎每秒钟能完成多少次完整的渲染和刷新。

这个值取决于很多因素:

  • MCU的主频和架构——算力够不够
  • 内存带宽——显存读写快不快
  • 渲染复杂度——动画里有多少对象、用了多少特效
  • LVGL的配置——比如是否开启了部分刷新、是否用了DMA2D

说白了,LVGL的帧率是软件实际跑出来的成绩。它可能比屏幕刷新率低,也可能比屏幕刷新率高。

注意:如果LVGL的帧率低于屏幕刷新率,动画就会掉帧。如果LVGL的帧率高于屏幕刷新率,多余的帧会被丢弃,造成撕裂(tearing)。

3. 帧率与刷新率的匹配关系

理想的匹配是:LVGL帧率 = 屏幕刷新率,或者LVGL帧率是屏幕刷新率的整数倍(比如60Hz屏幕配30fps或60fps)。

我习惯用一个表格来对比这两种情况:

场景 屏幕刷新率 LVGL帧率 用户感受
理想 60Hz 60fps 丝滑流畅
帧率不足 60Hz 30fps 明显卡顿,掉帧
帧率过高 60Hz 90fps 画面撕裂,浪费算力
整数倍匹配 60Hz 30fps 尚可接受(需开启垂直同步)

为什么会撕裂?因为LVGL在屏幕还没完成当前帧刷新时,就把下一帧的数据写进了显存。屏幕读到一半,画面就“劈叉”了。

4. LVGL的帧率控制机制

LVGL本身有一套帧率控制逻辑。核心是 lv_timer_handler() 这个函数。

你想想看,LVGL的主循环里,每次调用 lv_timer_handler(),它会处理所有待执行的定时器任务,包括动画的更新。然后通过 lv_disp_flush_ready() 通知屏幕驱动刷新。

LVGL默认的帧率上限是 LV_DISP_DEF_REFR_PERIOD,单位是毫秒。默认值是30ms,对应大约33fps。

// lv_conf.h 中的配置
#define LV_DISP_DEF_REFR_PERIOD    30    // 默认30ms,约33fps

这个值可以调。如果你把周期改成16ms,理论上帧率就能跑到60fps。但前提是你的MCU和屏幕能扛得住。

我的经验:不要盲目追求高帧率。我曾经在一个STM32F429的项目上,把刷新周期从30ms改到16ms,结果CPU占用率直接飙到95%,其他任务全被饿死了。后来我老老实实调回30ms,配合部分刷新和DMA2D,动画反而更稳。

5. 帧率与动画流畅度的关系

动画流畅度,说白了就是人眼的感受。这里有个常识:

  • 24fps:电影级,勉强能接受,但快速移动会模糊
  • 30fps:嵌入式UI的及格线,大部分场景够用
  • 60fps:丝滑,人眼基本感觉不到卡顿
  • 120fps:极致,但嵌入式设备很少需要

但要注意,帧率只是其中一个维度。还有一个关键因素是帧间隔的稳定性。

什么意思?就是每一帧之间的时间间隔要均匀。如果一帧用了10ms,下一帧用了30ms,再下一帧又用了10ms,即使平均帧率是50fps,人眼也会感觉到“一顿一顿”的。这叫微卡顿(micro-stutter)。

我在排查掉帧问题时,经常用逻辑分析仪抓 lv_timer_handler() 的执行时间。如果发现执行时间忽长忽短,那问题大概率出在某个耗时的渲染操作上。

6. 核心知识体系

下面这张图,是我自己总结的帧率与刷新率的关系图。你看一眼就能明白整个逻辑链条。

帧率与刷新率核心关系图 屏幕刷新率 (Hz) LVGL帧率 (fps) 帧率 vs 刷新率 帧率 < 刷新率 → 掉帧、卡顿 帧率 = 刷新率 → 流畅、完美 帧率 > 刷新率 → 撕裂、浪费 核心原则:LVGL帧率应尽量匹配屏幕刷新率,或为其整数倍

7. 实际排查中的思路

当你发现动画掉帧时,第一步不是去优化代码,而是先搞清楚:瓶颈在硬件还是软件?

  1. 先确认屏幕刷新率——看屏幕数据手册,或者用示波器量一下VSYNC信号。如果屏幕只有30Hz,那再怎么优化LVGL也跑不到60fps。
  2. 再测量LVGL实际帧率——在 lv_timer_handler() 前后加GPIO翻转,用逻辑分析仪抓周期。或者用LVGL自带的 lv_disp_get_fps() 接口。
  3. 对比两者——如果LVGL帧率远低于屏幕刷新率,那问题在软件。如果LVGL帧率高于屏幕刷新率,那要考虑开启垂直同步(VSYNC)或者限制帧率。

一个小技巧:我习惯在调试阶段,把LVGL的帧率信息打印到串口或者显示在屏幕角落。这样一跑动画,就能实时看到帧率变化。如果帧率突然掉到20fps以下,那肯定是有地方卡住了。

8. 避坑指南

最后,分享几个我踩过的坑:

  • 别把屏幕刷新率当帧率用——屏幕60Hz不代表LVGL能跑到60fps。中间差着MCU的算力和渲染开销。
  • 别盲目调高LVGL的刷新周期——调高意味着更短的渲染时间,CPU压力更大。如果MCU扛不住,反而会掉帧。
  • 注意帧间隔的抖动——平均帧率50fps不代表每一帧都稳定。用逻辑分析仪抓一下,看看帧间隔是不是均匀的。
  • 开启垂直同步(VSYNC)——如果屏幕支持,尽量开启。它能保证LVGL的帧和屏幕的刷新同步,避免撕裂。