Designing an Application with Microchip Graphics Suite (MGS)
Introduction
In most cases, the Graphical User Interface (GUI) is just one part of the application that is being developed. The application will have other software subsystems like communication stacks and operating systems/Real-Time Operating System (RTOS) that will run and share system resources with the GUI. These subsystems may need to communicate with the GUI to display system information on the screen or to receive user input.
This chapter provides guidelines and recommendations on best practices for designing such applications with the Microchip Graphics Suite (MGS) Harmony Library.
Main Blocks of a Microchip Graphics Application
In general, the MGS subsystem consists of the blocks shown in Figure 1.
Although the application can directly access the graphics peripherals and hardware through the drivers and peripheral libraries, in most cases, the application will only need to interface with the MGS Harmony Library and middleware to create a full-featured GUI.
To understand how to properly interface with the MGS Harmony Library and middleware, it is important to have a general idea of how the MGS Harmony Library and its supporting middleware work.
Graphics Middleware
The Graphics middleware includes the MGS Harmony Library, Input System Service, and Graphics Canvas. Their respective functions are described in Figure 2.
For most graphics applications, the MGS Harmony Library and Input System Service are used. The Graphics Canvas is for advanced use cases and will be covered in another section.
The MGS Harmony Library and Input System Service run as separate tasks in the application. In an MPLAB® Harmony or bare-metal graphics application, the Legato and Input System Service task routines are called inside the MGS Harmony system task.
For RTOS-based applications, these will run as RTOS tasks with their associated task priorities.
Input System Service
The Input System Service provides a callback service for events such as touch, keyboard, and mouse input to other subsystems. For touch events, the touch controller driver sends touch event and position data to the Input System Service. In turn, the Input System Service will call the registered event handlers for all the event listeners.
Any subsystem, like the MGS Harmony Library, that is registered as listener to the Input System events will receive a callback on these input events.
MGS Library Task
In Graphical User Interfaces (GUIs), a screen represents a frame and contains widgets. Widgets enable users to perform specific functions, view information or access a service through the UI. Examples of widgets are buttons, labels, and images.
The MGS Harmony Task (aka Legato_Task()) manages the states of the screens and widgets, and renders (draws) the screen on the graphics buffers.
Figure 4 shows a high-level diagram of where these operations happen in the actual source code.
In its initialization state, the MGS Harmony Task (1) registers as a listener to the Input System Service. This enables the MGS Harmony Library to receive touchscreen input.
The MGS Harmony Task will then switch to its main ‘running’ state, where it will (2) manage screen transitions, (3) process touch events where any widget that is a focus for the touch input is updated, (4) update the screen and widget states and (5) render the frame based on the screen and widget states. Any screen or widgets marked as ‘dirty’ will be redrawn on the graphics buffers.
Interfacing with the Input System Service
The Input System Service lets other tasks register as listeners to input events. The MGS Harmony Task is one such task that listens to input events through the Input System Service.
There are cases where the user application code may need to register as a listener for input events. One example is monitoring touchscreen activity to determine if the system has been idle for a certain period and may need to go to low power mode. Another example is detecting and processing touch gestures at the application level, like swiping to change screens.
To register as a listener, the SYS_INP_AddListener() API must be called together with the SYS_INP_InputListener parameter. The SYS_INP_InputListener must contain the handler functions for the specific input events that the listener is interested in.
Here’s an example of how the MGS Harmony Task registers as a listener for touch-down, up, and move events:
- First, the SYS_INP_InputListener parameter is declared as a global variable.
- Then it is initialized by assigning handler functions for each event that the task needs to listen to.
inputListener.handleTouchUp = &touchUpHandler;
inputListener.handleTouchMove = &touchMoveHandler;
- The handler functions are defined so that the relevant event is sent to the MGS Harmony Task event queue.
{
leInput_InjectTouchDown(evt->index, evt->x, evt->y);
}
{
leInput_InjectTouchUp(evt->index, evt->x, evt->y);
}
{
leInput_InjectTouchMoved(evt->index, evt->x, evt->y);
}
- Then the SYS_INP_AddListener function is called to register the event handlers.
For MPLAB® Harmony 3 projects, refer to a le_gen_harmony.c generated source file for an example.
Interfacing with the MGS Harmony Task
The MGS Harmony Task provides an interface for the application through screen and widget event callbacks. These callbacks allow application code to be executed within the MGS Harmony Task context.
Since these event callbacks are called within the MGS Harmony Task context, these callback functions must be non-blocking. Otherwise, graphics performance will suffer. Do not perform blocking or high-latency function calls inside these event callbacks.
If a widget or screen event must trigger a blocking or high-latency operation like an IO transfer, these operations must be performed in a separate task where these latencies and delays can be managed properly.
The event callback function can then be defined so that it just sets a status flag or sends a message to an application task that will perform the blocking or high-latency call.
Widget Event Callbacks
Widget event callbacks allow applications to receive events when a user interacts with the widget on the screen. For example, if the application needs to start a timer when a button widget is pressed, then the OnPressed() event for that specific button must be checked in MGS Harmony Composer.
Then the event_<widgetname>_OnPressed() function must be defined in the application source code.
{
//Start timer
APP_Start_Timer();
}
Screen Event Callbacks
Screen Events are generated when a screen is shown, hidden, or updated.
To enable screen events, the events property Screen Object Editor in the MGS Harmony Composer must be checked and then the code must be regenerated.
Then, the screen callback function must be defined in the application code.
Screen OnShow Event
The screen OnShow event is triggered when a new screen is created, right before the screen is drawn to the frame buffer. Having an application callback for this event is useful for pre-configuring widgets on a screen right before the screen is drawn on the frame buffer.
For example, if a label in the screen needs to show the current timer value when the screen is shown, then the OnShow callback function is the right place to call the Label widget APIs to configure that label.
Screen OnHide Event
The screen OnHide event is triggered when the current screen is about to be hidden. This happens right before the widget objects are destroyed and their memory allocations are freed. Having an application callback for this event is useful for saving the states of widgets before the screen is hidden or to free up any memory allocations in the Screen_OnShow callback and prevent memory leaks.
For example, if a toggle button on a screen is pressed, it would be good to save its pressed state to an application variable in the OnHide callback function. Then that application variable can be used to restore the pressed state of the button by calling the button widget’s setPressed() API in the screen’s OnShow event callback function.
Screen OnUpdate Event
The Screen_OnUpdate event for the currently active screen is periodically triggered at every execution cycle of the MGS Harmony Task. The OnUpdate() callback function is the recommended place where system-level events and status that affect the UI can be monitored and where MGS Harmony Graphics screen and widget states can be called to update the UI.
For example, if the UI needs to show a label with the current value of a counter that is constantly running, then that counter value can be polled in the OnUpdate() event callback function, and the label is updated if the counter value has changed.
Another example is if the UI needs to show an image or icon if data has been received from a communications interface like Universal Serial Bus (USB) or Wi-Fi®. In this case, a separate task that monitors the packets can set a flag that the OnUpdate() event callback function can check and then call the Image Widget API to show the image.
The above examples are recommended in pure bare-metal systems where tasks are executed round-robin in a main super loop function.
In the case of RTOS-based systems, where tasks can be pre-empted or executed at different priorities, message queues or other task synchronization functions can be used to pass data between the application task and the MGS Harmony Task. What is important is that these functions do not block the MGS Harmony Task.
Figure 13 shows an example of how a message queue can be used to send a message from an application task to the MGS Harmony Task to update the UI.
This approach offers several benefits:
- Ensures that screens and widgets are properly initialized and allocated before the application code is called. This avoids cases where the application will call a widget API before the widget is initialized, which will have an unpredictable result and can cause a code exception.
- Application code updates to widget states are atomic. This ensures that application-specific widget configurations are completed before the widget is rendered on the screen.
- Note that the MGS Harmony graphics APIs are not re-entrant nor thread-safe. So, for RTOS and multi-thread applications, using screen callbacks provides some level of thread safety since all MGS Harmony graphics API calls are performed inside the MGS Harmony Task.