AppWizard: Implementing a Real-Time Clock

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

AppWizard: Implementing a Real-Time Clock

AppWizard: Implementing a Real-Time Clock

Introduction

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.  

Methods

When implementing a real-time clock, there are a couple of general methods to do so.  

  1. Use an independent timer in your MCU
  2. Using animation objects

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). 

Method 1:  Independent MCU Timer

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.  

Interacting with Text Widgets

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 */
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

Method 2:  Animation Objects

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. 

  1. ID_VAR_SECS - This variable holds the current seconds value.
  2. ID_VAR_SECS_1 - This variable holds the next second value. 
  3. ID_TEXT_SECONDS - Text box that displays the current seconds value.
  4. ID_END_CNT - Variable that holds the value at which the seconds rolls over and increments the minute count
  5. ID_TEXT_MINS - Text box that holds the current minute count.
  6. ID_MIN_END_CNT - Variable that holds the value at which the minutes rolls over (which would also increment the hour count if the hours were implemented).
  7. ID_BUTTON_SECS - This is a hidden button that initiates actions when the seconds variable has reached the end count. 

Now, here are the interactions used to implement the clock feature using animation interactions. 

pastedImage_1.png

  1. The heart of the loop are the interactions triggered by ID_VAR_SECS. 
    1. ID_VAR_SECS -> ID_VAR_SECS_1:  When ID_VAR_SECS changes, it needs to add one to ID_VAR_SECS_1 so that the animation will animate to one second from the current time.
    2. ID_VAR_SECS -> ID_TEXT_SECONDS:  When ID_VAR_SECS changes, it also needs to start the animation from the current value to the next second (ID_VAR_SECS_1).
  2. A very essential part of the loop is ensuring the animation restarts every time.  So ID_TEXT_SECONDS needs to change the value of ID_VAR_SECS when the animation ends. ID_VAR_SECS is changed to the current time value, ID_VAR_SECS_1.
  3. When the ID_TEXT_SECONDS animation ends, it must also decrement the ID_VAR_END_CNT variable.  This is analogous to the control variable of a "For" loop being updated. This is done using the ADDVALUE job, adding '-1' to the variable, ID_VAR_END_CNT.
  4. When ID_VAR_END_CNT changes, it updates the hidden button, ID_BUTTON_SECS, with the new value.  This is analogous to a "For" loop checking whether its control variable is still within its limits.  
  5. The interactions in group 5 are interactions that restart the loop when the seconds reach the count that we desire.  When the loop is restarted, the following actions must be taken:
    1. Set ID_VAR_SECS and ID_VAR_SECS_1 to the initial value for the next loop ('0' in this case).  Note that ID_VAR_SECS_1 MUST be set before ID_VAR_SECS.  Additionally, if the loop is to continue, ID_VAR_SECS and ID_VAR_SECS_1 must be set to the same value.  
    2. ID_TEXT_SECONDS is set to the initial value.  If this isn't done, then the text box will try to animate from the final value to the initial value and then will look "weird".
    3. ID_VAR_END_CNT is reset to its initial value (60 in this case). 
  6. ID_BUTTON_SECS is also responsible for updating the minutes values.  In this case, it's incrementing the ID_TEXT_MINS value (counting up in minutes) and decrementing the ID_VAR_MIN_END_CNT 

Adjusting the time of an animation object

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.

pastedImage_1.png

Conclusion

So we have seen two different methods of implementing a real-time clock in an AppWizard GUI application.  Those methods are:

  1. Use an independent timer in your MCU
  2. Using animation objects

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.  

No ratings
Version history
Last update:
‎09-10-2020 03:33 AM
Updated by: