1. LVGL簡單介紹
關于LVGL的介紹,大家可以去它的官方文檔查看。下面關于LVGL的介紹均是引用自對官方文檔的翻譯。
LVGL,全稱是 Light and Versatile Graphics Library ,是一款免費開源的輕量多功能圖形庫。LVGL 提供創建嵌入式 GUI 所需的一切,該 GUI 具有易于使用的圖形元素、美觀的視覺效果和低內存占用。
主要特性:
· 強大的構建塊,如按鈕、圖表、列表、滑塊、圖像等。
· 具有動畫、抗鋸齒、不透明度、平滑滾動的高級圖形
· 各種輸入設備,如觸摸板、鼠標、鍵盤、編碼器等
· UTF-8 編碼的多語言支持
· 多顯示器支持,即同時使用多個TFT、單色顯示器
· 具有類似 CSS 樣式的完全可定制的圖形元素
· 獨立于硬件:與任何微控制器或顯示器一起使用
· 可擴展:能夠使用很少的內存(64 kB 閃存、16 kB RAM)進行操作
· 即使具有高級圖形效果,也可進行單幀緩沖區操作
· 用 C 編寫以實現最大兼容性(C++ 兼容)
· 無需嵌入式硬件即可在 PC 上啟動嵌入式 GUI 設計的模擬器
· 綁定到 MicroPython
· 快速 GUI 設計的教程、示例、主題
· 文檔可在線獲取并以 PDF 形式提供
· 根據 MIT 許可免費且開源
運行的設備要求:
基本上,每個能夠驅動顯示器的現代控制器都適合運行 LVGL。最低要求是:
· 16、32 或 64 位微控制器或處理器
· 建議使用 > 16 MHz 時鐘速度
· 閃存/ROM:> 64 kB 用于非常重要的組件(建議> 180 kB)
· 內存:
靜態 RAM 使用量:~2 kB,具體取決于所使用的功能和對象類型堆棧:> 2kB(建議> 8 kB)動態數據(堆):> 2 KB(如果使用多個對象,建議> 48 kB)。LV_MEM_SIZE通過在 中設置lv_conf.h。顯示緩沖區:> “水平分辨率”像素(建議> 10 “水平分辨率” )MCU 或外部顯示控制器中的一個幀緩沖區
· C99 或更新版本的編譯器
2. LVGL學習資源
下面搜集了一些LVGL的學習資源。
1、LVGL官網:
https://lvgl.io/
2、LVGL官網文檔教程:
https://docs.lvgl.io/master/
3、百問網對LVGL官方文檔的翻譯:
https://lvgl.100ask.net/master/
4、LVGL Github倉庫:
https://github.com/lvgl/lvgl
5、lvgl基于Visual sudio 的PC模擬器
https://github.com/lvgl/lv_sim_visual_studio
6、百問網基于LVGL的一個項目:
https://forums.100ask.net/t/topic/602
3. 移植前準備
移植的硬件平臺:
MCU:APM32F411
LCD驅動芯片:ST7789V
觸摸IC:CST816T
本次移植是基于 RT-Thread 系統上運行 LVGL 的,所以在移植前,我們需要準備好可以正常運行 RT-Thread 的Demo工程(可以到極海官網下載APM32F411的SDK)。
另外,還需準備好可以正常運行LCD的驅動和觸摸IC的驅動的代碼。
1、準備一份可以運行RT-Thread的Demo工程
2、下載LVGL_8.3源碼
到官方 Github 倉庫,然后選擇 LVGL_8.3 版本。
下載到的LVGL源碼,然后存放到工程目錄下的 middlewares 文件夾。
4. LVGL移植
4.1 移動和修改移植的接口文件
(1)移植接口文件修改
下載了LVGL的源碼之后,我們把移植要使用到的接口文件,修改文件名后,放到工程目錄的另一個文件夾。
(2)移植配置文件修改
在LVGL源碼的根目錄下有一個 lv_conf_template.h 頭文件,這個文件是 LVGL 庫的配置文件,可以修改該文件來設置庫的基本行為,禁用未使用的模塊和功能,調整編譯時緩沖區的大小等。
上面把LVGL移植接口和配置文件都復制到了工程的下面這個目錄:
4.2 Keil工程配置
(1)Keil工程添加文件
把LVGL的源碼導入到Keil工程下,其中 LVGL 源碼目錄下的 src 目錄的C文件可以全部導入Keil工程,然后再導入LVGL的移植接口文件。如下圖:
導入完成之后如下:
(2)修改工程頭文件路徑包含
(3)勾選C99模式
LVGL源碼的編譯需要C99模式的支持,不然會出現大量的報錯。
4.3 LVGL修改源碼
(1)修改 startup_apm32f411.s 文件的棧大小
官方提供的啟動文件的棧設置的比較小,我們需要改大一些。
(2)修改LVGL的配置文件 lvgl_conf.c
然后,下面是LVGL各個模塊的配置,可以根據自己的需要是否打開還是關閉。
該文件還有色彩深度的配置,顯示屏寬高的配置等,需要根據自己的硬件進行配置,不一一列舉了。
(3)修改LVGL顯示接口文件lv_port_disp.c
該文件就是LVGL的顯示接口文件,需要我們先準備好LCD顯示的描點函數。該文件主要要修改的點有:
1、打開顯示接口文件宏定義
2、修改lv_port_disp_init函數
對于lv_port_disp_init函數,官方提供了3種寫緩存的方式及設置顯示分辨。我們可以選擇其中一種方式即可,修改后的函數如:
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
/*-----------------------------
* Create a buffer for drawing
*----------------------------*/
/**
* LVGL requires a buffer where it internally draws the widgets.
* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
* The buffer has to be greater than 1 display row
*
* There are 3 buffering configurations:
* 1. Create ONE buffer:
* LVGL will draw the display's content here and writes it to your display
*
* 2. Create TWO buffer:
* LVGL will draw the display's content to a buffer and writes it your display.
* You should use DMA to write the buffer's content to the display.
* It will enable LVGL to draw the next part of the screen to the other buffer while
* the data is being sent form the first buffer. It makes rendering and flushing parallel.
*
* 3. Double buffering
* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
* This way LVGL will always provide the whole rendered screen in `flush_cb`
* and you only need to change the frame buffer's address.
*/
/* Example for 1) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * DISP_BUFFER_LINES]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * DISP_BUFFER_LINES); /*Initialize the display buffer*/
// /* Example for 2) */
// static lv_disp_draw_buf_t draw_buf_dsc_2;
// static lv_color_t buf_2_1[MY_DISP_HOR_RES * DISP_BUFFER_LINES]; /*A buffer for 10 rows*/
// static lv_color_t buf_2_2[MY_DISP_HOR_RES * DISP_BUFFER_LINES]; /*An other buffer for 10 rows*/
// lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * DISP_BUFFER_LINES); /*Initialize the display buffer*/
// /* Example for 3) also set disp_drv.full_refresh = 1 below*/
// static lv_disp_draw_buf_t draw_buf_dsc_3;
// static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/
// static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/
// lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2,
// MY_DISP_VER_RES * MY_DISP_VER_RES); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
/*Set the resolution of the display*/
disp_drv.hor_res = MY_DISP_HOR_RES;
disp_drv.ver_res = MY_DISP_VER_RES;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_1;
/*Required for Example 3)*/
//disp_drv.full_refresh = 1;
/* Fill a memory array with a color if you have GPU.
* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
* But if you have a different GPU you can use with this callback.*/
//disp_drv.gpu_fill_cb = gpu_fill;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
3、修改disp_flush函數
該函數就是調用底層LCD描點函數進行繪制UI界面的。
// LCD描點函數
void LCD_Color_Fill(u16 sx, u16 sy, u16 ex, u16 ey, u16 *color)
{
u16 i, j;
u16 height, width;
width = ex - sx + 1;
height = ey - sy + 1;
LCD_Address_Set(sx,sy+OFFSET_Y,ex,ey+OFFSET_Y);
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
LCD_WR_DATA(color[i * width + j]);
}
}
}
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
// 調用底層LCD描點函數
LCD_Color_Fill(area->x1, area->y1, area->x2, area->y2, (uint16_t *)color_p);
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
4.4 添加和修改RT-Thread環境的LVGL文件支持
我們需要把LVGL運行在RT-Thread系統,所以我們需要添加LVGL的RT-Thread接口文件。官方其實已經做好了對應的文件,我們復制到工程目錄下修改即可。
(1)添加LVGL的RT-Thread接口文件到Keil工程
然后把這兩個文件加入到Keil工程目錄下:
(2)修改lv_rt_thread_conf.h頭文件
因為官方已經做好了RT-Thread接口文件了,我們只需要簡單修改即可。修改如下:
4.5 添加LVGL Demo例程
前面的移植和修改代碼已經完成了LVGL的移植工程,下面我們添加一個簡單的 LVGLDemo 例程進行測試。
我們可以在官方的源碼目錄 .demos 目錄下選一個示例程序,或者也可以自己找一個其他的簡單的LVGL示例代碼。我下面選一個日歷demo例程進行演示。
最后編譯工程源碼和下載到APM32F411中。
編譯會有一些警告,這是LVGL源碼引入的,有些語**有警告,可以暫時忽略。
在APM32F411運行結果如下:
5. 給LVGL添加觸摸接口
前面的移植已經把顯示接口給完成了,而且上面也可以正常顯示了。但是LVGL的輸入接口還沒移植進來,不過有了前面的移植過程,添加觸摸輸入接口就很簡單了。主要就是修改 lv_port_indev.c 文件。
(1)把 lv_port_indev.c 文件的宏定義改為1
(2)修改 touchpad_is_pressed 和 touchpad_get_xy 函數
/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
/*Your code comes here*/
uint8_t num = cst816t_get_touch_points_num(); // 是否有觸摸點
if ((num != 0) && (num != 0xFF))
{
return true;
}
else
{
return false;
}
}
/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
// uint16_t tp_x = 0, tp_y = 0;
cst816t_read_pos((uint16_t *)x, (uint16_t *)y); // 底層獲取觸摸坐標函數
// (*x) = tp_x;
// (*y) = tp_y;
}
cst816t_read_pos 函數是要我們先寫好的獲取觸摸坐標的函數。另外還有一些沒用到的代碼我給注釋掉了。
增加完上述代碼就可以發現在屏幕可以點擊進行修改日期了,說明觸摸接口移植完成。
最后,APM32F411在RT-Thread系統下移植LVGL工程就全部完成了。整個過程還是比較簡單的,而且網上也有很多相關的教程。不過移植過程中還是碰到不是小問題的,有些細節并沒有在文章中一一寫出來,在移植過程中遇到一些問題就需要我們根據報錯提示對應解決就好。另外,在移植前最重要的就是要保證LCD驅動和觸摸驅動代碼正確,然后再進行移植,這樣出現問題我們比較好分析和定位問題。
-
微控制器
+關注
關注
48文章
7552瀏覽量
151417 -
嵌入式
+關注
關注
5082文章
19123瀏覽量
305150 -
移植
+關注
關注
1文章
379瀏覽量
28130 -
GUI
+關注
關注
3文章
660瀏覽量
39675 -
RT-Thread
+關注
關注
31文章
1289瀏覽量
40124
原文標題:APM32芯得 EP.47 | APM32F411在RT-Thread系統下移植LVGL-8.3
文章出處:【微信號:geehysemi,微信公眾號:Geehy極海半導體】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論