第51章 emWin6.x的Window窗口控件
本章节为大家讲解emWin6.x支持的窗口控件,窗口控件和前面讲的窗口本质上面是一样的,只不过这里是以控件的形式存在。
51.1 初学者重要提示
51.2 窗口控件基础知识
51.3 使用GUIBuilder创建窗口控件并用模拟器显示出来
51.4 官方WIDGET_Window.c实例讲解
51.5 实验例程说明(RTOS)
51.6 实验例程说明(裸机)
51.7 总结
51.1 初学者重要提示
- 窗口控件相对比较容易,本章节也没什么要特别注意的,重点是学习对话框的资源列表里面创建窗口控件的方法。
- 窗口控件的所有API函数在emWin手册中都有讲解,下图是中文版手册里面API函数位置:
下图是英文版手册里面API函数的位置:
51.2 窗口控件基础知识
窗口控件与前面讲解窗口管理器时介绍的窗口基本是没有区别的,这里的窗口控件主要是配合对话框一起使用,当然,单独创建也是没有问题的。窗口控件是使用对话框必须创建的控件之一,另一个是框架窗口控件,这两个控件必须二选一作为对话框资源列表里面的第一个控件。
窗口控件是对话框的主体,其余的按钮控件,编辑框控件,滚动条等控件都是建立在窗口控件上的。
51.2.1 键盘反应(输入聚焦)
窗口控件不支持输入聚焦,这点要特别注意。不支持输入聚焦的话,外置键盘或者类似外置键盘的输入设备给窗口控件发消息是没有任何反应的。
(输入聚焦是一个重要的知识点,使用外置键盘或者类似外置键盘的输入设备要用到)
51.2.2 窗口控件API函数
窗口控件的API函数比较简单,也没有什么要特别注意的,大家只需看官方手册中的API函数说明就够用了,我们教程这里不再赘述。
本章节教程配套例子是将窗口控件配合对话框一起使用的,实际项目中也推荐大家这么做,可以很方便的进行界面管理。在对话框上面使用窗口控件是通过函数WINDOW_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是可选的,函数WINDOW_CreateIndirect()没有用到参数Para,但用到了标记Flags,与函数WINDOW_CreateEx的形参WinFlags是等效的,具体形参WinFlags支持哪些标记参看emWin官方手册中的说明即可。
这里举一个对话框资源列表里面创建窗口控件的例子,帮助大家更好的理解:
代码语言:javascript复制/*
*********************************************************************************************************
* GUI_WIDGET_CREATE_INFO类型数组
*********************************************************************************************************
*/
static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] = {
{ WINDOW_CreateIndirect, "Window", ID_WINDOW_0, 0, 0, 800, 480, 0, 0x0, 0},
{ TEXT_CreateIndirect, "Text", ID_TEXT_0, 20, 35, 86, 31, 0, 0x64, 0 },
{ BUTTON_CreateIndirect, "Button", ID_BUTTON_0, 16, 87, 128, 37, 0, 0x0, 0 },
};
上面的对话框资源列表里面依次创建了窗口控件,文本控件和按钮控件。窗口控件的参数和GUI_WIDGET_CREATE_INFO结构成员的对应关系如下:
代码语言:javascript复制pfCreateIndirect = WINDOW_CreateIndirect;
pName = "Window";
Id = ID_WINDOW_0;
x0 = 0;
y0 = 0
xSize = 800;
ySzie = 480;
Flags = 0;
Para = 0x0;
NumExtraBytes = 0;
51.3 使用GUIBuilder创建窗口控件并用模拟器显示出来
GUIBuilder在MDK5.X的安装目录中,路径KeilMDK-Middleware7.12.0emWinTool (版本不同,红色数值不同)里面:
51.3.1 第一步:添加一个对话框,主体是窗口控件
- 找到GUIBuilder后,打开这个软件,并按照如下方式添加一个窗口控件。
- 修改窗口控件的大小为800*480。
51.3.2 第二步:在对话框上面建立文本控件
仅显示一个窗口控件的话,内容太少了,我们在上面添加一个文本控件。文本控件的的建立方法和上面窗口控件的建立方法是一样的。文本控件的字体大小和显示内容,大家可以任意设置。
- 添加文本控件。
- 对于添加的文本控件,用户是可以用鼠标任意拖动的,下面设置文本控件的字体,对齐方式,和显示的文本。首先,鼠标左击选中刚刚添加的文件,然后右击鼠标,选择Set font。
弹出如下界面,并选择字体GUI_FONT_32B_ASCII,点击OK。
设置字体后文本显示不全,用户可以通过鼠标调整其大小,调整方法如下:先左击选中相应控件,会出现绿色的边框,在边框的地方拖动鼠标即可修改大小
设置好字体以后再设置对齐方式,还是右击鼠标,选择Set text alignment,并选择居中
最后还是右击鼠标,选择Set text,并更改Content为armfly,修改的地方在左下角:
设置好以后,文本控件就算建立完毕。
51.3.3 第三步:在对话框上面建立按钮控件
为了使窗口控件内容不至于太少,我们在上面再添加一个按钮控件。文本控件的的建立方法和上面窗口控件的建立方法是一样的。按钮上面的字体大小和显示内容,大家可以任意设置。按钮上的文本不支持对齐方式设置,默认是居中显示,这里是显示字符armfly,字体GUI_FONT_24B_ASCII,建立后的效果如下所示:
对于建立的按钮控件,用户可以任意拖动,并通过鼠标调整其大小,调整方法如下:先左击选中相应控件,会出现绿色的边框,在边框的地方拖动鼠标即可修改大小。
51.3.4 第四步:建立好后点击File-save
保存方法如下:
保存后生成的文件在GUIBluder5.32软件所在的文件夹里面:
51.3.5 第五步:在模拟器上运行GUIBuilder生成的代码
在模拟器上面如何演示GUIBuilder生成的代码已经在第11章的11.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_SCROLLBAR_0 (GUI_ID_USER 0x02)
#define ID_SLIDER_0 (GUI_ID_USER 0x03)
// 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, 130, 28, 147, 35, 0, 0x0, 0 },
{ SCROLLBAR_CreateIndirect, "Scrollbar", ID_SCROLLBAR_0, 129, 74, 147, 28, 0, 0x0, 0 },
{ SLIDER_CreateIndirect, "Slider", ID_SLIDER_0, 133, 118, 137, 25, 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);
BUTTON_SetFont(hItem, GUI_FONT_24B_ASCII);
BUTTON_SetText(hItem, "armfly");
// 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:
// 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;
case ID_SCROLLBAR_0: // Notifications sent by 'Scrollbar'
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;
case WM_NOTIFICATION_VALUE_CHANGED:
// 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_SLIDER_0: // Notifications sent by 'Slider'
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;
case WM_NOTIFICATION_VALUE_CHANGED:
// 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生成的对话框创建函数 */
CreateWindow();
while(1)
{
GUI_Delay(10);
}
}
/*************************** End of file ****************************/
实际显示效果如下,分辨率800*480:
对于这个创建和演示过程,强烈建议初学者实际动手操作。
51.4 官方WIDGET_Window.c实例讲解
这个DEMO在模拟器中的位置:
主要功能介绍:
这个例子简单的演示了窗口控件的使用,主要功能是创建一个阻塞式对话框,如果用户点击按钮将控件关闭了,1秒后重新创建这个对话框。
程序代码如下:
代码语言:javascript复制#include "GUI.h"
#include "DIALOG.h"
/*********************************************************************
*
* Defines
*
**********************************************************************
*/
//
// Recommended memory to run the sample with adequate performance
//
#define RECOMMENDED_MEMORY (1024L * 5)
/*********************************************************************
*
* Static data
*
**********************************************************************
*/
/*********************************************************************
*
* _aDialog
*
* Function description
* Dialog resource using a WINDOW widget
*/
static const GUI_WIDGET_CREATE_INFO _aDialog[] = {
{ WINDOW_CreateIndirect, "", 0, 0, 0, 260, 200, 0 },
{ TEXT_CreateIndirect, "Dialog", 0, 80, 5, 100, 20, TEXT_CF_HCENTER },
{ BUTTON_CreateIndirect, "Close", GUI_ID_BUTTON0, 80, 160, 100, 20, 0 }
};
/*********************************************************************
*
* Static code
*
**********************************************************************
*/
/*********************************************************************
*
* _cbDialog
*
* Function description
* Callback routine of the dialog
*/
static void _cbDialog(WM_MESSAGE *pMsg) {
int NCode;
int Id;
switch (pMsg->MsgId) {
case WM_PAINT:
GUI_SetBkColor(GUI_GREEN);
GUI_Clear();
break;
case WM_NOTIFY_PARENT:
Id = WM_GetId(pMsg->hWinSrc); // Id of widget
NCode = pMsg->Data.v; // Notification code
switch (NCode) {
case WM_NOTIFICATION_RELEASED: // React only if released
switch (Id) {
case GUI_ID_BUTTON0:
GUI_EndDialog(pMsg->hWin, 0);
break;
}
break;
}
break;
default:
WM_DefaultProc(pMsg);
}
}
/*********************************************************************
*
* 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;
}
while(1) {
GUI_DispStringHCenterAt("WIDGET_Window samplenshows how to use a WINDOW widget", 160, 5);
/* 阻塞式对话框 */
GUI_ExecDialogBox(_aDialog, GUI_COUNTOF(_aDialog), _cbDialog, WM_HBKWIN, 30, 30);
GUI_Clear();
GUI_DispStringHCenterAt("Dialog has been closed", 160, 5);
GUI_Delay(1000);
GUI_Clear();
}
}
这个例子重点看对话框资源列表中窗口控件的创建。设置了窗口控件在对话框中起始坐标是(0, 0),长260个像素,高200个像素,Flags标记位设置为0。
实际显示效果如下:
51.5 实验例程说明(RTOS)
配套例子:
V7-566_emWin6.x实验_Window窗口控件(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分辨率界面效果。
51.6 实验例程说明(裸机)
配套例子:
V7-565_emWin6.x实验_Window窗口控件(裸机)
实验目的:
- 本实验主要学习窗口管理器之定时器的使用方法。
- 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动态内存。
51.7 总结
本章节主要为大家讲解了窗口控件的使用,相对比较简单,建议初学者多使用模拟器或者开发板做练习,熟能生巧。