This is the second article in the beginner’s guide series and it showcases an example application developed in MATLAB® Simulink® for the MR-CANHUBK344 evaluation board. The application illustrates the ease of utilizing UART capability through NXP®'s Model-Based Design Toolbox.
For more details on MR-CANHUBK344 and how to do the initial setup (Simulink environment, J-Link debugger, etc.) please refer to article 1.
The focus in this chapter will be to provide a detailed guide on how to configure the UART (Universal Asynchronous Receiver-Transmitter) peripheral, by covering all the necessary steps such as configuring an UART instance and its corresponding pins for data transmission and enabling the peripheral clock and interrupts.
Configuration of the MCU peripherals, the clock and pins direction, can be performed using S32 Configuration Tool (S32CT) which is proprietary to NXP. Please be advised that exactly the same microcontroller configuration can be achieved using Elektrobit Tresos Studio (EB Tresos).
Looking at the Schematic of the evaluation board (MR-CANHUBK344-SCHematic), we can see that the LPUART2 peripheral can be routed through the debug interface:
This is very convenient since the kit includes a DCD-LZ Programming Adapter, a small board that combines the SWD (Serial Wire Debug) and the Console UART into a single connector.
For configuring the LPUART2 peripheral pins, we must open the configuration project (please check article 1 for more information on this process) and access the Pins Tool (top right chip icon).
While in this screen, from the Peripherals Signals tab, we can route the lpuat2_rx to the PTA8 pin and lpuart2_tx to the PTA9 pin:
In this subchapter, we dive into configuring the UART peripheral, component that allows the serial communication. We will also explore the various settings and parameters that enable efficient data transmission and reception.
First, the LPUART_2 instance must be assigned to UartChannel_0 of MCAL AUTOSAR module by doing the following settings in the UARTGlobalConfig tab, which can be opened also from the Components tab:
UART asynchronous method is set to work using interrupts as opposed to DMA. This method dictates how the mechanism for the functions AsyncSend and AsyncReceive works. More will be discussed in chapter 3.
Other settings here include: Desire Baudrate (115200 bps), Uart Parity Type, Uart Stop Bit Number, etc. . These are important as they will have to be mirrored later in the PC terminal application.
Afterwards, go to GeneralConfiguration and please note that the interrupt callback has the name MBDT_Uart_Callback, this is already configured in the default S32K344-Q172 project:
MBDT_Uart_Callback is the name of the user defined callback which will have its implementation designed in the Simulink application. It will be called whenever there is an UART event: RX_FULL, TX_EMPTY, END_TRANSFER or ERROR.
We can give any name to this callback, but since it will also be later used in the Simulink model implementation, it would be easier to keep the same nomenclature, at least for the purpose of this example.
In this subchapter we enable the clock of the LPUART2 instance, in the Mcu component:
In the newly opened Mcu tab, go to McuModuleConfiguration then McuModeSettingConf and then to McuPeripheral:
Then enable the clock for the LPUART_2 peripheral:
UART is an asynchronous data transmission method, meaning that the sender and receiver don't share a common clock signal. Instead, they rely on predefined data rates (baud rates) to time the transmission and reception of bits. The clock, in this context, is used to establish the bit rate and ensure that both the sender and receiver are operating at the same speed. This synchronization enables successful data transmission and reception, preventing data loss or corruption.
In UART, the transmitting device sends data bits at regular intervals based on the clock rate, and the receiving device uses its own clock to sample and interpret these bits accurately. This asynchronous nature makes UART suitable for various applications, allowing data to be transmitted reliably even when devices have slightly different clock frequencies.
In this subchapter, we will illustrate how to enable the UART interrupts.
To find the corresponding settings we need to access the Platform component, afterwards we go to Interrupt Controller and then enable the UART interrupts:
The interrupt controller from the Platform component configures the microcontroller interrupts vector and the handler there is the one declared inside RTD (Real-Time Drivers), implemented also inside RTD. It means that the LPUART_UART_IP_2_IRQHandler is already defined and it is not recommended to change its name. We are just pointing the interrupts vector to use it.
In this chapter we will do the implementation for a simple Simulink model that uses the above configuration of the microcontroller to send a message via UART when the processor initially starts, and then echo back the characters that we type in a serial terminal.
For implementing our application we are going to create a Simulink model, where we can drag and drop the UART block from the Simulink library to implement the logic of our application.
The UART block can be found in the Simulink Library under the NXP Model-Based Design Toolbox for S32K3xx MCUs.
The UART block can be found under S32K3xx Core, System, Peripherals and Utilities in CDD Blocks:
After adding it to the Simulink canvas we can double click on it to access the block settings:
Here the desired function can be selected: GetVersionInfo, SyncSend, AsyncSend, GetStatus etc.
Some useful information can be found below, regarding the functions that will be later used in this example, as an addition to what the Help button already provides.
Uart_SyncSend is used for synchronous communications between the target and the UART terminal as it is checking the status of the previous transfers before proceeding with a new one (not to be confused with a synchronous serial communication, there is no separate clock line involved). This method of transferring data bytes ensures that the transmission buffers are free while it is blocking the main thread of execution until the corresponding transmit register empty flag is cleared.
Uart_AsyncSend function, as a method of transferring data, is called asynchronous because data can be transmitted at any time without blocking the main thread of execution. It is recommended to be used in conjunction with transfer interrupts handlers to avoid errors.
Uart_AsyncReceive is the function used to get the input data. Its output, Data Rx, is used to specify the location where the received characters will be stored. By placing this block in the initialize subsystem and in the interrupt callback, as we are about to see in the following chapter, we make sure that each character received will be stored and also that the receive interrupt is ready for the next event.
Also, for UART, the Hardware Interrupt Callback block can be added from ISR Blocks:
Here, the previously configured Interrupt handler (MBDT_Uart_Callback, see chapter 2) must be selected:
The Hardware Interrupt Handler Block is used to display all the user defined callbacks that can be configured in S32CT, allowing their implementation in the Simulink model. In case of UART, the MBDT_Uart_Callback will be present in this block, to allow the implementation of specific actions when an interrupt occurs on the configured LPUART instance. If we would have modified the name of the receive callback in S32CT, after updating the generated code, we would be able to see the change in the Simulink block by pressing the Refresh button.
Here’s how the overall picture of the implementation looks like on the Simulink canvas:
In the Variables section we can see a list of DataStoreMemory blocks which act as memory containers similar with the global variables in C code.
The Initialize block is a special Simulink function in which the implementation that we only want to be executed once, at startup, can be added.
Inside this function block the variable transfer_flag is initialized with value 1, marking that the next event will be to receive a byte.
Uart_AsyncReceive block sets a new buffer to be used in the interrupt routine where the character sent from the keyboard is stored. This function doesn’t actually read the character but only points to the memory location where the characters will be stored after reading it.
Uart_SyncSend function will output the string of characters: “Hello, MR-CANHUBK3 here! Please write a message and I will echo back the characters as you type them”, framed by NL and CR characters.
In UART Actions we have the Hardware Interrupt Handler Block that calls UartCallback at each transfer event, but we use the Event line to filter out all events except for END_TRANSFER.
Now let’s see what’s inside the If Action Subsystem block:
When a character is sent from the PC terminal and received in our application, an End Transfer event occurs (with receive direction) and the Send block is the executed path (because transfer_flag is equal to 1). This, in turn, will call the function Uart_AsyncSend to load the transmit buffer with that same byte that was received. Also the variable transfer_flag is changed from 1 to 2.
When the transmit buffer was successfully emptied, meaning that a character was sent to the PC terminal, an End Transfer event occurs (with transmit direction) and the Receive block is the executed path (because transfer_flag is now equal to 2). This, in turn, will call the function Uart_AsyncReceive to reset the receive buffer making it ready for the next receive event. Also the variable transfer_flag is changed from 2 to 1.
The Uart_GetStatus function block can be used to store the number of remaining bytes and the transfer status if further development of this example is desired.
The complete application together with the executable files can be found in the first attachment of this article (Article 2 - mrcanhubk344_uart_s32ct).
In this chapter we discuss the details of building, deploying, and testing the UART-based application. Our focus will be on the testing phase, creating an effective testing setup and ensuring that each element of the application performs correctly.
First of all please make sure that the hardware setup with all the wires connected looks like this:
Beside the hardware connections that are already mentioned in setup chapter from article 1, a USB to Serial converter device needs to be connected between the USB port of the PC and the DCD-LZ adapter that comes with the evaluation board. The DCD-LZ adapter is then connected to the evaluation board via the P6 debug port.
The J-Link debugger can be connected directly to the evaluation board or to the DCD-LZ adapter via the P26 JTAG port.
Once the hardware setup is complete, we can continue with the project build step.
Pressing the Build button in the Embedded Coder® app in Simulink, will generate the corresponding C code from the model. The code is then compiled and the executable file is created and deployed on the target (MR-CANHUBK3 evaluation board) using J-Link JTAG.
As previously mentioned, a Terminal emulator program needs to be installed and configured on your computer and an USB to Serial converter needs to be connected between the computer and the target, as illustrated in the above picture.
Probably the simplest choice for the Terminal would be PuTTY, which needs to be installed and then configured as follows:
We can see now that the UART settings from chapter 2.3 are mirrored here.
What port the USB-Serial converter uses can be found by looking it up in Device Manager, under the Ports tab.
Here’s what will appear in the terminal once the application is deployed and running on the board:
As a first part of the application’s functionality, after deployment, when the processor initially starts, a welcome message is sent: Hello, MR-CANHUBK3 here! Please write a message and I will echo back the characters as you type them.
In the second part of the functionality, after the initialization phase, the UART terminal automatically transmits ASCII bytes corresponding to whatever is typed in the terminal window. If everything works correctly you will be able to see, being sent back like an echo, the characters that were just typed. In this case: 13780 -> Each typed in character is echoed back!
In this chapter we will discuss about the NXP proprietary FreeMASTER tool and how it can be integrated with Model Based Design Toolbox applications. We will build a second Simulink model to demonstrate its capabilities.
FreeMASTER is a user-friendly real-time debug monitor and data visualization tool that enables runtime
configuration and tuning of embedded software applications. It supports non-intrusive monitoring of variables on a running system and can display multiple variables on oscilloscope-like form or as data in text form. You can download and find out more about it on the NXP website.
The FreeMaster blocks can be found under S32K3xx Core, System, Peripherals and Utilities in Utility Blocks:
FreeMASTER Config block allows the user to configure the FreeMASTER embedded-side software driver, which implements the serial interface between the application and the host PC. It actually inserts the service in the application, and it is the only one mandatory to be added to the Simulink canvas in order to have the FreeMASTER functionality available.
FreeMASTER Recorder block is optional and allows the user to call the Recorder function periodically, in places where the data recording should occur, in our case in the main step function.
For this example the only configuration that is needed, is to select the appropriate UART instance, in our case LPUART2, and set the Baudrate to 115200 bps:
It is important to mention that the UART instance that is used by the FreeMASTER toolbox cannot be properly used for other communication purposes. The reason for this is that, during initialization, the configuration for the transfer interrupt callbacks as well as the Tx and Rx buffers are changed definitively to be controlled by FreeMASTER.
If the above-mentioned blocks would be added in the previously described Simulink model, then only the welcome message would appear in the terminal at initialization phase (after powerup or MCU reset).
On the other hand, echoing back the characters that are typed in the terminal window would no longer work. This is not an issue since the terminal can no longer be used anyway. That is because the COM port of the PC is used by the FreeMASTER application, which would prevent any other app from accessing it.
For these reasons a new project needs to be created in Simulink for the FreeMASTER example application, but the UART configuration created in chapter 2 can definitely be reused.
Similar to the first part of the functionality that was described in chapter 4, FreeMASTER communication protocol is synchronous, using an implementation that resembles the one for the SyncSend function. The execution is blocking the Step Function (I.e., the main execution thread) for as long as it takes to free the transfer buffer, which normally happens instantly unless there is an error (like a broken physical wire). The flags that signal whether the transmission or reception registers are empty or full, respectively, are checked in a do-while loop in interrupts, in case of Long Interrupt Mode (See Mode setting in the FreeMaster configuration tab).
To better understand how FreeMASTER works and how it can help development, a dummy variable called counter was created which does nothing more than just store the incrementing value coming from the Counter Limited Simulink block. For the purpose of this example the limit of the block was set to 200, meaning that the counter will reset when the value is reached.
It is important to make sure that the compiler will not optimize the code in such a way that this variable could be renamed. If the variable is renamed it is difficult to be found in the associated FreeMaster project which will be described in the following section. Compiler optimizations on certain variables can be avoided by setting their Storage Class to Volatile or Exported Global as shown below:
As previously mentioned, what we need to add to the Simulink model are the two FreeMaster blocks Config and Recorder. Here’s a picture with the overall view of the working canvas:
Once the FreeMASTER blocks are added in the Simulink model, we can proceed with similar actions to the ones from chapter 4: press the Build button in the Embedded Coder app to generate the corresponding C code from the model, this code is then compiled, the s32k3xx_uart_fm_s32ct.elf file is created and deployed on the target (MR-CANHUBK3 evaluation board) using J-Link JTAG.
The complete application together with the executable files can be found in the second attachment of this article (Article 2 - mrcanhubk344_fm_s32ct).
Up until now, all that we discussed about FreeMASTER was related to the board side of the whole project: UART configuration, implementation of the Simulink model, hardware connections.
In what follows we will do the setup for the FreeMASTER application on a Windows PC. For this, we need to install and launch FreeMASTER 3.2 or a later version (as mentioned in chapter 5 , you can download it from the NXP website)
We now need to configure the hardware connection that is used for communicating with the board.
Under Project – Options… go to Comm tab and choose the corresponding port (as mentioned in chapter 4, you can find out what port your USB-Serial converter uses by looking it up inside Device Manager, under the Ports tab or leave the Port value as COM_ALL for automatic port finding):
In order to identify the variables that we want to watch, we need to point to the location where the .elf file is stored.
Go to MAP Files tab and choose …\mrcanhubk344_fm_s32ct\mrcanhubk344_fm_s32ct.elf as Default symbol file:
We need to create a Variable watch for the counter.
For this, simply expand the drop-down list and begin typing the initial letters of the variable’s name:
If the update rate of the value is not fast enough, the Sampling period can be decreased:
OK, now we have the variable but we need to track its value evolution over time. We could use an oscilloscope for this.
Create New Oscilloscope by right clicking on counter in the Watch window:
At this point you can press Start communication (the green GO! button). Let it run for a few seconds in order to have it looking like this:
FreeMASTER Recorder can be added to the window similarly to the method previously described.
Press Start communication (the green GO! Button). The Run/Stop buttons can be pressed at the desired moment for starting or respectively stopping the recording of the specified variable. Time triggers can also be used to replace the button presses.
The Simulink implementation can be updated at any point in time as needed. If the two FreeMASTER blocks are active then you should be able to add: multiple variables with the keyword volatile in front (in C code, if you wish to continue working with the generated code) or multiple DataStoreMemory blocks with Volatile Storage Class in Simulink. Then Build the model as usual to be able to monitor the newly added variables in the PC app.
After build and deploy are completed, when the FreeMASTER window regains focus on the screen, please make sure to click Yes. This means that the newly created .elf file was automatically detected and the list of symbols needs to be resynchronized:
This streamlined approach guarantees efficient variable tracking and management, elevating the debugging experience and the quality of model-based design.
The integration of Simulink UART and FreeMASTER blocks in model-based design offers an effective solution for developing and testing embedded systems. The Simulink UART block facilitates communication with external devices using UART protocols, enabling seamless data exchange. Meanwhile, the FreeMASTER tool enhances monitoring and control by providing real-time visualization of variables and parameters. Together, these tools streamline the development process, allowing for efficient testing, debugging, and optimization of embedded systems, ultimately leading to more reliable and robust products.
NXP is a trademark of NXP B.V. All other product or service names are the property of their respective owners. © 2023 NXP B.V. MATLAB, Simulink, and Embedded Coder are registered trademarks of The MathWorks, Inc. See mathworks.com/trademarks for a list of additional trademarks.