文章目录
- 🧠 一句话总结 LVGL 的运行核心:
- 🔁 1. `while(1)` 主循环中的 `lv_task_handler()`
- ⏱️ 2. `lv_timer_handler()` 定时器调度核心
- ✅ 并发控制
- ✅ 关键行为流程:
- 🌀 任务执行逻辑:
- 🧮 计算下一次调用间隔
- 📊 3. 额外性能监控与优化
- 🎯 总结一张图(逻辑关系)
- 🧩 你需要做的最关键一件事
- ✅ 总结重点
LVGL 的运行核心机制其实就围绕两个关键词展开:定时器轮询(Timer Loop)+ 任务处理器(Task Handler)。你贴的这段代码正是 LVGL 运行机制的心脏,下面我会从宏观和细节两个层面帮你理解这套机制的运行流程。
🧠 一句话总结 LVGL 的运行核心:
LVGL 通过定时器轮询机制(
lv_timer_handler
)周期性检查并执行任务(动画、事件、刷新等),由主循环中持续调用lv_task_handler()
驱动系统更新。
🔁 1. while(1)
主循环中的 lv_task_handler()
while(1) {
lv_task_handler();
usleep(5000); // 每 5ms 调用一次
}
- 这是你在主线程、RTOS 任务或定时器中 周期性调用 的函数;
- 实际内部会调用
lv_timer_handler()
,这是 LVGL 所有内部“定时行为”的核心; - 例:刷新控件、执行动画、检测事件、处理输入设备等,都是靠这里触发。
⏱️ 2. lv_timer_handler()
定时器调度核心
这是 LVGL 的任务调度中心,其关键职责如下:
✅ 并发控制
if(state_p->already_running) return 1;
防止多线程或中断中重复进入,保护调度流程。
✅ 关键行为流程:
lv_timer_t * timer_active = lv_ll_get_head(timer_head);
- LVGL 内部的“任务”是以
lv_timer_t
为单位组织的; - 每一个
lv_timer_t
都有周期(例如 30ms 执行一次); - 所有
timer
被链接在一个链表中,调度器依次遍历并执行;
🌀 任务执行逻辑:
if(lv_timer_exec(timer_active)) {
if(state_p->timer_created || state_p->timer_deleted) {
break;
}
}
- 每次调度,调用
lv_timer_exec()
试图执行任务; - 如果任务内部创建或删除了其他
timer
,则会中断本次遍历,从头再来(保证链表安全);
🧮 计算下一次调用间隔
uint32_t delay = lv_timer_time_remaining(next);
- 执行完任务后,计算距离下一次要执行的任务的最小延迟;
- 这个值可用于指导下一轮
lv_task_handler()
的调用周期优化(例如设置精确定时器)。
📊 3. 额外性能监控与优化
state_p->idle_last = (state_p->busy_time * 100) / idle_period_time;
- LVGL 会统计任务执行耗时,判断 CPU 占用率;
- 可用于分析系统是否卡顿,或优化任务调度频率;
🎯 总结一张图(逻辑关系)
主循环/RTOS任务
↓
lv_task_handler()
↓
lv_timer_handler()
↓
遍历 lv_timer 链表
↓
执行注册任务(动画、屏幕刷新、事件分发等)
↓
计算下次执行时间 & 性能分析
🧩 你需要做的最关键一件事
在你的系统中,确保 lv_task_handler()
每隔 5~10ms 被调用一次,即可激活 LVGL 的整个图形与事件系统。
当然可以,以下是为 lv_timer_handler()
添加的详细中文注释版,注释尽量保留原有逻辑结构,方便你后续阅读和调试:
// LVGL 的定时器调度主函数,每次被调用会遍历所有注册的定时器并执行到期的任务
LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void)
{
LV_TRACE_TIMER("begin");
lv_timer_state_t * state_p = &state;
// 避免定时器调度函数被重复进入(多线程或中断中可能会并发调用)
if(state_p->already_running) {
LV_TRACE_TIMER("already running, concurrent calls are not allow, returning");
return 1;
}
state_p->already_running = true;
// 如果定时器已被暂停,直接返回
if(state_p->lv_timer_run == false) {
state_p->already_running = false; // 释放“互斥锁”
return 1;
}
LV_PROFILER_BEGIN;
lv_lock(); // 进入临界区,保护链表等资源不被同时访问
// 获取当前时间(单位 ms)
uint32_t handler_start = lv_tick_get();
// 检查是否调用了 lv_tick_inc()(用于更新系统 tick)
if(handler_start == 0) {
state.run_cnt++;
if(state.run_cnt > 100) {
state.run_cnt = 0;
LV_LOG_WARN("It seems lv_tick_inc() is not called.");
}
}
/*************** 开始遍历并执行定时器任务 ***************/
lv_timer_t * next;
lv_timer_t * timer_active;
lv_ll_t * timer_head = timer_ll_p; // 定时器链表头指针
do {
// 每轮遍历前先清空“状态标志”
state_p->timer_deleted = false;
state_p->timer_created = false;
// 获取第一个定时器
timer_active = lv_ll_get_head(timer_head);
while(timer_active) {
// 提前获取下一个指针(因为本 timer 执行后可能会被删除)
next = lv_ll_get_next(timer_head, timer_active);
// 如果当前定时器已经到期,则执行回调函数
if(lv_timer_exec(timer_active)) {
// 如果执行期间创建/删除了定时器,链表结构可能被破坏,必须重新从头遍历
if(state_p->timer_created || state_p->timer_deleted) {
LV_TRACE_TIMER("Start from the first timer again because a timer was created or deleted");
break;
}
}
// 进入下一个定时器
timer_active = next;
}
} while(timer_active); // 如果因链表被修改跳出,则重新遍历
/*************** 计算下次定时器触发时间 ***************/
uint32_t time_until_next = LV_NO_TIMER_READY; // 默认无任务准备
next = lv_ll_get_head(timer_head);
while(next) {
if(!next->paused) {
// 获取该定时器距离下一次触发还需等待的时间
uint32_t delay = lv_timer_time_remaining(next);
if(delay < time_until_next)
time_until_next = delay; // 保留最短时间
}
next = lv_ll_get_next(timer_head, next);
}
/*************** 统计“空闲率”用于性能分析 ***************/
state_p->busy_time += lv_tick_elaps(handler_start); // 本轮花费时间
uint32_t idle_period_time = lv_tick_elaps(state_p->idle_period_start);
if(idle_period_time >= IDLE_MEAS_PERIOD) {
// 计算过去一段时间内的 CPU 空闲率(100% - 忙碌率)
state_p->idle_last = (state_p->busy_time * 100) / idle_period_time;
state_p->idle_last = state_p->idle_last > 100 ? 0 : 100 - state_p->idle_last;
// 重置统计值
state_p->busy_time = 0;
state_p->idle_period_start = lv_tick_get();
}
// 保存本次计算的“下次触发间隔”
state_p->timer_time_until_next = time_until_next;
state_p->already_running = false; // 解锁,允许下次进入
LV_TRACE_TIMER("finished (%" LV_PRIu32 " ms until the next timer call)", time_until_next);
lv_unlock(); // 退出临界区
LV_PROFILER_END;
return time_until_next; // 返回最短等待时间,供外部调度参考
}
✅ 总结重点
-
每次调用
lv_timer_handler()
:- 会遍历所有活跃的定时器;
- 执行到期的任务;
- 动态应对中途创建/删除定时器的情况;
- 返回下次最早触发的延时时间。
-
所有动画、控件刷新、事件分发等操作,最终都是通过定时器机制驱动。