一、
上周將lvgl粗略的移植到stm32f429上,界面刷新問題沒有好好處理,看著非常非??D,今天初步處理了這個問題,效果還算可以了,后邊應該是可以更進一步優化。我們先在裸機上移植,以后移植到OS上。移植的視頻效果在結尾處。
二、移植步驟
1、提前準備一個移植好LCD、觸摸驅動的工程,并修改工程名。
2、獲取lvgl圖形庫源碼。
3、將源碼添加到提前準備好的工程。
4、修改配置lv_conf.h文件。
5、調用lvgl的初始化程序 lv_init()。
6、在lvgl中注冊顯示和輸入設備驅動程序。
7、在中斷服務函數中調用lv_tick_inc(x)以告知lvgl經過的時間。
8、每隔幾毫秒調用lv_timer_handle()來處理lvgl相關的任務。
三、提前準備一個移植好LCD、觸摸驅動的工程,并修改工程名
我的板子是用的野火的,所以我直接找一個野火的例程進行修改,就用這個觸摸屏的例程。
根據個人喜好更改文件夾名以及工程名,工程名需要是英文,這些我在這里不多說了,自己修改就好,當然也可以不修改。
修改工程的時候建議將Browse information勾上,勾上以后函數跳轉很方便,不過勾上以后第一次編譯程序會很慢,這個也看個人需求。
四、獲取lvgl源碼
新建文件夾使用git獲取源碼。
獲取成功后可以看到一個lvgl的文件夾,這就是源碼了。
不熟悉git的結尾處我也會提供一份源碼。
五、將源碼添加到提前準備好的工程,修改配置lv_conf.h文件
到目前我們準備好lvgl的源碼以及準備移植的工程。
1、將lvgl文件夾直接放到lvgl_porting中,然后在新建一個lvgl_app文件夾,后邊自己的lvgl應用程序可以放這里。
2、進入lvgl這個文件里可以看到以下文件
3、進入src文件夾后,按照下圖文件夾名字在keil工程中添加目錄。
4、然后除了以下目錄下文件不添加,其他c文件全部添加,注意有的目錄下還有文件夾,文件夾里的c文件也要添加,不要漏了。
gpu暫時也不需要添加。
5、將lv_conf_template.h復制到上一層目錄,并修改lv_conf_template.h為lv_conf.h,這個是一定要修改的,官方介紹的也是這樣,工程里也是使用的lv_conf.h的頭文件。
6、打開改名后的lv_conf.h,使能文件。同時可以看到這里配置的色彩深度是16位的,對應RGB565格式,一會兒我們LCD屏也是配置RGB565使用。其他配置暫時先不用改。
7、將lvgl->examples->porting中選中的文件復制到與lv_conf.h文件同一目錄下,并把template字眼去掉。分別為顯示設備的驅動接口和輸入設備的接口,我們一會兒完善。
8、在keil工程新建兩個目錄lvgl_porting,lvgl_demo。lvgl_porting放如下三個文件。lvgl_demo一會兒放例程。
9、添加所有目錄下的頭文件
10、然后開始編譯一下工程,如果你C文件添加正確,以及頭文件目錄配置正確,這個時候已經可以編譯通過了。如果這里編譯報錯可以參考我結尾處附上的工程文件進行參考。
六、按照LVGL的運行需求修改工程
Basically, every modern controller which is able to drive a display is suitable to run LVGL. The minimal requirements are:
- 16, 32 or 64 bit microcontroller or processor
16 MHz clock speed is recommended
- Flash/ROM: > 64 kB for the very essential components (> 180 kB is recommended)
- RAM:
- Static RAM usage: ~2 kB depending on the used features and object types
- Stack: > 2kB (> 8 kB is recommended)
- Dynamic data (heap): > 4 KB (> 32 kB is recommended if using several objects). Set by LV_MEM_SIZE in lv_conf.h .
- Display buffer: > "Horizontal resolution" pixels (> 10 × "Horizontal resolution" is recommended)
- One frame buffer in the MCU or in an external display controller
- C99 or newer compiler
- Basic C (or C++) knowledge: pointers, structs, callbacks.
1、設置堆棧大小
2、配置C99模式
七、在lvgl中注冊顯示驅動程序
到目前為止我們基本沒修改任何代碼,只是使能了一些文件的宏,同時最繁瑣的事情我們已經做完了。接下來完善注冊顯示接口驅動以及設備輸入接口驅動。
顯示驅動程序
- 定義一個或者兩個繪制緩沖區,用來渲染屏幕內容
- 注冊顯示驅動
- 補充刷新界面的回調函數
定義緩沖區流程
通過lv_disp_draw_buf_t
變量初始化,如下所示:
/* Example for 1) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*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 * 10]; /*A buffer for 10 rows*/
static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*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 * 10); /*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 * LV_VER_RES_MAX); /*Initialize the display buffer*/
注意,lv_disp_draw_buf_t
需要是靜態的、全局的或動態分配的,而不是超出范圍時銷毀的局部變量。
如上所示,有三個示例。繪制緩沖區可以比屏幕小,如果刷新整個界面,需要刷新幾次就好了,如果只有小區域發生變化(如只按下按鈕),則只刷新該區域。官方建議緩沖區的大小至少為屏幕寬度的1/10。
如果使用 一個緩沖區 ,LVGL 將屏幕內容繪制到該繪制緩沖區中并將其發送到顯示器。 這樣 LVGL 需要等到緩沖區的內容發送到顯示器,然后再在其中繪制新內容。
如果使用 兩個緩沖區 ,LVGL 可以繪制到一個緩沖區中,而另一個緩沖區的內容被發送到后臺顯示。應使用 DMA 或其他硬件將數據傳輸到顯示器,讓 MCU 同時繪制。這樣,顯示的渲染和刷新變得并行。
注冊顯示驅動流程
其實打開lv_port_disp.c文件中描述的比較清楚如下代碼。
/*-----------------------------------
* 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 = 480;
disp_drv.ver_res = 320;
/*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);
注意,lv_disp_drv_t
也需要是靜態的、全局的或動態分配的,而不是超出范圍時銷毀的局部變量。
- 配置屏幕的分辨率
- 指定回調函數
- 指向緩沖區
- 注冊顯示驅動
實際操作
1、將lv_port_disp.c和lv_port_disp.h文件宏都打開,并修改.c文件中相應頭文件名字,將template去掉。
2、找到lv_port_disp_init()初始化函數,我們使用第一種方法,并修改屏幕寬度,我的屏幕是800*480的,所以我改為800。
如果你有外擴sram或者sdram這里可以配置與屏幕一樣大。
我這里有sdram我就放到sdram中了。
3、修改分辨率
回調函數disp_flush完善
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
- *area中有繪制區域的起始xy地址和結束xy地址
- *color_p,存放著從起始xy地址到結束xy地址的每一個像素顏色
知道了這兩個我們使用到參數后,我們需要編寫一個刷新函數LCD_FillRect_color()的。因為我們使用的這個工程已經配置好了DMA2D,我們直接用DMA2D傳輸,函數實現如下。
void LCD_FillRect_Color(int16_t x1, int16_t y1, int16_t x2, int16_t y2, lv_color_t *color)
{
uint32_t dst_addr = 0;//需要更新顯存中內容的起始地址
dst_addr = Ltdc_Handler.LayerCfg[ActiveLayer].FBStartAdress + 2*(800*y1 + x1);//計算需要更新的目標地址 800是屏幕寬度,2是每個像素占2個字節
/* DMA2D Config */
DMA2D- >CR = 0x00000000UL; //配置DMA2D的傳輸模式為 內存到內存
DMA2D- >FGMAR = (uint32_t)color; //拷貝的源地址,繪制緩存區
DMA2D- >OMAR = (uint32_t)dst_addr; //拷貝的目標地址,即顯存位置
DMA2D- >FGOR = 0; //源地址的偏移地址,它被添加到每一行的末尾以確定下一行的起始地址
DMA2D- >OOR = (800 - (x2 - x1 +1)); //目標地址的偏移地址,它被添加到每一行的末尾以確定下一行的起始地址
DMA2D- >FGPFCCR = LTDC_PIXEL_FORMAT_RGB565; //設置顏色格式;
DMA2D- >NLR = (uint32_t)((x2 - x1 + 1) < < 16) | (uint16_t)(y2 - y1 + 1);// 設置拷貝數據的長度和寬度
/* Start */
DMA2D- >CR |= DMA2D_CR_START;
while(DMA2D- >CR & DMA2D_CR_START) {}
}
將我們準備好的繪制函數LCD_FillRect_Color添加進來。
/*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)
{
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
LCD_FillRect_Color(area- >x1, area- >y1, area- >x2, area- >y2, (lv_color_t *)color_p);
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
回調函數完事兒。
顯示屏移植已經完成了,可以添加一個demo先點亮屏幕。
添加demo并修改main()函數
在main.c文件中添加頭文件
#include "./lvgl.h"
#include "../lv_port_disp.h"
#include "lv_demo_widgets.h"
修改顯示屏配置
調用如下初始化函數
添加demo文件,路徑:lvgldemoswidgets,并添加頭文件路徑。
進入lv_demo_widgets.c文件中,使能宏
將此處均設置為1
這個時候編譯不報錯是正確的。
八、在中斷服務函數中調用lv_tick_inc(x)以告知lvgl經過的時間
需要添加頭文件
#include "../lvgl/src/hal/lv_hal_tick.h"
九、每隔幾毫秒調用lv_timer_handle()來處理lvgl相關的任務
操作到現在,可以編譯下載測試了。
十、在lvgl中注冊輸入設備驅動程序
輸入設備可以是觸摸板、鼠標、鍵盤、編碼器、按鍵,我們使用觸摸板。
打開lv_port_indev.c文件,只留下觸摸板相關程序,其他程序全部刪除。
我們只需要把touchpad_read回調函數補充完整即可。
主要是提供當前的按壓狀態,釋放還是壓下,以及按下時的坐標即可,這里自己添加就好。
然后將lv_port_indev_init()添加到main()函數中即可。
到此已經移植完成了,剩下就是優化刷新程序以及lvgl控件的應用了。
-
RGB
+關注
關注
4文章
798瀏覽量
58386 -
LCD觸摸屏
+關注
關注
0文章
11瀏覽量
6684 -
STM32F429
+關注
關注
0文章
40瀏覽量
10647 -
裸機
+關注
關注
0文章
39瀏覽量
6347 -
LVGL
+關注
關注
1文章
79瀏覽量
2911
發布評論請先 登錄
相關推薦
評論