UART Circular buffer using DMA and IDLE line detection


I no longer support this method. I can’t help you with any errors or any doubts.

I ported this code written here to use it with HAL and CubeMx, So all credits go to the developer of,Tilen MAJERLE.

We all face the problem in UART while receiving unknown data size and If you are using HAL with CubeMx, there is no workaround for this. One might use DMA with a huge buffer size, but this will slow down the receiving process. Fortunately, the UART of STM32 have IDLE line detection interrupt which we are going to take advantage of. So today in this tutorial I will show you how to receive UART data using DMA and IDLE line detection.

What exactly is IDLE line Detection and why should we use it

Imagine that we are receiving unknown data and we don’t know the exact length of it. We can set DMA to receive particular number of bytes but than we miss the remaining data. Actually, they will be in buffer but we won’t be notified that DMA has data in memory. We have to wait until another packet with data bytes arrives to first flush old data together with new data. This can lead to timeouts in some cases.

UART have a feature called IDLE line detection and we are going to use this to sole the problem mentioned above.  Idle line is detected on RX line when there is no received byte for more than 1 byte time length. We will force DMA to call transfer complete interrupt when we disable DMA stream manually, thus disabling enable bit in stream control register. In this case DMA will make an interrupt if they are enabled and we can read number of bytes we need to still receive by reading NDTR register in DMA stream. From here, we can calculate how many elements we already received.

I know it sounds complicated, So I am not going to walk you through the details here. I will show you how to implement it directly.

 Setting Up the IDE

Okay it’s time to fire up your CubeMx and choose the USART2 (it’s best if you choose USART2. Code is little messed up). In the USART setting, enable the UART interrupt and setup the DMA as shown below

Okay That’s it for CubeMx. Now open the project and do the following things

  • Copy DMA_CIRCULAR.c in src folder
  • Copy DMA_CIRCULAR.h in inc folder

Now Right click the Application/User and select ADD existing file to the group and add DMA_CIRCULAR.c

In the main.c file do the following

include DMA_CIRCULAR.h

#include "DMA_CIRCULAR.h"

Define the size of the buffers

#define DMA_RX_BUFFER_SIZE          64
uint8_t DMA_RX_Buffer[DMA_RX_BUFFER_SIZE];

#define UART_BUFFER_SIZE            256
uint8_t UART_Buffer[UART_BUFFER_SIZE];

In the main function, enable the Idle Line interrupt and DMA Tx complete interrupt. Also Disable UART Half Tx interrupt and finally start the DMA Receive

__HAL_UART_ENABLE_IT(&huart2, UART_IT_IDLE);   // enable idle line interrupt
__HAL_DMA_ENABLE_IT (&hdma_usart2_rx, DMA_IT_TC);  // enable DMA Tx cplt interrupt

HAL_UART_Receive_DMA (&huart2, DMA_RX_Buffer, DMA_RX_BUFFER_SIZE);

hdma_usart2_rx.Instance->CR &= ~DMA_SxCR_HTIE;  // disable uart half tx interrupt

In DMA_CIRCULAR.c, change the following according to your setup

extern UART_HandleTypeDef huart2;
extern DMA_HandleTypeDef hdma_usart2_rx;

Open stm32f4xx_it.c and change the default interrupt handlers as shown in the picture below

This is it guys. Now you can start transmitting the data and see the result in the debugger window.


Check out the VIDEO Below

Notify of
Oldest Most Voted
Inline Feedbacks
View all comments