解码LVGL定时器

news/2025/11/11 18:27:25/文章来源:https://www.cnblogs.com/YouEmbedded/p/19211250

定时器核心概念

  • LVGL 定时器是按指定毫秒(ms)周期执行回调函数的机制,依赖内置计时器系统调度
  • 非抢占式:多个定时器不会互相中断,一个定时器的回调执行完,才会执行下一个,因此回调中可安全调用 LVGL 相关函数或普通函数
  • 调度依赖 lv_timer_handler():所有定时器的回调都在该函数中触发,必须定期调用(如主循环、操作系统线程中),否则定时器无法执行

核心调度函数:lv_timer_handler ()

/*** LVGL 定时器核心调度函数(带属性标记,用于编译器优化/特性识别)* @brief 触发所有就绪的定时器回调,处理定时器周期计数、状态判断,是定时器工作的核心* @return uint32_t 下次需要调用该函数的最小延迟(单位:ms),用于优化调度(如OS中线程睡眠时长)*                  返回 0 表示需立即再次调用,避免定时器延迟* @note LV_ATTRIBUTE_TIMER_HANDLER 是LVGL内置属性宏,用于标记该函数为定时器处理入口,*       编译器可能根据该属性做优化(如避免内联、指定代码段等),不可省略*       必须定期调用:推荐调用周期 ≤ 5ms,调用频率直接决定定时器响应精度*       返回值用途:在操作系统中,可根据返回值让LVGL线程睡眠对应时长,减少CPU占用*       函数内部逻辑:遍历所有定时器 → 检查是否就绪(周期到达/被lv_timer_ready标记)→ 执行回调 → 更新计数*       若回调函数执行耗时过长,会导致返回值变大,甚至影响其他定时器的准时触发,需控制单个回调耗时*       线程安全:可在多线程环境中调用(LVGL内部有保护机制),但需确保所有LVGL操作(如控件修改)在同一线程*/
LV_ATTRIBUTE_TIMER_HANDLER uint32_t lv_timer_handler(void);

定时器基本操作

创建定时器(核心接口)

/*** 创建一个定时器并返回其句柄* @param timer_cb  定时器回调函数(定时器触发时执行的函数)* @param period_ms 定时器执行周期(单位:ms),最小支持 1ms* @param user_data 用户自定义数据(回调中可通过 lv_timer_get_user_data 获取)* @return          成功返回定时器句柄(lv_timer_t*),失败返回 NULL(如内存不足)* @note            创建后定时器默认是启用状态(除非手动禁用)*                  回调函数原型必须符合 lv_timer_cb_t 定义*/
lv_timer_t *lv_timer_create(lv_timer_cb_t timer_cb, uint32_t period_ms, void *user_data);/*** 定时器回调函数原型* @param timer 触发回调的定时器句柄,可通过该句柄获取定时器信息(如用户数据、周期等)*/
typedef void (*lv_timer_cb_t)(lv_timer_t *timer);// 示例:创建定时器并传递用户数据
static void my_timer_cb(lv_timer_t *timer) {// 获取传递的用户数据void *user_data = lv_timer_get_user_data(timer);// 业务逻辑:如刷新控件显示、处理数据等
}// 用法示例
lv_obj_t *label = lv_label_create(lv_scr_act()); // 创建一个标签控件
// 创建定时器,周期 500ms,将 label 作为用户数据传递
lv_timer_t *timer = lv_timer_create(my_timer_cb, 500, label);

就绪与重置定时器

/*** 强制定时器在下一次调用 lv_timer_handler() 时执行(忽略剩余周期)* @param timer 要设置的定时器句柄(非 NULL)* @note 用于需要立即触发一次定时器的场景,执行后仍按原周期继续计时*/
void lv_timer_ready(lv_timer_t *timer);/*** 重置定时器的周期计数(重新开始计时)* @param timer 要重置的定时器句柄(非 NULL)* @note 调用后,定时器需等待完整的 period_ms 时间才会再次触发*       例如:周期 1000ms,已计时 600ms 时调用 reset,需再等 1000ms 才触发*/
void lv_timer_reset(lv_timer_t *timer);

