UART DMA with IDLE Line Detection
Long ago I covered the Uart Ring Buffer, where the data of unknown length could be received efficiently. Though the method was fine, but there were lot of changes needed to be made with different series of MCUs.
So Today we will see another efficient method, which is completely based on HAL, so the changes are not needed for different series of MCUs. This method uses DMA with the IDLE Line to trigger the interrupt.
Check out the UART Circular buffer (Ring Buffer) based on IDLE LINE STM32/UART CIRCULAR BUFFER at master · controllerstech/STM32 (github.com)
- Let’s say we are receiving some large data using the UART.
- This data is sent in chunks.
- There is some small delay between two chunks, and for this delay the line remains IDLE.
- That’s it, whenever the MCU detects this idle line, an interrupt will be triggered, and we can process the data, and prepare for the next chunk.
Since we are going to use DMA for this purpose, the CPU will be free for other operations.
The setup is pretty simple. We will select the UART, enable the DMA for Receiving Data, and turn on the UART Interrupt.
- Make sure the DMA is select is NORMAL mode.
- If there is any overrun bit turned on by default, Disable it.
- Data width should be Bytes, and Direction is Peripheral to Memory
Update for CORTEX M7
Cortex M7 Users might need to Disable the cache of the Respective SRAM, or else the DMA won’t be able to copy the data. Watch the video, I have explained it in the end
Let’s see some of the functions that we will use in this tutorial.
- HAL_UARTEx_ReceiveToIdle_DMA is used to receive the data using the DMA until, an IDLE event occurs, or all the data has been received.
- Here we will save the incoming data into the RxBuf, which will be processed later.
- When we enable DMA transfer using HAL, all the interrupts associated with it are also enabled.
- As we don’t need the Half Transfer interrupt, we will disable it.
Once the IDLE event occurs, an interrupt will be triggered, and the Rx event callback will be called. Now we will process the data inside this callback.
- Since RxBuf could be modified at any point due to incoming data, we will save the data into some other buffer (mainBuf).
- Here we will check if the current position + incoming data is less than the size of main buffer
- If it is, then we will save the data normally, and update the current position in the main buffer.
- But if the current position + incoming data exceeds the buffer length, then we need to start saving data from the beginning.
- To do this, we will first find out how much space is left in the buffer, and copy the data into that space.
- now shift the current position to 0, and write the rest of the data.
- And finally update the current position to keep track of it for the next case.
In the end we will start the DMA again, and get ready for the next transfer. We can do the processing of the data in the while loop, or in a RTOS task.
This is more like a working proof, so I would suggest that you watch the video below