Designing an Application with Microchip Graphics Suite (MGS)

Last modified by Microchip on 2024/09/26 17:05

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.

Main Blocks of a Microchip Graphics Application

Figure 1: Application Code

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.

Back to Top

 

Graphics Middleware

The Graphics middleware includes the MGS Harmony Library, Input System Service, and Graphics Canvas. Their respective functions are described in Figure 2.

Graphics Middleware Diagram

Figure 2: Graphics Middleware

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.

MGS Task and Input System Service Task in main task function

Figure 3: MGS Task and Input System Service Task

For RTOS-based applications, these will run as RTOS tasks with their associated task priorities.

Back to Top

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.

Back to Top

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.

MGS Task Flow

Figure 4: MGS Harmony Task Flow

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.

Back to Top

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.
SYS_INP_InputListener inputListener;
  • Then it is initialized by assigning handler functions for each event that the task needs to listen to.
inputListener.handleTouchDown = &touchDownHandler;
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.
void touchDownHandler(const SYS_INP_TouchStateEvent* const evt)
{
    leInput_InjectTouchDown(evt->index, evt->x, evt->y);
}
void touchUpHandler(const SYS_INP_TouchStateEvent* const evt)
{
    leInput_InjectTouchUp(evt->index, evt->x, evt->y);
}
void touchMoveHandler(const SYS_INP_TouchMoveEvent* const evt)
{
    leInput_InjectTouchMoved(evt->index, evt->x, evt->y);
}
  • Then the SYS_INP_AddListener function is called to register the event handlers.
SYS_INP_AddListener(&inputListener);

For MPLAB® Harmony 3 projects, refer to a le_gen_harmony.c generated source file for an example.

Back to Top

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.

Interfacing with the MGS Harmony Task from Application Task

Figure 5: Interfacing with the MGS Harmony Task from Application Task

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.

Interfacing with MGS Harmony Task with high-latency or blocking operations

Figure 6: Interfacing with MGS Harmony Task with High-Latency or Blocking Operations

Back to Top

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.

Widget Events Property

Figure 7: Widget Events Property

Then the event_<widgetname>_OnPressed() function must be defined in the application source code.

void event_CkrScrn2_ButtonWidget6_OnPressed(leButtonWidget* btn)
{
   //Start timer
   APP_Start_Timer();
}

Back to Top

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.

Screen Events Property

Figure 8: Screen Events Property

Then, the screen callback function must be defined in the application code.

It is recommended that the application code is defined in separate source files for each screen.

Back to Top

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.

Example of how to interface with MGS Harmony Task using On Show Screen Event Callback

Figure 9: Interface with MGS Harmony Task using On Show Screen Event Callback

Back to Top

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.

Example of how to interface with MGS Harmony Task using On Hide Screen Event Callback

Figure 10:  Interface with MGS Harmony Task using On Hide Screen Event Callback

Back to Top

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.

Example of how to interface with MGS Harmony Task using On Update Screen Event Callback

Figure 11: Interface with MGS Harmony Task using On Update Screen Event Callback

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.

Example of how to interface with MGS Harmony Task using On Update Screen Event Callback

Figure 12:  Interface with MGS Harmony Task using On Update Screen Event Callback

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.

Example of how to interface with MGS Harmony Task using On Update Screen Event Callback with Messages

Figure 13: Interface with MGS Harmony Task using On Update Screen Event Callback with Messages

The above examples show how event callbacks can be used as the interface between the MGS Harmony Library and the application-level tasks or other subsystems. Since the callbacks enable the application to specify the appropriate inter-task communication mechanism for the system (whether it’s bare-metal, RTOS or others), we strongly recommend that all MGS Harmony graphics API calls, including widget and screen APIs, be performed inside these event callbacks. MGS Harmony graphics API calls from outside the event callback functions must be avoided as much as possible.

This approach offers several benefits:

  1. 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.
  2. 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.
  3. 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.

Back to Top