故事是這樣的,最近支持某客戶使用littleVGL開(kāi)發(fā)一款帶顯示效果的產(chǎn)品,由于之前沒(méi)有相關(guān)經(jīng)驗(yàn),擔(dān)心會(huì)有問(wèn)題。沒(méi)想到,使用GUI-Guider后很快就完成了相關(guān)應(yīng)用的設(shè)計(jì)開(kāi)發(fā)。于是乎突發(fā)奇想,是否可以讓GUI-Guider變成串口屏的組態(tài)工具呢?
1. 什么是串口屏
我們先來(lái)認(rèn)識(shí)下串口屏,字面意思就是帶串口的屏,最核心有兩個(gè)功能:
可以通過(guò)PC端設(shè)計(jì)屏幕顯示界面
可以通過(guò)串口修改屏幕顯示內(nèi)容
直白的講,就是屏幕用于顯示,顯示的數(shù)據(jù)源來(lái)自串口的對(duì)端設(shè)備
1.1 組態(tài)串口屏
很早以前,工業(yè)現(xiàn)場(chǎng)有時(shí)需要HMI,為工作人員提供便捷的操作環(huán)境,但不同的應(yīng)用現(xiàn)場(chǎng)有不同的操作界面,有組態(tài)軟件經(jīng)驗(yàn)的廠商就想到了用嵌入式板卡跑WinCE的方案,這樣很容易將Windows中的代碼移植過(guò)來(lái),用戶只需要在Windows端的組態(tài)工具根據(jù)現(xiàn)場(chǎng)應(yīng)用進(jìn)行組態(tài),然后將生產(chǎn)的工程文件,對(duì)象文件,數(shù)據(jù)庫(kù)等文件下載到WinCE對(duì)應(yīng)的板卡中即可實(shí)現(xiàn)所見(jiàn)即所得的顯示開(kāi)發(fā)過(guò)程。
后來(lái)也有廠商使用Cortex-A8+Linux+QT的方式實(shí)現(xiàn)該方案。這種串口屏還是比較貴的,畢竟使用的處理器平臺(tái),成本比較高。
由于該方案使用的組態(tài)軟件,屏是串口協(xié)議的主設(shè)備,并且支持豐富的串口協(xié)議,比如各類的PLC,常見(jiàn)的西門子S7-200 PPI,三菱FX, 通用設(shè)備Modbus-RTU/TCP,各類儀表,變頻器等。
屏的數(shù)據(jù)可以通過(guò)軟件配置的方式與串口外設(shè)關(guān)聯(lián),先在設(shè)備窗口設(shè)定變量,之后在界面設(shè)計(jì)的時(shí)候關(guān)聯(lián)變量即可
?
1.2 通用串口屏
之后又接觸到了低成本的通用串口屏,這類串口屏與組態(tài)屏相同的一點(diǎn)是,都可以通過(guò)PC端軟件進(jìn)行界面設(shè)計(jì)。
?
?
?
區(qū)別在于價(jià)格更美麗,但該屏的串口是協(xié)議的從設(shè)備,并且一般僅支持一種協(xié)議(就像下面這種0x80~0x85這種),用戶使用時(shí)需要通過(guò)外部MCU作為串口的主,并實(shí)現(xiàn)相關(guān)協(xié)議去修改顯示畫(huà)面的數(shù)據(jù)內(nèi)容,具體框架可以參考下圖:
2. 串口屏設(shè)計(jì)分析
2.1 組態(tài)屏設(shè)計(jì)
前面大概講過(guò),組態(tài)屏一般是把組態(tài)軟件交叉編譯到嵌入式板卡所支持的WinCE或Linux中運(yùn)行,串口屏拆開(kāi)后可以理解為一個(gè)嵌入式處理器的小電腦,處理器性能和樹(shù)莓派應(yīng)該差不多。如果有興趣推薦大家可以研究一個(gè)開(kāi)源方案pvbrowser,它可以在樹(shù)莓派上運(yùn)行,這個(gè)軟件底層也是基于QT開(kāi)發(fā)的,很早之前玩過(guò),但是不花錢的東西看上去并不美好。
2.2 通用屏設(shè)計(jì)
要分析通用的設(shè)計(jì),我們可以拆開(kāi)看看,下面是兩個(gè)不同公司的設(shè)計(jì):
先看行業(yè)大佬的板子吧,上面基本看不到啥,都繼承到一起了,外面還有顆SPI的flash用于存非易失的素材或參數(shù)
這家公司自己開(kāi)了個(gè)芯片,好像也支持有能力的客戶做二次開(kāi)發(fā),網(wǎng)上能下到參考原理圖和軟件SDK。
換一個(gè)廠商,這家用的分離方案,MCU+FPGA+DRAM+NAND:
大膽推測(cè)一些系統(tǒng)框架,下圖是幾種方案組合,最大的區(qū)別點(diǎn)在于,MCU, FPGA, Flash, DRAM之間的連接方式,主要是灰色和藍(lán)色這兩條路徑:
MCU作為推屏的核心器件,往往采用灰色路徑,DRAM和Flash都掛在MCU上,F(xiàn)PGA僅實(shí)現(xiàn)顯示驅(qū)動(dòng)的作用,也可以用ILI9341/9488這種顯示驅(qū)動(dòng)芯片替代,該方案的瓶頸在MCU和驅(qū)動(dòng)芯片之間的接口帶寬以及MCU本身的處理性能,針對(duì)屏的尺寸比較大(分辨率比較高)或者需要?jiǎng)討B(tài)顯示效果的應(yīng)用是一個(gè)考驗(yàn)。
FPGA作為推屏的核心器件,采用藍(lán)色路徑,DRAM和Flash都掛在FPGA上,MCU主要起解析串口命令,并修改FPGA中雙口RAM的功能(RAM區(qū)與屏幕數(shù)據(jù)源綁定),MCU可能還會(huì)使用FatFS來(lái)獲取SDcard中PC端生成的文件,并將其解析后存儲(chǔ)在Flash上。 從實(shí)際效果看,這個(gè)產(chǎn)品可能使用方案b,因?yàn)槠胀ǖ腗CU主頻較低,受帶寬影響,大屏情況下很難實(shí)現(xiàn)較為流暢的動(dòng)畫(huà)效果。
3. GUI-Guider到串口屏
回到之前的想象,GUI-Guider是否可以成為用戶組態(tài)工具,當(dāng)前版本肯定是不行的,因?yàn)樾枰ㄟ^(guò)串口修改的數(shù)據(jù)在界面設(shè)計(jì)時(shí)并沒(méi)有做地址關(guān)聯(lián),如果想做成組態(tài)串口屏,還需要設(shè)置從站參數(shù)信息。當(dāng)然我們今天先從簡(jiǎn)單的通用串口屏入手,假設(shè)GUI-Guider后續(xù)會(huì)像VGUS那樣提供數(shù)據(jù)地址關(guān)聯(lián)的接口。
以默認(rèn)的SliderProgress為例,我們先看GUI-Guilder能給我們提供什么:
這里以IAR為示例,導(dǎo)出工程。
可以得到以下的工程目錄,最主要的就是紅框中生成的部分,它包含了除littleVGL源碼外的所有和屏幕相關(guān)的code
下來(lái)的操作就是將MCUxpresso SDK中的lvgl_demo_widgets_bm工程文件夾Copy到該目錄,這樣就可以成功編譯該示例(IAR打開(kāi)ewp文件后save workspace就可以生成eww文件)
我們現(xiàn)在要做的就是將這個(gè)IAR工程分成兩個(gè)工程,其中一個(gè)由PC段編譯生成和界面相關(guān)的代碼(后稱littlevgl_guider),另一個(gè)生成底層的刷屏和UART通信代碼RuntimeSystem(后稱RTS),大體結(jié)構(gòu)如下:
通過(guò)對(duì)整體代碼的分析可以看出,實(shí)際上要做到上面這種固件的分割,只需要將littlevgl_support.c這個(gè)文件拆成兩部分即可。RTS和littlevgl_guider這兩個(gè)固件之間通過(guò)在固定地址的指針函數(shù)結(jié)構(gòu)體相互傳遞,如果有疑問(wèn)的朋友可以參考《如何在MCU中使用二進(jìn)制庫(kù)》。
RTS中將和刷屏相關(guān)的函數(shù)結(jié)構(gòu)體放到0x2000這個(gè)地址:
#define LCD_INTERFACE_ADDR0x2000 typedef struct { ????void (*DEMO_InitLcd)(void); ????void (*DEMO_InitLcdClock)(void); ????void (*DEMO_InitLcdBackLight)(void); ????void (*DEMO_FlushDisplay)(lv_disp_drv_t *, const lv_area_t *, lv_color_t *); ????void (*DEMO_InitTouch)(void); ????bool (*DEMO_ReadTouch)(lv_indev_drv_t *, lv_indev_data_t *); ????void (*AppTask)(void); } LCD_interface_t; __root const LCD_interface_t g_lcd_if @LCD_INTERFACE_ADDR = { .DEMO_InitLcd = DEMO_InitLcd, .DEMO_InitLcdClock = DEMO_InitLcdClock, .DEMO_InitLcdBackLight = DEMO_InitLcdBackLight, .DEMO_FlushDisplay = DEMO_FlushDisplay, .DEMO_InitTouch = DEMO_InitTouch, .DEMO_ReadTouch = DEMO_ReadTouch, .AppTask = AppTask };
littlevgl_guider工程中的littlevgl_support.c函數(shù)可以通過(guò)指針函數(shù)調(diào)用RTS底層接口,這樣就完成了RTS到littlevgl_guider的調(diào)用
#define LCD_INTERFACE_ADDR0x2000 typedef struct { void (*DEMO_InitLcd)(void); void (*DEMO_InitLcdClock)(void); void (*DEMO_InitLcdBackLight)(void); void (*DEMO_FlushDisplay)(lv_disp_drv_t *, const lv_area_t *, lv_color_t *); void (*DEMO_InitTouch)(void); bool (*DEMO_ReadTouch)(lv_indev_drv_t *, lv_indev_data_t *); ????void (*AppTask)(void); } LCD_interface_t; #define LCD_IF ((LCD_interface_t *)(LCD_INTERFACE_ADDR)) /*------------------------- * Initialize your display * -----------------------*/ LCD_IF->DEMO_InitLcd();
同樣的方式,在littlevgl_guider中通過(guò)定義指針函數(shù)結(jié)構(gòu)體的方式共享LittleVGL的相關(guān)函數(shù)給RTS
#define LITTLEVGL_INTERFACE_ADDR0x32000 __root const Littlevgl_interface_t g_lvgl_if @LITTLEVGL_INTERFACE_ADDR = { .littlevgl_Init = lvgl_Init, .littlevgl_task = lvgl_task, .littlevgl_tick_inc = lvgl_tick_inc, .littlevgl_dis_flush_ready = lvgl_dis_flush_ready };
在RTS中通過(guò)類似的方式實(shí)現(xiàn)littleVGL的刷屏
#define LITTLEVGL_INTERFACE_ADDR0x32000 typedef struct { void (*littlevgl_Init)(void); void (*littlevgl_task)(void); ????void (*littlevgl_tick_inc)(uint32_t ); ????void (*littlevgl_dis_flush_ready)(lv_disp_drv_t *); } Littlevgl_interface_t; #define LVGL_IF ((Littlevgl_interface_t *)(LITTLEVGL_INTERFACE_ADDR)) void AppTask() { ????DEMO_SetupTick(); ????LVGL_IF->littlevgl_Init(); for (;;) { while (!s_lvglTaskPending) { } s_lvglTaskPending = false; LVGL_IF->littlevgl_task(); } }
兩個(gè)不同的工程需要通過(guò)鏈接文件將它們Flash/Ram隔離開(kāi),最后還有一點(diǎn)需要注意的,如果僅僅通過(guò)函數(shù)指針調(diào)用littlevgl_guider函數(shù)是無(wú)法正常運(yùn)行的,因?yàn)閘ittlevgl_guider工程沒(méi)有走cmain的過(guò)程,需要初始化的全局變量都沒(méi)有進(jìn)行初始化,所以RTS可以通過(guò)Bootloader加載APP的方式跳到littlevgl_guider中(但注意不要切SP,棧還用RTS的),讓它走完初始化流程。
littlevgl_guider函數(shù)周期檢查共享RAM的值,如果有變化,就更新變量到屏幕即可。下圖是將整個(gè)工程拆分的示例
寫(xiě)在最后
不是所有的硬件平臺(tái)都可以使用該方法,因?yàn)镚UI Guilder目前是將固件和資源編譯在一起的,所以如果圖像資源(包括字庫(kù))比較大,則固件占用Flash也會(huì)很大,僅僅靠MCU內(nèi)部Flash很難滿足要求。i.MX RT系列是比好的選擇,因?yàn)樗С諼IP(通過(guò)AHB讀取QSPI-FLASH),固件大小就不會(huì)受到制約,主頻也夠高,可以保證顯示效果。
編輯:黃飛
?
評(píng)論