引言
設計GUI的顯示元素動起來,實際上是多張圖按照一定的節奏和變化依次刷屏實現的。LVGL在內核設計了timer
對象,用于實現周期回調的功能,開發者可以在周期調用的回調函數中,修改GUI顯示內容的屬性,觸發LVGL顯示內容的刷新,然后由LVGL的例行執行程序重新刷屏,從而實現動畫效果。
LVGL中的timer對象
LVGL的后臺是通過時間片輪詢對LVGL的交互任務進行調度的。
在具體的MCU平臺上適配LVGL時,通常會使用SysTick定時器中斷服務程序調用lv_tick_inc()
函數驅動LVGL內部的計數器遞增,然后在主線程中調用lv_timer_handler()
函數以時間片為節點執行LVGL的日常任務(輸入事件、刷屏等操作)。從源碼上看,整個LVGL的程序框架,就是建立在定時器刻度的輪詢調度之上的,可見包含LVGL的MCU工程的頂級業務邏輯代碼如下:
lv_ui guider_ui;
int main(void)
{
BOARD_Init();
SysTick_Config(CLOCK_SYS_FREQ / 100); /* 10ms. */
lv_init();
lv_port_disp_init();
setup_ui(&guider_ui);
events_init(&guider_ui);
custom_init(&guider_ui);
while (1)
{
lv_timer_handler(); /* 執行LVGL時間片輪詢. */
}
}
void SysTick_Handler(void)
{
lv_tick_inc(10); /* 驅動LVGL的定時器. */
}
在LVGL中,當需要實現一些基于時間變化顯示內容的效果(動畫),可以借用LVGL內部的定時器對象來實現周期執行程序的效果。關于LVGL應用程序中使用定時器的API主要有兩個:
- lv_timer_create()
- lv_timer_del()
其中,使用lv_timer_create()
函數創建一個定時器實例,并會向其中傳送初始化參數,指定本定時器的觸發周期
和觸發時執行的回調函數
。而lv_timer_del()
函數會回收這個定時器的資源(包括變量,以及掛在定時器任務列表中的指針),以減輕LVGL后臺調度器的負擔。
基于timer對象實現儀表走針
這里以設計一個速度表盤指針轉動的樣例,展示timer定時器對象的用法。實現原理,在定時器組件的回調函數中,會周期性地改變meter
組件中speed_scale_1_ndline_0
屬性的值。當改變LVGL顯示組件的任何可視化組件的屬性后,LVGL會自動刷新顯示內容,從而形成動畫效果。
在GUI Guider的編輯頁面中新建一個meter
組件的對象,如圖x所示。
圖x 在GUI Guider中新建meter對象
將新建meter對象改名為meter_speed
,這個名字將會在后面自定義編寫代碼時,用于引用這個meter對象實例。手工調整一下表盤的尺寸。
然后,選中主編輯區的頁面區域(對應選中當前的screen對象),再在編輯區域的右側,在事件(Events)
頁面中,單擊加號,創建一個事件,編輯事件。
編輯事件的觸發方式,即在什么時機產生當前編輯的事件,常見的事件觸發方式有:Load Start(開始載入當前對象時)、Loaded(完成載入當前對象時)、Unload Start(開始從本對象切換到別處時)、Unloaded(完成從本對象切換到別處時)等,如圖x所示。每種顯示元素的對象都有各自的觸發方式,甚至還有一些顯示元素的對象沒有觸發事件可創建。
圖x 在GUI Guider中為事件編輯觸發方式
選定Trigger觸發方式
后,還需要選定Target目標對象
。此時,目標對象
的下拉列表中會自動整理出當前整個GUI Guider工程中已經創建的有所顯示元素對象。如圖x所示。
圖x 在GUI Guider中為事件編輯目標對象
這里需要重點說明的是,觸發方式描述的當前在編輯區域中選中的對象的動作,當選定觸發方式后,本事件的標簽名稱也就自動改成了<當前對象名>_<觸發方式>
。目標對象反映的是即將改變屬性的對象,對應部分可選的屬性也在事件頁面下方展示出來,可在圖形頁面中配置。當然,并不是所有的可編輯屬性都被做成了在對話框中可配置的,本例就在最后一行,選擇了執行自定義程序,在彈出的代碼編輯對話框中添加了創建定時器對象timer_meter_speed
的語句。如圖x所示。
圖x 在GUI Guider中為事件編輯自定義執行的程序代碼
圖x中所示,自定義編寫C源碼,當觸發Screen的Loaded事件時,將執行圖中代碼,創建timer_meter_speed
定時器對象實例。保存工程后生成代碼,對應地,可在GUI Guider工程中的./generated/event_init.c
文件中看到對應生成的代碼。
#include "events_init.h"
#include < stdio.h >
#include "lvgl.h"
#include "custom.h"
static lv_timer_t * timer_meter_speed;
void events_init(lv_ui *ui)
{
}
static void screen_event_handler(lv_event_t *e)
{
lv_event_code_t code = lv_event_get_code(e);
switch (code)
{
case LV_EVENT_SCREEN_LOADED:
{
timer_meter_speed = lv_timer_create(timer_meter_speed_cb, 100, &guider_ui);
}
break;
case LV_EVENT_SCREEN_UNLOADED:
{
lv_timer_del(timer_meter_speed);
}
break;
default:
break;
}
}
void events_init_screen(lv_ui *ui)
{
lv_obj_add_event_cb(ui- >screen, screen_event_handler, LV_EVENT_ALL, ui);
}
但是回調函數timer_meter_speed_cb()
仍需要開發者自行在custom.c
文件中自行創建,GUI Guider不會自動更新。代碼如下:
static int32_t speed = 50;
static bool is_increase = true;
/**
* Create a demo application
*/
void custom_init(lv_ui *ui)
{
/* Add your codes here */
}
void timer_meter_speed_cb(lv_timer_t *t)
{
lv_ui * gui = t- >user_data;
lv_meter_set_indicator_value(gui- >screen_meter_speed, gui- >screen_meter_speed_scale_1_ndline_0, speed);
if (speed >= 90)
{
is_increase = false;
}
if (speed <= 20)
{
is_increase = true;
}
if (is_increase)
{
speed++;
}
else
{
speed--;
}
}
這里實現的內容是,在MCU上運行LVGL時,一旦顯示完成當前的屏幕頁面后,立即創建定時器對象timer_meter_speed
,這個定時器將會每隔100ms調用一次回調函數timer_meter_speed_cb()
,在timer_meter_speed_cb()
函數中,更新變量speed
的值,然后通過lv_meter_set_indicator_value()
函數,改變screen_meter_speed
對象中screen_meter_speed_scale_1_ndline_0
屬性的值。每隔100ms改變一次screen_meter_speed
對象的屬性值,將會觸發顯示內容的刷新,從而在顯示屏上顯現出動畫效果。
將GUI Guider生成的程序下載到MCU之前,可以先在GUI Guider的工程中模擬運行一次,一方面可以幫忙排除編譯錯誤(GUI Guider使用armgcc將LVGL和生成的源碼一起編譯),另一方面也方便預覽運行效果。使用LVGL組件的過程不涉及到對具體MCU硬件的依賴,因此完全可以在模擬環境中預覽實際執行的效果。如圖x所示。
圖x 預覽在GUI Guider中創建的表盤對象
最后,編譯Keil工程,下載到MCU開發板。可以看到在MCU開發板上運行動圖的效果,如圖x所示。
圖x 在MCU開發板上運行轉動指針的表盤
小結
timer對象本身只是一個實現周期調度的機制,開發者可以在周期回調的函數內部,修改和重置顯示各種對象的屬性,觸發LVGL刷屏,從而實現動畫的效果。
-
計數器
+關注
關注
32文章
2261瀏覽量
94985 -
定時器
+關注
關注
23文章
3255瀏覽量
115375 -
GUI
+關注
關注
3文章
662瀏覽量
39891 -
調度器
+關注
關注
0文章
98瀏覽量
5298 -
LVGL
+關注
關注
1文章
91瀏覽量
3083
發布評論請先 登錄
相關推薦
評論