作一個小項目時須要實現GUI及相關操做(響應按鍵)。用的SoC的優勢是功耗低,可是受限於硬件能力,以前的SDK裏並無對GUI有很好的支持。後面對GUI的界面外觀還有必定的要求,就在網上搜了一下開源GUI的相關資料。最終使用ucGUI實現了GUI操做,這裏把相關的學習過程作一個簡單小結,全部相關資料上傳到了百度雲盤(連接:http://pan.baidu.com/s/1qYvv84G 密碼:1o4l)php
基本流程是選擇開源GUI——移植ucGUI——實現GUI元素的顯示——顯示單頁GUI界面——顯示含有嵌套關係的GUI系統——優化顯示流程。html
用的SoC不支持linux,也不是Cortex-M系列的,最後只找到了兩個合適的開源GUI——zlggui和ucGUI。zlggui代碼量很是小,實現的gui也十分簡單,適合我這種新手入門。把代碼簡單過了一遍並移植了下,算是加深了LCD上顯示漢字、圖片的代碼實現(移植過程我主要參考了http://www.openedv.com/posts/list/32830.htm內容,zlggui源碼見網盤)。node
接着就是本身最終使用的ucGUI了,我用了3.90版本,參考了STM32上移植ucGUI的流程實現了在本身項目中的移植(正點原子上有不少教學貼,我參考的是附件中《ZK_UCGUI移植解析.pdf》文件)。移植過程不算複雜,主要是提供(1)LCD初始化函數(2)LCD畫點函數(3)LCD的一些參數配置(分辨率,RGB位數等)。以後使用ucGUI的函數成功顯示出一串字符,移植大致完成。linux
以後就是熟悉如何使用ucGUI的函數,我是一邊看《ucGUI中文手冊.pdf》一邊用硬件來作實驗驗證。項目中沒有要求實現動畫,我就主要學習了文本顯示、位圖顯示、按鈕、窗口、抗鋸齒的內容,後面也知足了開發要求。緩存
一個GUI操做界面,通常也就包含按鈕、文本、背景圖等元素,項目裏主要就是實現顯示圖片和顯示字符。顯示圖片就使用emwin提供的圖片轉換工具,將圖片轉換爲c文件,調用ucGUI接口函數便可(工具見文件夾中emwin.zip)。試驗後發如今使用工具轉換位圖時,選中壓縮選項時生成的c文件大小要小不少,不過GUI顯示圖片花費的時間相對長一些(須要先解壓縮獲得像素點數據再顯示)。函數
顯示字符方面包括漢字和非漢字,一種經常使用的方法是使用網友提供的小軟件導入選擇的字體生成對應c文件,以後調用ucGUI的顯示字符函數顯示便可,相關資料見《定製UCGUI使用的漢字庫》文件夾。這種方法在顯示字號較大的字體時鋸齒明顯,不太美觀。以後在網上找到另外一種方法,利用ucGUI的抗鋸齒功能繪製字符,字符文件佔用空間相對大些可是顯示效果要好不少。我主要借鑑的是http://blog.sina.com.cn/s/blog_74bd70030102v8od.html,用到的工具見《FontCvtST》目錄。工具
顯示元素實現後,就是嘗試顯示單頁GUI界面,ucGUI教程裏有提供過一個「對話框」控件,控件中能夠按需求擺放一些元素(包括按鈕、文本、圖片等)並實現對這些元素操做(按鈕按下、文本切換等)的響應。不過其響應函數相對複雜。本身偷懶採用的是使用窗口管理器方式,本身構造一個針對該界面的響應函數,僞代碼以下:post
static void _cbWindow1(WM_MESSAGE *pMSG)學習
{字體
/*窗口1的響應函數*/
}
static void _cbWindow2(WM_MESSAGE *pMSG)
{
/*窗口2的響應函數*/
}
void gui_demo(MENU_KEY *key)
{
建立各個窗口及其界面元素(按鈕、文本等),只執行一次;
switch(*key)
{
/*讓窗口響應按鍵值實現文本切換等效果*/
WM_Invalidate(&window1);
/*根據按鍵值更新界面指針等*/
}
}
以後在循環任務中循環調用該函數便可實現該界面對於不一樣按鍵的響應。
GUI操做界面通常不止一頁,而是多頁且多級的。爲了實現不一樣界面的切換,本身使用鏈表將不一樣的GUI界面串聯起來;同時爲了統一循環任務中對界面GUI響應函數的調用形式,使用一個全局指針指向當前工做的界面。給出部分代碼以下:
struct func_node
{
void (*p_func)(MENU_KEY *key);//上面描述的該界面的響應函數,項目中界面響應按鍵值來更新
void (*p_clear)(void);//銷燬該界面的函數
struct func_node *pre_node;//同一級的該界面的上一個界面
struct func_node *next_node;//同一級的該界面的下一個界面
struct func_node **last_level;//上一級的界面
struct func_node **next_level;//下一級界面
}
假若有界面A、B、C,B和C爲二級界面,且都是A的下一級界面。代碼實現思想以下:
struct func_node node_A, node_B, node_C;
struct func_node *pnode, *pnode_level_1, *pnode_level_2;
其中pnode始終指向要顯示的界面;pnode_level_1始終指向要顯示的一級界面;pnode_level_2始終指向要顯示的二級界面。以後初始化各個func_node變量,並利用鏈表串聯起來
pnode_level_1 = &node_A;
pnode_level_2 = &node_B;
pnode_level_1->next_level = &pnode_level_2;
node_B.next_node = &node_C;
node_C.pre_node = &node_B;
p_node = pnode_level_1;
函數void (*p_func)(MENU_KEY *key)就是上一小節中定義的界面響應函數,函數中須要包含根據按鍵值實時修改p_node、p_node_level_1和p_node_level_2部分。目的在於使它們分別始終指向要顯示的界面、要顯示的一級界面和要顯示的二級界面。當須要更換界面時,以下更新p_node
void (*p_func)(MENU_KEY *key)
{
switch(*key)
{
/*更新指向界面的指針p_node、p_node_level_1和p_node_level_2*/
若是須要切換到下一級菜單
p_node = (struct func_node *)*(pnode->next_level);
若是切換到上一級菜單
p_node = (struct func_node *)*(pnode->last_level);
若是切換同一級的下一頁菜單
p_node = p_node->next_node;
若是切換同一級的上一頁菜單
p_node = p_node->pre_node;
}
}
在循環任務中以下執行
while(1)
{
得到按鍵值key;
p_node->p_func(key);
}
這樣處理,每個界面對應一個單獨的界面響應函數,將複雜的響應處理封裝在p_func()中;同時在循環任務中統一了調用界面響應函數的接口。
後面感受GUI刷新速率不夠快,在網上搜到一些有關優化顯示流程的資料。優化主要包括如下幾個方面:
(1)優化ucGUI的畫點函數,該函數是被調用的最頻率的函數,所以優化效果十分顯著。
(2)利用lcd控制芯片的特性優化ucGUI的劃線函數
(3)利用ucGUI的窗口緩存,分配較大的內存用於緩存窗口繪製
具體我參考了文件中《ucgui液晶顯示深度優化篇.pdf》文檔,也上傳到了雲盤中。
http://www.eepw.com.cn/article/272288.htm中介紹了ucGUI繪製GIF動畫
http://bbs.armfly.com/read.php?tid=377中有不少關於ucGUI的資料,十分給力
總體來講比較偷懶地使用了ucGUI提供的功能,也知足了項目需求。