前言
恩智浦“FRDM-MCXN947”評測活動由安富利和與非網協同舉辦。本篇內容由與非網用戶發布,已獲轉載許可。原文可在與非網(eefocus)工程師社區查看。
背景
上一期【用戶測評(六):NXP FRDM-MCXN947 FLEXIO_SPI驅動TFT LCD】以FLEXIO_SPI驅動了TFT LCD,此貼移植LVGL,測試刷屏速度。
LVGL移植
01引入SDK組件
在MCUXpresso For VS Code開發環境中,引入NXP SDK組件的方法很簡單,如下圖所示:
1. 在VS Code側邊欄打開MCUXpresso插件圖標,點擊打開;
2. 鼠標右鍵單擊項目,展開選項;
3. 選擇Configure展開子菜單欄;
4. 選擇Manage Components彈開組件選擇窗口;
在彈出的組件選擇框中查找或者輸入lvgl并勾選,導入LVGL組件,如下圖所示。
需要注意此方法導入的SDK組件并不會把源碼拷貝到當前工程目錄中,只是修改了文件armgcc/config.cmake文件,如下:
set(CONFIG_USE_xxxx true)# 其他組件 # 引入 LVGL 時添加的兩行 set(CONFIG_USE_middleware_lvgltrue) set(CONFIG_USE_middleware_lvgl_templatetrue)
02添加LVGL顯示適配層
參考LVGL官方的移植文檔,LVGL移植主要干以下兩件事:
1.LVGL初始化,LCD硬件初始化;
2.LVGL刷新緩沖區接口的實現;
1. 新增文件
新增3個文件,如下所示:
bsp/lvgl_port/ lv_conf.h lvgl_support.c lvgl_support.h
lv_conf.h是LVGL配置頭文件,配置選項非常多,這里僅介紹幾個重要的配置選項:LV_COLOR_DEPTH設置顏色深度;LV_MEM_SIZE設置LVGL內部使用的內存分配池的大小;LV_USE_XXX使能widgets組件;LV_BUILD_EXAMPLES允許編譯內置的示例到LVGL庫文件中;
lv_support.c是適配接口實現文件;
lv_support.h是適配接口對外的接口文件,包括宏定義、函數聲明等;
2. lv_port_disp_init()
此函數做了以下幾件事:
1. 初始化LVGL顯示緩沖區內存,調用lv_disp_draw_buf_init();
2. 初始化LCD硬件,但是已經在外部調用LCD_init(),此處無需調用;
3. 注冊disp_drv.flush_cb = DEMO_FlushDisplay,需要實現自己的DEMO_FlushDisplay()函數;
4. 最后注冊顯示驅動lv_disp_drv_register(&disp_drv);
voidlv_port_disp_init(void) { staticlv_disp_draw_buf_tdisp_buf; memset(s_frameBuffer,0,sizeof(s_frameBuffer)); lv_disp_draw_buf_init(&disp_buf, (void*)s_frameBuffer[0], (void*)s_frameBuffer[1], LCD_VIRTUAL_BUF_SIZE); /*------------------------- * Initialize your display * -----------------------*/ //NOTE:已在其他位置調用 LCD_Init() ,此處無需調用 /*----------------------------------- * Register the display in LittlevGL *----------------------------------*/ staticlv_disp_drv_tdisp_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 =LCD_WIDTH; disp_drv.ver_res =LCD_HEIGHT; /*Used to copy the buffer's content to the display*/ disp_drv.flush_cb =DEMO_FlushDisplay; /*Set a display buffer*/ disp_drv.draw_buf =&disp_buf; /*Finally register the driver*/ lv_disp_drv_register(&disp_drv); }
3. lvgl刷新緩沖區接口的實現
在bsp/lvgl_port/lvgl_support.c中實現如下函數,最終調用LCD中的函數LCD_DrawBitmap()實現把LVGL緩沖區內容刷新到屏幕上。
/* 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_flush_ready()' has to be called when finished * This function is required only when LV_VDB_SIZE != 0 in lv_conf.h*/ staticvoidDEMO_FlushDisplay(lv_disp_drv_t*disp_drv,constlv_area_t*area,lv_color_t*color_p) { lv_coord_tx1 = area->x1; lv_coord_ty1 = area->y1; lv_coord_tx2 = area->x2; lv_coord_ty2 = area->y2; int32_tlength = (x2 - x1 +1) * (y2 - y1 +1) *LCD_FB_BYTE_PER_PIXEL; LCD_DrawBitmap(x1, y1, x2, y2, (uint16_t*)color_p); /* IMPORTANT!!! * Inform the graphics library that you are ready with the flushing*/ lv_disp_flush_ready(disp_drv); }
LCD_DrawBitmap()函數實現非常重要,如果速度慢導致卡頓,如果速度快則可以流暢地刷屏。在調試過程中實現了兩個版本:
1. 逐個打點,調用LCD_WriteData_16Bit(),刷屏卡成PPT;
2. 利用EDMA直接發送整個緩沖區,速度很快,達到31FPS;
voidLCD_DrawBitmap(uint16_txstart,uint16_tystart,uint16_txend,uint16_tyend,uint16_t*color) { LCD_SetWindows(xstart, ystart, xend, yend); uint16_twidth = xend - xstart +1; uint16_theight = yend - ystart +1; uint16_tsize = width * height; #if0 //NOTE:逐個打點,非常慢--卡成 PPT for(uint16_ti = ystart; i <= yend; i++) { ? ??for?(uint16_t?j = xstart; j <= xend; j++) { ? ? ??Lcd_WriteData_16Bit(*color); ? ? ? color++; ? ? } ? } #else ??//?NOTE:?EDMA 發送一個緩沖幀,速度很快 -- 31FPS ??Lcd_WriteData_16BitArray(color, size); #endif }
EDMA發送整個緩沖幀的實現
逐個打點的速度太慢,這里就不展示了。在移植LCD驅動時就發現刷屏非常慢,迫切地需要實現一個利用EDMA發送緩沖區地函數,在LVGL刷屏時正好可以用到。
函數LCD_WriteData_16BitArray()如下,利用EDMA的特性,一下子傳輸整個緩沖區比逐個打點快很多。
這里遇到了一個坑,LVGL配置顏色深度為16bit,一個像素點占據兩個字節,寫代碼時腦子短路了,知道這個細節,但是在配置EDMA傳輸參數時沒有反映過來,一開始屏幕沒有顯示,后來debug才發現EDMA傳輸啟動就卡主了,原來下面的xfer.dataSize配置需要注意size * 2。
/** * @brief SPI 發送一個數組 * * @param Data * @param size */ voidLcd_WriteData_16BitArray(uint16_t*Data,uint32_tsize) { flexio_spi_transfer_txfer = {0}; LCD_CS_CLR; LCD_RS_SET; xfer.txData = (uint8_t*)Data; xfer.rxData =NULL; xfer.dataSize = size *2;//NOTE:16bit顏色,一個像素點占據2個字節 (這里差點坑哭了!!!) xfer.flags = kFLEXIO_SPI_16bitMsb; FLEXIO_SPI_MasterTransferCreateHandleEDMA(&spiDev, &g_spiHandle, spi_master_completionCallback,NULL, &txHandle, &rxHandle); FLEXIO_SPI_MasterTransferEDMA(&spiDev, &g_spiHandle, &xfer); while(!completeFlag); completeFlag =false; LCD_CS_SET; }
03創建LVGL任務
改造上一版的程序,把LCD和LVGL初始化拎出來放到一個單獨的GUI Task中,如下所示:
/** *@briefLVGL Core 線程 * *@parampvParameters */ staticvoidgui_task_entry(void*pvParameters) { lv_port_pre_init(); lv_init(); lv_port_disp_init(); lv_port_indev_init(); s_lvgl_initialized =true; lv_demo_benchmark(); while(1) { lv_task_handler(); vTaskDelay(pdMS_TO_TICKS(5)); } } voidgui_task_create(void) { // LCD init LCD_Init(); // LVGL init if(xTaskCreate(gui_task_entry,"gui_task", ZYGOTE_TASK_STACK_SIZE,NULL,ZYGOTE_TASK_PRIORITY,NULL) != pdPASS) { PRINTF("Task creation failed!. "); while(1); } }
04添加LVGL心跳
上面的gui_task_entry()在死循環中每隔5毫秒進入一次lvgl任務,但是還沒有更新LVGL心跳,是不會刷新屏幕的。
當前使用了FreeRTOS,一個簡單的做法是把LVGL心跳放到vApplicationTickHook()中,非常簡單快捷。但是需要注意,需要在FreeRTOSConfig.h中使能宏定義configUSE_TICK_HOOK = 1才能使這個vApplicationTickHook()函數生效。
/*! * @brief FreeRTOS tick hook. */ voidvApplicationTickHook(void) { if(s_lvgl_initialized) { lv_tick_inc(1); } }
05編譯
編譯出錯undefined reference to `lv_demo_benchmark'
第一反映是查看lv_conf.h文件
1.LV_BUILD_EXAMPLES宏定義是否啟用了;
2.LV_USE_DEMO_BENCHMARK宏定義是否啟用了;
檢查過了,確認過來,啟用了,但是還是鏈接失敗,最后不到lv_demo_benchmark符號。
找到SDK的cmake模塊文件
找到SDK的cmake文件,如下lvgl拆分成了多個模塊文件:
middleware_lvgl.cmake是lvgl核心組件的cmake模塊文件;
middleware_lvgl_unused_files.cmake其實是一個沒有什么實際意義的cmake模塊文件;
middleware_lvgl_demo_widgets.cmake是把lvgl demo widgets示例的源碼加入編譯;
middleware_lvgl_demo_stress.cmake是把lvgl demo stress示例的源碼加入編譯;
middleware_lvgl_demo_benchmark.cmake是把 lvgl demo benchmark示例的源碼加入編譯;
middleware_lvgl_template.cmake是把lvgl_sdk目錄下的lvgl_support.c等三個文件添加到編譯中;
這里就發現了MCUXPresso for VS Code的兩個漏洞:
1. SDK組件管理器中添加了LVGL組件,但是沒有自動把其中的3個demo相關的模塊加入到源碼中;
2. 雖然把middleware_lvgl_template.cmake所在的組件加了進來,但是并沒有把源碼拷貝過來,難道需要用戶手動去改SDK目錄下的lvgl_sdk目錄?可以是這樣一改會對所有的依賴此SDK的工程都產生影響?
3. middleware_lvgl.cmake模塊,居然依賴于middleware_lvgl_template.cmake模塊,這個是我不能理解的。就是第2點,LVGL適配層可以字節寫,但是依賴middleware_lvgl_template組件,那么所有用戶工程都依賴同一份SDK中的lvgl_sdk/template模塊,不合理。
middleware_lvgl.cmake文件大致如下:
# 從這里看出 lvgl core 居然依賴 middleware_lvgl if(CONFIG_USE_middleware_lvgl_template) # 添加 lvgl core 代碼到編譯系統,添加頭文件路徑為公共頭文件搜索路徑 else() # 報錯 message(SEND_ERROR"middleware_lvgl dependency does not meet, please check ${CMAKE_CURRENT_LIST_FILE}.") endif()
我的解決辦法
1. 修改armgcc/config.cmake文件,手動增加middleware_lvgl_demo_benchmark組件;
2. 把middleware_lvgl_template禁用;
3. (臨時解決方案)同時修改sdk/middleware/lvg/middleware_lvgl.cmake文件,去掉CONFIG_USE_middleware_lvgl_template,強行設置為TRUE;
config.cmake文件中最終關于LVGL的配置如下:
middleware_lvgl.cmake文件修改如下:
編譯成功
最后編譯成功,運行成功,lvgl_demo_benchmark運行成功。
運行
運行速度對比:
debug,刷屏打點,卡成PPT,沒有拍視頻;
debug,EDMA發送緩沖區,19FPS;
release,EDMA發送緩沖區,31FPS;
演示視頻見B站:
https://www.bilibili.com/video/BV13pUbYhEPx/?spm_id_from=888.80997.embed_other.whitelist&t=1.46976&bvid=BV13pUbYhEPx
總結
01MCUXpresso IDE的不足與建議
其實我最早使用的就是MCUXpresso IDE,發現添加組件不是那么順利,例如在一個no-os的工程中添加FreeRTOS組件,發現FreeRTOS源碼的確拷貝到了當前工程中,但是FreeROTS的porting層的源碼卻沒有拷貝過來,當時沒有搞清楚機制,編譯失敗就放棄了。
后來幾經嘗試之后才發現SDK組件管理器中關于FreeROTS有很多零碎的組件,如下圖:
1. 標號(1)是FreeRTOS內核源碼,但是缺少porting層源碼;
2. 標號(2)是FreeRTOS的適配層,內存管理驅動;
3. 標號(3)是多核通信的FreeRTOS實現方法;
總之關于FreeRTOS有很多零碎的組件分散在各個單元,每個組件的Description太簡短了,不能讓人一眼就明白用途,且各個組件的依賴關系也沒有說明,不易輕松上手。
建議:Descriptiong文字描述的詳細些;各個組件的依賴關系也明確的寫出來。
02MCUXpresso For VS Code的不足與建議
不足之處:
1. 在VS Code開發環境中添加組件是很方便,但是對于新手不友好,特別是不熟悉CMake的人來說,編譯找不到頭文件、函數未定義,讓新手望而卻步。
2. 某些組件有core和porting層,其中core層可以所有項目通用,但是porting對于每個項目來說可能不一樣;當前工程添加了這個組件只是在CMake標記了組件的core和porting層都納入編譯,但是它們依然存放在SDK目錄下,改動一處影響所有其他工程,這很不好。
建議:添加組件把組件的porting層拷貝到當前工程,這樣每個工程都有自己的適配層,互不影響。
-
恩智浦
+關注
關注
14文章
5956瀏覽量
114063 -
移植
+關注
關注
1文章
395瀏覽量
28606 -
SPI
+關注
關注
17文章
1788瀏覽量
94997 -
開發環境
+關注
關注
1文章
240瀏覽量
17061 -
LVGL
+關注
關注
1文章
102瀏覽量
3580
原文標題:用戶測評(七):移植LVGL跑benchmark
文章出處:【微信號:AvnetAsia,微信公眾號:安富利】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
恩智浦MCX N23的官方評估板FRDM-MCXN236詳解

在恩智浦FRDM-MCXN947開發板部署DeepSeek大語言模型

FRDM-MCXN947的純Linux命令行環境搭建
關于將Flash寫入FRDM-MCXN947的問題求解
FRDM-MCXN947在初始化lpI2C時, I2C無法正常工作怎么解決?
富昌電子推薦兩款恩智浦的MCX A和MCX N系列微控制器
【上海】5月25日-基于恩智浦MCX N系列MCU結合RT-Thread的應用與實踐 線下培訓

恩智浦新品MCX N系列線下培訓來啦!LVGL、AI等超多精彩Demo演示,快來報名吧!

基于Label CIFAR10 image on FRDM-MCXN947例程實現鞋和帽子的識別

評論