STM32 UART: Receive Data Using IDLE Line Detection
Receiving data of unknown length over UART can be tricky when working with STM32 microcontrollers. Traditional methods often rely on fixed-size buffers or special end characters, which are not always efficient or reliable. A better solution is to use the UART IDLE line detection feature, which allows the microcontroller to detect when the sender stops transmitting and trigger an interrupt automatically.
This is the 5th tutorial in the STM32 UART Series. In this tutorial, we’ll learn how to use STM32 UART IDLE line detection to receive variable-length data efficiently. We’ll cover both interrupt-based and DMA-based approaches using STM32 HAL functions, and explain how to configure the peripheral in CubeMX, handle incoming data, and process complete messages seamlessly. This method is ideal for applications like Modbus communication, serial data streaming, and protocols without fixed frame lengths.
STM32 UART Receive Using IDLE Line – Video Tutorial
This video explains how to use the IDLE line detection feature in STM32 UART to receive data of unknown length efficiently. Watch the step-by-step setup, CubeMX configuration, and code implementation for both interrupt and DMA methods. Follow along with the video to fully understand how IDLE line reception works in real applications.
Watch the VideoUnderstanding the UART IDLE Line Feature in STM32
We receive data over UART at a fixed baud rate. Because of this, there is always a small and consistent delay between consecutive bits or bytes during transmission. When the sender stops transmitting for a period longer than this normal gap, the UART peripheral recognizes it as an IDLE line condition. We can use this IDLE line detection to determine that the sender has paused or stopped sending data, allowing us to process the data received so far.
The sender typically stops transmitting in two scenarios:
- When it has sent the entire message and then stops completely.
- When large data is transmitted in multiple chunks, with a brief delay between each chunk.
For example, when receiving a large dataset over UART, the data often arrives in parts. Between these parts, the line remains IDLE for a short time. As soon as the microcontroller detects this IDLE line, it triggers an interrupt, giving us the opportunity to process the received data and prepare for the next chunk.
In this tutorial, we’ll explore how to handle both of these cases using the UART IDLE line feature in STM32.
Using UART IDLE Line Interrupt in STM32
The interrupt method should be used when receiving small amount of unknown data. Here the interrupt is triggered when all the required data has been received or an IDLE Line is detected before that. We should note that the receive buffer should be large enough to store all the data.
STM32 UART CubeMX Configuration
Let’s start by configuring the UART peripheral in the STM32 CubeMX. The image below shows the UART configuration in CubeMX.
We will use the Asynchronous Mode for the communication. Only 2 pins are used in the Asynchronous mode, TX and RX. The baud rate is set to 115200. We need to use the same baud rate for the transmitter device also.
We shall enable the UART interrupt in the NVIC Settings as shown in the image below.
UART IDLE Line Interrupt Handling
We will call the function HAL_UARTEx_ReceiveToIdle_IT
inside the main function. This function will enable the UART to receive the data in IDLE line interrupt mode.
uint8_t RxData[30];
int indx = 0;
int main()
{
....
HAL_UARTEx_ReceiveToIdle_IT(&huart2, RxData, 30);
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
HAL_Delay(1000);
}
}
The RxData buffer is defined to store a maximum of 30 bytes of data. The indx variable will be used to store the size. In the main function we will call the function HAL_UARTEx_ReceiveToIdle_IT to receive 30 bytes of data in the interrupt mode.
When 30 bytes of data is received or an IDLE Line is detected before that, an interrupt will trigger and the HAL_UARTEx_RxEventCallback
will be called. We can process the received data in this callback.
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
indx = Size;
HAL_UARTEx_ReceiveToIdle_IT(&huart2, RxData, 30);
}
The @Size parameter of this callback represents how many data bytes has been received when the interrupt is triggered. We will store the size to the indx variable so that it can be later used in the while loop or any other function.
The interrupt is disabled after it gets triggered, so we need to call the interrupt again at the end of the callback. This is to make sure that the data reception is continuous.
UART IDLE Line Interrupt Results in STM32
The following output demonstrates how the UART IDLE line interrupt works in STM32. These images show the transmitted data from the serial monitor and how the microcontroller receives and processes it using the IDLE line detection feature.
In the first image, you can see 9 data bytes being sent from the serial monitor. The second part of the output displays how these bytes are stored in the RxData
buffer, along with the current value of the indx
variable. This confirms that the IDLE line interrupt correctly detects the end of transmission and processes the data.
This interrupt-based method works well for receiving small packets of data when the exact size is unknown. As long as the data size is less than or equal to the buffer length, the reception will be reliable.
Using UART IDLE Line DMA in STM32
Just like how we use interrupt to receive small amount of data, we will use UART DMA to receive large amount of data.
STM32 UART CubeMX Configuration
Let’s start by configuring the UART peripheral in the STM32 CubeMX. The image below shows the UART configuration in CubeMX.
We will use the Asynchronous Mode for the communication. Only 2 pins are used in the Asynchronous mode, TX and RX. The baud rate is set to 115200. We need to use the same baud rate for the transmitter device also.
We shall enable the UART_RX DMA in the DMA Settings as shown in the image below.
Note: The DMA is configured in Circular mode, which is essential when working with large or continuous data streams. In circular mode, once the DMA reaches the end of the buffer, it automatically wraps around to the beginning and continues receiving data without stopping. This ensures that no data is lost, even if the incoming stream is longer than the buffer size or arrives continuously.
STM32 UART IDLE Line DMA Code
If the data is in few kilobytes, we can receive the entire data in the buffer. This can be done by calling the function HAL_UARTEx_ReceiveToIdle_DMA
just once. Below is the code for the same.
uint8_t RxData[4096];
uint16_t indx = 0;
int count = 0;
int main()
{
....
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RxData, 4096);
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
HAL_Delay(1000);
}
}
The RxData
buffer is defined to store a maximum of 4096 bytes of data. The indx
variable will be used to store the size. The count
variable tracks how many times the callback was called. In the main function we will call the function HAL_UARTEx_ReceiveToIdle_DMA
to receive 4096 bytes of data in the DMA mode.
When 4096 bytes of data is received or an IDLE Line is detected before that, an interrupt will trigger and the HAL_UARTEx_RxEventCallback
is called. We can process the received data in the callback.
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
indx = Size;
count++;
}
The @Size parameter of this callback represents how many data bytes has been received when the interrupt is triggered. We will store the size to the indx variable so that it can be later used in the while loop or any other function. We will also increment the count variable to track how many times this callback was called.
STM32 UART IDLE Line Reception Using DMA – Results
The image below demonstrate the output when receiving data using the UART IDLE line detection with DMA in circular mode on an STM32 microcontroller. They show how the MCU handles large data streams efficiently without losing any bytes.
In the example above, the microcontroller successfully receives the entire dataset. The indx
variable reflects the total size of the transmitted data, confirming that all bytes were captured correctly.
When transmitting large datasets, the sender often divides the data into smaller chunks (23 bytes per chunk in this example). Each pause between chunks triggers the IDLE line interrupt multiple times. With DMA in circular mode, new data is written sequentially into the buffer, and the Size
variable retains its value across multiple calls. This cumulative approach ensures that the indx
variable represents the total data received, not just the bytes from the most recent interrupt.
The count
variable indicates how many times the interrupt callback was triggered during the transfer, providing insight into the chunking behavior of the sender.
This DMA-based method is ideal for receiving data in the range of a few kilobytes, as the buffer can store all incoming bytes in RAM. The received data can then be processed either immediately within the callback or later in the main loop. The indx
variable serves as a reliable measure of the total data received.
STM32 UART DMA for Receiving Large Data
When handling very large amounts of data, often in the range of megabytes, a simple UART reception approach may fail. This happens because defining a buffer large enough to hold all the data in RAM is impractical. To overcome this, we use a smaller buffer to receive data in chunks. Each chunk is then transferred to an external memory storage, such as an SD card or external flash memory.
The following code demonstrates this approach using STM32 HAL functions.
Code Explanation
Here, RxData is a 256-byte buffer used for chunked reception, while FinalBuf represents the complete data storage in an external memory device. The indx1 variable keeps track of the current position in RxData, and indx2 tracks the position in FinalBuf. The rxcplt variable counts how many 256-byte chunks have been received, which helps update indx1 and indx2 correctly. Finally, count tracks the number of times the RX callback is triggered.
In the main function, we call HAL_UARTEx_ReceiveToIdle_DMA
to start receiving data in DMA mode. This function enables the UART peripheral to receive data until the buffer is full or an IDLE line is detected.
#define RXSIZE 256
uint8_t RxData[RXSIZE];
uint8_t FinalBuf[4096];
uint16_t indx1 = 0, indx2=0, rxcplt=0;
int count = 0;
int main()
{
....
HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RxData, RXSIZE);
while (1)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
HAL_Delay(1000);
}
}
Processing Received Data in the Callback
When 256 bytes are received or an IDLE line is detected before that, an interrupt triggers the RX event callback. This is where the received data is processed and stored in the FinalBuf buffer.
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
memcpy (FinalBuf+indx2, RxData+indx1, Size);
if (Size == RXSIZE)
{
rxcplt++;
indx2 = RXSIZE*rxcplt;
indx1=0;
}
else
{
indx2 = indx2 + (Size-indx1);
indx1 = Size;
}
}
In the callback function:
- The
memcpy
function copies the received data from RxData to FinalBuf. - If a full 256-byte chunk is received, rxcplt is incremented, and indx2 and indx1 are updated accordingly.
- If less than 256 bytes are received, indx2 is incremented by the actual number of bytes received, and indx1 is updated to the new position in RxData.
This approach ensures large data can be reliably received in small chunks without exhausting the MCU’s RAM.
As you can see above, the indx2 variable is the same as the size of the file sent by the software. The rxcplt is 8, which represents how many times the entire 256 bytes were received. The indx1 variable represents the number of bytes received after last time the 256 bytes were received. 256*8 + 128 = 2176.
Conclusion
In this tutorial, we learned how to efficiently receive large amounts of data using STM32 UART with DMA, even when the data size exceeds the available RAM. By using a small intermediate buffer and transferring the received data to an external memory like an SD card or flash, we can handle data in megabytes without any issues. The HAL_UARTEx_ReceiveToIdle_DMA
function combined with the RX event callback provides a robust solution for high-speed, large-scale data reception.
If you want to deepen your understanding of STM32 UART communication, check out our other UART tutorials, including STM32 UART Interrupt Communication, STM32 UART Polling Method, and STM32 UART DMA for Small Data Transfers. These tutorials will help you master UART in different scenarios and optimize your embedded projects.
Browse More STM32 UART Tutorials
STM32 UART PART 2 – Transmit using DMA & Interrupt
STM32 UART PART 3 – Receive Data in Blocking & Interrupt mode
STM32 UART PART 4 – Receive Data using DMA
STM32 UART PART 6 – Communication using Single Wire
STM32 UART PART 7 – How to use 1-Wire Protocol
STM32 UART PART 8 – Lin Protocol PART 1
STM32 UART PART 9 – Lin Protocol PART 2
UART IDLE Line Project Download
Info
You can help with the development by DONATING Below.
To download the project, click the DOWNLOAD button.
STM32 UART IDLE Line FAQs
Yes, you can connect STM32 to a PC using a USB-to-TTL converter. The data sent over UART can be read by serial monitor tools like PuTTY, RealTerm, or Arduino Serial Monitor.
STM32CubeMX usually sets the default baud rate to 115200. However, you can manually change it to match the requirements of your external device or serial monitor.
You can connect the TX pin of the STM32 to a USB-to-TTL module, open a serial monitor, and check if the transmitted data appears correctly. You may also use an oscilloscope to view the signal.
While it’s possible, blocking mode is not ideal for real-time data logging. It may cause delays in sensor reading. For better performance, consider using UART with interrupt or DMA.
In most cases, HAL_UART_Transmit
handles internal timing, so delays aren't required. But in time-sensitive applications or when sending large chunks of data, a small delay may help prevent data overflow or ensure smooth transmission.
Search This Site
Subscribe
