1. 程式人生 > 其它 >【第3版emWin教程】第53章 emWin6.x的按鈕Button控制元件

【第3版emWin教程】第53章 emWin6.x的按鈕Button控制元件

教程不斷更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429

第53章 emWin6.x的按鈕Button控制元件

本章節為大家講解emWin支援的按鈕控制元件,按鈕控制元件還是非常實用的,實際專案中用到的地方很多,控制元件的本質就是視窗,或者說是具有特定外觀效果的視窗,對於初學者來說,是務必要掌握好的一個控制元件。

53.1初學者重要提示

53.2 按鈕控制元件基礎知識

53.3 使用GUIBuilder建立按鈕控制元件並用模擬器顯示出來

53.4 官方WIDGET_SimpleButton.c例項

53.5 實驗例程說明(RTOS)

53.6 實驗例程說明(裸機)

53.7 總結

53.1 初學者重要提示

  1. 對於初學者來說,學習按鈕控制元件最先要解決的問題就是如何讓按鈕支援的點選訊息,鬆手訊息和移開訊息跟使用者的應用關聯起來,初學者要帶著此疑問來學習本章節。
  2. 本教程配套的emWin版本是6.x,從emWin5.28版本開始,面板色是自動使能的,所以部分按鈕控制元件的API函式是失效的,主要是按鈕控制元件的顏色設定函式。此問題經常被初學者問,詳情請看這個帖子即可:https://www.armbbs.cn/forum.php?mod=viewthread&tid=20669
  3. 按鈕控制元件的所有API函式在emWin手冊中都有講解,下圖是中文版手冊裡面API函式位置:

下圖是英文版手冊裡面API函式的位置:

53.2 按鈕控制元件基礎知識

按鈕控制元件還是非常實用的,實際專案中用到的地方很多,控制元件的本質就是視窗,或者說是具有特定外觀效果的視窗。

不開啟面板色時,按鈕控制元件的顯示效果如下:

開啟面板後顯示效果如下

53.2.1 按鈕控制元件支援的通知程式碼

按鈕控制元件支援的通知程式碼主要是用來處理按鈕訊息用的,按鈕控制元件主要支援三種訊息事件,點選訊息,釋放訊息和移開訊息(點選了按鈕,且從按鈕所在的區域移開了但沒有釋放)。比如使用者通過觸控式螢幕點選了按鈕,視窗管理器會給按鈕父視窗回撥函式傳送訊息WM_NOTIFY_PARENT來通知父視窗,進而再區分是點選訊息,釋放訊息還是移開訊息,使用者就可以在相應的訊息程式碼裡面加入要實現的功能。

以下事件是作為WM_NOTIFY_PARENT訊息的一部分由按鈕控制元件傳送給其父視窗,用來區分不同的按鈕訊息。

53.2.2 按鈕控制元件支援的鍵盤反應(輸入聚焦)

按鈕控制元件支援輸入聚焦,也支援外接鍵盤或者類似外接鍵盤的輸入裝置給按鈕控制元件發訊息。當前僅支援以下兩種按鍵訊息:

(輸入聚焦是一個重要的知識點,使用外接鍵盤或者類似外接鍵盤的輸入裝置要用到)

特別注意一點,對於按鈕控制元件來說,要先聚焦到這個控制元件上了,給按鈕控制元件傳送按鍵訊息才會有反應。

53.2.3 按鈕控制元件API函式

按鈕控制元件的API函式比較多,但呼叫都不難,大家只需看官方手冊中的API函式說明就夠用了,後面用到的函式中有需要特別注意的再跟大家進行講解。

本章節教程配套例子是將按鈕控制元件配合對話方塊一起使用的,實際專案中也推薦大家這麼做,可以很方便的進行介面管理。在對話方塊上面使用按鈕控制元件是通過函式BUTTON_CreateIndirect ()來實現的。根據第47章47.7.1小節講解的<WIDGET>_CreateIndirect()函式,GUI_WIDGET_CREATE_INFO結構體型別定義如下:

