A common need for GUI applications is to implement a clock function. Whether it be to create a clock interface for the end user's benefit, or just to time animations or other actions, implementing an accurate clock is a useful and important feature for GUI applications. The aim of this document is to help you implement clock functions in your AppWizard project.
When implementing a real-time clock, there are a couple of general methods to do so.
Each of these methods have their advantages and disadvantages. If you just need a timer that doesn't require extra code and you don't require control or assurance of precision, or maybe you can't spare another timer, using an animation object (method #2) may be a good option in that application. If your application requires an assurance of precision or requires other real-time actions to be performed that AppWizard can't control, it is best to implement an independent timer in your MCU (method #1).
Implementing a timer via an independent MCU timer allows better control and guarantees the precision because it isn't a shared clock and the developer can adjust the interrupt priorities such that the timer interrupt has the highest priority. AppWizard timing uses a common timer and then time slices activities similar to how an operating system works. It is for this reason that implementing an independent MCU timer is best when you need control over the precision of the timer or you need other real-time actions to be triggered by this timer. When implementing a timer using an independent MCU timer (like the RTC module), an understanding of how to interact with Text widgets is needed. Let's look at this first.
Editing Text widgets occurs through the use of the emWin library API (the emWin library is the underlying code that AppWizard builds upon). The Text widget API functions are documented in the emWin Graphic Library User Guide and Reference Manual, UM3001. Most of the Text widget API functions require a Text widget handle. Be sure to not confuse this handle for the AppWizard ID. Imagine a clock example where there are two Text widgets in the interface: one for the minutes and one for the seconds. The AppWizard IDs of these objects might be ID_TEXT_MINS and ID_TEXT_SECONDS respectively (again, these are not to be confused with the handle to the Text widget for use by emWin library functions). The first action software should take is to obtain the handle for the Text widgets. This can be done using the WM_GetDialogItem function. The code to get the active window handle and the handle for the two Text widgets is shown below:
activeWin = WM_GetActiveWindow();
textBoxMins = WM_GetDialogItem(activeWin, ID_TEXT_MINS);
textBoxSecs = WM_GetDialogItem(activeWin, ID_TEXT_SECONDS);
Note that this function requires the handle to the parent window of the Text widget. If your application has multiple windows or screens, you may need to be creative in how you acquire this handle, but for this example, the software can simply call the WM_GetActiveWindow function (since there is only one screen).
When to call these functions can be a bit tricky as well. They can be called before the MainTask() function of the application is called and the application will not crash. However, the handles won't be correct and the Text widgets will not be updated as expected. It's recommended that these handles be initialized when the screen is initialized. An example of how this would be done is shown below:
void cbID_SCREEN_CLOCK(WM_MESSAGE * pMsg) {
extern WM_HWIN activeWin;
extern WM_HWIN textBoxMins;
extern WM_HWIN textBoxSecs;
extern WM_HWIN textBoxDbg;
if(pMsg->MsgId == WM_INIT_DIALOG)
{
activeWin = WM_GetActiveWindow();
textBoxMins = WM_GetDialogItem(activeWin, ID_TEXT_MINS);
textBoxSecs = WM_GetDialogItem(activeWin, ID_TEXT_SECONDS);
textBoxDbg = WM_GetDialogItem(activeWin, ID_TEXT_DBG);
}
GUI_USE_PARA(pMsg);
}
Once the Text widget handles have been acquired, the text can be updated using the TEXT_SetText() function or the TEXT_SetDec() function in this case, because the Text widgets are configured for decimal mode, since we want to display numbers. An example of the code to do this is shown below.
/* TEXT_SetDec(Text Widget Handle, Value as Int, Length, Shift, Sign, Leading Spaces) */
if(TEXT_SetDec(textBoxSecs, (int)gSecs, 2, 0, 0, 0))
{
/* Perform action here if necessary */
}
if(TEXT_SetDec(textBoxMins, (int)gMins, 2, 0, 0, 0))
{
/* Perform action here if necessary */
}
When implementing a real-time clock using animation objects, it is necessary to implement a loop. This could be done outside of the AppWizard GUI (in your code) but because the timing precision can't be guaranteed, it's just as easy to implement a loop in the AppWizard GUI if you know how (it isn't very intuitive as to how to do this). Before examining the interactions to do this, let's look at the variables and objects needed to do this.
Now, here are the interactions used to implement the clock feature using animation interactions.
The animation object (as well as other emWin objects) use the GUI_X_DELAY function for timing. It is up to the host software to implement this function. In the i.MX RT examples, the General Purpose Timer (GPT) is used for this timer. So how the GPT is configured will affect the timing of the application and the how fast or slow the animations run. The GPT is configured in the function BOARD_InitGPT() which resides in the main source file. The recommended way to adjust the speed of the timer is by changing the divider value to the GPT.
So we have seen two different methods of implementing a real-time clock in an AppWizard GUI application. Those methods are:
Using an independent timer in your MCU may be preferred as it allows for better control over the timing, can allow for real-time actions to be performed that AppWizard can't control, and provides some assurance of precision. Using animation objects may be preferred if you just need a quick timer implementation that doesn't require you to manually add code to your project or use a second timer.