【說在前面的話】
LVGL的剛剛完成了對LVGL8的維護更新,發布了v8.3.5版。相對master分支上正在開發的LVGL9,該版本是一個吐血推薦的穩定版本:
它是 LVGL8 的維護性更新,API保持不變,只做了一些修修補補的工作
修復和更新了對眾多GPU的支持,包括
Arm-2D
NXP-PXP 和 NXP-VGLite
用LVGL做產品的小伙伴可以放心食用。
【如何獲取 LVGL cmsis-pack】
1、用戶可以通過LVGL在Github的倉庫直接下載:
https://github.com/lvgl/lvgl/tree/release/v8.3/env_support/cmsis-pack
2、關注公眾號【裸機思維】后,發送消息“LVGL”獲取網盤鏈接。
3、用戶也直接通過MDK的Pack-Installer進行直接安裝,就像lwIP那樣:
無論采用哪種方法,一旦完成安裝,以后就可以通過Pack-Installer來獲取最新版本啦。
【如何在MDK中部署LVGL】
步驟一:配置RTE
在MDK中通過菜單 Project->Manage->Run-Time Enviroment 打開RTE配置窗口:
在RTE配置界面中找到LVGL,將其展開:
與其它平臺下部署LVGL不同,cmsis-pack允許大家像點菜那樣只將所需的模塊(或者功能)加入到工程中。
注意,這里必點的是“Essential”,它是LVGL的核心服務。一般來說,為了使用LVGL所攜帶的豐富控件(Widgets),我們還需要選中“Extra Themes”。如果你是第一次為當前硬件平臺進行LVGL移植,則非常推薦加點“Porting”——它會為你添加移植所需的模板,非常方便。 單擊“OK”關閉RTE配置窗口,我們會看到LVGL已經被加入到工程列表中了:
此時,我們就已經可以成功編譯了。
步驟二:配置LVGL
將LVGL展開,找到配置頭文件 lv_conf_cmsis.h:
該文件其實就是LVGL官方移植文檔中所提到的lv_conf.h,它是基于lv_conf_template.h修改而來。值得說明的是,一些模塊的開關宏都被刪除了,例如:
LV_USE_GPU_ARM2D LV_USE_GPU_STM32_DMA2D LV_USE_GPU_NXP_PXP ……
這是因為,當我們在RTE配置窗口中勾選對應選項時,cmsis-pack就會自動把對應的宏定義加入到 RTE_Components.h 里——換句話說,再也不用我們手動添加啦!
其它對LVGL的配置,請參考官方文檔,這里就不再贅述。
步驟三:使用模板進行移植
當我們在RTE中選擇了porting模塊后,三個移植模板會被加入到工程列表中。
它們是可以編輯的,保存在當前工程的RTE/LVGL目錄中。
這些模板極大的簡化了我們的驅動移植過程,下面,我們將以lv_port_disp_template為例,為大家介紹這些模板的使用方法:
1、打開 lv_port_disp_template.h,將開頭處 #if0 修改為 #if 1,使整個頭文件生效:
2、打開lv_port_disp_template.c,將開頭處#if0修改為#if 1,使整個遠文件生效。
4、根據官方 porting 文檔的指導,根據你的硬件實際情況,在三種緩沖模式中做出選擇:
需要特別強調的是:如果你的系統沒有DMA或者替用戶完成Frame Buffer刷新的專門LCD控制器,那么雙緩沖其實是沒有意義的(因為無論如何都是CPU在干活,因此不會比單緩沖模式有任何性能上的本質不同)。
5、找到disp_init() 函數,并在其中添加LCD的初始化代碼。該函數會被 lv_port_disp_init()調用。
6、找到 disp_flush()函數,并根據你的硬件實際情況,將其改寫。比如這是使用 GLCD_DrawBitmap進行實現的參考代碼:
/*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.*/ static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { if (disp_flush_enabled) { GLCD_DrawBitmap(area->x1, //!< x area->y1, //!< y area->x2 - area->x1 + 1, //!< width area->y2 - area->y1 + 1, //!< height ???????????????????? (const?uint8_t?*)color_p); ????} /*IMPORTANT!!! *Inform the graphics library that you are ready with the flushing*/ lv_disp_flush_ready(disp_drv); }
GLCD_DrawBitmap 用于將給定的顯示緩沖區刷新到LCD,其函數原型如下:
/** fn int32_t GLCD_DrawBitmap (uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *bitmap) rief Draw bitmap (bitmap from BMP file without header) param[in] x Start x position in pixels (0 = left corner) param[in] y Start y position in pixels (0 = upper corner) param[in] width Bitmap width in pixels param[in] height Bitmap height in pixels param[in] bitmap Bitmap data eturns - 0: function succeeded - -1: function failed */ int32_t GLCD_DrawBitmap (uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *bitmap)
這里,5個參數之間的關系如下圖所示:
簡單來說,這個函數就是把bitmap指針所指向的“連續存儲區域”中保存的像素信息拷貝到LCD的一個指定矩形區域內,這一矩形區域由位置信息(x,y)和體積信息(width,height)共同確定。
很多LCD都支持一個叫做“操作窗口”的概念,這里的窗口其實就是上圖中的矩形區域——一旦你通過指令設置好了窗口,隨后連續寫入的像素就會被依次自動填充到指定的矩形區域內(而無需用戶去考慮何時進行折行的問題)。
此外,如果你有幸使用帶LCD控制器的芯片——LCD的顯示緩沖區被直接映射到Cortex-M芯片的4GB地址空間中,則我們可以使用簡單的存儲器讀寫操作來實現上述函數,以STM32F746G-Discovery開發板為例:
//!STM32F746G-Discovery #define GLCD_WIDTH 480 #define GLCD_HEIGHT 272 #defineLCD_DB_ADDR 0xC0000000 #define LCD_DB_PTR ((volatile uint16_t *)LCD_DB_ADDR) int32_t GLCD_DrawBitmap (uint32_t x, uint32_t y, uint32_t width, uint32_t height, const uint8_t *bitmap) { volatileuint16_t*phwDes=LCD_DB_PTR+y*GLCD_WIDTH+x; const uint16_t *phwSrc = (const uint16_t *)bitmap; for (int_fast16_t i = 0; i < height; i++) { memcpy ((uint16_t *)phwDes, phwSrc, width * 2); phwSrc += width; phwDes += GLCD_WIDTH; } ????return 0; }
7、在 main.c 中加入對 lv_port_disp_template.h 的引用:
#include "lv_port_disp_template.h"
8、在main()函數中對LVGL進行初始化:
int main(void) { ... lv_init(); lv_port_disp_init(); ... while(1) { } }
至此,我們就完成了LVGL在MDK工程的部署。是不是特別簡單?
【時間相關的移植】
根據官方移植文檔的要求,我們實際上還需要處理兩個問題:
讓lvgl 知道從復位開始經歷了多少毫秒
以差不多5ms為間隔,調用函數lv_timer_handler() 來進行事件處理(包括刷新)
依據平臺的不同,小伙伴們當然有自己的解決方案。這里,我推薦一個MDK環境下基于perf_counter的方案,它更通用,也更簡單。關于它的使用文章,小伙伴可以參考《【喂到嘴邊了的模塊】超級嵌入式系統“性能/時間”工具箱》,這里就不再贅述。 步驟一:獲取 perf_counter 的cmsis-pack
關注公眾號【裸機思維】后,向后臺發送關鍵字“perf_counter”獲取對應的cmsis-pack網盤鏈接。下載后安裝。
步驟二:在工程中部署 perf_counter
打開RTE配置窗口,找到 Utilities 后展開,選中 perf_counter的 Core:
需要說明的是,無論你用不用操作系統,這里關于各類操作系統的 Patch 你即便不選擇也能正常工作,不必擔心。單擊OK后即完成了部署。 在main()函數中初始化 perf_counter(別忘記添加對頭文件 perf_counter.h 的包含):
#include "perf_counter.h" intmain(void) { /*配置MCU的系統時鐘頻率 */ /*重要:更新 SystemCoreClock 變量*/ SystemCoreClockUpdate(); /*初始化perf_counter*/ init_cycle_counter(true); ... while(1) { } ... }
需要特別說明的是:
調用 init_cycle_counter() 之前,最好通過 SystemCoreClockUpdate() 來將當前的系統頻率更新到關鍵全局變量 SystemCoreClock 上。你當然也可以自己用賦值語句來做,比如:
extern uint32_t SystemCoreClock; SystemCoreClock=72000000ul; /* 72MHz */
如果你已經有應用或者RTOS占用了SysTick(一般都是這樣),則應該將 true 傳遞給 init_cycle_counter() 作為參數——告訴 perf_counter SysTick已經被占用了;反之則應該傳遞 false,此時 perf_counter 會用最大值 0x00FFFFFF來初始化SysTick。
步驟三:更新超級循環
最新版本的LVGL為用戶提供了一個全新的方式來周期性的刷新 LVGL任務函數:lv_timer_handler_run_in_period(毫秒數)。無論是裸機還是RTOS環境,你都可以簡單的將其插入超級循環中——以指定的ms數為間隔刷新LVGL的任務函數,例如:
int main(void) { ... lv_init(); lv_port_disp_init(); lv_port_indev_init(); ... while(1){ lv_timer_handler_run_in_period(LV_DISP_DEF_REFR_PERIOD); } }
【跑分從未如此簡單】
完成移植后,也許你會急于想知道當前環境下自己的平臺能跑出怎樣的幀率吧?別著急,LVGL的cmsis-pack已經為您最好了準備。打開RTE配置窗口,勾選benchmark:
在main.c 中加入對lv_demo_benchmark.h 的“間接”引用:
#include"demos/lv_demos.h"
在 LVGL 初始化代碼后,加入benchmark 無腦入口函數:
int main(void) { lv_init(); lv_port_disp_init(); #if LV_USE_DEMO_BENCHMARK lv_demo_benchmark(); #endif while(1) { lv_timer_handler_run_in_period(5); } }
編譯,運行,走起:
嗯…… Slow but common case……
最新的 benchmark 允許我們通過lv_demo_benchmark_set_finished_cb()注冊一個回調函數——用于告知我們所有測試已經完成:
static void on_benchmark_finished(void) { } int main(void) { lv_init(); lv_port_disp_init(); lv_port_indev_init(); lv_demo_benchmark_set_finished_cb(&on_benchmark_finished); lv_demo_benchmark(); //lv_demo_benchmark_set_max_speed(true); //lv_demo_benchmark_run_scene(43);//runsceneno31 while(1) { lv_timer_handler(); } }
如果我們對具體某一個測試場景感興趣,還可以在注釋掉 lv_demo_benchmark()后通過函數lv_demo_benchmark_run_scene() 來運行指定編號的場景。
【裝逼從未如此簡單】
完成移植后,也許你“又”會急于想知道當前環境下自己的平臺能跑出怎樣的效果吧?(咦?為什么要說又?)別著急,LVGL的cmsis-pack已經為您最好了準備。打開RTE配置窗口,勾選Demo:Widgets:
在main.c中加入對lv_demo_widgets.h的“間接”引用:
#include "demos/lv_demos.h"
在 LVGL 初始化代碼后,加入Demo Widgets的無腦入口函數:
int main(void) { lv_init(); lv_port_disp_init(); #if LV_USE_DEMO_BENCHMARK lv_demo_benchmark(); #endif #if LV_USE_DEMO_WIDGETS lv_demo_widgets(); #endif while(1) { lv_timer_handler_run_in_period(5); } }
需要特別注意的是:要跑這個Demo,Stack(棧)不能小于 4K,切記,切記!
編譯,運行,走起:
【說在后面的話】
最后,對在MDK中用cmsis-pack來部署LVGL的過程感到好奇,但又想有個參考的小伙伴,可以關注下面這個開源項目(也是我負責維護的): https://github.com/lvgl/lv_port_an547_cm55_sim 按照readme的教程,你甚至不需要硬件就可以在MDK中免費模擬一個Arm開發板來跑LVGL。加之最近MDK為非商業應用場景提供了幾乎沒有什么限制的社區版,大家已經可以挺直腰板白嫖MDK啦。
此外,如果你是Raspberry Pi Pico的愛好者,還可以參考這個官方倉庫(“又”是我維護的哦):
https://github.com/lvgl/lv_port_raspberry_pi_pico_mdk
審核編輯:湯梓紅
-
ARM
+關注
關注
134文章
9097瀏覽量
367556 -
gpu
+關注
關注
28文章
4740瀏覽量
128948 -
STM32
+關注
關注
2270文章
10900瀏覽量
356012 -
GitHub
+關注
關注
3文章
471瀏覽量
16442 -
LVGL
+關注
關注
1文章
83瀏覽量
2969
原文標題:害怕追新?LVGL8發布穩定性更新(附部署教程)
文章出處:【微信號:pzh_mcu,微信公眾號:痞子衡嵌入式】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論