概述
本文介紹目前LVGL的應(yīng)用小知識(shí),希望對(duì)采用MCU設(shè)計(jì)UI界面的用戶有所啟發(fā),開發(fā)出界面更友好的消費(fèi)品或者工業(yè)產(chǎn)品,造福大眾。
01.
LVGL系統(tǒng)架構(gòu)
LVGL系統(tǒng)框架
應(yīng)用程序創(chuàng)建GUI并處理特定任務(wù)的應(yīng)用程序。
LVGL本身是一個(gè)圖形庫。我們的應(yīng)用程序通過調(diào)用LVGL庫來創(chuàng)建GUI。它包含一個(gè)HAL(硬件抽象層)接口,用于注冊(cè)顯示和輸入設(shè)備驅(qū)動(dòng)程序。
驅(qū)動(dòng)程序除特定的驅(qū)動(dòng)程序外,它還有其他的功能,可驅(qū)動(dòng)顯示器到GPU (可選)、讀取觸摸板或按鈕的輸入。
根據(jù)MCU,有兩種典型的硬件設(shè)置。一個(gè)帶有內(nèi)置LCD/TFT驅(qū)動(dòng)器的外圍設(shè)備,而另一種是沒有內(nèi)置LCD/TFT驅(qū)動(dòng)器的外圍設(shè)備。在這兩種情況下,都需要一個(gè)幀緩沖區(qū)來存儲(chǔ)屏幕的當(dāng)前圖像。
1.集成了TFT/LCD驅(qū)動(dòng)器的MCU如果MCU集成了TFT/LCD驅(qū)動(dòng)器外圍設(shè)備,則可以直接通過RGB接口連接顯示器。在這種情況下,幀緩沖區(qū)可以位于內(nèi)部RAM(如果MCU有足夠的RAM)中,也可以位于外部RAM(如果MCU具有存儲(chǔ)器接口)中。
2.如果MCU沒有集成TFT/LCD驅(qū)動(dòng)程序接口,則必須使用外部顯示控制器(例如SSD1963、SSD1306、ILI9341 )。在這種情況下,MCU可以通過并行端口,SPI或通過I2C與顯示控制器進(jìn)行通信。幀緩沖區(qū)通常位于顯示控制器中,從而為MCU節(jié)省了大量RAM。
02.
建立一個(gè)LVGL項(xiàng)目
要在我們的項(xiàng)目中使用 lvgl ,我們起碼需要獲取到官方的這兩個(gè)庫:
lvgl(lvgl)核心圖形庫的官方 GitHub 倉庫地址:https://github.com/lvgl/lvgl。
lvgl(lv_drivers)輸入輸出設(shè)備驅(qū)動(dòng)官方 GitHub 倉庫地址:https://github.com/lvgl/lv_drivers
我們可以克隆或下載這兩個(gè)庫的最新版本,將它們復(fù)制到我們的項(xiàng)目中,然后進(jìn)行適配。
目錄 lvgl 就是 lvgl 的官方圖形庫
目錄 lv_drivers 是 lvgl 輸入輸出設(shè)備驅(qū)動(dòng)官方示例配置
目錄 lv_examples 是 lvgl 的官方demo(可選,但不要直接使用到實(shí)際項(xiàng)目中)
配置文件
上面的三個(gè)庫中有一個(gè)類似名為 lv_conf_template.h 的配置頭文件(template就是模板的意思)。通過它可以設(shè)置庫的基本行為,裁剪不需要模塊和功能,在編譯時(shí)調(diào)整內(nèi)存緩沖區(qū)的大小等等。
將 lvgl/lv_conf_template.h 復(fù)制到 lvgl 同級(jí)目錄下,并將其重命名為 lv_drv_conf.h 。打開文件并將開頭的 #if 0 更改為 #if 1 以使能其內(nèi)容。
將 lv_drivers/lv_drv_conf_template.h 復(fù)制到 lv_drivers 同級(jí)目錄下,并將其重命名為 lv_conf.h 。打開文件并將開頭的 #if 0 更改為 #if 1 以使能其內(nèi)容。
(可選)將 lv_examples/lv_ex_conf_template.h 復(fù)制到 lv_examples 同級(jí)目錄下,并將其重命名為 lv_ex_conf.h 。打開文件并將開頭的 #if 0 更改為 #if 1 以使能其內(nèi)容。
準(zhǔn)備lvgl配置文件

使能配置文件
lv_conf.h 也可以復(fù)制到其他位置,但是應(yīng)該在編譯器選項(xiàng)中添加 ``LV_CONF_INCLUDE_SIMPLE``定義(例如,對(duì)于gcc編譯器為``-DLV_CONF_INCLUDE_SIMPLE`` ) 并手動(dòng)設(shè)置包含路徑。
在配置文件中,注釋說明了各個(gè)選項(xiàng)的含義。我們?cè)谝浦矔r(shí)至少要檢查以下三個(gè)配置選項(xiàng),其他配置根據(jù)具體的需要進(jìn)行修改:
LV_HOR_RES_MAX 顯示器的水平分辨率。
LV_VER_RES_MAX 顯示器的垂直分辨率。
LV_COLOR_DEPTH 顏色深度,其可以是:
8 - RG332
16 - RGB565
32 - (RGB888和ARGB8888)
初始化LVGL
準(zhǔn)備好這三個(gè)庫:lvgl、lv_drivers、lv_examples 后,我們就要開始使用lvgl帶給我們的功能了。使用 lvgl 圖形庫之前,我們還必須初始化 lvlg 以及相關(guān)其他組件。初始化的順序?yàn)椋?/p>
調(diào)用 lv_init() 初始化 lvgl 庫;
初始化驅(qū)動(dòng)程序;
在 LVGL 中注冊(cè)顯示和輸入設(shè)備驅(qū)動(dòng)程序;
在中斷中每隔 x毫秒 調(diào)用 lv_tick_inc(x) 用以告知 lvgl 經(jīng)過的時(shí)間;
每隔 x毫秒 定期調(diào)用 lv_task_handler() 用以處理與 LVGL相關(guān)的任務(wù)。
03.
顯示接口
要設(shè)置顯示,必須初始化 lv_disp_buf_t 和 lv_disp_drv_t 變量。
lv_disp_buf_t 保存顯示緩沖區(qū)信息的結(jié)構(gòu)體
lv_disp_drv_t HAL要注冊(cè)的顯示驅(qū)動(dòng)程序、與顯示交互并處理與圖形相關(guān)的結(jié)構(gòu)體、回調(diào)函數(shù)。
顯示緩存區(qū)
關(guān)于緩沖區(qū)大小,有 3 種情況:
一個(gè)緩沖區(qū) LVGL將屏幕的內(nèi)保存到緩沖區(qū)中并將其發(fā)送到顯示器。緩沖區(qū)可以小于屏幕。在這種情況下,較大的區(qū)域?qū)⒈恢禺嫵啥鄠€(gè)部分。如果只有很小的區(qū)域發(fā)生變化(例如按下按鈕),則只會(huì)刷新該部分的區(qū)域。
兩個(gè)非屏幕大小的緩沖區(qū) 具有兩個(gè)緩沖區(qū)的 LVGL 可以將其中一個(gè)作為顯示緩沖區(qū),而另一緩沖區(qū)的內(nèi)容發(fā)送到后臺(tái)顯示。應(yīng)該使用 DMA 或其他硬件將數(shù)據(jù)傳輸?shù)斤@示器,以讓CPU同時(shí)繪圖。這樣,渲染和刷新并行處理。與 一個(gè)緩沖區(qū) 的情況類似,如果緩沖區(qū)小于要刷新的區(qū)域,LVGL將按塊繪制顯示內(nèi)容
兩個(gè)屏幕大小的緩沖區(qū) 與兩個(gè)非屏幕大小的緩沖區(qū)相反,LVGL將始終提供整個(gè)屏幕的內(nèi)容,而不僅僅是塊。這樣,驅(qū)動(dòng)程序可以簡(jiǎn)單地將幀緩沖區(qū)的地址更改為從 LVGL 接收的緩沖區(qū)。因此,當(dāng)MCU具有 LCD/TFT 接口且?guī)彌_區(qū)只是 RAM 中的一個(gè)位置時(shí),這種方法的效果很好。
顯示驅(qū)動(dòng)器
一旦緩沖區(qū)初始化準(zhǔn)備就緒,就需要初始化顯示驅(qū)動(dòng)程序。在最簡(jiǎn)單的情況下,僅需要設(shè)置 lv_disp_drv_t 的以下兩個(gè)字段:
buffer 指向已初始化的 lv_disp_buf_t 變量的指針。
flush_cb 回調(diào)函數(shù),用于將緩沖區(qū)的內(nèi)容復(fù)制到顯示的特定區(qū)域。刷新準(zhǔn)備就緒后,需要調(diào)用lv_disp_flush_ready()。LVGL可能會(huì)以多個(gè)塊呈現(xiàn)屏幕,因此多次調(diào)用flush_cb。使用 lv_disp_flush_is_last() 可以查看哪塊是最后渲染的。
其中,有一些可選的數(shù)據(jù)字段:
hor_res 顯示器的水平分辨率。(默認(rèn)為 lv_conf.h 中的 LV_HOR_RES_MAX )
ver_res 顯示器的垂直分辨率。(默認(rèn)為 lv_conf.h 中的 LV_VER_RES_MAX )
color_chroma_key 在 chrome 鍵控圖像上將被繪制為透明的顏色。(默認(rèn)為 lv_conf.h 中的 LV_COLOR_TRANSP )
user_data 驅(qū)動(dòng)程序的自定義用戶數(shù)據(jù)。可以在 lv_conf.h 中修改其類型。
anti-aliasing 使用抗鋸齒(anti-aliasing)(邊緣平滑)。缺省情況下默認(rèn)為 lv_conf.h 中的 LV_ANTIALIAS 。
rotated 如果 1 交換 hor_res 和 ver_res 。兩種情況下 LVGL 的繪制方向相同(從上到下的線條),因此還需要重新配置驅(qū)動(dòng)程序以更改顯示器的填充方向。
screen_transp 如果為 1 ,則屏幕可以具有透明或不透明的樣式。需要在 lv_conf.h 中啟用 LV_COLOR_SCREEN_TRANSP 。
要使用GPU,可以使用以下回調(diào):
gpu_fill_cb 用顏色填充內(nèi)存中的區(qū)域。
gpu_blend_cb 使用不透明度混合兩個(gè)內(nèi)存緩沖區(qū)。
gpu_wait_cb 如果在 GPU 仍在運(yùn)行 LVGL 的情況下返回了任何 GPU 函數(shù),則在需要確保GPU渲染就緒時(shí)將使用此函數(shù)。
注意,這些功能需要繪制到內(nèi)存(RAM)中,而不是直接顯示在屏幕上。
其他一些可選的回調(diào),使單色、灰度或其他非標(biāo)準(zhǔn)RGB顯示一起使用時(shí)更輕松、優(yōu)化:
rounder_cb 四舍五入要重繪的區(qū)域的坐標(biāo)。例如。2x2像素可以轉(zhuǎn)換為2x8。如果顯示控制器只能刷新特定高度或?qū)挾鹊膮^(qū)域(對(duì)于單色顯示器,通常為8 px高),則可以使用它。
set_px_cb 編寫顯示緩沖區(qū)的自定義函數(shù)。如果顯示器具有特殊的顏色格式,則可用于更緊湊地存儲(chǔ)像素。(例如1位單色,2位灰度等)。這樣,lv_disp_buf_t中使用的緩沖區(qū)可以較小,以僅保留給定區(qū)域大小所需的位數(shù)。set_px_cb不能與兩個(gè)屏幕大小的緩沖區(qū)一起顯示緩沖區(qū)配置。
monitor_cb 回調(diào)函數(shù)告訴在多少時(shí)間內(nèi)刷新了多少像素。
clean_dcache_cb 清除與顯示相關(guān)的所有緩存的回調(diào)
要設(shè)置 lv_disp_drv_t 變量的字段,需要使用 lv_disp_drv_init(&disp_drv) 進(jìn)行初始化。最后,要為 LVGL 注冊(cè)顯示設(shè)備,需要調(diào)用lv_disp_drv_register(&disp_drv)。
04.
輸入設(shè)備接口
(一)、輸入設(shè)備的類型
要設(shè)置輸入設(shè)備,必須初始化 lv_indev_drv_t 變量:

類型 (indev_drv.type)可以是:
LV_INDEV_TYPE_POINTER 觸摸板或鼠標(biāo)
LV_INDEV_TYPE_KEYPAD 鍵盤或小鍵盤
LV_INDEV_TYPE_ENCODER 帶有左,右,推動(dòng)選項(xiàng)的編碼器
LV_INDEV_TYPE_BUTTON 外部按鈕按下屏幕
read_cb (indev_drv.read_cb)是一個(gè)函數(shù)指針,將定期調(diào)用該函數(shù)指針以報(bào)告輸入設(shè)備的當(dāng)前狀態(tài)。它還可以緩沖數(shù)據(jù)并在沒有更多數(shù)據(jù)要讀取時(shí)返回 false ,或者在緩沖區(qū)不為空時(shí)返回 true 。
進(jìn)一步了解有關(guān) 輸入設(shè)備 的更多信息。
(二)、觸摸板,鼠標(biāo)或任何指針
可以單擊屏幕點(diǎn)的輸入設(shè)備屬于此類別。
即使?fàn)顟B(tài)為 LV_INDEV_STATE_REL ,觸摸板驅(qū)動(dòng)程序也必須返回最后的 X/Y 坐標(biāo)。
要設(shè)置鼠標(biāo)光標(biāo),請(qǐng)使用 lv_indev_set_cursor(my_indev,&img_cursor) 。( my_indev 是 lv_indev_drv_register 的返回值)鍵盤或鍵盤
(三)、觸摸板或鍵盤
帶有所有字母的完整鍵盤或帶有一些導(dǎo)航按鈕的簡(jiǎn)單鍵盤均屬于此處。
要使用鍵盤/觸摸板:
注冊(cè)具有 LV_INDEV_TYPE_KEYPAD 類型的 read_cb 函數(shù)。
在 lv_conf.h 中啟用 LV_USE_GROUP
必須創(chuàng)建一個(gè)對(duì)象組:lv_group_t * g = lv_group_create(),并且必須使用 lv_group_add_obj(g,obj) 向其中添加對(duì)象
必須將創(chuàng)建的組分配給輸入設(shè)備:lv_indev_set_group(my_indev,g)( my_indev 是 lv_indev_drv_register 的返回值)
使用 LV_KEY _… 在組中的對(duì)象之間導(dǎo)航。有關(guān)可用的密鑰,請(qǐng)參見 lv_core/lv_group.h。
(四)、編碼器
可以通過下面四種方式使用編碼器:
按下按鈕
長(zhǎng)按其按鈕
轉(zhuǎn)左
右轉(zhuǎn)
簡(jiǎn)而言之,編碼器輸入設(shè)備的工作方式如下:
通過旋轉(zhuǎn)編碼器,可以專注于下一個(gè)/上一個(gè)對(duì)象。
在簡(jiǎn)單對(duì)象(如按鈕)上按下編碼器時(shí),將單擊它。
如果將編碼器按在復(fù)雜的對(duì)象(如列表,消息框等)上,則該對(duì)象將進(jìn)入編輯模式,從而轉(zhuǎn)動(dòng)編碼器即可在對(duì)象內(nèi)部導(dǎo)航。
長(zhǎng)按按鈕,退出編輯模式。
要使用編碼器(類似于鍵盤),應(yīng)將對(duì)象添加到組中。
(五)、使用帶有編碼器邏輯的按鈕
除了標(biāo)準(zhǔn)的編碼器行為外,您還可以利用其邏輯來使用按鈕導(dǎo)航(聚焦)和編輯小部件。如果只有幾個(gè)按鈕可用,或者除編碼器滾輪外還想使用其他按鈕,這將特別方便。
需要有3個(gè)可用的按鈕:
LV_KEY_ENTER 將模擬按下或推動(dòng)編碼器按鈕
LV_KEY_LEFT 將向左模擬轉(zhuǎn)向編碼器
LV_KEY_RIGHT 將正確模擬轉(zhuǎn)向編碼器
其他鍵將傳遞給焦點(diǎn)小部件
如果按住這些鍵,它將模擬indev_drv.long_press_rep_time中指定的時(shí)間段內(nèi)的編碼器單擊。
(六)、按鍵
按鈕是指屏幕旁邊的外部“硬件”按鈕,它們被分配給屏幕的特定坐標(biāo)。如果按下按鈕,它將模擬在指定坐標(biāo)上的按下。(類似于觸摸板)
使用 lv_indev_set_button_points(my_indev, points_array) 將按鈕分配給坐標(biāo)。points_array應(yīng)該看起來像const lv_point_t points_array [] = {{12,30},{60,90},…}
points_array不能超出范圍。將其聲明為全局變量或函數(shù)內(nèi)部的靜態(tài)變量。
(七)、其它功能
除了 read_cb 之外,還可以在 lv_indev_drv_t 中指定 feedback_cb 回調(diào)。輸入設(shè)備發(fā)送任何類型的事件時(shí),都會(huì)調(diào)用feedback_cb。(獨(dú)立于其類型)。它允許為用戶提供反饋,例如在LV_EVENT_CLICK上播放聲音。
可以在lv_conf.h中設(shè)置以下參數(shù)的默認(rèn)值,但可以在lv_indev_drv_t中覆蓋默認(rèn)值:
拖拽限制(drag_limit) 實(shí)際拖動(dòng)對(duì)象之前要滑動(dòng)的像素?cái)?shù) drag_throw 拖曳速度降低[%]。更高的價(jià)值意味著更快的減速
(drag_throw) 拖曳速度降低[%]。更高的價(jià)值意味著更快的減速
(long_press_time) 按下時(shí)間發(fā)送 LV_EVENT_LONG_PRESSED (以毫秒為單位)
(long_press_rep_time) 發(fā)送 LV_EVENT_LONG_PRESSED_REPEAT 的時(shí)間間隔(以毫秒為單位)
(read_task) 指向讀取輸入設(shè)備的lv_task的指針。可以通過 lv_task_.。。() 函數(shù)更改其參數(shù)
每個(gè)輸入設(shè)備都與一個(gè)顯示器關(guān)聯(lián)。默認(rèn)情況下,新的輸入設(shè)備將添加到最后創(chuàng)建的或顯式選擇的顯示設(shè)備(使用lv_disp_set_default())。相關(guān)的顯示已存儲(chǔ),并且可以在驅(qū)動(dòng)程序的顯示字段中更改。
(八)、心跳
LVGL 需要系統(tǒng)滴答聲才能知道動(dòng)畫和其他任務(wù)的經(jīng)過時(shí)間。
為此我們需要定期調(diào)用 lv_tick_inc(tick_period) 函數(shù),并以毫秒為單位告知調(diào)用周期。例如, lv_tick_inc(1) 用于每毫秒調(diào)用一次。
為了精確地知道經(jīng)過的毫秒數(shù),lv_tick_inc 應(yīng)該在比 lv_task_handler() 更高優(yōu)先級(jí)的例程中被調(diào)用(例如在中斷中),即使 lv_task_handler 的執(zhí)行花費(fèi)較長(zhǎng)時(shí)間。
(九)、任務(wù)處理器(Task Handler)
要處理 LVGL 的任務(wù),我們需要定期通過以下方式之一調(diào)用 lv_task_handler() :
mian 函數(shù)中設(shè)置 while(1) 調(diào)用
定期定時(shí)中斷(低優(yōu)先級(jí)然后是 lv_tick_inc()) 中調(diào)用
定期執(zhí)行的 OS 任務(wù)中調(diào)用
計(jì)時(shí)并不嚴(yán)格,但應(yīng)保持大約5毫秒以保持系統(tǒng)響應(yīng)。
05.
日志記錄
LVGL 內(nèi)置有日志模塊,用于記錄用戶庫中正在發(fā)生的事情。
(一)、日志級(jí)別
要啟用日志記錄,需要在 lv_conf.h 中將 LV_USE_LOG 設(shè)置為 1 ,并將 LV_LOG_LEVEL 設(shè)置為以下值之一:
LV_LOG_LEVEL_TRACE 記錄所有信息
LV_LOG_LEVEL_INFO 記錄重要事件
LV_LOG_LEVEL_WARN 記錄是否發(fā)生了警告事件
LV_LOG_LEVEL_ERROR 記錄錯(cuò)誤信息,當(dāng)系統(tǒng)可能發(fā)生故障時(shí)或致命錯(cuò)誤
LV_LOG_LEVEL_NONE 不要記錄任何東西
級(jí)別高于設(shè)置的日志級(jí)別的事件也將被記錄。例如。如果使用 LV_LOG_LEVEL_WARN ,也會(huì)記錄錯(cuò)誤。
(二)、使用printf記錄
如果您的系統(tǒng)支持printf,則只需在 lv_conf.h 中啟用 **LV_LOG_PRINTF **即可發(fā)送帶有 printf 的日志。
(三)、自定義日志功能
如果不能使用 printf 或想要使用自定義函數(shù)進(jìn)行日志記錄,可以使用 lv_log_register_print_cb() 注冊(cè) “l(fā)ogger” 回調(diào)。
編輯:jq
-
處理器
+關(guān)注
關(guān)注
68文章
20254瀏覽量
252237 -
編碼器
+關(guān)注
關(guān)注
45文章
3953瀏覽量
142626 -
gpu
+關(guān)注
關(guān)注
28文章
5194瀏覽量
135434 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4417瀏覽量
67502 -
LVGL
+關(guān)注
關(guān)注
2文章
124瀏覽量
4559
原文標(biāo)題:華芯微特小課堂--LVG免費(fèi)開源GUI圖形庫
文章出處:【微信號(hào):gh_ed4f95bde4df,微信公眾號(hào):華芯微特32位MCU】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
LVGL創(chuàng)始人要來中國?是的!
瑞薩電子攜手LVGL PRO推進(jìn)嵌入式圖形用戶界面開發(fā)
重大更新,LVGL有UI編輯器用了,2秒內(nèi)加載,快到飛起!
menuconfig修改不使能lvgl后無法編譯怎么解決?
LVGL近期很多人問,那它和Qt哪個(gè)好?
基于rtt+lvgl 8.3 光標(biāo)不顯示怎么解決?
rtthread添加lvgl編譯報(bào)錯(cuò)怎么解決?
基于RTThread nano的LVGL線程卡頓怎么解決?
10分鐘上手睿擎平臺(tái)GUI開發(fā):第一個(gè)LVGL圖形應(yīng)用
從“代碼迷宮”到“視覺藍(lán)圖”——LVGL的嵌入式UI設(shè)計(jì)哲學(xué)!
如何在linux小核下運(yùn)行lvgl?
瑞芯微RK3506開發(fā)板必備之LVGL應(yīng)用開發(fā)手冊(cè),深圳觸覺智能出品
觸覺智能RK3506核心板,工業(yè)應(yīng)用之LVGL顯示方案分享
全面解讀目前LVGL的應(yīng)用小知識(shí)
評(píng)論