So hello guys, welcome back to NXP Semiconductors S32K144 MCU Tutorial series. In the last blog we had started with S32K144 MCU GPIO Peripheral. In this blog we are going to explore the UART Peripheral. Going to Start with UART peripheral. Objective would be to get.
- familiarity with UART peripheral for S32K144 MCU.
- Would be understanding UART peripheral from Hardware point of view in S32K144 MCU.
- Going to understand then how to use UART peripheral via S32K SDK/uart driver.
- Would also be demonstrating the uart_echo_pall sketch.
So read along the blog and do tell me its reviews!
Table of Contents
UART Peripheral Theory
UART is a serial communication protocol which is used to interface external sensor & IoT modules to the Host MCU. IoT modules like: GSM Module, GPS Module, BLE Modules, WiFi Modules are connected to Host MCU via UART protocol. Thus, understanding of UART communication protocol and its driver implementation, plays a crucial role.
To know about UART peripheral in theory, viewers can refer to this blog.
LPUART Peripheral in S32K144 MCU
In S32K144 MCU, UART can be used via 2 peripherals: LPUART & FlexIO.
LPUART is referred as Low Power Universal Asynchronous Receiver/Transmitter. LPUART is onchip peripheral only to do UART communication protocol. UART is a serial communication protocol which is done via UART peripheral in the Microcontrollers.
Also, in S32K144, there is FlexIO peripheral through which on-board serial communication protocols like UART, I2C & SPI can be emulated. So, through FlexIO peripheral, also UART peripheral can be implemented. To know about FlexIO peripheral in S32K144, refer to this blog.
Features of UART via LPUART peripheral in S32K144 MCU:
- LPUART peripheral of S32K144 supports full duplex mode and has NRZ data encoding (That is Logic 1 bit represents High value and Logic 0 bit represents Low value).
- It has programmable Baud rates(
- LPUART peripheral supports Interrupts mode, DMA mode or pooled operation mode for transmitting/receiving the data using LPUART protocol or for detecting below listed errors while LPUART communication session is happening:
- Transmit data register empty and transmission complete.
- Receive data register full.
- Receive overrun, parity error, framing error, and noise error.
- Idle receivers detect.
- Active edge on receive pin.
- Break detect supporting LIN.
- Receive data match.
- S32K144 also supports hardware parity generation and checking to ensure the integrity of the data while LPUART communication is happening.
- It has Programmable LPUART data length of 7-10 bits and LPUART stop bits of 1-2 bits.
- S32K144 MCU has feature to use the LPUART peripheral to wake up the MCU (if it is programmed in sleep modes), it can be waked up in either of the 3 below mentioned events:
- Idle line wakeup
- Address mark wakeup
- Receive data match.
- Support of request to send (RTS) and clear to send (CTS) signals, for hardware flow control, example of hardware flow control is a half-duplex radio modem to computer interface.
- Support of IrDA 1.4 format with programmable pulse width for IR communication.
- LPUART peripheral of S32K144 supports independent FIFO buffer for transmission and receiving of Data.
- Also, LPUART peripheral FIFO of S32K144 has additional features like:
- configurable watermark (lowest number of bytes) for transmit and receive buffer, when watermark is achieved corresponding status is changed of LPUART peripheral.
- Option for receiver to assert request after a configurable number of idle.
characters if receive FIFO is not empty.
Tx and Rx FIFO in S32K144 for all LPUART Instances both are of 4 words size. 1 Word is 1 byte(8 bits). So FiFo size is 4 byte long(32 bits).
LPUART Hardware Pinout in S32K144 MCU
LPUART Pinout and Hardware Instances
LPUART peripheral in S32K144 has 3 instances: LPUART0, LPUART1, LPUART2.
All the LPUART Instances has 4 pins:
- RX: Receiving of data
- TX: Transmission of data
- CTS: Clear to Send pin for hardware flow control.
- RTS: Request to Send for hardware flow control.
Each LPUART instance supports all the 4 pins, with below mentioned pin details.
In LPUART0 there are following number of pins:
- For RX pin (LPUART Function) there are 3 MCU Pins.
- For TX (LPUART Function) there are 3 MCU pins
- For CTS (LPUART Function) there are 2 MCU pins
- For RTS (LPUART Function) there are 2 MCU pins.
In LPUART1 there are following number of pins:
- For RX pin (LPUART Function) there are 3 MCU Pins.
- For TX (LPUART Function) there are 3 MCU pins
- For CTS (LPUART Function) there are 3 MCU pins
- For RTS (LPUART Function) there are 3 MCU pins.
In LPUART2 there are following number of pins:
- For RX pin (LPUART Function) there are 3 MCU Pins.
- For TX (LPUART Function) there are 3 MCU pins
- For CTS (LPUART Function) there are 3 MCU pins
- For RTS (LPUART Function) there are 3 MCU pins.
How to use LPUART Pins in S32K144 MCU for doing UART Communication?
Now for doing UART communication, we need to connect hardware UART pins of the MCU, for doing so follow the steps:
- At first select which instance of LPUART wanna use, once selected. As told above their are 3 instances of LPUART in S32K144 MCU.
- Once selected, then use above excel to short select which pin of corresponding LPUART has to be used.
- Lets say we use LPUART1 and use PTC7 and PTC6 as Tx & Rx pins of LPUART.
- So connect these pins of MCU with external UART Module.
- Remember Tx pin of MCU is connected to Rx pin of external UART Module and Rx pin of MCU is connected to Tx pin of external UART Module.
- Here i am going to use USB to serial FTDI connector with Original S32K144 evaluation2 board.
- With ElecronicsV2 board, it has on-board UART driver IC at LPUART1 PTC7 and PTC6 pins.
How to configure UART Peripheral in S32K144 using S32 Design Studio IDE
UART Peripheral of S32K144 MCU can be configured using the Peripheral configuration tools of S32 Design Studio. These tools provide us with the GUI interface to configure the UART Peripheral of the MCU. To know in detail about the Peripheral configuration of S32 Design Studio refer to these.
Go to perpheral view in S32 Configuration Tool
Then go to component view, where we can see different SDK modules for the correspodning S32 MCU.
Now we have to use LPUART, which is a peripheral of the MCU. So it would be having its driver files to use. So go to the Driver section and click on its + sign.
Here you can see list of all driver files, that are available. So search for lpuart and select it.
LPUART SDK for S32K144 MCU
LPUART SDK
S32K SDK/drivers provide an easy to use and quick way to use LPUART peripheral in S32K144, which is known as LPUART SDK.
Each S32 SDK driver can be configured and enabled to use in the project via S32 Configuration Tool. Will be digging into that part, in next section. For now, let’s understand the LPUART SDK in some detail, so as to use UART peripheral via LPUART.
In the SDK of LPUART there are header files and source files for LPUART Driver and LPUART Interrupt.
- LPUART interrupt files contains functions for using &configuring of LPUART interrupts and IRQ handler in S32K144 MCU.
- LPUART driver files contains functions for using/configuration of LPUART Peripheral.
LPUART Driver
LPUART driver files are further divided into LPUART Peripheral Abstraction Layer (PAL) & LPUART Low-level drivers., as shown below:
- LPUART Peripheral Abstraction Layer(PAL): contains functions and variables that are directly used in main.c or application code. And internally these functions use the LPUART Low-level drivers & LPUART IRQ . So if hardware is changed LPUART PAL would remain same and only internal low-level driver files need to be changed or modified. By this way we don’t have make many changes on application level.
- LPUART Low-level driver: contains functions that configures the LPUART peripheral registers for initializing the peripheral, using the peripheral and processing the data of peripheral at hardware level. These files are the ones which actually interacts with the hardware and make it configurable to our needs. 
In the blogs we will be exploring the LPUART Peripheral Abstraction Layer files (PAL) in more details, as that would be directly used in our application project development(main.c) and driver creation of UART modules (GPS, GSM, IoT Modules and etc)
UART Peripheral in S32K144 MCU Tweet
LPUART PAL
In LPUART PAL there are 2 files lpuart_driver.c and lpuart_driver.h files.
Lets get into these files:
- lpuart_driver.h: contains the enums, structures and function declarations that would be used in application code. Only functions which are declared in this header file can be used in main.c or application project.
lpuart_driver.c: contains the function definations of the declared functions(uses the low-level driver functions) along with some static functions also that are restricted to use in this file only.
Functions
How to use LPUART PAL for doing UART Communication in S32K144 MCU.
- Features of LPUART PAL.
- Initialization and configuration API’s of UART peripheral using LPUART PAL.
- LPUART_DRV_Init: This function is the first function that has to be used in main.c to initialize the LPUART peripheral. This function is just like Serial.begin(9600) of Arduino IDE environment.
status_t LPUART_DRV_Init(uint32_t instance, lpuart_state_t * lpuartStatePtr,
const lpuart_user_config_t * lpuartUserConfig);
This function has 3 function parametrs as follows:
- instance: integer number indicating which instance of LPUART we are going to use.
- lpuart_user_config and lpuart_state_t: structure pointers have to be sent in this function as parameters. (For the info on these structures refers next section)
#define INST_LPUART_LPUART_1 1
extern lpuart_state_t lpUartState1;
/* External declaration of LPUART configuration structure */
extern const lpuart_user_config_t lpUartInitConfig1;
lpuart_state_t lpUartState1;
const lpuart_user_config_t lpUartInitConfig1 = {
.transferType = LPUART_USING_INTERRUPTS,
.baudRate = 9600UL,
.parityMode = LPUART_PARITY_DISABLED,
.stopBitCount = LPUART_ONE_STOP_BIT,
.bitCountPerChar = LPUART_8_BITS_PER_CHAR,
.rxDMAChannel = 0UL,
.txDMAChannel = 0UL
};
/* Initialize LPUART instance */
LPUART_DRV_Init(INST_LPUART_LPUART_1, &lpUartState1, &lpUartInitConfig1);
In LPUART PAL their are 3 ways by which we can send and receive data.
- Interrupt based
- DMA Based
- Pooling Based.
In this blog, we are going to explore and understand pooling based method and it’s API. As to use by interrupt and DMA based, we have to know about Interrupt and DMA. So we are going to cover Interrupt and DMA based LPUART communication under topics: Interrupts in S32K144 and DMA in S32K144 MCU.
What is pooling method in Embedded?
LPUART PAL API’s for doing pooling based communication
- LPUART_DRV_SendDataPolling
status_t LPUART_DRV_SendDataPolling(uint32_t instance, const uint8_t *txBuff, uint32_t txSize);
This function has 3 parametrs:
- instance: integer number indicating which instance of LPUART we are going to use.
- txBuff: buffer pointer, pointing to the data which needs to be send out.
- txSize: size of data that has to be sent.
/* Welcome message displayed at the console */
#define welcomeMsg "This example is an simple echo using LPUART\r\n\
it will send back any character you send to it.\r\n\
The board will greet you if you send 'Hello Board'\r\
\nNow you can begin typing:\r\n"
/* Send a welcome message */
LPUART_DRV_SendDataPolling(INST_LPUART_LPUART_1, (uint8_t *)welcomeMsg, strlen(welcomeMsg));
- LPUART_DRV_ReceiveDataPolling
status_t LPUART_DRV_ReceiveDataPolling(uint32_t instance, uint8_t *rxBuff, uint32_t rxSize);
This function has 3 function parameters:
- instance: integer number indicating which instance of LPUART we are going to use.
- rxBuff: buffer pointer, would be containing the received data.
- rxSize: size of data that has to be received.
/* Receive buffer size */
#define BUFFER_SIZE 256U
/* Buffer used to receive data from the console */
uint8_t buffer[BUFFER_SIZE];
LPUART_DRV_ReceiveDataPolling(INST_LPUART_LPUART_1, buffer, 1U);
Data Types
There are 2 structures that are important and will be used often
- lpuart_user_config_t: This structure has members to configure the LPUART according to user defined settings.
/*! @brief LPUART configuration structure
*
* Implements : lpuart_user_config_t_Class
*/
typedef struct
{
uint32_t baudRate; /*!< LPUART baud rate */
lpuart_parity_mode_t parityMode; /*!< parity mode, disabled (default), even, odd */
lpuart_stop_bit_count_t stopBitCount; /*!< number of stop bits, 1 stop bit (default) or 2 stop bits */
lpuart_bit_count_per_char_t bitCountPerChar; /*!< number of bits in a character (8-default, 9 or 10);
for 9/10 bits chars, users must provide appropriate buffers
to the send/receive functions (bits 8/9 in subsequent bytes);
for DMA transmission only 8-bit char is supported. */
lpuart_transfer_type_t transferType; /*!< Type of LPUART transfer (interrupt/dma based) */
uint8_t rxDMAChannel; /*!< Channel number for DMA rx channel.
If DMA mode isn't used this field will be ignored. */
uint8_t txDMAChannel; /*!< Channel number for DMA tx channel.
If DMA mode isn't used this field will be ignored. */
} lpuart_user_config_t;
/* External declaration of LPUART configuration structure */
extern const lpuart_user_config_t lpUartInitConfig1;
const lpuart_user_config_t lpUartInitConfig1 = {
.transferType = LPUART_USING_INTERRUPTS,
.baudRate = 9600UL,
.parityMode = LPUART_PARITY_DISABLED,
.stopBitCount = LPUART_ONE_STOP_BIT,
.bitCountPerChar = LPUART_8_BITS_PER_CHAR,
.rxDMAChannel = 0UL,
.txDMAChannel = 0UL
};
- lpuart_state_t: This structure has data members, which keep track of the on-going transfers .
/*!
* @brief Runtime state of the LPUART driver.
*
* Note that the caller provides memory for the driver state structures during
* initialization because the driver does not statically allocate memory.
*
* Implements : lpuart_state_t_Class
*/
typedef struct
{
const uint8_t * txBuff; /*!< The buffer of data being sent.*/
uint8_t * rxBuff; /*!< The buffer of received data.*/
volatile uint32_t txSize; /*!< The remaining number of bytes to be transmitted. */
volatile uint32_t rxSize; /*!< The remaining number of bytes to be received. */
volatile bool isTxBusy; /*!< True if there is an active transmit.*/
volatile bool isRxBusy; /*!< True if there is an active receive.*/
volatile bool isTxBlocking; /*!< True if transmit is blocking transaction. */
volatile bool isRxBlocking; /*!< True if receive is blocking transaction. */
lpuart_bit_count_per_char_t bitCountPerChar; /*!< number of bits in a char (8/9/10) */
uart_callback_t rxCallback; /*!< Callback to invoke for data receive
Note: when the transmission is interrupt based, the callback
is being called upon receiving a byte; when DMA transmission
is used, the bytes are copied to the rx buffer by the DMA engine
and the callback is called when all the bytes have been transferred. */
void * rxCallbackParam; /*!< Receive callback parameter pointer.*/
uart_callback_t txCallback; /*!< Callback to invoke for data send
Note: when the transmission is interrupt based, the callback
is being called upon sending a byte; when DMA transmission
is used, the bytes are copied to the tx buffer by the DMA engine
and the callback is called when all the bytes have been transferred. */
void * txCallbackParam; /*!< Transmit callback parameter pointer.*/
lpuart_transfer_type_t transferType; /*!< Type of LPUART transfer (interrupt/dma based) */
#if FEATURE_LPUART_HAS_DMA_ENABLE
uint8_t rxDMAChannel; /*!< DMA channel number for DMA-based rx. */
uint8_t txDMAChannel; /*!< DMA channel number for DMA-based tx. */
#endif
semaphore_t rxComplete; /*!< Synchronization object for blocking Rx timeout condition */
semaphore_t txComplete; /*!< Synchronization object for blocking Tx timeout condition */
volatile status_t transmitStatus; /*!< Status of last driver transmit operation */
volatile status_t receiveStatus; /*!< Status of last driver receive operation */
} lpuart_state_t;
extern lpuart_state_t lpUartState1;
lpuart_state_t lpUartState1;
UART Demo Code for S32K144 MCU
S32 SDK/provides as example demo on LPUART driver to demonstarate how to use the LPUART driver for UART communicatio protocol. Demo Example is named as: lpuart_echo_s32k144
/*
* Copyright 2020 NXP
* All rights reserved.
*
* NXP Confidential. This software is owned or controlled by NXP and may only be
* used strictly in accordance with the applicable license terms. By expressly
* accepting such terms or by downloading, installing, activating and/or otherwise
* using the software, you are agreeing that you have read, and that you agree to
* comply with and are bound by, such license terms. If you do not agree to be
* bound by the applicable license terms, then you may not retain, install,
* activate or otherwise use the software. The production use license in
* Section 2.3 is expressly granted for this software.
*/
/* ###################################################################
** Filename : main.c
** Project : lpuart_echo_s32k144
** Processor : S32K144
** Version : Driver 01.00
** Compiler : GNU C Compiler
** Date/Time : 2020-02-20, 12:27, # CodeGen: 1
** Abstract :
** Main module.
** This module contains user's application code.
** Settings :
** Contents :
** No public methods
**
** ###################################################################*/
/*!
** @file main.c
** @version 01.00
** @brief
** Main module.
** This module contains user's application code.
*/
/*!
** @addtogroup main_module main module documentation
** @{
*/
/* MODULE main */
/* Including needed modules to compile this module/procedure */
#include "sdk_project_config.h"
#include
#include
volatile int exit_code = 0;
/* Welcome message displayed at the console */
#define welcomeMsg "This example is an simple echo using LPUART\r\n\
it will send back any character you send to it.\r\n\
The board will greet you if you send 'Hello Board'\r\
\nNow you can begin typing:\r\n"
/* Error message displayed at the console, in case data is received erroneously */
#define errorMsg "An error occurred! The application will stop!\r\n"
/* Timeout in ms for blocking operations */
#define TIMEOUT 500U
/* Receive buffer size */
#define BUFFER_SIZE 256U
/* Buffer used to receive data from the console */
uint8_t buffer[BUFFER_SIZE];
uint8_t bufferIdx;
/* UART rx callback for continuous reception, byte by byte */
void rxCallback(void *driverState, uart_event_t event, void *userData)
{
/* Unused parameters */
(void)driverState;
(void)userData;
/* Check the event type */
if (event == UART_EVENT_RX_FULL)
{
/* The reception stops when newline is received or the buffer is full */
if ((buffer[bufferIdx] != '\n') && (bufferIdx != (BUFFER_SIZE - 2U)))
{
/* Update the buffer index and the rx buffer */
bufferIdx++;
LPUART_DRV_SetRxBuffer(INST_LPUART_LPUART_1, &buffer[bufferIdx], 1U);
}
}
}
/*!
\brief The main function for the project.
\details The startup initialization sequence is the following:
* - startup asm routine
* - main()
*/
int main(void)
{
/* Write your local variable definition here */
status_t status;
/* Declare a buffer used to store the received data */
uint32_t bytesRemaining;
/* Write your code here */
/* For example: for(;;) { } */
/* Initialize and configure clocks
* - see clock manager component for details
*/
CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT,
g_clockManCallbacksArr, CLOCK_MANAGER_CALLBACK_CNT);
CLOCK_SYS_UpdateConfiguration(0U, CLOCK_MANAGER_POLICY_AGREEMENT);
/* Initialize pins
* - See PinSettings component for more info
*/
PINS_DRV_Init(NUM_OF_CONFIGURED_PINS0, g_pin_mux_InitConfigArr0);
/* Initialize LPUART instance */
LPUART_DRV_Init(INST_LPUART_LPUART_1, &lpUartState1, &lpUartInitConfig1);
/* Install the callback for rx events */
LPUART_DRV_InstallRxCallback(INST_LPUART_LPUART_1, rxCallback, NULL);
/* Send a welcome message */
LPUART_DRV_SendDataBlocking(INST_LPUART_LPUART_1, (uint8_t *)welcomeMsg, strlen(welcomeMsg), TIMEOUT);
/* Infinite loop:
* - Receive data from user
* - Echo the received data back
*/
while (1)
{
/* Receive and store data byte by byte until new line character is received,
* or the buffer becomes full (256 characters received)
*/
LPUART_DRV_ReceiveData(INST_LPUART_LPUART_1, buffer, 1U);
/* Wait for transfer to be completed */
while(LPUART_DRV_GetReceiveStatus(INST_LPUART_LPUART_1, &bytesRemaining) == STATUS_BUSY);
/* Check the status */
status = LPUART_DRV_GetReceiveStatus(INST_LPUART_LPUART_1, &bytesRemaining);
if (status != STATUS_SUCCESS)
{
/* If an error occurred, send the error message and exit the loop */
LPUART_DRV_SendDataBlocking(INST_LPUART_LPUART_1, (uint8_t *)errorMsg, strlen(errorMsg), TIMEOUT);
break;
}
/* Append string terminator to the received data */
bufferIdx++;
buffer[bufferIdx] = 0U;
/* If the received string is "Hello Board", send back "Hello World" */
if(strcmp((char *)buffer, "Hello Board\n") == 0)
{
strcpy((char *)buffer, "Hello World\n");
}
/* Send the received data back */
LPUART_DRV_SendDataBlocking(INST_LPUART_LPUART_1, buffer, bufferIdx, TIMEOUT);
/* Reset the buffer index to start a new reception */
bufferIdx = 0U;
}
for(;;) {
if(exit_code != 0) {
break;
}
}
return exit_code;
} /*** End of main routine. DO NOT MODIFY THIS TEXT!!! ***/
/* END main */
/*!
** @}
*/
lpuart_echo_s32k144 demo code explanation:
Next blogs to read
UART Peripheral in S32K144
Explore this blog to know about UART peripheral in NXP S32K144 MCU’s
ElecronicsV3 Board
Low cost Development Board for NXP S32K144 MCU as beginner friendly automotive kits
Author: Kunal Gupta
Author