element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • About Us
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Embedded and Microcontrollers
  • Technologies
  • More
Embedded and Microcontrollers
Blog STM32H7B3I - TouchGFX Application Framework: Model, View, Presentation, Message Queue
  • Blog
  • Forum
  • Documents
  • Quiz
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Embedded and Microcontrollers to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 10 Aug 2020 11:37 AM Date Created
  • Views 4392 views
  • Likes 1 like
  • Comments 5 comments
  • stm32h7b3i
  • rt
  • stm32h7b3i-dk
Related
Recommended

STM32H7B3I - TouchGFX Application Framework: Model, View, Presentation, Message Queue

Jan Cumps
Jan Cumps
10 Aug 2020

I'm selected for the STM32H7B3I-DK -  DISCOVERY KIT road test.

I'm working on a touch screen GUI for my electronic load.

 

image

 

In this part of the review, I explore the application layer:

  • request data from the business layer
  • display that data
  • use the Model-View-Presentation style paradigm, where the display logic only deals with display matters
  • use a freeRTOS message queue to send business layer data to the TouchGFX application

 

image

image source: ST TouchGFX documentation

 

The business layer in my example is a SCPI client.

The TouchGFX app has no knowledge of the fact that the SCPI client uses UART to talk to the my electronic load.

Ah, and it's in C++.

 

I made compromises in my example:

  • I use a message queue to inform the model that new data arrived. But the other way around, I just do plain method calls.
    This could (should) also be implemented by a queue. But by using it in one direction is proof that it works.
  • Although I use a message queue to inform the presentation layer that SCPI data arrived, I then directly talk to the business layer to retrieve the data buffer.
    In theory this 2nd part should either be done in the Model layer, or the buffer (or a pointer to it) should be part of the message.
    Since there is a single buffer that's always used, I went for a simpler way of directly fetching the buffer data. Discuss.
  • I used the compatibility layer for message queues freeRTOS V1 style. Because I used the logic of ST example: http://sw-center.st.com/touchgfx/TouchGFX/knowledgebase/c_task_condenser_example.4.9.3.zip.
    This is a very clean Model - View - Presentation example. If you want to see an implementation that doesn't make compromises, check that one.

If you want to read up on the exchange with the business system, check this breakout of ST on backend communication.

 

Graphic Design

 

A simple design: a button and a textbox. When I click the button, I want to ask the load for its SCPI identification.

When that ID arrives, I want that the textbox displays it.

If I click the button again, I want the textbox cleared.

 

image

 

The button needs to have an event handler. I am not explaining that here because done in the previous posts.

For later reference, the function called when the button is clicked is fnClickID(). The implementation is discussed in the code part of this article.

And I'll attach the code.

 

The text area is a bit more involved. To be able to change the text, we need to define a wildcard.

image

This will create a 40 character buffer that we can write into. We'll do that in our View code, then ask the screen to redraw that part.

 

The Model Layer

 

Our model class can send a command to the business layer, and inform the Presentation when it receives info from that business layer.

 

extern "C"
{
xQueueHandle gui_msg_q;
}

Model::Model() : modelListener(0)
{
    gui_msg_q = xQueueGenericCreate(1, 1, 0);
}

void Model::tick()
{
    // Check for messages from backend, with zero timeout
    uint8_t msg = 0;
    if (xQueueReceive(gui_msg_q, &msg, 0) == pdTRUE)
    {
        if (msg == SCPI_IDN)
        {
            // Notify current presenter that IDN is received,and hand over payload
            modelListener->scpi_idn((const char*)ScpiMgr::getReply());
        }
    }
}

void Model::request_scpi_idn() {
  ScpiMgr::idn();
}

 

The tick() is a standard feature. The TouchGFX loop keeps the model alive by making it tick every so often.

In that method, we chack if data is received from the business layer (SCPI).

If yes, we check what type of message, and then inform the presentation layer of that.

At this moment, the only thing the model (and other classes) understand is the reply on a *IDN? command.

 

The other way around, asking the business layer to send a SCPI command in request_scpi_idn(), is - as I discussed in the compromises section - done by directly calling that layer. Discuss.

 

The Presentation Layer

 

When the LCD asks for info, or when the Model receives info, this one will take care of the interaction.

 

