UART Circular buffer using DMA and IDLE line detection

Description

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.

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

 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 here.

In the main.c file do the following things:-

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.

Result

DOWNLOAD

You can buy me a coffee sensor 🙂

download the CODE below

, , , , , , , , , , ,

52
Leave a Reply

avatar
30 Comment threads
22 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
31 Comment authors
Gert LauritsenadminIndraMARIO MAINTilen, author of original post with design flaw Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Moddiot
Guest
Moddiot

Thanks for your post. Anyway, I tried to do that but it does not work for me… but the problem is mine… I think I need to study more about DMA. Thanks!

userxx
Guest
userxx

Sorry, but there is another code on site than in this film. Can you fix it?

hendalage
Guest
hendalage

Thanks for sharing this great tutorial. I just want to know if i can use this on STM32f103C8 micro-controller as well?

Corey
Guest
Corey

What keeps the “IDLE Line interrupt” from firing over-and-over-and-over when you aren’t transmitting data? For example, I need to handle bursts of UART data. I can get 500 bytes of data now, then another 200 bytes 5 seconds later. I want to use DMA to handle this sudden inrush of data, but as soon as the 500 bytes of data is received, the micro chokes because it’s always in the “IDLE Line interrupt”

barry
Guest
barry

I realy have to use this feature, but there are erros with a new cubmx verseion probably (Juli 28, 2018). In the DMA_CIRCULAR.c he could not resolve some registers, because of name changes I think. Forethermore I think that thay changed the DMA_HandleTypeDef.

guerrouf
Guest
guerrouf

Thank you for this wonderful work
I’m working on STM32F103c8
I tried to adjust the DMA_CIRCULAR.c file on STM32F103c8 and did not succeed in it
Please help me and send me a copy of this uart-circular.zip running on STM32F103c8
thank you.

Jim
Guest
Jim

Hi there, thanks for you tutorial. What exactly is the purpose of the UART buffer? Why can’t we just have one buffer that stores the data

Murali
Guest
Murali

I have tried for STM32F030R8 and STM32L4R5ZI controllers, for both the controllers the data from the Realterm is not received in DMA_RX_Buffer. For both the controllers I made some changes in the code. there is no error in the code. If need the code I will send it.

luca
Guest
luca

Very good tutorial!
What change for micro Stm32F091

luca
Guest
luca

How do the change code if i would to use with the STM32f0 micro?

Dave Comer
Guest

Great tutorial on the DMA. I’m just starting to use the power of this peripheral and finding good examples can be challenging. Especially using the circular mode.

naren
Guest
naren

hi guys, has someone got the code for STM32-F103RB. I cant get it to work on this board.

Museb Momin
Guest

THANK YOU SIR GREAT WORK

Alfredo
Guest
Alfredo

I have implanted this in an STM32F302 and with a single USART it works perfectly, now I am with a project that I must use 2 USART. Is it possible to use it with 2 USART at the same time? Or the circular buffer will cause problems?. Someone has tried?.

Arunsankar
Guest
Arunsankar

hi, ,i tried same in stm32f103 , also i make changes stream to channel in dma_irq module. Even though i didn’t get exact output. DMA recieve in very first time after that it wont . how to rectify that in stm32f103? give some suggestions?

Kitanel
Guest
Kitanel

Hi, i’m trying to use this code to transmitt resived via DMA data back to UART, it work’s good until i dont send string, wich size is more than rx_buffer size.
Can you check your code or help me to undestand wthere is my error?

jn
Guest
jn

Hi, I’m trying out the tutorial on STM32F4 here (https://www.youtube.com/watch?v=tWryJb2L0cU and several others). I try it on several different processors (STM32F407VE, STM32F103C8T6), but the result is always the same. Instead of comprehensible communication, I get only meaningless characters. The RS232 to USB converter works elsewhere. The development environment is “Keil uVision5”. Anyone knowing where the problem might be?

Cruse
Guest
Cruse

When I am using STM32Cube FW_F1 V1.7.0 on STM32F103ZET6 MCU, the register CR and DMA_SxCR_HTIE have been renamed. After searching HAL manuals, I suppose it should be:
[ hdma_usart2_rx.Instance->CCR &= ~DMA_IT_HT; ]

Gavin
Guest
Gavin

Hi, where should I download the code? Thank you.

Bhushan
Guest
Bhushan

Hi,

I am looking for same thing but for STM32L4R5.

Please guide me to do the driver changes change.

Juri
Guest
Juri

I used your code to implement the LIN Protocol. But why not work HAL_UART_Transmit_DMA? The command is triggered once. What needs to be changed for it to start working?

Juri
Guest
Juri

What to do to function HAL_UART_Transmit_DMA? It works once and is blocked.

Wahran
Guest
Wahran

Where does one get DMA_CIRCULAR.c and DMA_CIRCULAR.h? I can’t find any links for them on this tutorial.

Pramod Butte
Guest
Pramod Butte

I cannot find the download… trying to create account at the my.st.com, but not receiving any email to create account

Hossein
Guest
Hossein

Hi
I have tried this for stm32f746 discovery board. everything is ok but in DMA_RX_Buffer just exist last character after every transition.
but in your video you receive complete data in DMA_RX_Buffer.
Is there any advice?
Thanks in advance.

K
Guest
K

Hi. I have issue with downloading your code. The link is directed to ST site with error message.

Tilen, author of original post with design flaw
Guest
Tilen, author of original post with design flaw

Your implementation, copied by other site, has very important design flaw preventing it to work reliable. DMA shall be configured in circular mode and never disabled. Original post is from stm32f4-discovery.net and truly right code is available here: https://github.com/MaJerle/STM32_USART_DMA_RX

MARIO MAIN
Guest
MARIO MAIN

I have a problem with the code when I insert a HAL_Delay() or any delay function. I need to send some fixed-length data by serial (i.e.10 byte), but my communication protocol requires a delay (i.e. 30ms) between different instructions. In addition, some characters are inserted at the start point of any instructions. Generally, the main program sends a command every 1-10sec, but sometimes 2-3 instructions are given sequentially. My micro is a STM32F446RE. I modify your code so I have: (In DMA_CIRCULAR.c, function DMA_IrqHandler (DMA_HandleTypeDef *hdma): /code …. …. /* Correct values for remaining data */ Write += tocopy; len… Read more »

Indra
Guest
Indra

Hi, thank you for the code
it’s working but after several USART transfer, infinite loop error always happen.
here’s my debug screenshot, I am using Atollic TrueStudio https://ibb.co/vdL5R0z
Is there any advice?
Thanks in advance

Gert Lauritsen
Guest
Gert Lauritsen

To make this works there has to be 3 change to DMA_CIRCULAR.c First you have to check type of interrupt not if its enabled void DMA_IrqHandler (DMA_HandleTypeDef *hdma) { typedef struct { __IO uint32_t ISR; /*!< DMA interrupt status register */ __IO uint32_t Reserved0; __IO uint32_t IFCR; /*!StreamBaseAddress; if (__HAL_DMA_GET_TC_FLAG_INDEX(hdma)) // if the source is TC !!!!!!!!!!!! This line { // Clear the transfer complete flag regs->IFCR = DMA_FLAG_TCIF1_5 <StreamIndex; then you have to reset all bit ind last line in the same void regs->IFCR = 0x7FU <StreamIndex; // clear all interrupts insted of 0x3FU The last thing is that… Read more »

Menu