Skip to content

Commit

Permalink
F4_HAL/uart: Fix data loss when using DMA Rx with hardware flow control.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
dlech committed Dec 6, 2020
1 parent 58fee7c commit 9cc7d02
Showing 1 changed file with 10 additions and 2 deletions.
12 changes: 10 additions & 2 deletions STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 9cc7d02

Please sign in to comment.