typedef struct {
GUI_WIDGET_CREATE_FUNC * pfCreateIndirect;    // 間接建立函式
const char * pName;                           // 控制元件名(不是所有控制元件都需要)
I16 Id;                                       // 控制元件ID
I16 x0, y0, xSize, ySize;                     // 控制元件的座標位置和大小
I16 Flags;                                    // 控制元件用到的標誌,沒有就寫0
I32 Para;                                     // 控制元件用到的引數,沒有就寫0
U32 NumExtraBytes;                            // 函式 <WIDGET>_SetUserData & <WIDGET>_GetUserData用到的
// 額外位元組。
} GUI_WIDGET_CREATE_INFO;

上面結構體成員裡面的標記Flags和引數Para是可選的,函式BUTTON_CreateIndirect ()都沒有用到。這裡舉一個對話方塊資源列表裡面建立按鈕控制元件的例子,幫助大家更好的理解:

/*
*********************************************************************************************************
*                           巨集定義
*********************************************************************************************************
*/
#define ID_FRAMEWIN_0 (GUI_ID_USER + 0x00)
#define ID_BUTTON_0   (GUI_ID_USER + 0x01)
#define ID_BUTTON_1   (GUI_ID_USER + 0x02)

/*
*********************************************************************************************************
*                           GUI_WIDGET_CREATE_INFO型別陣列
*********************************************************************************************************
*/
static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] = 
{
{ FRAMEWIN_CreateIndirect, "Framewin", ID_FRAMEWIN_0, 0, 0, 800, 480, 0, 0x0, 0 },
  { BUTTON_CreateIndirect, "Button", ID_BUTTON_0, 35, 37, 159, 49, 0, 0x0, 0 },
  { BUTTON_CreateIndirect, "Button", ID_BUTTON_1, 36, 127, 159, 46, 0, 0x0, 0 },
};

上面的對話方塊資源列表裡面依次建立了框架視窗控制元件,兩個按鈕控制元件。第一個按鈕控制元件的引數和GUI_WIDGET_CREATE_INFO結構成員的對應關係如下:

pfCreateIndirect = BUTTON_CreateIndirect;
pName = "Button";
Id = ID_BUTTON_0;
x0 = 35;
y0 = 37;
xSize = 159;
ySzie = 49;
Flags = 0;
Para = 0x0;
NumExtraBytes = 0;

對於這個對應關係要注意以下兩點:

  • 這裡的x0和y0座標位置是相對於對話方塊資源列表中框架視窗的客戶端視窗的位置座標。
  • Id號,這裡的Id號是使用者自定義的,emWin在GUI.h檔案中也定義了部分按鈕Id號,使用者是可以直接使用的:
#define GUI_ID_BUTTON0    0x170
#define GUI_ID_BUTTON1    0x171
#define GUI_ID_BUTTON2    0x172
#define GUI_ID_BUTTON3    0x173
#define GUI_ID_BUTTON4    0x174
#define GUI_ID_BUTTON5    0x175
#define GUI_ID_BUTTON6    0x176
#define GUI_ID_BUTTON7    0x177
#define GUI_ID_BUTTON8    0x178
#define GUI_ID_BUTTON9    0x179

53.3 使用GUIBuilder建立按鈕控制元件並用模擬器顯示出來

GUIBuilder在MDK5.X的安裝目錄中,路徑\Keil\MDK-Middleware\7.12.0\emWin\Tool (版本不同,紅色數值不同)裡面:

53.3.1 第一步:建立一個對話方塊

  • 找到GUIBuilder後,開啟這個軟體,並按照如下方式建立一個對話方塊。

  • 修改框架視窗大小為800*480。

  • 下面設定對話方塊標題的字型,對齊方式,和顯示的文字。首先,在建立的對話方塊上面右擊滑鼠,選擇Set font。

彈出如下介面,並選擇字型GUI_FONT_32B_ASCII,點選OK。

