GUI Guider是恩智浦推出的一個PC端開發(fā)工具,專門用于開發(fā)LVGL GUI界面的圖形化開發(fā)工具。LVGL是一個非常不錯的開源gui,一直想試一試的。這次主要分享自己移植lvgl,以及利用GUI Guider開發(fā)基于lvgl的界面設(shè)計。 GUI Guider早就安裝了1.0版本,也試著創(chuàng)建項目看了看,發(fā)現(xiàn)目前支持的開發(fā)板有點少,而且自己還沒有那些板子。就一直想著移植到其他板子上玩玩。這次研究了一下,也參考網(wǎng)上的一些資料,成功移到自己的板子上了?,F(xiàn)在來分享一下本次研究過程。
初始計劃是先移植lvgl在自己的板子上無OS運行,然后再移植GUI Guider設(shè)計的界面代碼,再實現(xiàn)輸入設(shè)備操作。
本次板子使用的LPC55S69開發(fā)板,外帶ili9488控制LCD,lcd屏320*480分辨率。lcd驅(qū)動已經(jīng)完成,準(zhǔn)備移植lvgl。首先將lvgl代碼復(fù)制到工程目錄下。
然后在lvgl目錄下新建lvgl_app和lvgl_port這2個文件夾,lvgl_app主要放設(shè)計的應(yīng)用文件,lvgl_port主要放需要移植的文件。
如下圖,將lvgl的example/port目錄下移植文件復(fù)制到lvgl_port目錄下。
參考網(wǎng)上移植方法修改lcd對接lvgl的驅(qū)動顯示接口文件lv_port_disp_xxxl.c。
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
/*-----------------------------
* Create a buffer for drawing
*----------------------------*/
/* LVGL requires a buffer where it draws the objects. The buffer's has to be greater than 1 display row
*
* There are three buffering configurations:
* 1. Create ONE buffer with some rows:
* LVGL will draw the display's content here and writes it to your display
*
* 2. Create TWO buffer with some rows:
* 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. Create TWO screen-sized buffer:
* Similar to 2) but the buffer have to be screen sized. When LVGL is ready it will give the
* whole frame to display. This way you only need to change the frame buffer's address instead of
* copying the pixels.
* */
/* Example for 1) */
static lv_disp_buf_t disp_buf_1;
static lv_color_t buf1_1[LV_HOR_RES_MAX * 10]; /*A buffer for 10 rows*/
lv_disp_buf_init(&disp_buf_1, buf1_1, NULL, LV_HOR_RES_MAX * 10); /*Initialize the display buffer*/
// /* Example for 2) */
// static lv_disp_buf_t disp_buf_2;
// static lv_color_t buf2_1[LV_HOR_RES_MAX * 10]; /*A buffer for 10 rows*/
// static lv_color_t buf2_2[LV_HOR_RES_MAX * 10]; /*An other buffer for 10 rows*/
// lv_disp_buf_init(&disp_buf_2, buf2_1, buf2_2, LV_HOR_RES_MAX * 10); /*Initialize the display buffer*/
// /* Example for 3) */
// static lv_disp_buf_t disp_buf_3;
// static lv_color_t buf3_1[LV_HOR_RES_MAX * LV_VER_RES_MAX]; /*A screen sized buffer*/
// static lv_color_t buf3_2[LV_HOR_RES_MAX * LV_VER_RES_MAX]; /*An other screen sized buffer*/
// lv_disp_buf_init(&disp_buf_3, buf3_1, buf3_2, LV_HOR_RES_MAX * LV_VER_RES_MAX); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
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 = LV_HOR_RES_MAX;
disp_drv.ver_res = LV_VER_RES_MAX;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
/*Set a display buffer*/
disp_drv.buffer = &disp_buf_1;
#if LV_USE_GPU
/*Optionally add functions to access the GPU. (Only in buffered mode, LV_VDB_SIZE != 0)*/
/*Blend two color array using opacity*/
disp_drv.gpu_blend_cb = gpu_blend;
/*Fill a memory array with a color*/
disp_drv.gpu_fill_cb = gpu_fill;
#endif
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
復(fù)制代碼
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*/
// int32_t x;
// int32_t y;
// for(y = area->y1; y <= area->y2; y++) {
// for(x = area->x1; x <= area->x2; x++) {
// /* Put a pixel to the display. For example: */
// /* put_px(x, y, *color_p)*/
// color_p++;
// }
// }
lcd_fill_buff(area->x1,area->y1,area->x2,area->y2,(uint8_t *)color_p);
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
復(fù)制代碼
主要是這2個函數(shù)內(nèi)容,暫時不用GPU加速之內(nèi)。
再就是配置lv_conf.h文件,關(guān)于lvgl的一些配置信息。
主函數(shù)修改移植如下:主要是定時器SysTick
void SysTick_Handler(void)
{
lv_tick_inc(1);
}
void lv_example_btn_1(void)
{
lv_obj_t * label;
lv_obj_t * btn1 = lv_btn_create(lv_scr_act(),NULL);
lv_obj_align(btn1, NULL, LV_ALIGN_CENTER, 0, -40);
label = lv_label_create(btn1,NULL);
lv_label_set_text(label, "Button");
lv_obj_t * btn2 = lv_btn_create(lv_scr_act(), NULL);
lv_obj_align(btn2,NULL, LV_ALIGN_CENTER, 0, 40);
label = lv_label_create(btn2,NULL);
lv_label_set_text(label, "Toggle");
}
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief Main function
*/
int main(void)
{
char ch;
int i;
/* Init board hardware. */
/* set BOD VBAT level to 1.65V */
POWER_SetBodVbatLevel(kPOWER_BodVbatLevel1650mv, kPOWER_BodHystLevel50mv, false);
/* attach main clock divide to FLEXCOMM0 (debug console) */
CLOCK_AttachClk(BOARD_DEBUG_UART_CLK_ATTACH);
BOARD_InitPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
init_cycle_counter(false);
PRINTF("hello world.rn");
lcd_init();
lcd_clear(0xf800);
lv_init();
extern void lv_port_disp_init(void);
lv_port_disp_init();
lv_example_btn_1();
while (1)
{
lv_task_handler();
}
}
復(fù)制代碼
現(xiàn)在基本完成了移植,然后準(zhǔn)備編譯下載看看效果。這里要特別注意:需要配置堆棧大小。我就是在這里遇到坑了。按上面移植之后一直顯示不正常,而且程序跑飛了。
后面參考網(wǎng)上移植的說要配置堆棧至少0x800大小,然后我就去找堆棧配置看看。在這里一般堆棧是在匯編*.s文件中,結(jié)果發(fā)現(xiàn)匯編文件中沒有堆棧配置,最后找到KEIL的配置下修改了堆棧大小。
最后重新編譯下載,終于成功顯示。
效果如下
移植過程寫的比較簡單,具體參考如下代碼:lpc55s69_lcd.rar (7.21 MB, 點擊下方附件下載)