第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 初学者重要提示
- 对于初学者来说,学习按钮控件最先要解决的问题就是如何让按钮支持的点击消息,松手消息和移开消息跟用户的应用关联起来,初学者要带着此疑问来学习本章节。
- 本教程配套的emWin版本是6.x,从emWin5.28版本开始,皮肤色是自动使能的,所以部分按钮控件的API函数是失效的,主要是按钮控件的颜色设置函数。此问题经常被初学者问,详情请看这个帖子即可:https://www.armbbs.cn/forum.php?mod=viewthread&tid=20669 。
- 按钮控件的所有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结构体类型定义如下:
代码语言:javascript复制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 ()都没有用到。这里举一个对话框资源列表里面创建按钮控件的例子,帮助大家更好的理解:
代码语言:javascript复制/*
*********************************************************************************************************
* 宏定义
*********************************************************************************************************
*/
#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结构成员的对应关系如下:
代码语言:javascript复制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的安装目录中,路径KeilMDK-Middleware7.12.0emWinTool (版本不同,红色数值不同)里面:
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小节详细讲述了,这里不再赘述。可以在模拟器上面运行的完整代码如下:
代码语言:javascript复制/*********************************************************************
* *
* 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: support@segger.com *
* *
**********************************************************************
*/
// 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 ****************************/
创建的这个例子相对比较容易,主要实现了在对话框上面创建了两个按钮控件,对话框的主体是用的框架窗口控件。
- 在对话框的资源列表中创建两个按钮控件。
- 通过函数WM_GetDialogItem获得对话框上ID为ID_BUTTON_0的按钮控件句柄。
- 通过函数BUTTON_SetFont设置按钮控件ID_BUTTON_0的字体。
- 通过函数BUTTON_SetText设置按钮控件上显示的文本。
- 按钮控件ID_BUTTON_0的点击消息WM_NOTIFICATION_CLICKED,如果用户需要按钮按下后执行某项功能,就可以在这个消息里面加入用户功能。
- 按钮控件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后重新创建,如此循环。
程序代码如下:
代码语言:javascript复制#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();
}
}
- 按钮句柄。
- 创建一个按钮控件,直接创建到桌面窗口上了,按钮创建函数BUTTON_Create第5个参数GUI_ID_OK是系统定义好的ID,最后一个参数WM_CF_SHOW表示创建后立即显示,要不是无法立即显示的,其它参数大家看手册中的介绍即可。
- 通过函数BUTTON_SetText设置按钮上显示的文本。
- 等待按钮被按下。
- 通过函数WM_DeleteWindow删除创建的按钮,因为按钮的本质也是窗口,所以用这个函数删除是没有问题的。
显示效果如下:
53.5 实验例程说明(RTOS)
配套例子:
V7-570_emWin6.x实验_Button按钮控件(RTOS)
实验目的:
- 本实验主要学习按钮控件的使用。
- 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文件中的配置如下:
代码语言:javascript复制#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按钮控件(裸机)
实验目的:
- 本实验主要学习按钮控件的使用。
- emWin功能的实现在MainTask.c文件里面。
emWin界面显示效果:
800*480分辨率界面效果。
emWin动态内存配置:
GUIConf.c文件中的配置如下:
代码语言:javascript复制#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大家都可以试试。