修改定时器核心参数

/*** 设置定时器的新回调函数* @param timer 定时器句柄(非 NULL)* @param new_cb 新的回调函数(符合 lv_timer_cb_t 原型)* @note 修改后,下一次定时器触发时执行新回调*/
void lv_timer_set_cb(lv_timer_t *timer, lv_timer_cb_t new_cb);/*** 设置定时器的新执行周期* @param timer 定时器句柄(非 NULL)* @param new_period 新周期(单位:ms),最小 1ms* @note 修改后立即生效,下一次计时按新周期计算*/
void lv_timer_set_period(lv_timer_t *timer, uint32_t new_period);/*** 设置定时器的新用户数据* @param timer 定时器句柄(非 NULL)* @param new_user_data 新的用户数据(void* 类型,可传递任意数据)* @note 后续回调中通过 lv_timer_get_user_data 获取的是新数据*/
void lv_timer_set_user_data(lv_timer_t *timer, void *new_user_data);/*** 获取定时器的用户数据(回调中常用)* @param timer 定时器句柄(非 NULL)* @return 返回设置的用户数据(void* 类型,需手动强转成实际类型)*/
void *lv_timer_get_user_data(const lv_timer_t *timer);

设置定时器重复次数

/*** 设置定时器的执行次数(默认无限重复)* @param timer 定时器句柄(非 NULL)* @param count 重复次数:-1 = 无限重复(默认值);≥0 = 执行 count 次后自动删除* @note 执行次数耗尽后,LVGL 会自动调用 lv_timer_del 删除定时器,无需手动删除*       示例:count=5 → 定时器触发 5 次后自动销毁*       若中途修改 count,以新值重新计数(如已执行 3 次,改为 count=2 → 再执行 2 次后删除)*/
void lv_timer_set_repeat_count(lv_timer_t *timer, int32_t count);

启用与禁用定时器

/*** 启用定时器(恢复定时器计时和触发)* @param timer 定时器句柄(非 NULL)* @note 启用后,定时器按周期正常计时,到达周期触发回调*/
void lv_timer_enable(lv_timer_t *timer);

暂停与恢复定时器

/*** 暂停定时器(暂停计时,区别于禁用)* @param timer 定时器句柄(非 NULL)* @note 暂停后,定时器停止计时,但保持启用状态*       恢复(lv_timer_resume)后,继续从暂停时的剩余周期计时(而非重新开始)*       示例:周期 1000ms,已计时 600ms 时暂停,恢复后再计时 400ms 触发回调*/
void lv_timer_pause(lv_timer_t *timer);/*** 恢复被暂停的定时器* @param timer 定时器句柄(非 NULL)* @note 仅对已暂停的定时器有效,对禁用状态的定时器无效*/
void lv_timer_resume(lv_timer_t *timer);

其他常用接口

创建空定时器(无默认回调和用户数据)

/*** 创建一个空定时器(无回调、无用户数据、默认周期 0ms)* @return 成功返回定时器句柄,失败返回 NULL* @note 创建后需手动设置回调、周期、用户数据等参数才能使用*       用途:动态配置定时器参数的场景(如根据运行时状态设置不同回调)*       示例:*          lv_timer_t *timer = lv_timer_create_basic();*          lv_timer_set_cb(timer, my_cb);*          lv_timer_set_period(timer, 200);*/
lv_timer_t *lv_timer_create_basic(void);

获取 lv_timer_handler() 空闲时间百分比

