2、帧率(FPS)测量基础:什么是帧率?如何用LVGL内置工具测量FPS
好,咱们正式开始聊性能调优。第一个绕不开的话题,就是帧率。
你可能听过「60帧流畅,30帧能看」这种说法。但在嵌入式设备上,情况要复杂得多。我见过不少项目,界面跑起来卡卡的,开发人员第一反应就是「优化代码」。其实很多时候,连问题出在哪都没搞清楚。
所以这一节,我们先打好基础。搞清楚帧率到底是什么,以及怎么用LVGL自带的工具把它测出来。
2.1 帧率到底是什么?
帧率,英文叫FPS(Frames Per Second)。说白了,就是每秒钟屏幕能刷新多少次画面。
举个例子:
- 60 FPS:每秒刷新60次,每帧约16.7ms
- 30 FPS:每秒刷新30次,每帧约33.3ms
- 15 FPS:每秒刷新15次,每帧约66.7ms
人眼对画面的流畅度感知,大致有个门槛。我个人习惯把30 FPS当作「及格线」,低于这个值,滑动、动画就会明显感觉卡顿。60 FPS是「优秀线」,大部分场景下已经非常丝滑。
但这里有个坑:帧率不是越高越好。你想想看,如果你的屏幕刷新率只有60Hz,那就算软件跑出100 FPS,实际显示出来的还是60帧。多出来的算力全浪费了,还增加了功耗和发热。
核心观点:帧率的目标是「匹配屏幕刷新率」,而不是「无限高」。对于大多数嵌入式屏幕(60Hz),稳定跑满60 FPS就是最优解。
2.2 为什么帧率会掉?
帧率下降,本质上就是「一帧画完的时间超过了预期」。我遇到过的情况,大致分三类:
- 渲染太慢:比如用了太多透明效果、复杂的渐变、大图片缩放。GPU(如果有的话)忙不过来。
- CPU被占死:主循环里做了太多计算,比如数据解析、文件读写。LVGL没时间处理绘制任务。
- 内存带宽不够:尤其是用SPI接口的屏幕,刷一帧数据要很久。这时候就算CPU和GPU再快,也卡在传输上。
嗯,这里要注意:很多时候掉帧不是单一原因造成的。我曾经调试一个项目,发现帧率在20~40之间剧烈波动。一开始以为是渲染问题,折腾了半天。后来用逻辑分析仪一看,原来是SPI时钟频率设低了,加上DMA没配置好。改完之后,帧率直接稳定在55以上。
2.3 LVGL内置的FPS测量工具
LVGL很贴心,自带了一个FPS监控功能。不需要你额外写代码去计时,开个宏就行。
2.3.1 如何开启?
在 lv_conf.h 文件中,找到下面这个宏:
#define LV_USE_PERF_MONITOR 1
把它设为1。然后重新编译、下载固件。你会看到屏幕右上角出现一个小窗口,里面显示着实时数据。
小提示:如果没看到,检查一下你的 lv_conf.h 是否被正确包含。有些IDE会自动生成默认配置,覆盖了你的修改。
2.3.2 显示的信息怎么看?
开启后,右上角会显示类似这样的内容:
FPS: 58
CPU: 12%
我来解释一下:
- FPS:当前实时帧率。注意是「实时」的,会不断跳动。
- CPU:LVGL绘制任务占用的CPU百分比。注意,这不是整个系统的CPU占用率,只是LVGL这一块的。
这两个数据要结合起来看。比如:
- FPS低,CPU也低(比如10%):说明瓶颈不在LVGL内部,可能是你的主循环被其他任务卡住了,LVGL没机会运行。
- FPS低,CPU很高(比如90%):说明LVGL本身在拼命干活,但性能跟不上。这时候需要优化渲染。
我个人习惯,先看CPU占用率。如果CPU占用率不到50%,帧率却很低,那基本可以断定是「外部阻塞」的问题。
2.3.3 代码里怎么用?
如果你想在代码里获取FPS值,做更精细的控制,可以用这个API:
lv_obj_t * perf_monitor = lv_scr_act();
/* 获取FPS值 */
uint32_t fps = lv_refr_get_fps();
/* 获取CPU占用率 */
uint32_t cpu_usage = lv_refr_get_cpu_usage();
这两个函数返回的是整数。你可以把它们打印到串口,或者显示在自定义的调试界面上。
注意:这些API只在 LV_USE_PERF_MONITOR 开启时才有效。如果没开,返回的值是0。
2.4 实战:测量一个简单界面的FPS
光说不练假把式。我们写个最简单的例子,看看实际效果。
void my_fps_test(void)
{
/* 创建一个按钮 */
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn, 100, 50);
lv_obj_center(btn);
/* 添加一个标签 */
lv_obj_t * label = lv_label_create(btn);
lv_label_set_text(label, "点击我");
/* 创建一个滑动条,增加一点渲染负载 */
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_set_size(slider, 200, 20);
lv_obj_align(slider, LV_ALIGN_BOTTOM_MID, 0, -30);
}
把这个函数在你的主循环里调用一次。然后观察右上角的FPS数值。
在大多数MCU上,这个界面的FPS应该能轻松跑到50以上。如果低于30,那你的硬件平台可能有点吃力,或者驱动配置有问题。
我曾经在一个低端Cortex-M3上跑这个测试,结果只有22 FPS。排查了半天,发现是屏幕驱动里用了「软件模拟SPI」,没用硬件SPI。改成硬件SPI+DMA后,直接跳到58 FPS。你看,有时候问题根本不在LVGL本身。
2.5 常见问题与避坑
- FPS显示为0:检查
LV_USE_PERF_MONITOR是否真的被定义了。有些IDE的预处理器会忽略某些文件。 - FPS跳动剧烈:正常现象。因为LVGL的刷新是「按需刷新」,没有变化时不会重绘。建议观察几秒钟的平均值。
- 开了监控后界面变慢:嗯,监控本身也会消耗一点点性能。但通常影响很小,可以忽略。
总结一下:帧率是性能调优的「体温计」。学会用LVGL内置工具测量FPS,是调优的第一步。别急着优化代码,先看看数据怎么说。
下一节,我们会深入分析「哪些操作最耗性能」,以及如何用LVGL的 profiling 工具定位瓶颈。到时候见。