1. LVGL定时器机制解析
在嵌入式GUI开发中,定时器是最基础也最容易被忽视的组件。LVGL的定时器系统采用单链表结构管理,通过lv_timer_create()创建的每个定时器都会被添加到全局链表LV_GC_ROOT(_lv_timer_ll)中。这个设计看似简单,但在实际项目中我遇到过不少开发者踩坑的情况。
核心工作机制是这样的:主循环中调用lv_timer_handler()时,系统会遍历链表检查每个定时器的period和last_run时间戳。当lv_tick_get() - timer->last_run >= timer->period条件满足时,回调函数就会被执行。这里有个关键细节——lv_tick_get()默认依赖用户实现的lv_tick_inc()来维护1ms递增的计数器,很多不稳定问题都源于此处。
重要提示:LVGL官方推荐在硬件定时器中断里调用
lv_tick_inc(1),但有些开发者图省事直接在while(1)里用HAL_Delay(1)模拟,这会导致定时精度受其他任务阻塞影响。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 定时器创建与销毁的陷阱
2.1 定时器创建时的内存管理
用lv_timer_create(cb, period, user_data)创建定时器时,LVGL会在堆上分配lv_timer_t结构体。我在项目审计时发现,很多内存泄漏都是因为只调用了lv_timer_del()而没真正释放内存。正确的做法应该是:
c复制lv_timer_t * timer = lv_timer_create(my_cb, 100, NULL);
/* 使用后需要删除时 */
lv_timer_del(timer);
timer = NULL; // 避免野指针
实测发现,在频繁创建销毁定时器的场景下(比如动态界面),如果不做NULL赋值,残留的指针可能导致非法访问。曾经有个项目因此导致HardFault,调试了整整两天才定位到问题。
2.2 回调函数的执行上下文
定时器回调是在lv_timer_handler()的上下文中执行的,这意味着:
- 回调里不能有阻塞操作(如
while(!flag)等待) - 如果需要长时间运行的任务,应该用`lv_