void scrHomePresenter::scpi_idn(const char* data) {
  view.scpi_idn(data);
}

void scrHomePresenter::request_scpi_idn() {
  model-> request_scpi_idn();
}

 

Not hard to follow. The first one is called by the Model when a *IDN? reply arrives in the queue.

It calls the view, with a reference to the SCPI payload.

 

The second one is called by the view when it wants *IDN? info (read: when the button is clicked).

 

The View Layer

 

This one can detect the button click, and can refresh the text area when new data arrives.

 

void scrHomeView::scpi_idn(const char* data) {
  Unicode::strncpy(txtIDNBuffer, data, TXTIDN_SIZE);
  txtIDN.invalidate();
}

void scrHomeView::fnClickID() {
  static bool bClear = false;
  if (bClear) {
    Unicode::strncpy(txtIDNBuffer, (const char*)" ", TXTIDN_SIZE);
    txtIDN.invalidate();
  } else {
    presenter->request_scpi_idn();
  }
  bClear = !bClear;
}

 

The first function is the one that knows how to display the *IDN? payload.

It copies the payload in the text buffer (+ translate from ascii to Unicode), and requests a redraw of the text area only.

 

The second function is the handler for the button click.

In essence, it just asks the Presentation layer to go fetch *IDN? info from the business layer.

To make the application a bit more interesting, it has an additional functionality: on a second click, it wipes out the text area content.

 

The Business Layer

 

This is the SCPI engine. I'll only briefly discuss it here, because it's not TouchGFX relevant. But you'll recognise that it sends messages to the queue when an answer arrives from the instrument.

 

class ScpiMgr {
public:
  static void commsFinished(uint16_t lenght);
  static void idn();
  static uint8_t * getReply();
private:
  static uint8_t _scpi_message;
};

 

extern UART_HandleTypeDef *huart;
extern uint8_t uBuffer[64];

extern "C"
{
extern xQueueHandle gui_msg_q;
}

extern "C" void commsFinished(uint16_t lenght) {
  ScpiMgr::commsFinished(lenght);
}

uint8_t ScpiMgr::_scpi_message;

void ScpiMgr::commsFinished(uint16_t lenght) {
  xQueueSend(gui_msg_q, &_scpi_message, 0);
}

void ScpiMgr::idn() {
  _scpi_message = SCPI_IDN;
  uint8_t str[] = "*IDN?\n";
  HAL_UART_Transmit(huart, str, sizeof(str), 100);
}

uint8_t * ScpiMgr::getReply() {
  return uBuffer;
}

 

The extern "C" function is there as a bridge to the C and C++ world. The Mx Generator creates a plain C main.c.

I use this function to call the C++ SCPI manager from that C code.

Because this class is full static (balearicdynamics, I use this instead of a Singleton), I'll show the header and code:

 

commsFinished() is called by the UART handler (interrupt driven, see previous articles)  when it received a full SCPI message.

This then calls ScpiMgr::commsFinished(). And that posts our message to the TouchGFX queue.

 

ScpiMgr::idn() is the api function that allows to send a SCPI message. It just uses the ST HAL layer to send the payload over UART.

As mentioned a few times before, this should better be implemented with a queue, similar to how we talk from TouchGFX to the SCPI manager. Discuss.

 

ScpiMgr::getReply() is the api function to get at the SCPI payload buffer. Used by the Model to retrieve that data.

 

To make the picture completer, here's how I detect the end of a UART payload arriving (in the TaskUARTListen() freeRTOS task that deals with serial comms):

 

  /* Infinite loop */
  for(;;)
  {
    HAL_UART_Receive_IT(huart, in, 1);
    uint32_t ulNotificationValue = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );

    if( ulNotificationValue == 1 )
    {
      /* The transmission ended as expected. */
      uBuffer[uCnt++] = in[0];
      if (in[0] == '\n') { // end of communication
        commsFinished(uCnt);
        uCnt = 0;
      }
    }
    else
    {
      /* The call to ulTaskNotifyTake() timed out. */
    }

 

I know that the last character the electronic load sends on a data request, is a '\n'.

When I detect that, I call the SCPI manager to notify it that the payload arrived.

 

Here's a capture of an exchange.

image

 

SCPI doesn't always reply on a message. Only on queries. For this design it doesn't matter because request and response are separate mechanisms.