/*** 获取 lv_timer_handler() 函数执行时的空闲百分比(仅反映该函数内部的空闲状态)* @return 空闲百分比(0~100):0 = 完全忙碌,100 = 完全空闲* @note 不代表整个系统的空闲时间,仅反映 LVGL 定时器调度的繁忙程度*       在操作系统中使用时,该值可能有误导性(无法反映 OS 空闲线程的消耗)*       用途:判断 LVGL 定时器调度是否过载(如返回 0 持续一段时间,说明回调执行耗时过长)*/
uint8_t lv_timer_get_idle(void);

异步调用(下一次 lv_timer_handler() 执行)

/*** 安排一个函数在下一次调用 **lv_timer_handler**() 时执行(仅执行一次)* @param func 要执行的函数(原型:void (*lv_async_cb_t)(void*))* @param data 传递给 func 的用户数据* @note 用于无法立即执行的操作(如避免阻塞当前流程、当前上下文不允许操作 LVGL 控件)*       执行后自动销毁,无需手动删除*       例:在中断中触发 LVGL 控件更新(中断中不能直接操作 LVGL,可通过该函数异步执行)*/
void lv_async_call(lv_async_cb_t func, void *data);/*** 异步调用的函数原型* @param data 传递的用户数据(void* 类型,需手动强转)*/
typedef void (*lv_async_cb_t)(void *data);

定时器删除(关键知识点)

手动删除定时器(外部删除)

/*** 手动删除定时器(释放定时器占用的内存)* @param timer 要删除的定时器句柄(非 NULL)* @note 删除后,定时器句柄失效,不可再使用(避免野指针访问)*       若定时器正在执行回调,删除操作会在回调执行完后生效*       必须确保删除时定时器未被其他地方引用(如全局变量存储的句柄需置 NULL)*       示例:*          lv_timer_t *timer = lv_timer_create(my_cb, 500, NULL);*          // ... 业务逻辑 ...*          lv_timer_del(timer); // 删除定时器*          timer = NULL; // 避免野指针*/
void lv_timer_del(lv_timer_t *timer);

回调函数中自删定时器

/*** 在定时器回调中删除自身(常用场景:执行一次后自动删除,无需设置 repeat_count)* @note 示例:* static void my_timer_cb(lv_timer_t *timer) {*     // 业务逻辑 ...*     lv_timer_del(timer); // 执行完业务后删除自身*     timer = NULL; // 可选:若句柄是全局变量,需置 NULL* }* * // 创建时无需设置 repeat_count(默认无限重复,但回调中自删)* lv_timer_create(my_timer_cb, 1000, NULL);*/

控件与定时器关联的安全删除

场景:定时器用户数据是 LVGL 控件(如 label),需安全删除定时器和控件,避免野指针。

安全删除步骤:

  • 先禁用定时器(防止删除过程中定时器触发,访问已销毁的控件)
  • 删除定时器(释放定时器内存)
  • 删除控件(释放控件内存)
  • 将定时器句柄和控件句柄置 NULL(避免野指针访问)

示例代码:

// 全局/局部静态变量存储句柄(方便删除时访问)
static lv_timer_t *label_timer = NULL;
static lv_obj_t *my_label = NULL;// 定时器回调:刷新 label 显示
static void label_refresh_cb(lv_timer_t *timer) {// 先判断控件是否有效(避免已删除后访问)if(lv_obj_is_valid(my_label)) {static int count = 0;char buf[32];sprintf(buf, "Count: %d", count++);lv_label_set_text(my_label, buf);} else {// 控件已失效,删除定时器lv_timer_del(timer);label_timer = NULL;}
}// 安全删除函数
void safe_delete_label_and_timer(void) {// 禁用定时器(防止删除过程中触发回调)if(label_timer != NULL) {lv_timer_enable(false);}// 删除定时器if(label_timer != NULL) {lv_timer_del(label_timer);label_timer = NULL; // 置 NULL,避免野指针}// 删除控件(判断控件是否有效)if(lv_obj_is_valid(my_label)) {lv_obj_del(my_label);my_label = NULL; // 置 NULL,避免野指针}
}// 初始化
void init_label_and_timer(void) {// 创建控件my_label = lv_label_create(lv_scr_act());lv_label_set_text(my_label, "Count: 0");// 创建定时器,传递 label 作为用户数据label_timer = lv_timer_create(label_refresh_cb, 500, my_label);
}

