STM32 UART DMA Idle Detection

From Stm32World Wiki
Revision as of 13:41, 13 November 2024 by Lth (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
UART to UART test setup.jpg

Using an UART to send and receive data is a very common way to communicate between two devices.

A common approach to receiving data from an UART is to have an interrupt generated after each character has been received. The problem with this approach is that a lot of interrupts will be generated leaving very few CPU cycles to process the received data without losing characters. A better approach is to use DMA for this, but this leaves another challenge since DMA in STM32 generally generate an interrupt when the buffer is half full and then another one when the buffer is full.

Video

We have created a tutorial video describing this approach. The video can be viewed here: https://www.youtube.com/watch?v=Eh7Szh-K-u8.

The Code

The callback (interrupt handler) is actually quite simple. It is important to understand when it is triggered. Using the 'ReceiveToIdle' it will be executed in 3 different situations:

  1. When the buffer is half full (exactly)
  2. When the buffer is full and wrap around
  3. When the RX is idle (1 character pause)

The callback is executed with a parameter indicating the current offset of the last character received (ST call it 'Size' but we will use 'offset'). If we keep track of the last (starting with 0) it becomes really easy to determine which part of the buffer needs processing.

/**
 * @brief  UART Event Callback.  Fired on idle or if dma buffer half full or full.
 * @param  huart  Pointer to a UART_HandleTypeDef structure that contains
 *                the configuration information for the specified UART module.
 * @param  offset A offset counter pointing to last valid character in DMA buffer.
 * @retval None
 */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t offset) {

	static uint16_t last_offset = 0;

	// Ignore if called twice (which will happen on every half buffer)
	if (offset != last_offset) {

		// If wrap around reset last_size
		if (offset < last_offset)
			last_offset = 0;

		while (last_offset < offset) {
			process_character((char) dmabuf[last_offset]);
			++last_offset;
		}

	}

}

We can now fire up the receiver:

HAL_UARTEx_ReceiveToIdle_DMA(&huart4, (uint8_t*) &dmabuf, DMA_BUFFER_SIZE);

Miscellaneous Links