ucGUI的學習小結


前言    

      做一個小項目時需要實現GUI及相關操作(響應按鍵)。用的SoC的優點是功耗低,但是受限於硬件能力,之前的SDK里並沒有對GUI有很好的支持。后面對GUI的界面外觀還有一定的要求,就在網上搜了一下開源GUI的相關資料。最終使用ucGUI實現了GUI操作,這里把相關的學習過程做一個簡單小結,所有相關資料上傳到了百度雲盤(鏈接:http://pan.baidu.com/s/1qYvv84G 密碼:1o4l)

      基本流程是選擇開源GUI——移植ucGUI——實現GUI元素的顯示——顯示單頁GUI界面——顯示含有嵌套關系的GUI系統——優化顯示流程。

1.選擇開源GUI

      用的SoC不支持linux,也不是Cortex-M系列的,最后只找到了兩個合適的開源GUI——zlggui和ucGUI。zlggui代碼量非常小,實現的gui也十分簡單,適合我這種新手入門。把代碼簡單過了一遍並移植了下,算是加深了LCD上顯示漢字、圖片的代碼實現(移植過程我主要參考了http://www.openedv.com/posts/list/32830.htm內容,zlggui源碼見網盤)。

2.移植ucGUI    

      接着就是自己最終使用的ucGUI了,我用了3.90版本,參考了STM32上移植ucGUI的流程實現了在自己項目中的移植(正點原子上有很多教學貼,我參考的是附件中《ZK_UCGUI移植解析.pdf》文件)。移植過程不算復雜,主要是提供(1)LCD初始化函數(2)LCD畫點函數(3)LCD的一些參數配置(分辨率,RGB位數等)。之后使用ucGUI的函數成功顯示出一串字符,移植大體完成。

3.實現GUI元素的顯示    

      之后就是熟悉如何使用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》目錄。

4.顯示單頁GUI界面

  顯示元素實現后,就是嘗試顯示單頁GUI界面,ucGUI教程里有提供過一個“對話框”控件,控件中可以按需求擺放一些元素(包括按鈕、文本、圖片等)並實現對這些元素操作(按鈕按下、文本切換等)的響應。不過其響應函數相對復雜。自己偷懶采用的是使用窗口管理器方式,自己構造一個針對該界面的響應函數,偽代碼如下:

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);

    /*根據按鍵值更新界面指針等*/

  }

}

    之后在循環任務中循環調用該函數即可實現該界面對於不同按鍵的響應。

5.顯示含有嵌套關系的GUI系統

      GUI操作界面一般不止一頁,而是多頁且多級的。為了實現不同界面的切換,自己使用鏈表將不同的GUI界面串聯起來;同時為了統一循環任務中對界面GUI響應函數的調用形式,使用一個全局指針指向當前工作的界面。給出部分代碼如下:

(1)定義一個結構體表示一個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;//下一級界面

}

(2)將各個界面串聯起來

      假如有界面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;

(3)界面的響應函數中更新指向界面的指針

      函數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;

  }

}

(4)界面響應函數的調用

      在循環任務中如下執行

while(1)

{

  獲得按鍵值key;

  p_node->p_func(key);

}

    這樣處理,每一個界面對應一個單獨的界面響應函數,將復雜的響應處理封裝在p_func()中;同時在循環任務中統一了調用界面響應函數的接口。 

6.優化顯示流程

      后面感覺GUI刷新速率不夠快,在網上搜到一些有關優化顯示流程的資料。優化主要包括以下幾個方面:

(1)優化ucGUI的畫點函數,該函數是被調用的最頻率的函數,因此優化效果十分顯著。

(2)利用lcd控制芯片的特性優化ucGUI的划線函數

(3)利用ucGUI的窗口緩存,分配較大的內存用於緩存窗口繪制

      具體我參考了文件中《ucgui液晶顯示深度優化篇.pdf》文檔,也上傳到了雲盤中。

7.其他

http://www.eepw.com.cn/article/272288.htm中介紹了ucGUI繪制GIF動畫

http://bbs.armfly.com/read.php?tid=377中有很多關於ucGUI的資料,十分給力

      整體來說比較偷懶地使用了ucGUI提供的功能,也滿足了項目需求。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM