UART Reception with DMA on STM32: Ring Buffers for Loss-Free Data

July 14, 2026 1 min read 6 views

At high baud rates (1 Mbit/s and above), generating an interrupt per character is a heavy load even on a Cortex-M4. The answer is to receive with circular DMA and only act when the line goes idle.

Why circular DMA?

The DMA controller writes into the buffer without touching the CPU. When it reaches the end it wraps around automatically, so a continuous stream is never interrupted.

The IDLE line interrupt

The UART peripheral raises the IDLE flag once the line has been quiet for a while. In that interrupt we read the DMA remaining-transfer counter (NDTR) to find how far the buffer has been filled.

uint16_t pos = BUF_SIZE - __HAL_DMA_GET_COUNTER(huart->hdmarx);
if (pos != old_pos) {
    /* process the range old_pos .. pos */
}

Things to watch

  • The buffer should be at least twice the longest message.
  • With D-Cache enabled, protect the DMA buffer with __DSB() and cache invalidation.
  • Do not forget to clear the overrun flag, otherwise reception stops silently.

With this design CPU usage stays below 1% even at 3 Mbit/s.

Share: