第28章 STemWin多任务设计(模拟器)
第28章 STemWin多任务设计(模拟器)
本期教程主要是跟大家讲一下STemWin多任务的设计,官方手册对于这部分内容在开头的时候讲的非常好,本期教程主要是把这部分内容给大家做个介绍,并在模拟器上跑一下多任务。
28. 1 单任务系统(超级循环)
28. 2 多任务系统:一个任务调用emWin
28. 3 多任务系统:多个任务调用emWin
28. 4 官方推荐运行方式
28. 5 运行多任务(模拟器)
28. 6 总结
28.1 单任务系统(超级循环)
STemWin可以配置为不使用RTOS,这时就是整个程序在一个超循环中运行。通常,软件的所有组件都进行周期性调用。因为未使用实时内核,所以软件的实时部分必须使用中断。此类型系统主要用于小型系统,或者实时操作特性无关紧要时。平时大家写裸机程序的时候基本都是下面这种方式:
- void main (void)
- {
- /* 初始化硬件 */
- HARDWARE_Init();
-
- /* 初始化软件组件 */
- XXX_Init();
- YYY_Init();
-
- /* 超级循环: 规则的调用所以软件组件*/
- while (1)
- {
- /* Exec all compontents of the software */
- XXX_Exec();
- YYY_Exec();
- }
- }
加入了STemWin的初始化函数后就是下面这样:
- void main (void)
- {
- /* 初始化硬件 */
- HARDWARE_Init();
-
- /* 初始化软件组件 */
- XXX_Init();
- YYY_Init();
- GUI_Init(); //初始化emWin
-
- /* 超级循环: 规则的调用所以软件组件*/
- while (1)
- {
- /* Exec all compontents of the software */
- XXX_Exec();
- YYY_Exec();
- GUI_Exec(); //调用刷新函数
- }
- }
关于emWin的使用没有实际的限定。照例,需要在调用GUI_Init()之后才能使用该软件。之后,任何API函数都可使用。如果使用了窗口管理器的回调机制,则必须定期调用emWin更新函数。这通常通过从超循环内调用GUI_Exec()来完成。各种模块化函数,如GUI_Delay()和GUI_ExecDialog()不应在循环中使用,因为它们会阻断其它软件模块。可使用不支持多任务(#define GUI_OS 0)的默认配置;不需要内核接口例程。
l 优点
因为未使用实时内核 (->较小的ROM大小,仅一个堆栈->用于堆栈的RAM较少),所以不存在使用RTOS中的抢先/同步问题。
l 缺点
超循环类型程序的大小如果超出某个值,会变得很难维护。实时特性较差,因为一个软件组件无法被任何其它组件中断 (只能通过中断)。这意味着一个软件组件的反应时间取决于系统中所有其它组件的执行时间。
28.2 多任务系统:一个任务调用emWin
使用了实时内核(RTOS)。用户程序分成不同部分,在不同的任务中执行,这些任务通常具有不同的优先级。通常,实时关键任务 (需要一定的反应时间)将具有最高优先级。一个单任务用于用户界面,它调用emWin函数。此任务在系统中通常具有最低优先级,或至少是最低优先级任务中的一个(一些统计任务或简单的空闲时间处理可能具有更低的优先级)。中断可以但不是必须用于软件的实时部分。
如果使用了窗口管理器的回调机制,则必须从调用emWin的任务定期调用emWin更新函数(通常为GUI_Exec()、GUI_Delay())。 由 于emWin仅由一个任务调用,所以对于emWin而言,它与在单任务系统中的使用是相同的。可使用不支持多任务(#define GUI_OS 0)的默认配置;不需要内核接口例程。可使用任何实时内核,商用的或专用的。
l 优点
该系统的实时特性非常优秀。任务的实时特性仅受以较高优先级运行的任务的影响。这意味着对在低优先级任务中运行的程序组件的更改完全不会影响实时特性。如果从低优先级任务执行用户界面,则意味着用户界面的更改不影响实时特性。因此,使用这种系统可轻松将软件的不同组件分配给开发团队的不同成员,成员彼此间可以高度独立地工作。
l 缺点
用户需要拥有实时内核(RTOS),这需要资金并会耗费ROM和RAM(用于堆栈)。此外,还必须考虑任务的同步,以及如何将信息从一个任务传输到另一个任务。
28.3 多任务系统:多个任务调用emWin
使用了实时内核(RTOS)。用户程序分成不同部分,在不同任务中执行,这些任务通常具有不同的优先级。通常,实时关键任务 (需要一定的反应时间)将具有最高优先级。多个任务用于用户界面,调用emWin函数。这些任务在系统中通常具有低优先级,所以它们不影响系统的实时特性。中断可以但不是必须用于软件的实时部分。
如果使用了窗口管理器的回调机制,则必须从调用emWin的一个或多个任务定期调用emWin更新函数 (通常为GUI_Exec()、GUI_Delay())。不可使用不支持多任务(#define GUI_OS 0)的默认配置。配置需要启用多任务支持并定义从中调用emWin的任务的最大数目 (摘自GUIConf.h):
#define GUI_OS 1 // Enable multitasking support
#define GUI_MAX_TASK 5 // Max. number of tasks that may call emWin
内核接口例程是必需的,并需要与正在使用的内核匹配。可使用任何实时内核,商用的或专用的。
l 优点
该系统的实时特性非常优秀。任务的实时特性仅受以较高优先级运行的任务的影响。这意味着对在低优先级任务中运行的程序组件的更改完全不会影响实时特性。如果从低优先级任务执行用户界面,则意味着用户界面的更改不影响实时特性。因此,使用这种系统可轻松将软件的不同组件分配给开发团队的不同成员,成员彼此间可以高度独立地工作。
l 缺点
用户必须拥有实时内核(RTOS),这需要资金并会耗费部分ROM和RAM(用于堆栈)。此外,还必须考虑任务的同步,以及如何将信息从一个任务传输到另一个任务。
28.4 官方推荐运行方式
上面介绍了三种STemWin的运行方式,下面介绍下官方推荐的运行方式:
1、 仅从一个任务调用emWin更新函数(即GUI_Exec()、GUI_Delay()),这有助于保持程序结构清晰。如果在您的系统中有足够的RAM,则指定一个任务 (具有最低优先级)来更新emWin。如下面示例所示,此任务将不断调用GUI_Exec(),且不进行任何其它操作。
2、 保持实时任务 (它们确定I/O、接口、网络等方面的系统特性)与调用emWin的任务分开,这有助于确保最佳的实时性能。
3、 如果可能,仅为用户界面使用一个任务,这有助于保持程序结构简单并简化调试工作。(当然,这不是必须的,而且可能在一些系统中不适合。)
官方推荐的这几点比较的笼统,后面我会在教程中再跟大家详细讲解。
28.5 运行多任务(模拟器)
这个例子在模拟器中的位置:
实际运行效果如下:
下面把相关的代码跟大家解释下:
- #ifndef SKIP_TEST
- #include <stddef.h>
- #include "GUI.h"
- #include "FRAMEWIN.h"
-
- #if GUI_OS == 0
- #error Multitasking sample requires task awareness (#define GUI_OS 1)
- #endif
-
- /*******************************************************************
- *
- * Define how to create a task and start multitasking
- *
- ********************************************************************
- */
- #ifndef WIN32
- #include "RTOS.h" /* Definitions for embOS */
- #define CREATE_TASK(pTCB, pName, pFunc, Priority, pStack) OS_CREATETASK(pTCB, pName, pFunc, Priority, pStack)
- #define START_MT() OS_Terminate(0)
- #define Delay(t) OS_Delay(t)
- #else
- #include "SIM.h" /* Definitions for the Win32 simulation */
- #define CREATE_TASK(pTCB, pName, pFunc, Priority, pStack) SIM_CreateTask(pName, pFunc)
- #define START_MT() SIM_Start()
- #define Delay(t) SIM_Delay(t)
- #endif
-
- /*******************************************************************
- *
- * Static data
- *
- ********************************************************************
- */
- #ifndef WIN32
- //
- // Stacks
- //
- static OS_STACKPTR int Stack_0[600];
- static OS_STACKPTR int Stack_1[600];
- static OS_STACKPTR int Stack_2[600];
- static OS_TASK aTCB[3]; // Task control blocks
- #endif
-
- /*******************************************************************
- *
- * Static code
- *
- ********************************************************************
- */
- /*******************************************************************
- *
- * _cbCallbackT0
- */
- static int XPos;
- static const char aText[] = "Moving text...";
-
- static void _cbCallbackT0(WM_MESSAGE * pMsg) {(1)
- switch (pMsg->MsgId) {
- case WM_PAINT:
- //
- // Handle the paint message
- //
- GUI_SetBkColor(GUI_RED);
- GUI_SetColor(GUI_BLACK);
- GUI_SetFont(&GUI_FontComic24B_ASCII);
- GUI_Clear();
- GUI_DispStringAt(aText, XPos, 0);
- break;
- default:
- WM_DefaultProc(pMsg);
- }
- }
-
- /*******************************************************************
- *
- * _cbCallbackT1
- */
- static void _cbCallbackT1(WM_MESSAGE * pMsg) {(2)
- WM_HWIN hWin = (FRAMEWIN_Handle)(pMsg->hWin);
- switch (pMsg->MsgId) {
- case WM_PAINT:
- //
- // Handle the paint message
- //
- GUI_SetBkColor(GUI_BLUE);
- GUI_SetColor(GUI_WHITE);
- GUI_SetFont(&GUI_FontComic24B_ASCII);
- GUI_SetTextAlign(GUI_TA_HCENTER | GUI_TA_VCENTER);
- GUI_Clear();
- GUI_DispStringHCenterAt("Moving window...",
- WM_GetWindowSizeX(hWin) / 2,
- WM_GetWindowSizeY(hWin) / 2);
- break;
- default:
- WM_DefaultProc(pMsg);
- }
- }
-
- /*******************************************************************
- *
- * _cbBackgroundWin
- */
- static void _cbBackgroundWin(WM_MESSAGE* pMsg) {(3)
- switch (pMsg->MsgId) {
- case WM_PAINT:
- //
- // Handle only the paint message
- //
- GUI_SetBkColor(0x00CC00);
- GUI_Clear();
- GUI_SetFont(&GUI_Font24_ASCII);
- GUI_DispStringHCenterAt("emWin - multitasking demo\n", 160, 5);
- GUI_SetFont(&GUI_Font13_1);
- GUI_DispStringAt("Scrolling text and moving windows without flickering", 5, 35);
- default:
- WM_DefaultProc(pMsg);
- }
- }
-
- /*******************************************************************
- *
- * _Task_0
- */
- static void _Task_0(void) {(4)
- //
- // Create frame window
- //
- FRAMEWIN_Handle hFrameWin = FRAMEWIN_Create("Task 0", NULL, WM_CF_SHOW | WM_CF_STAYONTOP, 0, 70, 200, 40);
- //
- // Create child window
- //
- WM_HWIN hChildWin = WM_CreateWindowAsChild(0, 0, 0, 0, WM_GetClientWindow(hFrameWin),
- WM_CF_SHOW | WM_CF_MEMDEV, _cbCallbackT0, 0);
- FRAMEWIN_SetActive(hFrameWin, 0);
- //
- // Make sure the right window is active...
- //
- WM_SelectWindow(hChildWin);
- //
- // ...and the right font is selected
- //
- GUI_SetFont(&GUI_FontComic24B_ASCII);
- while(1) {
- GUI_RECT Rect;
- int XLen;
-
- XLen = GUI_GetStringDistX(aText); // Get the length of the string
- WM_GetClientRect(&Rect); // Get the size of the window
- //
- // Show moving text
- //
- for (XPos = 0; XPos < (Rect.x1 - Rect.x0) - XLen; XPos++) {
- WM_InvalidateWindow(hChildWin);
- Delay(50);
- }
- for (; XPos >= 0; XPos--) {
- WM_InvalidateWindow(hChildWin);
- Delay(100);
- }
- }
- }
-
- /*******************************************************************
- *
- * _Task_1
- */
- static void _Task_1(void) {(5)
- //
- // Create frame window
- //
- FRAMEWIN_Handle hFrameWin = FRAMEWIN_Create("Task 1", NULL, WM_CF_SHOW | WM_CF_STAYONTOP,
- 20, 170, 200, 40);
- //
- // Create child window
- //
- WM_HWIN hChildWin = WM_CreateWindowAsChild(0, 0, 0, 0, WM_GetClientWindow(hFrameWin), WM_CF_SHOW | WM_CF_MEMDEV,
- _cbCallbackT1, 0);
- FRAMEWIN_SetActive(hFrameWin, 0);
- while(1) {
- int i;
- int nx = 80;
- int ny = 90;
- //
- // Move window continously
- //
- for (i = 0; i < ny; i++) {
- WM_MoveWindow(hFrameWin, 0, -2);
- Delay(50);
- }
- for (i = 0; i < nx; i++) {
- WM_MoveWindow(hFrameWin, 2, 0);
- Delay(50);
- }
- for (i = 0; i < ny; i++) {
- WM_MoveWindow(hFrameWin, 0, 2);
- Delay(50);
- }
- for (i = 0; i < nx; i++) {
- WM_MoveWindow(hFrameWin, -2, 0);
- Delay(50);
- }
- }
- }
-
- /*******************************************************************
- *
- * _GUI_Task
- *
- * This task does the background processing.
- * The MainTask job is to update invalid windows, but other things such as
- * evaluating mouse or touch input may also be done.
- */
- static void _GUI_Task(void) {(6)
- while(1) {
- GUI_Exec(); // Do the background work ... Update windows etc.)
- GUI_X_ExecIdle(); // Nothing left to do for the moment ... Idle processing
- }
- }
-
-
- /*********************************************************************
- *
- * Public code
- *
- **********************************************************************
- */
- /*********************************************************************
- *
- * MainTask
- */
- void MainTask(void) {
- //
- // Init GUI
- //
- GUI_Init();
- WM_SetCreateFlags(WM_CF_MEMDEV); // Use memory devices on all windows to avoid flicker
- WM_SetCallback(WM_HBKWIN, _cbBackgroundWin); // Set callback for background window
- //
- // Create tasks
- //
- CREATE_TASK(&aTCB[0], "Task_0", _Task_0, 80, Stack_0);(7)
- CREATE_TASK(&aTCB[1], "Task_1", _Task_1, 60, Stack_1);
- CREATE_TASK(&aTCB[2], "GUI_TASK", _GUI_Task, 1, Stack_2);
- //
- // Start multitasking
- //
- START_MT();
- }
-
- #endif
跟大家讲这个DEMO,主要是想让大家对emWin的多任务框架有个了解,对于初学者可以不必了解主要是实现了什么功能。但必须得知道这个多任务实现的框架。
1. 任务0所创建窗口的回调函数。
2. 任务1所创建窗口的回调函数。
3. 桌面窗口的回调函数。
4. 任务0所跑的具体函数
5. 任务1所跑的具体函数。
6. 这个任务专门用emWin的刷新。
7. 创建三个任务。
28.6总结
本期教程就跟大家讲这么多,大家要认真的学习一下本期教程开头对emWin多种运行方式的介绍。
谢谢分享!