設定好字型以後再設定對齊方式,還是右擊滑鼠,選擇Set text alignment,並選擇居中

配置完成後,上面的文字Framewin會居中顯示,然後還是滑鼠右擊,選擇Set title text,並更改Framewin為armfly,修改的地方在左下角:

設定好以後,對話方塊就算建立完畢。

53.3.2 第二步:在對話方塊上面建立按鈕

按鈕的建立方法和上面的對話方塊是一樣的。按鈕上面的字型大小和顯示內容,大家可以任意設定。按鈕上的文字不支援對齊方式設定,預設是居中顯示,這裡是顯示字元LED1,字型GUI_FONT_24B_ASCII,建立後的效果如下所示:

對於建立的按鈕控制元件,使用者可以任意拖動,並通過滑鼠調整其大小,調整方法如下:先左擊選中相應控制元件,會出現綠色的邊框,在邊框的地方拖動滑鼠即可修改大小

同樣的方法,我們再建立一個按鈕控制元件,這裡顯示字元LED2。

53.3.3 第五步:建立好後點擊File-save

儲存方法如下:

儲存後生成的檔案在GUIBuilder5.32軟體所在的資料夾裡面:

53.3.4 第五步:在模擬器上執行GUIBuilder生成的程式碼

在模擬器上面如何演示GUIBuilder生成的程式碼已經在第6章的6.3小節詳細講述了,這裡不再贅述。可以在模擬器上面執行的完整程式碼如下:

/*********************************************************************
*                                                                    *
*                SEGGER Microcontroller GmbH & Co. KG                *
*        Solutions for real time microcontroller applications        *
*                                                                    *
**********************************************************************
*                                                                    *
* C-file generated by:                                               *
*                                                                    *
*        GUI_Builder for emWin version 5.32                          *
*        Compiled Oct  8 2015, 11:59:02                              *
*        (c) 2015 Segger Microcontroller GmbH & Co. KG               *
*                                                                    *
**********************************************************************
*                                                                    *
*        Internet: www.segger.com  Support: [email protected]       *
*                                                                    *
**********************************************************************
*/

// USER START (Optionally insert additional includes)
// USER END

#include "DIALOG.h"

/*********************************************************************
*
*       Defines
*
**********************************************************************
*/
#define ID_FRAMEWIN_0 (GUI_ID_USER + 0x00)
#define ID_BUTTON_0   (GUI_ID_USER + 0x01)
#define ID_BUTTON_1   (GUI_ID_USER + 0x02)


// USER START (Optionally insert additional defines)
// USER END

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

// USER START (Optionally insert additional static data)
// USER END

/*********************************************************************
*
*       _aDialogCreate
*/
static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] = {
  { FRAMEWIN_CreateIndirect, "Framewin", ID_FRAMEWIN_0, 0, 0, 800, 480, 0, 0x64, 0 },
  { BUTTON_CreateIndirect, "Button", ID_BUTTON_0, 35, 37, 159, 49, 0, 0x0, 0 },  //--------------(1)
  { BUTTON_CreateIndirect, "Button", ID_BUTTON_1, 36, 127, 159, 46, 0, 0x0, 0 },
  // USER START (Optionally insert additional widgets)
  // USER END
};

/*********************************************************************
*
*       Static code
*
**********************************************************************
*/

// USER START (Optionally insert additional static code)
// USER END

