LVGL的核心:lv_timer_handler

文章目录

    • 🧠 一句话总结 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()

    • 会遍历所有活跃的定时器;
    • 执行到期的任务;
    • 动态应对中途创建/删除定时器的情况;
    • 返回下次最早触发的延时时间。
  • 所有动画、控件刷新、事件分发等操作,最终都是通过定时器机制驱动。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/904771.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据机构】2. 线性表之“顺序表”

- 第 96 篇 - Date: 2025 - 05 - 09 Author: 郑龙浩/仟墨 【数据结构 2】 文章目录 数据结构 - 2 -线性表之“顺序表”1 基本概念2 顺序表(一般为数组)① 基本介绍② 分类 (静态与动态)③ 动态顺序表的实现**test.c文件:****SeqList.h文件:****SeqList.c文件:** 数据结构 - 2 …

101 alpha——8 学习

alpha (-1 * rank(((sum(open, 5) * sum(returns, 5)) - delay((sum(open, 5) * sum(returns, 5)),这里我们操作符都明白&#xff0c;现在来看金融意义 金融意义 里层是这个 (sum(open, 5) * sum(returns, 5)) - delay((sum(open, 5) * sum(returns, 5)), 10 这里是两个相减…

auto推导类型原则

auto 是 C11 引入的类型自动推导关键字&#xff0c;它允许编译器根据表达式的类型来推导变量的确切类型。虽然使用 auto 可以让代码更简洁&#xff0c;但理解它的类型推导规则非常关键&#xff0c;尤其是在涉及指针、引用、const、模板等场景时。 ✅ 一、基本推导原则 auto x …

使用智能表格做FMEDA

一、优点 使用智能表格替代excel做FMEDA具备以下优势&#xff1a; 减少维护成本&#xff08;数据库关联&#xff0c;修改方便&#xff09;便于持续优化&#xff08;失效率分布&#xff0c;失效率模型可重复使用&#xff09;多人同步编写&#xff08;同时操作&#xff0c;同步…

IP协议.

IP 协议是互联网的核心协议&#xff0c;工作在网络层。它给网络中的设备分配唯一的 IP 地址&#xff0c;把上层数据封装成数据包&#xff0c;然后根据目的 IP 地址通过路由器等设备进行转发&#xff0c;实现数据在不同网络间的传输。它还能在必要时对数据包进行分片和重组&…

archlinux 详解系统层面

Arch Linux 深度解析&#xff1a;从设计哲学到系统架构 一、Arch Linux 概述&#xff1a;滚动发行的极客之选 Arch Linux 是一款以 滚动更新&#xff08;Rolling Release&#xff09; 为核心特性的 Linux 发行版&#xff0c;强调 轻量、灵活、高度可定制&#xff0c;旨在让用…

HTML8:媒体元素

视频和音频 视频元素 video 音频 audio <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>媒体元素学习</title> </head> <body> <!--音频和视频 src:资源路径 controls:控制条…

SpringBoot3集成Oauth2——1(/oauth2/token方法的升级踩坑)

备注&#xff1a;本文适用于你在SpringBoot2.7以前集成过oauth2&#xff0c;并且项目已经正式投入使用的情况&#xff0c;否则&#xff0c;我建议你直接学习或者找资料学习最新的oauth2集成&#xff0c;就不要纠结于老版本的oauth2。 原因&#xff1a;Spring Security 5.x和Sp…

笔记本电脑实现网线内网 + Wi-Fi外网同时使用的配置方案

1、同时连接两个网络‌ 插入网线连接内网&#xff0c;确保内网IP地址正常获取&#xff08;如10.143.88.x&#xff09;&#xff1b;连接Wi-Fi接入外网&#xff0c;确认可正常访问互联网&#xff08;如网关为192.168.8.1&#xff09;。 2、 记录关键网络参数‌ 内网网关&#…

从韦斯利・卡普洛看北斗星咨询公司的技术咨询引领之路

在科技与商业深度交融的时代&#xff0c;技术咨询公司扮演着举足轻重的角色&#xff0c;它们宛如连接技术创新与企业实际需求的桥梁&#xff0c;助力企业在复杂多变的市场环境中找准技术发展方向&#xff0c;实现可持续增长。《对话 CTO&#xff0c;驾驭高科技浪潮》的第 5 章聚…

首版次软件测试的内容有哪些?首版次软件质量影响因素是什么?

首版次软件测试不仅是简单的“找错”&#xff0c;更是系统地验证和评估软件各项功能和性能指标是否符合设计标准。 一、首版次软件测试常见的测试内容   1.功能测试&#xff1a;对照需求文档&#xff0c;确认功能模块是否按预期实现&#xff0c;用户操作流程是否顺畅。   …

从零开始的python学习(六)P86+P87+P88

本文章记录观看B站python教程学习笔记和实践感悟&#xff0c;视频链接&#xff1a;【花了2万多买的Python教程全套&#xff0c;现在分享给大家&#xff0c;入门到精通(Python全栈开发教程)】 https://www.bilibili.com/video/BV1wD4y1o7AS/?p6&share_sourcecopy_web&v…

从设计到开发,原型标注图全流程标准化

一、原型标注图是什么&#xff1f; 原型标注图&#xff08;Annotated Prototype&#xff09;是设计原型&#xff08;Prototype&#xff09;的详细说明书&#xff0c;通过图文结合的方式&#xff0c;将设计稿中的视觉样式、交互逻辑、适配规则等技术细节转化为开发可理解的标准…

飞云分仓操盘副图指标操作技术图文分解

如上图&#xff0c;副图指标-飞云分仓操盘指标&#xff0c;指标三条线蓝色“首峰线”&#xff0c;红色“引力1”&#xff0c;青色“引力2”&#xff0c;多头行情时“首峰线”和“引力1”之间显示为红色&#xff0c;“引力1”和“引力2”多头是区间颜色显示为紫色。 如上图图标信…

【LUT技术专题】ECLUT代码解读

目录 原文概要 1. 训练 2. 转表 3. 测试 本文是对ECLUT技术的代码解读&#xff0c;原文解读请看ECLUT。 原文概要 ECLUT通过EC模块增大网络感受野&#xff0c;提升超分效果&#xff0c;实现SRLUT的改进&#xff0c;主要是2个创新点&#xff1a; 提出了一个扩展卷积&…

动态规划之背包问题:组合优化中的经典NP挑战

背包问题概念&#xff1a; 背包问题是一种经典的组合优化的NP问题&#xff0c;在计算机科学、运筹学等领域有着广泛的应用。 问题可以简单的描述为&#xff1a; 假设有一个容量为C的背包和n个物品&#xff0c;每个物品i都有重量w[i]和价值v[i]。目标是选择一些物品放入背包&…

vue3: pdf.js5.2.133 using typescript

npm install pdfjs-dist5.2.133 项目结构&#xff1a; <!--* creater: geovindu* since: 2025-05-09 21:56:20* LastAuthor: geovindu* lastTime: 2025-05-09 22:12:17* 文件相对于项目的路径: \jsstudy\vuepdfpreview\comonents\pdfjs.vue* message: geovindu* IDE: vscod…

H2Database SQL 插入流程

H2Database SQL 插入流程 插入数据时会先进行 SQL 解析,然后找到插入表对应的 Primary Index 对应的 BTree,然后根据二分法定位到插入的叶子节点,将 key(主键) 和 value(Row) 插入到指定的叶子节点. 解析 SQL session 加锁 创建 savepoint获取or创建事务 设置 savepoint 执行…

虚拟机ubantu20.04系统桥接模式下无法ping通外网,但可以ping通本机的解决方案

1.出现的问题&#xff1a; 虚拟机ubantu20.04系统桥接模式下无法ping通外网,但可以ping通本机。 2.解决方案&#xff1a; 如果 DHCP 未分配 IP 地址&#xff0c;可以手动配置静态 IP&#xff1a; 1.编辑网络配置文件&#xff1a; sudo nano /etc/netplan/01-netcfg.yaml 修…

面对渠道竞争,品牌该如何应对?

无论是传统零售渠道还是电商平台的&#xff0c;渠道竞争仍旧是品牌维持和扩大影响力绕不开的一环。品牌想要保证自身的市场地位和盈利能力&#xff0c;就需要充分发挥各方面的优势&#xff0c;来应对多变的市场环境。 一、改变产品定位 在存量市场上&#xff0c;消费者本身拥有…