1. LVGL动画基础:帧率概念、LVGL刷新机制、lv_timer与lv_anim的区别

做嵌入式UI开发,动画调优是个绕不开的坎。我刚开始接触LVGL时,总觉得动画卡顿是硬件不行,后来才发现——很多时候是我们没搞懂LVGL的刷新机制。这一章,咱们先把地基打牢。

1.1 帧率概念:别被「60fps」骗了

帧率,说白了就是屏幕每秒刷新多少次。LVGL里默认是60fps,也就是每16.6ms刷新一帧。但这里有个坑——60fps不代表你的动画一定能跑到60fps。

我在一个STM32F429的项目上遇到过:UI跑起来只有20fps,但硬件明明能跑60fps。查了半天,发现是渲染任务把CPU占满了。你想想看,帧率是目标,不是结果。

关键概念:

  • 目标帧率:你设置的刷新间隔(如60fps)
  • 实际帧率:硬件真正能跑到的帧数
  • 掉帧:实际帧率低于目标帧率,动画就会卡顿

帧率不是越高越好。我建议根据硬件选:

硬件平台 推荐帧率 说明
低端MCU(如STM32F1) 30fps CPU资源有限,跑60fps反而容易掉帧
中端MCU(如STM32F4/H7) 50-60fps 带硬件加速,可以跑满
高端MPU(如i.MX RT) 60fps+ 双核或GPU加持,可以更高

我的习惯:开发初期先设30fps,等UI稳定了再往上调。这样能快速定位性能瓶颈。

1.2 LVGL刷新机制:它到底怎么画屏的?

LVGL的刷新机制,我画了张图帮你理解:

LVGL刷新机制流程图 用户操作 / 定时器触发 标记脏区域 (invalidate) lv_task_handler() 主循环 刷新脏区域 (flush) 显示完成 关键点 • 只刷新脏区域 • 主循环周期决定帧率 • flush回调由驱动实现 性能瓶颈常在这 • 脏区域过大 • flush回调太慢 • 主循环被阻塞

LVGL的刷新机制,核心就一句话:只刷新变化的部分。这叫「脏区域刷新」。你动了一个按钮,LVGL不会重绘整个屏幕,只重绘按钮那一块。

嗯,这里要注意:lv_task_handler() 是LVGL的主循环函数。你必须在主循环里定期调用它,LVGL才能处理动画和刷新。我见过有人把它放在中断里调,结果系统直接崩了——千万别这么干。

我曾经踩过的坑:在一个项目里,我把 lv_task_handler() 放在了一个高优先级任务里,结果低优先级的触摸任务一直抢不到CPU,UI直接死机。后来我把主循环放在中等优先级,才解决问题。

1.3 lv_timer vs lv_anim:到底用哪个?

很多新手分不清 lv_timer 和 lv_anim。我刚开始也迷糊过。简单说:

  • lv_timer:定时器,每隔固定时间执行一次回调。适合周期性任务,比如轮询传感器、更新状态栏。
  • lv_anim:动画引擎,自动处理插值和回调。适合做平滑动画,比如移动、缩放、渐变。

我画了个对比表,一目了然:

特性 lv_timer lv_anim
用途 周期性任务 平滑动画
自动插值 ❌ 需要手动实现 ✅ 自动计算中间值
回调频率 固定间隔 每帧调用
性能开销 低 中(需计算插值)
适用场景 状态轮询、数据更新 移动、缩放、透明度变化

举个例子。你想让一个按钮每隔500ms闪烁一次,用 lv_timer:

// lv_timer 示例:按钮闪烁
lv_timer_t *timer = lv_timer_create(btn_blink_cb, 500, btn);

static void btn_blink_cb(lv_timer_t *t) {
    lv_obj_t *btn = (lv_obj_t *)t->user_data;
    static bool visible = true;
    visible = !visible;
    lv_obj_add_flag(btn, visible ? LV_OBJ_FLAG_HIDDEN : 0);
}

但如果你想让按钮平滑移动到另一个位置,用 lv_anim:

// lv_anim 示例:按钮平滑移动
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_var(&a, btn);
lv_anim_set_exec_cb(&a, (lv_anim_exec_xcb_t)lv_obj_set_x);
lv_anim_set_values(&a, 0, 200);  // 从x=0移动到x=200
lv_anim_set_time(&a, 1000);      // 持续1秒
lv_anim_set_path_cb(&a, lv_anim_path_ease_out);  // 缓出效果
lv_anim_start(&a);

我的建议:能用 lv_anim 就别用 lv_timer 手撸动画。LVGL的动画引擎已经帮你做了插值和帧率控制,自己写定时器做动画,很容易出现帧率不稳、卡顿的问题。

1.4 性能调优的第一把钥匙

理解了帧率、刷新机制和定时器/动画的区别,你就拿到了性能调优的第一把钥匙。我个人习惯,拿到一个新项目,先做三件事:

  1. 测帧率:用 lv_disp_get_inactive_time() 或外接逻辑分析仪,看实际帧率
  2. 看脏区域:打开LVGL的调试模式,看脏区域是不是太大
  3. 检查主循环:确保 lv_task_handler() 没有被阻塞

这三步走完,80%的性能问题都能定位到。剩下的,就是具体优化了——那是后面章节的事。

本章核心要点:

  • 帧率是目标,不是结果。实际帧率受硬件和渲染负载影响
  • LVGL采用脏区域刷新,只重绘变化部分
  • lv_timer 适合周期性任务,lv_anim 适合平滑动画
  • 性能调优第一步:测帧率、看脏区域、查主循环