/*********************************************************************
*
*       _cbDialog
*/
static void _cbDialog(WM_MESSAGE * pMsg) {
  WM_HWIN hItem;
  int     NCode;
  int     Id;
  // USER START (Optionally insert additional variables)
  // USER END

  switch (pMsg->MsgId) {
  case WM_INIT_DIALOG:
    //
    // Initialization of 'Framewin'
    //
    hItem = pMsg->hWin;
    FRAMEWIN_SetFont(hItem, GUI_FONT_32B_ASCII);
    FRAMEWIN_SetTextAlign(hItem, GUI_TA_HCENTER | GUI_TA_VCENTER);
    FRAMEWIN_SetText(hItem, "armfly");
    //
    // Initialization of 'Button'
    //
    hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_0); //--------------(2)
    BUTTON_SetFont(hItem, GUI_FONT_24B_ASCII);         //--------------(3)
    BUTTON_SetText(hItem, "LED1");                     //--------------(4)
    //
    // Initialization of 'Button'
    //
    hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_1);
    BUTTON_SetFont(hItem, GUI_FONT_24B_ASCII);
    BUTTON_SetText(hItem, "LED2");
    // USER START (Optionally insert additional code for further widget initialization)
    // USER END
    break;
  case WM_NOTIFY_PARENT:
    Id    = WM_GetId(pMsg->hWinSrc);
    NCode = pMsg->Data.v;
    switch(Id) {
    case ID_BUTTON_0: // Notifications sent by 'Button'
      switch(NCode) {
      case WM_NOTIFICATION_CLICKED:  //--------------(5)
        // USER START (Optionally insert code for reacting on notification message)
        // USER END
        break;
      case WM_NOTIFICATION_RELEASED: //--------------(6)
        // USER START (Optionally insert code for reacting on notification message)
        // USER END
        break;
      // USER START (Optionally insert additional code for further notification handling)
      // USER END
      }
      break;
    case ID_BUTTON_1: // Notifications sent by 'Button'
      switch(NCode) {
      case WM_NOTIFICATION_CLICKED:
        // USER START (Optionally insert code for reacting on notification message)
        // USER END
        break;
      case WM_NOTIFICATION_RELEASED:
        // USER START (Optionally insert code for reacting on notification message)
        // USER END
        break;
      // USER START (Optionally insert additional code for further notification handling)
      // USER END
      }
      break;
    // USER START (Optionally insert additional code for further Ids)
    // USER END
    }
    break;
  // USER START (Optionally insert additional message handling)
  // USER END
  default:
    WM_DefaultProc(pMsg);
    break;
  }
}

/*********************************************************************
*
*       Public code
*
**********************************************************************
*/
/*********************************************************************
*
*       CreateFramewin
*/
WM_HWIN CreateFramewin(void);
WM_HWIN CreateFramewin(void) {
  WM_HWIN hWin;

  hWin = GUI_CreateDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), _cbDialog, WM_HBKWIN, 0, 0);
  return hWin;
}

/*********************************************************************
*
*       MainTask
*/
void MainTask(void) 
{
     /* 初始化 */
    GUI_Init();

    /* 視窗自動使用儲存裝置 */
    WM_SetCreateFlags(WM_CF_MEMDEV);

    /* 建立對話方塊,使用GUIBulder5.32生成的對話方塊建立函式 */
    CreateFramewin ();

    while(1)
    {
        GUI_Delay(10);
    }
}

/*************************** End of file ****************************/

建立的這個例子相對比較容易,主要實現了在對話方塊上面建立了兩個按鈕控制元件,對話方塊的主體是用的框架視窗控制元件。

  1. 在對話方塊的資源列表中建立兩個按鈕控制元件。
  2. 通過函式WM_GetDialogItem獲得對話方塊上ID為ID_BUTTON_0的按鈕控制元件控制代碼。
  3. 通過函式BUTTON_SetFont設定按鈕控制元件ID_BUTTON_0的字型。
  4. 通過函式BUTTON_SetText設定按鈕控制元件上顯示的文字。
  5. 按鈕控制元件ID_BUTTON_0的點選訊息WM_NOTIFICATION_CLICKED,如果使用者需要按鈕按下後執行某項功能,就可以在這個訊息裡面加入使用者功能。
  6. 按鈕控制元件ID_BUTTON_0的釋放訊息WM_NOTIFICATION_RELEASED,如果使用者需要按鈕釋放後執行某項功能,就可以在這個訊息裡面加入使用者功能。按鈕還有一個移開訊息WM_NOTIFICATION_MOVED_OUT,一般情況下用到機會很少,所以使用GUIBuilder建立的時候,也就預設沒有把這個加上。

