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:-
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.
You can buy me a
coffee sensor 🙂
download the CODE below