关键注意点:

  • 回调中必须先判断控件是否有效(lv_obj_is_valid),避免控件已删除但定时器未删除时访问野指针
  • 删除顺序:先禁用定时器 → 再删除定时器 → 最后删除控件,确保回调不会访问已删除的控件
  • 句柄管理:删除后必须置 NULL,防止后续代码误使用失效句柄

常见问题与注意事项

  • 定时器不执行?
    • 检查 lv_timer_handler() 是否定期调用(如主循环中是否遗漏)
    • 检查定时器是否被禁用或暂停
    • 检查周期是否设置过小(如 0ms,会导致不执行)
  • 定时器执行延迟?
    • 增大 lv_timer_handler()的调用频率(推荐 ≤5ms)
    • 减少回调函数的执行耗时(非抢占式,单个回调耗时过长会阻塞其他定时器)
  • 野指针问题?
    • 定时器用户数据是堆内存 / 控件时,需确保数据 / 控件未被提前释放
    • 删除定时器后,立即将句柄置 NULL,避免后续访问
  • 中断中能否操作定时器?
    • 可以调用 lv_timer_createlv_timer_enable等接口(LVGL 定时器接口线程安全)
    • 不能在中断中直接操作 LVGL 控件,需通过 lv_async_call 异步执行控件操作

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

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

相关文章

ORACLE解析游标生成JSON

ORACLE解析游标生成JSON 1. 背景 存储过程中使用oracleutl_http调用rest接口,并以JSON的方式传输数据.此需求下,业务和环境有如下限制:业务已经通过sys_refcursor生成了业务数据,不希望重新编写存储过程 当前ORACLE版本…

习题解析之:鸡兔同笼

习题解析之:鸡兔同笼【问题描述】大约在1500年前,《孙子算经》中记载一个有趣的问题:今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何?大概的意思是:有若干只鸡兔同在一个笼子里,从上面数,有35个头,从…

如何选择锡林郭勒西林瓶灌装旋盖机?环境温湿度要求详解

在制药、生物制剂及精细化工等行业中,西林瓶灌装机的稳定运行高度依赖于环境条件的精准控制。尤其在锡林郭勒等气候干燥、昼夜温差较大的地区,设备对温度与湿度的适应性成为用户选购时的重要考量因素。通常,西林瓶灌…

DeepSeek权威测评榜单2025年11月最新geo优化公司推荐

近年来,随着全球化进程加速,中国企业出海需求持续攀升,但在海外市场拓展中,geo 优化服务的质量参差不齐成为制约企业发展的关键痛点。据 2024 年《中国企业全球化报告》显示,超 72% 的出海企业因 geo 定位不准确、…

ECB33-PGB2N4E32-I单板机智能交通监控应用方案解析