實際顯示效果如下,解析度800*480:

對於這個建立和演示過程,強烈建議初學者實際動手操作。

53.4 官方WIDGET_SimpleButton.c例項講解

這個DEMO在模擬器中的位置:

主要功能介紹:

這個例子簡單的演示瞭如何使用函式BUTTON_Create直接的建立按鈕控制元件,而上面45.3小節中是通過函式BUTTON_CreateIndirect間接的建立。如果使用者點選了按鈕後,按鈕會被刪除,1s後重新建立,如此迴圈。

程式程式碼如下:

#include "GUI.h"
#include "BUTTON.h"

/*********************************************************************
*
*       Defines
*
**********************************************************************
*/
//
// Recommended memory to run the sample with adequate performance
//
#define RECOMMENDED_MEMORY (1024L * 5)

/*******************************************************************
*
*       static code
*
********************************************************************
*/
/*******************************************************************
*
*       _DemoButton
*/
static void _DemoButton(void) {
  BUTTON_Handle hButton; //--------------(1)

  GUI_SetFont(&GUI_Font8x16);
  GUI_DispStringHCenterAt("Click on button...", 160, 90);
  //
  // Create the button and set text
  //
  hButton = BUTTON_Create(110, 110, 100, 40, GUI_ID_OK, WM_CF_SHOW); //--------------(2)
  BUTTON_SetText(hButton, "Click me..."); //--------------(3)
  //
  // Let window manager handle the button
  //
  while (GUI_WaitKey() != GUI_ID_OK);     //--------------(4)
  //
  // Delete the button
  //
  WM_DeleteWindow(hButton);               //--------------(5)
  GUI_ClearRect(0, 50, 319, 239);
  GUI_Delay(1000);
}

/*********************************************************************
*
*       Public code
*
**********************************************************************
*/
/*********************************************************************
*
*       MainTask
*/
void MainTask(void) {
  GUI_Init();
  //
  // Check if recommended memory for the sample is available
  //
  if (GUI_ALLOC_GetNumFreeBytes() < RECOMMENDED_MEMORY) {
    GUI_ErrorOut("Not enough memory available."); 
    return;
  }
  GUI_SetBkColor(GUI_BLACK);
  GUI_Clear();
  GUI_SetColor(GUI_WHITE);
  GUI_SetFont(&GUI_Font24_ASCII);
  GUI_DispStringHCenterAt("WIDGET_SimpleButton - Sample", 160, 5);
  while (1) {
    _DemoButton();
  }
}
  1. 按鈕控制代碼。
  2. 建立一個按鈕控制元件,直接建立到桌面視窗上了,按鈕建立函式BUTTON_Create第5個引數GUI_ID_OK是系統定義好的ID,最後一個引數WM_CF_SHOW表示建立後立即顯示,要不是無法立即顯示的,其它引數大家看手冊中的介紹即可。
  3. 通過函式BUTTON_SetText設定按鈕上顯示的文字。
  4. 等待按鈕被按下。
  5. 通過函式WM_DeleteWindow刪除建立的按鈕,因為按鈕的本質也是視窗,所以用這個函式刪除是沒有問題的。

顯示效果如下:

53.5 實驗例程說明(RTOS)

配套例子:

V7-570_emWin6.x實驗_Button按鈕控制元件(RTOS)

實驗目的:

  1. 本實驗主要學習按鈕控制元件的使用。
  2. emWin功能的實現在MainTask.c檔案裡面。

實驗內容:

1、K1按鍵按下,串列埠或者RTT列印任務執行情況(串列埠波特率115200,資料位8,奇偶校驗位無,停止位1)。

2、(1) 凡是用到printf函式的全部通過函式App_Printf實現。

(2) App_Printf函式做了訊號量的互斥操作,解決資源共享問題。

3、預設上電是通過串列埠列印資訊,如果使用RTT列印資訊:

