From 9cc7d025cd0397e798a6fc87eb86be61a01e2161 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Sun, 6 Dec 2020 12:09:53 -0600 Subject: [PATCH] F4_HAL/uart: Fix data loss when using DMA Rx with hardware flow control. Data loss can occur when using DMA Rx + hardware flow control. Consider the following situation where a device is receiving UART data that consists of one byte that gives the size of the data followed by "size" bytes: - MCU requests to read one byte from UART by calling HAL_UART_Receive_IT() - The remote may or may not have already sent the byte, but it doesn't matter, this works correctly. - After the UART_Receive_IT() callback, the MCU requests to read "size" bytes by calling HAL_UART_Receive_DMA(). - If the remote has not sent the next byte yet, then all is well, but it could have sent the next byte already, which is waiting in the DR register. The RTS line will be high which prevents the remote from sending any more data, so no overrun will occur. But since __HAL_UART_CLEAR_OREFLAG() reads the DR register, this byte will be lost and the DMA read will read one extra byte from the incoming stream causing the algorithm to get out of sync. This adds a check to see if flow control is enabled and only calls __HAL_UART_CLEAR_OREFLAG() if flow control is disabled. This should prevent breaking users who aren't using flow control and may already depend on this behavior. --- STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c b/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c index b8d2039e..0dc1a379 100644 --- a/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c +++ b/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c @@ -975,7 +975,11 @@ HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData HAL_DMA_Start_IT(huart->hdmarx, (uint32_t)&huart->Instance->DR, *(uint32_t*)tmp, Size); /* Clear the Overrun flag just before enabling the DMA Rx request: can be mandatory for the second transfer */ - __HAL_UART_CLEAR_OREFLAG(huart); + /* If hardware flow control is enabled, then there shouldn't be an overrun and this would cause data loss since + __HAL_UART_CLEAR_OREFLAG reads the DR register. */ + if (!(huart->Instance->CR3 & USART_CR3_RTSE)) { + __HAL_UART_CLEAR_OREFLAG(huart); + } /* Process Unlocked */ __HAL_UNLOCK(huart); @@ -1052,7 +1056,11 @@ HAL_StatusTypeDef HAL_UART_DMAResume(UART_HandleTypeDef *huart) if(huart->RxState == HAL_UART_STATE_BUSY_RX) { /* Clear the Overrun flag before resuming the Rx transfer*/ - __HAL_UART_CLEAR_OREFLAG(huart); + /* If hardware flow control is enabled, then there shouldn't be an overrun and this would cause data loss since + __HAL_UART_CLEAR_OREFLAG reads the DR register. */ + if (!(huart->Instance->CR3 & USART_CR3_RTSE)) { + __HAL_UART_CLEAR_OREFLAG(huart); + } /* Reenable PE and ERR (Frame error, noise error, overrun error) interrupts */ SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);