一、方案概述与技术架构 1.1 系统整体架构设计 基于ECB33-PGB2N4E32-I的智能交通监控系统采用分层架构: 边缘感知层(前端设备): ├── 4K高清摄像头 4路 ├── 雷达测速传感器 2路 ├── 环境监测传感器(温湿…

北京GEO优化服务商2025权威推荐:抢占AI搜索流量新入口

技术驱动流量变革,选对服务商是制胜关键 在生成式AI重塑信息分发格局的今天,传统搜索引擎流量正以每年15%的速度向生成式引擎迁移。据行业数据显示,2025年全球GEO(生成式引擎优化)市场规模已突破120亿美元,35%的…

雅思报班哪个机构比较好?过来人分享选择经验与价格课程对比

雅思报班哪个机构比较好?过来人分享选择经验与价格课程对比Posted on 2025-11-11 18:14 打不死的小强996 阅读(0) 评论(0) 收藏 举报在选择雅思培训机构时,不少考生发现市面上课程质量参差不齐,教师资历难以核…

深入解析:第三方课题验收测试机构:【API测试工具Apifox使用指南】

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

云原生周刊丨runc 三大高危漏洞曝光

云原生热点 CNCF 宣布 Crossplane 毕业:迈入云原生智能控制平面新阶段 Crossplane 是一个开源的 Kubernetes 扩展,用 Kubernetes 的 API 来管理各云厂商的基础设施,把 K8s 变成“通用控制平面”。 近日,Crossplane…

Web Worker 入门指南

在浏览器环境中,JavaScript 是单线程运行的,这意味着当执行一些耗时的操作时(例如大量计算、数据处理等),会阻塞主线程,导致用户界面卡顿,影响用户体验。为了解决这个问题,Web Worker 提供了一种在后台线程中运…

鸿蒙NEXT系列之精析NDK UI API(节点增删和属性设置) - 实践

鸿蒙NEXT系列之精析NDK UI API(节点增删和属性设置) - 实践2025-11-11 18:04 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !import…

通用cursor rules总结

所有项目通用规则 命名:base.mdc点击查看代码 --- description: globs: alwaysApply: true --- # 核心开发原则## 通用开发原则 - **可测试性**:编写可测试的代码,组件应保持单一职责 - **DRY 原则**:避免重复代码…

【JVS更新日志】开源框架升级vue 3、低代码、企业计划、智能BI及其他产品迎来新版本! - 实践

【JVS更新日志】开源框架升级vue 3、低代码、企业计划、智能BI及其他产品迎来新版本! - 实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !impo…

银川西林瓶灌装旋盖机推荐2025,运行稳定连续8小时无故障

在制药、诊断试剂及高端保健品等细分领域,西林瓶灌装设备的运行稳定性已成为用户采购决策中的核心考量因素。据2024年行业调研数据显示,在连续工作8小时以上的生产场景中,设备无故障率对产线效率的影响权重高达67%。…

【ACM出版 | EI检索稳定】2025年人工智能、业务转型和数据科学创新国际学术会议(ICBTDS 2025)

人工智能(AI)正以前所未有的速度重塑全球商业格局,推动各行业业务模式转型和数据科学创新。【人工智能、大数据、机器学习、管理、经济相关主题稿件均可接收】 【稿件投稿录用可免费参会作口头报告】 2025年人工智能…

echarts 树形结构图实例

<html> <head><meta charset="UTF-8"> <title>用户关系图</title><style> </style><script type="text/javascript" src="https://fastly.jsd…

pg_hba.conf配置里peer,indent和md5的区别

在 PostgreSQL 的 pg_hba.conf配置文件中,peer、ident和 md5是三种不同的认证方法。以下是它们的详细区别: 1. peer​ 认证方法 特点: 仅适用于本地连接(local connection type) 基于操作系统用户身份进行认证 不…

基于Simulink的双电机PID控制仿真实现方案

基于Simulink的双电机PID控制仿真实现方案,包含S函数建模、阶跃信号输入和可视化分析:一、系统架构设计二、模块实现 1. 电机动力学S函数(motor_sfun.m) function [sys,x0,str,ts] = motor_sfun(t,x,u,flag) switc…

锡林郭勒西林瓶灌装清洗耗材月成本分析?查行情享优惠

近年来,随着制药装备智能化水平持续提升,西林瓶灌装设备及相关配套耗材的采购成本正逐步趋于理性。行业普遍呈现出“高配置、低门槛”的价格趋势,性价比已成为用户选购西林瓶灌装生产线的核心考量指标。尤其在清洗环…

AI Agent OS 探索有价值的论文分析(1):Sleep-time Compute

Sleep-time Compute 分析 1.摘要 论文信息论文地址:[2504.13171v1] Sleep-time Compute: Beyond Inference Scaling at Test-time开源项目:GitHub - letta-ai/sleep-time-compute: accompanying material for sleep-…