MDK AC5,MDK AC6或IAR通過使能bsp.h檔案中的巨集定義為1即可

#define Enable_RTTViewer 1

4、各個任務實現的功能如下:

App Task Start 任務 :啟動任務,這裡用作BSP驅動包處理。

App Task MspPro任務 :訊息處理,這裡用作LED閃爍。

App Task UserIF 任務 :按鍵訊息處理。

App Task COM 任務 :暫未使用。

App Task GUI 任務 :GUI任務。

μCOS-III任務除錯資訊(按K1按鍵,串列埠列印):

RTT 列印資訊方式:

程式設計:

任務棧大小分配:

μCOS-III任務棧大小在app_cfg.h檔案中配置:

#define APP_CFG_TASK_START_STK_SIZE 512u

#define APP_CFG_TASK_MsgPro_STK_SIZE 2048u

#define APP_CFG_TASK_COM_STK_SIZE 512u

#define APP_CFG_TASK_USER_IF_STK_SIZE 512u

#define APP_CFG_TASK_GUI_STK_SIZE 2048u

任務棧大小的單位是4位元組,那麼每個任務的棧大小如下:

App Task Start 任務 :2048位元組。

App Task MspPro任務 :8192位元組。

App Task UserIF 任務 :2048位元組。

App Task COM 任務 :2048位元組。

App Task GUI 任務 :8192位元組。

系統棧大小分配:

μCOS-III的系統棧大小在os_cfg_app.h檔案中配置:

#define OS_CFG_ISR_STK_SIZE 512u

系統棧大小的單位是4位元組,那麼這裡就是配置系統棧大小為2KB

emWin動態記憶體配置:

GUIConf.c檔案中的配置如下:

#define EX_SRAM   1/*1 used extern sram, 0 used internal sram */

#if EX_SRAM
#define GUI_NUMBYTES  (1024*1024*24)
#else
#define GUI_NUMBYTES  (100*1024)
#endif

通過巨集定義來配置使用內部SRAM還是外部的SDRAM做為emWin的動態記憶體,當配置:

#define EX_SRAM 1 表示使用外部SDRAM作為emWin動態記憶體,大小24MB。

#define EX_SRAM 0 表示使用內部SRAM作為emWin動態記憶體,大小100KB。

預設情況下,本教程配套的所有emWin例子都是用外部SDRAM作為emWin動態記憶體。

emWin介面顯示效果:

800*480解析度介面效果。

53.6 實驗例程說明(裸機)

配套例子:

V7-569_emWin6.x實驗_Button按鈕控制元件(裸機)

實驗目的:

  1. 本實驗主要學習按鈕控制元件的使用。
  2. emWin功能的實現在MainTask.c檔案裡面。

emWin介面顯示效果:

800*480解析度介面效果。

emWin動態記憶體配置:

GUIConf.c檔案中的配置如下:

#define EX_SRAM   1/*1 used extern sram, 0 used internal sram */

#if EX_SRAM
#define GUI_NUMBYTES  (1024*1024*24)
#else
#define GUI_NUMBYTES  (100*1024)
#endif

通過巨集定義來配置使用內部SRAM還是外部的SDRAM做為emWin的動態記憶體,當配置:

#define EX_SRAM 1 表示使用外部SDRAM作為emWin動態記憶體,大小24MB。

#define EX_SRAM 0 表示使用內部SRAM作為emWin動態記憶體,大小100KB。

預設情況下,本教程配套的所有emWin例子都是用外部SDRAM作為emWin動態記憶體。

53.7 總結

本期教程主要是跟大家講解了按鈕控制元件的使用,希望初學者可以使用GUIBulider實際建立一個例子在模擬器和開發板上面都執行下。另外,教程中只是使用了部分的按鈕控制元件API,其它的API大家都可以試試。

微信公眾號:armfly_com 安富萊論壇:www.armbbs.cn 安富萊淘寶:https://armfly.taobao.com