STM32 UART Example
In this post, I will show the examples of using HAL library to manipulate the UART on my NUCLEO-F446RE board. The UART settings are: 115200-8-N-1.
The example can:
- receive data from PC and display the received buffer on the serial monitor.
- send data to the serial monitor.
- the user led can be toggled by the preset UART command.
UART using polling method
Use HAL_UART_Transmit
and HAL_UART_Receive
for the UART transmit and receive functionailities, these are blocking functions. This method is normally not used because it blocks other processes, while waiting to finish data reception.
Here is an exmaple:
// main.c
#include <stdio.h>
#include <string.h>
uint32_t count = 0;
uint8_t uartRxBuffer[2] = {0};
uint8_t uartTxBuffer[20] = {0};
...
while (1)
{
count++;
/* UART TX */
sprintf((char *)uartTxBuffer, "✅Count: %lu\r\n", count);
HAL_UART_Transmit(&huart2, uartTxBuffer, strlen((char *)uartTxBuffer), 1000); // Transmit the count value
/* UART RX */
HAL_UART_Receive(&huart2, uartRxBuffer, sizeof(uartRxBuffer), 1000); // Receive data from UART
HAL_UART_Transmit(&huart2, uartRxBuffer, sizeof(uartRxBuffer), 1000); // Echo back the received data
memset(uartRxBuffer, 0, sizeof(uartRxBuffer)); // Clear the receive buffer
HAL_Delay(1000); // Delay for 1 second
}
In serial monitor it looks like:

From the output, we can see the problem of using the polling method
If no data is received, it will wait until the time out specified in the
HAL_UART_Receive
function. Worst case is, if the time out specified isHAL_MAX_DELAY
, then the program will not proceed until data has been received via UART.
UART using interrupt method
Use HAL_UART_Receive_IT
and HAL_UART_Transmit_IT
for the UART transfer using interrupt method. When receiving data using interrupt mode, only when the data is received, it will cause the interrupt and inside the ISR, the received data is echoed back to the serial monitor.
Here is the code implementation:
// main.c
...
/**
* @brief callback function for UART RX interrupt
*
* @param huart
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART2)
{
HAL_UART_Transmit_IT(&huart2, uartRxBuffer, sizeof(uartRxBuffer)); // Echo back the received data
HAL_UART_Receive_IT(&huart2, uartRxBuffer, sizeof(uartRxBuffer)); // Re-enable the interrupt
}
}
...
int main()
{
...
HAL_UART_Receive_IT(&huart2, uartRxBuffer, sizeof(uartRxBuffer));
while (1)
{
sprintf((char *)uartTxBuffer, "✅Count: %lu\r\n", count++);
HAL_UART_Transmit_IT(&huart2, uartTxBuffer, sizeof(uartTxBuffer));
HAL_Delay(1000); // Delay for 1 second
}
}
The performance in the serial monitor:

UART using DMA method
HAL_UART_Transmit_DMA
and HAL_UART_Receive_DMA
can be used for UART transfer using DMA mode. Using DMA is particularly useful when transferring large amounts of data.
Using UART DMA mode also allows to receive unknown length data buffer. In the previous example, the received data has a fixed length, and only when the amount of data has been received, the data is echoed. To allow receiving unknown length buffer, the function
HAL_UARTEx_ReceiveToIdle_DMA
can be used, it allows to receive an amount of data till either the expected number of data is received or an IDLE event occurs. Note that the ISR callback function isHAL_UARTEx_RxEventCallback
.
Here’s how to use DMA mode to receive unknown length of data:
// main.c
uint32_t count = 0;
uint8_t uartRxBuffer[20] = {0};
uint8_t uartTxBuffer[20] = {0};
/**
* @brief callback function for UART RX idle interrupt
*
* @param huart
* @param Size
*/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
if (huart->Instance == USART2)
{
HAL_UART_Transmit_IT(&huart2, uartRxBuffer, Size); // Echo back the received data
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uartRxBuffer, sizeof(uartRxBuffer)); // Re-enable the interrupt
__HAL_DMA_DISABLE_IT(huart->hdmarx, DMA_IT_HT); // Disable half transfer interrupt
}
}
int main(void)
{
...
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uartRxBuffer, sizeof(uartRxBuffer));
__HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT); // Disable half transfer interrupt
while (1)
{
sprintf((char *)uartTxBuffer, "✅Count: %lu\r\n", count++);
HAL_UART_Transmit_IT(&huart2, uartTxBuffer, sizeof(uartTxBuffer));
HAL_Delay(1000); // Delay for 1 second
}
}
The performance in the serial monitor:

Note:
Using the function
HAL_UARTEx_ReceiveToIdle_IT
can also receive unknown length of data. Attention that, ONLY when usingHAL_UARTEx_ReceiveToIdle_DMA
, it’s better to disable the half transfer interrupt. If this is not disabled, theRxEvent
will be triggered as well once the received data amount reaches half of the size we set. Call the function__HAL_DMA_DISABLE_IT
to disable the half transfer interrupt, see example above.