Difference between revisions of "STM32 UART DMA Idle Detection"
Jump to navigation
Jump to search
Line 1: | Line 1: | ||
[[Category:STM32]][[Category:STM32 Development]][[Category:STM32 HAL]][[Category:C]]{{metadesc|UART DMA with idle detection}} | [[Category:STM32]][[Category:STM32 Development]][[Category:STM32 HAL]][[Category:C]]{{metadesc|UART DMA with idle detection}} | ||
− | Using an [[UART]] to send and receive data is a very common way to communicate between two devices. | + | 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. | ||
+ | |||
+ | == The Code == | ||
+ | |||
+ | The callback (interrupt handler): | ||
+ | |||
+ | <pre> | ||
+ | /** | ||
+ | * @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; | ||
+ | } | ||
+ | |||
+ | } | ||
+ | |||
+ | } | ||
+ | </pre> | ||
+ | |||
+ | We can now fire up the receiver: | ||
+ | |||
+ | <pre> | ||
+ | HAL_UARTEx_ReceiveToIdle_DMA(&huart4, (uint8_t*) &dmabuf, DMA_BUFFER_SIZE); | ||
+ | </pre> | ||
+ | |||
+ | |||
== Miscellaneous Links == | == Miscellaneous Links == |
Revision as of 02:42, 12 April 2022
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.
The Code
The callback (interrupt handler):
/** * @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);