LVGL调度

LVGL调度
THEDI前言
在做项目的过程中使用到了LVGL,遇到了一些问题,于LVGL的调度有关,所以这里写一篇笔记记录LVGL调度相关内容
LVGL任务调度
任务处理介绍
LVGL通过定时器轮询机制
周期性检查并执行所有任务(动画、事件、刷新等),由主循环中持续调用lv_task_handler()
处理任务。
所有动画、控件刷新、事件分发等任务
操作,都是在该函数内执行,并且最终都是通过定时器机制
驱动。
我们必须在循环中间隔5~20ms持续调用lv_task_handler()
来处理任务,
在使用LVGL时,通常伴随RTOS的使用,所以我们都是将lvgl任务处理单独由一个任务执行,保证lv_task_handler()
周期性的被调用处理所有的事件(UI刷新,输入检测等)
1 | /* LVGL Handler task,驱动LVGL运行 */ |
定时器机制
LVGL内部维护一个定时器链表
,在每次调用lv_task_handler()
时,会对整个定时器链表
进行一个遍历的操作,并对链表中所有的已经到期
的定时器执行定时器回调函数
(我们注册的输入设备回调函数等等)进行一个执行。最终达到驱动系统更新的目的,属于非阻塞的延迟执行机制
。
任务执行方式
LVGL采用协作式
但线程调度,任务(定时器回调、动画、事件处理)按以下规则运行:
- 顺序执行:每个到期定时器的回调函数会
一次性完整执行
,完成后才会处理下一个任务,不会被抢占
- 优先级控制:定时器可设置
优先级
,但仅影响执行顺序
因为没有时间片划分,每次任务都要执行完成才会执行下一个任务,所以任务必须快速结束,否则会影响其他任务的执行!!!
调度流程源码分析
1.在初始化阶段,对于各个设备
(显示、输入设备),我们会对其进行创建对象
和设置对应的回调函数
2.其中创建对象的时候,会跟随创建一个定时器
,并将对应对象的回调函数
作为参数传入
这就是对应设备的回调函数lv_indev_read_timer_cb
内部实现(这里是输入设备的), 最终就是调用我们初始化时注册的indev->read_cb
- 定时器创建的内部会设置
定时器回调函数
设置为我们对应类型设备的回调函数
,同时作为结点
插入到我们的定时器链表
当中
4.每隔5~10ms,我们调用的lv_timer_handler
时,会对整个定时器链表
进行一个遍历的操作,并对链表中所有的已经到期
的定时器执行定时器回调函数
(我们注册的包括输入设备采集、动画、事件分发等所有的设备和事件)进行一个执行。最终达到驱动系统更新的目的
每个定时器都会判断是否到期,只要到期才会执行对应定时器的timer_cb回调函数
每个timer都有上次运行时间戳timer->last_run
,定时器间隔timer->period
以下是为 lv_timer_handler()
添加的详细中文注释版,注释尽量保留原有逻辑结构,方便后续阅读和调试:
1 | // LVGL 的定时器调度主函数,每次被调用会遍历所有注册的定时器并执行到期的任务 |
LVGL时基
时基介绍
LVGL需要一个系统滴答(系统时基)作为它的核心时间管理机制,来获取动画和其他任务经过的事件,以便驱动LVGL内部任务lv_task_handler()
的正常执行(管理动画(如按钮点击的过渡效果)、处理输入设备(如触摸屏、按键)、定时更新 UI)
如果缺少时基,LVGL就认为时间静止了,LVGL涉及时间相关的内容就不会生效,比如:
- 动画卡住或不动
- 定时器、超时、刷新全部失效
- 触摸/输入响应延迟或无效
配置时基
我们在移植lvgl时,需要完成改时基的配置
为LVGL提供时基的方法有两种:使用定时器、FreeRTOS的Tick钩子函数
Tick 接口 — LVGL 文档:官方建议使用FreeRTOS的话,就用Tick钩子函数提供时基,实现告知LVGL现在距离上一次多少毫秒过去了
的功能
FreeRTOS钩子函数
FreeRTOS的钩子函数vApplicationTickHook()
会在每次系统时钟节拍(tick)中断时被调用(通常是SysTick中断),详细介绍见笔记或官方文档
下面我们来介绍如何进行配置
1.在FreeRTOSConfig.h
中配置configUSE_IDLE_HOOK
为 1
,启用钩子函数
运行周期:由configTICK_RATE_HZ
决定, 一般设置为1000,也就是1ms
- 自己实现
vApplicationTickHook
,里面调用lv_tick_inc(1)
,告诉LVGL已经过去了1ms
1 | void vApplicationTickHook() |
注意:vApplicationTickHook() 从ISR内执行,因此必须非常短,且不能被阻塞!
定时器
LVGL调度相关API
我们在移植使用lvgl时就需要对两个部分:lv_task_handler
和lv_tick_inc()
进行分别移植调用!
lv_task_handler()
说明:
lv_task_handler()
内部调用lv_timer_handler()
作用:驱动LVGL的主循环,刷新UI、执行动画、处理输入设备等任务。
用法:使用LVGL必须在主循环或RTOS独立任务中每5~20ms调用一次。
以下就是在FreeRTOS中单独的一个任务用于驱动LVGL,官方推荐执行lvgl的最小栈空间>2KB
1 | osThreadId_t LvglHandlerTaskHandle; |
- lv_tick_inc()
作用:为LVGL提供毫秒
级的时间流逝(时基)。
用法:通常每1ms调用一次,比如RTOS tick钩子、定时器中断。
用法见上方配置时基部分