What would be unexpected is a response without sending a request first. But that should not happen.

 

Related Posts
First Experience with CubeIDE
Create a TouchGFX Project with support for the Designer, CubeIDE and Debugger - Pt 1: Screen Works
Create a TouchGFX Project with support for the Designer, CubeIDE and Debugger - Pt 2: Touch Works
TouchGFX Simple Example: react on a button click
USB, freeRTOS ,Task Notifications and Interrupts
the Development Kit STMod+ Connector and Using UART2
TouchGFX Application Framework: Model, View, Presentation, Message Queue
TouchGFX Application Framework: A Mock GUI - Show Statuses, Switch Screens
TouchGFX Application Framework: MVP and the ModelListener
Write a CubeIDE MX application: Hardware Cryptography with DMA
Attachments:
STM32H7B3I-DK-TGFX-eload_20200810.zip
  • Sign in to reply

Top Comments

  • Jan Cumps
    Jan Cumps over 5 years ago +2
    The SCPI ID for the load mentiones The Breadboard. That's a tribute to our Robert Peter Oakes , the father of the electronic load: www.youtube.com/watch If you reached this blog series while searching…
  • DAB
    DAB over 5 years ago +2
    Nice update Jan. DAB
  • balearicdynamics
    balearicdynamics over 5 years ago +2
    Nice update. Enrico
  • Jan Cumps
    Jan Cumps over 5 years ago

    Simulator info:

     

    The TouchGFX Designer has a simulator. It's an essential part of the design flow.

    When you've built a part of the GUI, you can simulate the look-and-feel on your PC.

    To do that, the simulator actually compiles your TouchGFX code and then executes it. It's more than just simulating - it's e real run but on a virtual display.

     

    Now, when you add calls to the backend systems, things may become more complex. Because the TouchGFX designer doesn't have the knowledge of all dependencies you need in your project.

    They have provided a solution that's as elegant and simple as you can get: a define.

    #ifndef SIMULATOR
    ....
    #endif

     

     

    If you place all logic that's outside the graphical biosphere, but that's natural part of the model, view or presentation logic, between these defines, the simulator will compile and run just fine.

    How impacting this is actually depends on how well you adhere to the Model - View - Presenter pattern. If you do it right, you only need this in your model.

     

    In my case that was the case:

    #include <gui/model/Model.hpp>
    #include <gui/model/ModelListener.hpp>
    
    #ifndef SIMULATOR
    #include "FreeRTOS.h"
    #include "queue.h"
    #include "scpi_msg.h"
    #include "ScpiMgr.h"
    extern "C"
    {
    xQueueHandle gui_msg_q;
    }
    
    #endif
    
    Model::Model() : modelListener(0)
    {
    #ifndef SIMULATOR
        gui_msg_q = xQueueGenericCreate(1, 1, 0);
    #endif
    }
    
    void Model::tick()
    {
    #ifndef SIMULATOR
        // Check for messages from backend, with zero timeout
        uint8_t msg = 0;
        if (xQueueReceive(gui_msg_q, &msg, 0) == pdTRUE)
        {
            if (msg == SCPI_IDN)
            {
                // Notify current presenter that IDN is received,and hand over payload
                modelListener->scpi_idn((const char*)ScpiMgr::getReply());
            }
        }
    #endif
    }
    
    void Model::request_scpi_idn() {
    #ifndef SIMULATOR
      ScpiMgr::idn();
    #endif
    }

    • Cancel
    • Vote Up 0 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • balearicdynamics
    balearicdynamics over 5 years ago

    Nice update.

     

    Enrico

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • DAB
    DAB over 5 years ago

    Nice update Jan.

     

    DAB

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 5 years ago

    The SCPI ID for the load mentiones The Breadboard. That's a tribute to our Robert Peter Oakes , the father of the electronic load:

     

    You don't have permission to edit metadata of this video.
    Edit media
    x
    image
    Upload Preview
    image

     

    If you reached this blog series while searching for STMicro TouchGFX tips and tricks: go view the video above to check out the genesis of hardware that's being controlled.

    • Cancel
    • Vote Up +2 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • balearicdynamics
    balearicdynamics over 5 years ago

    Because this class is full static (balearicdynamics, I use this instead of a Singleton)

     

    Well done mate!

    Enrico

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube