前言
LVGL是一個免費開源的嵌入式圖形庫,它特別擅長為資源受限的微控制器或小型處理器設備打造漂亮的用戶界面。由于它設計得非常輕巧高效,同時對內存和處理器要求不高,因此能輕松適配各種屏幕尺寸和類型的嵌入式硬件平臺。它提供了異常豐富的現成組件,像按鈕、圖表、列表這些常用控件一應俱全,并且支持高級特性比如流暢的動畫、抗鋸齒字體顯示和矢量圖形的平滑縮放效果。
開發(fā)者可以用C語言相對便捷地構建出視覺效果現代、交互流暢的界面,加上其完善的文檔和活躍的中文社區(qū)支持,LVGL大大降低了在嵌入式系統(tǒng)上開發(fā)專業(yè)級GUI的門檻和復雜度,是物聯網設備、工控面板、穿戴設備等嵌入式屏幕交互開發(fā)的強力助手。
本期我們介紹在STM32中移植并使用LVGL進行GUI開發(fā),開發(fā)板采用STM32N6570-DK,開發(fā)平臺采用STM32CubeIDE1.18.1,LVGL版本為v8.2,參考資料為正點原子LGVL教程。
1
LGVL庫下載和精簡
在LVGL的官網中可以跳轉到其GitHub倉庫處,分支選擇為v8.2版本并下載。
LVGL源文件僅保留如圖五個文件即可,其中examples文件夾可以進一步裁剪。
examples文件夾中僅保留porting文件夾內容即可,因此精簡完的LVGL庫只有如下文件:
2
工程搭建
除了lvgl庫以外,需要準備好顯示屏驅動程序和觸摸驅動程序,并有一個可以用的工程模板。
該工程除了屏幕的驅動之外,還需要開啟一個定時器用來給lvgl底層提供時基,推薦定時1毫秒。
確認頭文件和源文件地址設置正確并開啟了編譯優(yōu)化。
打開lv_conf_template.h文件,將宏定義使能修改為1,代表著使能這部分內容。
并且將lv_conf_template.h重命名為lv_conf.h!!!
并且將lv_conf_template.h重命名為lv_conf.h!!!
并且將lv_conf_template.h重命名為lv_conf.h!!!
(重要的事情說三遍)
接著打開examples的porting文件夾中的lv_port_disp_template.c/.h還有l(wèi)v_port_indev_template.c/.h添加刷新和觸摸驅動。
這四個文件(.c/.h),都要將宏定義使能修改為1。
lv_port_disp_template.c的初始化函數void lv_port_disp_init(void)提供了三種刷新方式,我們選擇方式1(局部刷新)并修改屏幕寬度和屏幕大小。
這里的800*10可以根據自己的RAM大小修改,例如我修改為800*480
/*Initialize your display and the required peripherals.*/
staticvoiddisp_init(void)
{
? ??/*You code here*/
??BSP_LCD_Init(0,?0);//屏幕初始化,換做自己原來的
}
/*Flush the content of the internal buffer the specific area on the display
?*You can use DMA or any hardware acceleration to do this operation in the background but
?*'lv_disp_flush_ready()' has to be called when finished.*/
staticvoiddisp_flush(lv_disp_drv_t * disp_drv, constlv_area_t * area, lv_color_t * color_p)
{
? ??/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
? ??BSP_LCD_FillRGBRect(0, area->x1, area->y1, (uint16_t *)color_p, area->x2-area->x1+1, area->y2-area->y1+1);
? ??/*IMPORTANT!!!
? ? ?*Inform the graphics library that you are ready with the flushing*/
? ??lv_disp_flush_ready(disp_drv);
}
并在disp_init函數和disp_flush函數中提供屏幕初始化和區(qū)域刷新驅動。
lv_port_indev_template.c/.h提供了輸入設備邏輯,我們將沒有用到的設備內容全部刪除/注釋掉,由于我們僅采用觸摸屏故僅保留touchpad內容。
所以初始化中僅保留touchpad相關函數和語句,其他函數也僅保留如下四個函數。
TS_State_t TS_State;//觸摸屏全局變量
/*Initialize your touchpad*/
staticvoidtouchpad_init(void)
{
? ??/*Your code comes here*/
? TS_Init_t TS_Init;
? ??/* 初始化觸摸屏 */
? TS_Init.Width?=?800; ? ? ? ?// 屏幕寬度
? TS_Init.Height?=?480; ? ? ??// 屏幕高度
? ? TS_Init.Orientation?=?TS_SWAP_NONE;?// 方向,不旋轉
? TS_Init.Accuracy?=?5; ? ? ??// 精度,5個像素的抖動會被過濾
??BSP_TS_Init(0, &TS_Init);
}
/*Will be called by the library to read the touchpad*/
//這個函數沒做修改
staticvoidtouchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
? ? staticlv_coord_t last_x =?0;
? ? staticlv_coord_t last_y =?0;
? ??/*Save the pressed coordinates and the state*/
? ??if(touchpad_is_pressed()) {
? ? ? ??touchpad_get_xy(&last_x, &last_y);
? ? ? ? data->state =?LV_INDEV_STATE_PR;
? ? }?else?{
? ? ? ? data->state =?LV_INDEV_STATE_REL;
? ? }
? ??/*Set the last pressed coordinates*/
? ? data->point.x?= last_x;
? ? data->point.y?= last_y;
}
/*Return true is the touchpad is pressed*/
staticbooltouchpad_is_pressed(void)
{
? ??/*Your code comes here*/
? ??//檢測到觸摸
??if?(BSP_TS_GetState(0, &TS_State) ==?BSP_ERROR_NONE) {
? ? ?if?(TS_State.TouchDetected) {
? ? ? ? ? returntrue;//檢測到返回真
? ? ?}
? }
? ? returnfalse;
}
/*Get the x and y coordinates if the touchpad is pressed*/
staticvoidtouchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
? ??/*Your code comes here*/
??// 獲取觸摸坐標
? ? (*x) = TS_State.TouchX;
? ? (*y) = TS_State.TouchY;
}
保留touchpad內容并完成“觸摸初始化”,“檢測觸摸生效”,“獲取觸摸位置”三個內容。
??HAL_TIM_Base_Start_IT(&htim18);//開啟定時器
??lv_init();//Lvgl初始化
??lv_port_disp_init();//屏幕初始化
??lv_port_indev_init();//輸入設備輸出化
//上述三個步驟順序不能調換
while?(1)
? {
? ??lv_timer_handler();
? ??HAL_Delay(5);
? }
在main主函數中加入lvgl初始化和設備初始化,并在While循環(huán)中添加延時并觸發(fā)定時器時基句柄(這里的時間不用太精確)。
voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
? ?static?Timnumber?=?0;
? ?if(htim->Instance?==?TIM18)
? ?{
? ? ?lv_tick_inc(1);//lvgl時基觸發(fā) 1
? ? ?//如果是5ms延時就填入5
? ? ?Timnumber++;
? ? ?if(Timnumber?==?500)
? ? ?{
? ? ? ?//每500秒LED反轉
? ? ? ?HAL_GPIO_TogglePin(GPIOO,?GPIO_PIN_1);
? ? ? ?Timnumber?=?0;
? ? ?}
? ?}
}
定時器回調函數中添加lvgl時基函數。(記得開啟定時器TIM哦),至此lvgl的工程配置完畢。
3
lvgl測試
lvgl提供了好幾個測試Demo,我們將stressDemo進行測試。
在lvgl.h中找到LV_USE_DEMO_STRESS宏定義并將其設置為1使能。
main中包含這個Demo的頭文件,并在lvgl初始化后調用demo例程。
??HAL_TIM_Base_Start_IT(&htim18);//開啟定時器
??lv_init();//Lvgl初始化
??lv_port_disp_init();//屏幕初始化
??lv_port_indev_init();//輸入設備輸出化
??lv_demo_stress();//demo例程
??while(1)
? {
? ??lv_timer_handler();
? ??HAL_Delay(5);
??
效果如下: