STM32 UART Part 4: Receive Unknown Length Using DMA

Using DMA in normal (non-circular) mode with the IDLE line interrupt turns UART reception into a smart, event-driven system. Instead of blindly filling buffers, the DMA captures bursts of unknown-length data in one shot, and the IDLE flag elegantly signals the firmware to process the frame and re-arm DMA for the next transfer.

In this guide, we shall cover the following:

  • Interrupt vs DMA-IDLE line approach.
  • STM32CubeMX configuration.
  • Firmware development.
  • Results.

1. Interrupt vs DMA-IDLE Line Approach:

Interrupt-driven (single character per interrupt)

How it works:

  • Each received character triggers an interrupt (RxCpltCallback).
  • The CPU stores the character in a buffer and re-enables the receive interrupt for the next byte.

Advantages:

  • Very simple to implement and debug.
  • Works well for short, low-speed transmissions.
  • Fine-grained control over each received character (can parse on the fly).

Disadvantages:

  • High interrupt load at higher baud rates (e.g., 115200 bps and above).
  • CPU overhead increases linearly with data rate.
  • Risk of missing characters if interrupts are delayed.

DMA + IDLE Line method

How it works:

  • DMA transfers multiple characters directly into a buffer without CPU intervention.
  • The IDLE line interrupt signals when the sender has stopped transmitting.
  • At that point, the firmware processes all data at once and restarts DMA.

Advantages:

  • Very efficient — DMA handles transfers, reducing CPU overhead.
  • Much better for high baud rates and large bursts of data.
  • Lower risk of overrun compared to per-character interrupts.
  • Easy to detect end-of-frame with IDLE flag.

Disadvantages:

  • Slightly more complex to set up (DMA + UART + IDLE ISR).
  • Data is processed in blocks, not per-character (requires buffering logic).
  • Buffer size must be carefully chosen to avoid overflow.

Summary

  • Interrupt method → Best for small, sporadic messages or low baud rates (simple systems).
  • DMA + IDLE line → Best for high-throughput, unknown-length data streams (e.g., communication with GPS, modems, RS485, etc.).

In short:

FeatureInterrupt-driven (per byte)DMA + IDLE Line
Working principleEach received character triggers an interrupt and is copied to buffer.DMA continuously transfers incoming data to buffer, IDLE interrupt indicates end of transmission.
CPU loadHigh (interrupt on every byte).Low (interrupt only on IDLE or buffer full).
Efficiency at high baud ratesPoor — CPU easily overloaded.Excellent — DMA handles data movement.
Implementation complexitySimple, beginner-friendly.More complex (DMA + UART + IDLE handling).
Data processingReal-time, per-character.Processed in chunks when IDLE line is detected.
Risk of data lossHigher — interrupts can be delayed.Lower — DMA buffers reduce risk.
Best suited forShort, low-rate messages (commands, debug).High-speed, unknown-length streams (GPS, modems, RS485, logs).

2. STM32CubeMX Configuration:

We shall continue from here.

Open the .ioc as follows:

Next, head to connectivity, select the desired UART (USART2 in this guide), and select DMA as follows:

Next, add DMA for UART_RX as follows:

  • Set Mode to Normal.
  • Make sure Data Width for both to Byte.

Thats all for the configuration. Save the project and this will configure the project.

3. Firmware Development:

Once the project has been configured, main.c shall be opened.

In main.c, these are already defined in user code begin PV from the previous guide:

#define BufferSize 100

uint8_t uart_rx_data[BufferSize]={0};

This is where the received data shall be stored.

Next, in user code begin 2 in main function:

HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uart_rx_data, BufferSize);

&huart2

  • Handle for USART2 peripheral.
  • Contains all the configuration info (baud rate, word length, parity, DMA linkage, etc.) needed for this UART instance.
  • Tells HAL which UART to use for this operation.

uart_rx_data

  • Pointer to the receive buffer in RAM where incoming UART bytes will be stored.
  • DMA writes received bytes directly into this buffer without CPU involvement.

BufferSize

  • The maximum number of bytes the DMA is allowed to transfer into the buffer before stopping.
  • Prevents overwriting memory outside the buffer.

Once the buffer is filled or IDLE line is detected, the HAL_UARTEx_RxEventCallback will be called.

In user code begin 0, declare the function as follows:

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{

	if(huart->Instance==USART2)
	{

		HAL_UART_Transmit(&huart2, uart_rx_data, Size, 100);
		HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uart_rx_data, BufferSize);
	}

}

This callback is automatically called by HAL when:

  • The IDLE line is detected (receiver goes idle).
  • Or the DMA buffer gets full (all BufferSize bytes are received).

The Size argument tells you how many bytes were actually received before the event occurred.

Inside the function:

  1. Check which UART triggered the eventif(huart->Instance == USART2)
    • Ensures this code only runs if the event came from USART2.
    • Useful if you have multiple UARTs running.
  2. Echo the received data backHAL_UART_Transmit(&huart2, uart_rx_data, Size, 100);
    • Immediately sends the received data back over UART2.
    • Acts like an echo test, confirming that reception worked.
    • Size ensures only the valid portion of the buffer is transmitted (not the whole buffer).
  3. Restart DMA receptionHAL_UARTEx_ReceiveToIdle_DMA(&huart2, uart_rx_data, BufferSize);
    • Re-arms the DMA + IDLE receive mechanism.
    • Necessary because after each event, DMA is no longer active.
    • Without this line, the system would stop receiving after the first chunk.

Thats all for the polling mode. Save the project and run it on your MCU as follows:

4. Results:

Using you favourite terminal UART application:

Noticed that we managed to receive variable string length and echo back them without any issue.

Feel free to improve it and make it able to work with your application.

Happy coding 😉

Add Comment

Your email address will not be published. Required fields are marked *