STM32 UART Variable Length Reception: A Comprehensive Guide

Click the blue text above to follow us

Embedded Training – Choose Jufeng Smart Link

Why Use UART Variable Length Reception

Standard blocking reception functions

HAL_UART_Receive(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size,uint32_t Timeout) // huart: handle
// pData: variable to hold received data
// Size: size of data to be received

Interrupt reception functions

HAL_UART_Receive_IT(UART_HandleTypeDef * huart, uint8_t * pData, uint16_t Size) // huart: handle
// pData: variable to hold received data
// Size: size of data to be received

Both blocking and interrupt reception share a commonality: they are fixed-length receptions. The parameter Size indicates the expected length of the received data. When the UART sends data of variable length, character loss may occur, resulting in incomplete reception (the effect of using fixed-length reception for variable-length data is shown in the figure below). To ensure that the lower machine receives accurate commands, it is often necessary for the UART to freely receive variable-length data. This is typically achieved using the UART idle interrupt.

STM32 UART Variable Length Reception: A Comprehensive Guide

What is UART Idle Interrupt

The UART idle interrupt refers to an interrupt signal generated by the UART controller when there is no data in the UART receive buffer. In UART communication, after receiving a complete data frame, there is usually a stop bit indicating the end of data transmission. When there is no data in the receive buffer, the UART controller detects the continuous idle state of the stop bit and generates an idle interrupt signal. Using the UART idle interrupt can eliminate the need for polling the receive buffer, improving system performance and efficiency.

Note:

(1) The idle interrupt must be manually unmasked.

(2) The idle interrupt must be manually cleared; otherwise, the interrupt will remain set, causing the system to continuously enter the interrupt handler.

Implementing Variable Length Reception via UART Idle Interrupt

Each time a character is received, an interrupt is triggered, saving the currently received character to a receive buffer. When the next character arrives, it continues to save the character to the next element of the array.

When all data has been received, the idle interrupt is triggered, and the saved array contains the received data. Then, operations such as judgment and sending can be performed on this array.

Steps:

1. Add global variables in main.c

uint8_t buf[1]={0};        // Used to store a single received character

2. Enable UART idle interrupt and start reception interrupt in the main function

__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); // Enable UART idle interrupt

HAL_UART_Receive_IT(&huart1,buf,1); // Start reception interrupt, receiving one character at a time

3. Add global variables in stm32xxxx_it.c

uint8_t len=0; // Reception counter
uint8_t buf_recv[256]; // Reception buffer for storing complete data frames
extern uint8_t buf[1]; // Used to store a single received character

4. Add the following code in the UART interrupt service function (USART1_IRQHandler):

When data arrives, sequentially store the bytes in buf_recv.

When data transmission stops (idle interrupt occurs), process the received complete data frame and reset the reception state.

Remember to include the header file #include<string.h>

/* USER CODE BEGIN USART1_IRQn 0 */

if (__HAL_UART_GET_FLAG(&amp;huart1, UART_FLAG_IDLE)) // Check if idle interrupt occurred
{
__HAL_UART_CLEAR_FLAG(&amp;huart1,UART_CLEAR_IDLEF); // Clear idle interrupt flag

/**** Handle the received complete data here
// I am echoing the received data for functional testing; generally, printing does not occur in the interrupt function (printing is relatively time-consuming)

HAL_UART_Transmit(&amp;huart1,buf_recv,sizeof(buf_recv),1000);

****/

len=0; // Reset reception counter
memset(buf_recv,0,sizeof(buf_recv)); // Clear reception buffer
}else // Normal reception interrupt handling
{
buf_recv[len]=USART1-&gt;RDR; // Store the received single byte in the buffer
len++; // Increment reception counter
HAL_UART_Receive_IT(&amp;huart1,buff,1); // Re-enable reception interrupt, prepare to receive the next byte
}

/* USER CODE END USART1_IRQn 0 */

UART Idle Interrupt + DMA Method (Recommended)

Disadvantages of Traditional Interrupt Method:

Each byte received triggers an interrupt, requiring the CPU to respond frequently, leading to inefficiency. At high baud rates (e.g., 115200, 1Mbps), the CPU may be overwhelmed by numerous interrupts and unable to handle other tasks.

Advantages of DMA Method:

Data transfer is entirely managed by the DMA controller, and the CPU is notified only when data reception is complete (or the buffer is full), allowing it to focus on other tasks (such as algorithm processing, protocol parsing, etc.). This is suitable for high baud rate, large data volume UART communication (e.g., GPS modules, wireless communication, high-speed data acquisition).

Steps:

1. Configure STM32CubeMX

STM32 UART Variable Length Reception: A Comprehensive Guide

2. Define global variables in main.c

uint8_t buf_ctl[256];    // Reception data buffer, size can be customized as needed

3. Enable UART idle interrupt and enable UART DMA reception in the main function

__HAL_UART_ENABLE_IT(&amp;huart1,UART_IT_IDLE); // Enable UART idle interrupt

HAL_UART_Receive_DMA(&amp;huart1,buf_ctl,256); // Enable DMA reception

4. Modify the UART interrupt service function in stm32xxxx_it.c

Add global variables

extern uint8_t buf_ctl[256]; // Reception data buffer
uint8_t len=0; // Record actual reception length

In the UART interrupt service function (USART1_IRQHandler), handle the UART idle interrupt, stop DMA transfer, calculate the reception length, process the complete data, and restart DMA to prepare for receiving the next frame of data.

void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */

if (__HAL_UART_GET_FLAG(&amp;huart1, UART_FLAG_IDLE)) // Get UART idle interrupt flag
{
__HAL_UART_CLEAR_FLAG(&amp;huart1,UART_CLEAR_IDLEF); // Clear idle interrupt flag
HAL_UART_DMAStop(&amp;huart1); // Stop DMA transfer to prevent data overwrite

/**** Handle the received complete data here; I am echoing the received data for functional testing; generally, printing does not occur in the interrupt function (printing is relatively time-consuming)

len = 256 - hdma_usart1_rx.Instance-&gt;CNDTR; // Calculate the actual length of received data

buf_ctl[len]='\0'; // Add string terminator at the end of received data (suitable for ASCII data)
HAL_UART_Transmit(&amp;huart1,buf_ctl,len+1,500); // Echo received data, ASCII text send length len+1, binary data send length len

****/

memset(buf_ctl,0,sizeof(buf_ctl)); // Clear reception buffer
HAL_UART_Receive_DMA(&amp;huart1, buf_ctl, 256); // Restart DMA, prepare to receive the next frame of data.

}

/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&amp;huart1);
/* USER CODE BEGIN USART1_IRQn 1 */

/* USER CODE END USART1_IRQn 1 */
}

Effect Demonstration

STM32 UART Variable Length Reception: A Comprehensive Guide

Other Notes:

STM32 UART Variable Length Reception: A Comprehensive Guide

STM32 UART Variable Length Reception: A Comprehensive Guide

It is recommended to refer to the specific model’s reference manual and HAL library header files during actual development to confirm the correct flag operation methods.

STM32 UART Variable Length Reception: A Comprehensive Guide

Original link: https://blog.csdn.net/m0_71284871/article/details/148892767

Leave a Comment