故事是這樣的,最近支持某客戶使用littleVGL開發一款帶顯示效果的產品,由于之前沒有相關經驗,擔心會有問題。沒想到,使用GUI-Guider后很快就完成了相關應用的設計開發。于是乎突發奇想,是否可以讓GUI-Guider變成串口屏的組態工具呢?
1. 什么是串口屏
我們先來認識下串口屏,字面意思就是帶串口的屏,最核心有兩個功能:
- 可以通過PC端設計屏幕顯示界面
- 可以通過串口修改屏幕顯示內容
直白的講,就是屏幕用于顯示,顯示的數據源來自串口的對端設備
1.1 組態串口屏
很早以前,工業現場有時需要HMI,為工作人員提供便捷的操作環境,但不同的應用現場有不同的操作界面,有組態軟件經驗的廠商就想到了用嵌入式板卡跑WinCE的方案,這樣很容易將Windows中的代碼移植過來,用戶只需要在Windows端的組態工具根據現場應用進行組態,然后將生產的工程文件,對象文件,數據庫等文件下載到WinCE對應的板卡中即可實現所見即所得的顯示開發過程。
后來也有廠商使用Cortex-A8+Linux+QT的方式實現該方案。這種串口屏還是比較貴的,畢竟使用的處理器平臺,成本比較高。
由于該方案使用的組態軟件,屏是串口協議的主設備,并且支持豐富的串口協議,比如各類的PLC,常見的西門子S7-200 PPI,三菱FX, 通用設備Modbus-RTU/TCP,各類儀表,變頻器等。
屏的數據可以通過軟件配置的方式與串口外設關聯,先在設備窗口設定變量,之后在界面設計的時候關聯變量即可
1.2 通用串口屏
之后又接觸到了低成本的通用串口屏,這類串口屏與組態屏相同的一點是,都可以通過PC端軟件進行界面設計。
區別在于價格更美麗,但該屏的串口是協議的從設備,并且一般僅支持一種協議(就像下面這種0x80~0x85這種),用戶使用時需要通過外部MCU作為串口的主,并實現相關協議去修改顯示畫面的數據內容,具體框架可以參考下圖:
2. 串口屏設計分析
2.1 組態屏設計
前面大概講過,組態屏一般是把組態軟件交叉編譯到嵌入式板卡所支持的WinCE或Linux中運行,串口屏拆開后可以理解為一個嵌入式處理器的小電腦,處理器性能和樹莓派應該差不多。如果有興趣推薦大家可以研究一個開源方案pvbrowser,它可以在樹莓派上運行,這個軟件底層也是基于QT開發的,很早之前玩過,但是不花錢的東西看上去并不美好。
2.2 通用屏設計
要分析通用的設計,我們可以拆開看看,下面是兩個不同公司的設計:
- 先看行業大佬的板子吧,上面基本看不到啥,都繼承到一起了,外面還有顆SPI的flash用于存非易失的素材或參數
這家公司自己開了個芯片,好像也支持有能力的客戶做二次開發,網上能下到參考原理圖和軟件SDK。 - 換一個廠商,這家用的分離方案,MCU+FPGA+DRAM+NAND:
大膽推測一些系統框架,下圖是幾種方案組合,最大的區別點在于,MCU, FPGA, Flash, DRAM之間的連接方式,主要是灰色和藍色這兩條路徑:- MCU作為推屏的核心器件,往往采用灰色路徑,DRAM和Flash都掛在MCU上,FPGA僅實現顯示驅動的作用,也可以用ILI9341/9488這種顯示驅動芯片替代,該方案的瓶頸在MCU和驅動芯片之間的接口帶寬以及MCU本身的處理性能,針對屏的尺寸比較大(分辨率比較高)或者需要動態顯示效果的應用是一個考驗。
- FPGA作為推屏的核心器件,采用藍色路徑,DRAM和Flash都掛在FPGA上,MCU主要起解析串口命令,并修改FPGA中雙口RAM的功能(RAM區與屏幕數據源綁定),MCU可能還會使用FatFS來獲取SDcard中PC端生成的文件,并將其解析后存儲在Flash上。
從實際效果看,這個產品可能使用方案b,因為普通的MCU主頻較低,受帶寬影響,大屏情況下很難實現較為流暢的動畫效果。
3. GUI-Guider到串口屏
回到之前的想象,GUI-Guider是否可以成為用戶組態工具,當前版本肯定是不行的,因為需要通過串口修改的數據在界面設計時并沒有做地址關聯,如果想做成組態串口屏,還需要設置從站參數信息。當然我們今天先從簡單的通用串口屏入手,假設GUI-Guider后續會像VGUS那樣提供數據地址關聯的接口。
以默認的SliderProgress為例,我們先看GUI-Guilder能給我們提供什么:
這里以IAR為示例,導出工程。
可以得到以下的工程目錄,最主要的就是紅框中生成的部分,它包含了除littleVGL源碼外的所有和屏幕相關的code
下來的操作就是將MCUxpresso SDK中的lvgl_demo_widgets_bm工程文件夾Copy到該目錄,這樣就可以成功編譯該示例(IAR打開ewp文件后save workspace就可以生成eww文件)
我們現在要做的就是將這個IAR工程分成兩個工程,其中一個由PC段編譯生成和界面相關的代碼(后稱littlevgl_guider),另一個生成底層的刷屏和UART通信代碼RuntimeSystem(后稱RTS),大體結構如下:
通過對整體代碼的分析可以看出,實際上要做到上面這種固件的分割,只需要將littlevgl_support.c這個文件拆成兩部分即可。RTS和littlevgl_guider這兩個固件之間通過在固定地址的指針函數結構體相互傳遞,如果有疑問的朋友可以參考《如何在MCU中使用二進制庫》。
RTS中將和刷屏相關的函數結構體放到0x2000這個地址:
#define LCD_INTERFACE_ADDR 0x2000
typedefstruct
{
void (*DEMO_InitLcd)(void);
void (*DEMO_InitLcdClock)(void);
void (*DEMO_InitLcdBackLight)(void);
void (*DEMO_FlushDisplay)(lv_disp_drv_t *, constlv_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函數可以通過指針函數調用RTS底層接口,這樣就完成了RTS到littlevgl_guider的調用
#define LCD_INTERFACE_ADDR 0x2000
typedefstruct
{
void (*DEMO_InitLcd)(void);
void (*DEMO_InitLcdClock)(void);
void (*DEMO_InitLcdBackLight)(void);
void (*DEMO_FlushDisplay)(lv_disp_drv_t *, constlv_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中通過定義指針函數結構體的方式共享LittleVGL的相關函數給RTS
#define LITTLEVGL_INTERFACE_ADDR 0x32000
__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中通過類似的方式實現littleVGL的刷屏
#define LITTLEVGL_INTERFACE_ADDR 0x32000
typedefstruct
{
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();
}
}
兩個不同的工程需要通過鏈接文件將它們Flash/Ram隔離開,最后還有一點需要注意的,如果僅僅通過函數指針調用littlevgl_guider函數是無法正常運行的,因為littlevgl_guider工程沒有走cmain的過程,需要初始化的全局變量都沒有進行初始化,所以RTS可以通過Bootloader加載APP的方式跳到littlevgl_guider中(但注意不要切SP,棧還用RTS的),讓它走完初始化流程。
littlevgl_guider函數周期檢查共享RAM的值,如果有變化,就更新變量到屏幕即可。下圖是將整個工程拆分的示例
寫在最后
不是所有的硬件平臺都可以使用該方法,因為GUI Guilder目前是將固件和資源編譯在一起的,所以如果圖像資源(包括字庫)比較大,則固件占用Flash也會很大,僅僅靠MCU內部Flash很難滿足要求。i.MX RT系列是比好的選擇,因為他支持XIP(通過AHB讀取QSPI-FLASH),固件大小就不會受到制約,主頻也夠高,可以保證顯示效果。
-
PC
+關注
關注
9文章
2065瀏覽量
154020 -
屏幕
+關注
關注
6文章
1191瀏覽量
55748 -
串口
+關注
關注
14文章
1543瀏覽量
76189 -
串口屏
+關注
關注
8文章
530瀏覽量
37320
發布評論請先 登錄
相關推薦
評論