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.

Circular Buffer

Check out the UART Circular buffer (Ring Buffer) based on IDLE LINE STM32/UART CIRCULAR BUFFER at master · controllerstech/STM32 (github.com)

What is IDLE Line

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

CubeMX Setup

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

Some Insight into the CODE

Let’s see some of the functions that we will use in this tutorial.

HAL_UARTEx_ReceiveToIdle_DMA(&huart2, RxBuf, RxBuf_SIZE);
__HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);
  • 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.



void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if (huart->Instance == USART2)
	{
		oldPos = newPos;  // Update the last position before copying new data

		/* If the data in large and it is about to exceed the buffer size, we have to route it to the start of the buffer
		 * This is to maintain the circular buffer
		 * The old data in the main buffer will be overlapped
		 */
		if (oldPos+Size > MainBuf_SIZE)  // If the current position + new data size is greater than the main buffer
		{
			uint16_t datatocopy = MainBuf_SIZE-oldPos;  // find out how much space is left in the main buffer
			memcpy ((uint8_t *)MainBuf+oldPos, RxBuf, datatocopy);  // copy data in that remaining space

			oldPos = 0;  // point to the start of the buffer
			memcpy ((uint8_t *)MainBuf, (uint8_t *)RxBuf+datatocopy, (Size-datatocopy));  // copy the remaining data
			newPos = (Size-datatocopy);  // update the position
		}

		/* if the current position + new data size is less than the main buffer
		 * we will simply copy the data into the buffer and update the position
		 */
		else
		{
			memcpy ((uint8_t *)MainBuf+oldPos, RxBuf, Size);
			newPos = Size+oldPos;
		}


		/* start the DMA again */
		HAL_UARTEx_ReceiveToIdle_DMA(&huart2, (uint8_t *) RxBuf, RxBuf_SIZE);
		__HAL_DMA_DISABLE_IT(&hdma_usart2_rx, DMA_IT_HT);

	}
}
  • 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.

Result

This is more like a working proof, so I would suggest that you watch the video below

Check out the VIDEO Below

DOWNLOAD

You can buy me a coffee Sensor by clicking DONATE OR Just click DOWNLOAD to download the code

controllerstech

Subscribe
Notify of
guest
3 Comments
Newest
Oldest Most Voted
Inline Feedbacks
View all comments
Menu