2026-03-13 10:29 AM
I think this is a very similar (perhaps the same) problem as described here. I replied to that thread but was asked to start a new thread - here it is. I'm reposting because I didn't feel the prior thread has a satisfactory answer (de-initialize and re-initialize the UART). HAL_UARTEx_ReceiveToIdle_DMA() seems to be intrinsically a very fragile function.
If I call `HAL_UARTEx_ReceiveToIdle_DMA()` when the UART receive line is actively receiving data (when the other end is transmitting). A good percentage of the time it returns HAL_ERROR.
I'm using STM32L4 HAL 1.13.6 (Cube 1.18.2).
The problem is that the UART generates an overrun error immediately when `UART_Start_Receive_DMA()` enables the overrun error. The overrun error overwrites `huart->ReceptionType = HAL_UART_RECEPTION_STANDARD` which causes `HAL_UARTEx_ReceiveToIdle_DMA()` to return HAL_ERROR as shown in the code snippet from `HAL_UARTEx_ReceiveToIdle_DMA()` below:
/* Set Reception type to reception till IDLE Event*/
huart->ReceptionType = HAL_UART_RECEPTION_TOIDLE;
huart->RxEventType = HAL_UART_RXEVENT_TC;
status = UART_Start_Receive_DMA(huart, pData, Size);
/* Check Rx process has been successfully started */
if (status == HAL_OK)
{
if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE) <<<< this check fails
{
__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_IDLEF);
ATOMIC_SET_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);
}
else
{
/* In case of errors already pending when reception is started,
Interrupts may have already been raised and lead to reception abortion.
(Overrun error for instance).
In such case Reception Type has been reset to HAL_UART_RECEPTION_STANDARD. */
status = HAL_ERROR; <<<< takes this path
}I verified the problem with a tracepoint on husart3.ReceptionType. This is the stack trace:
Thread #1 [main] 1 [core: 0] (Suspended : Signal : SIGTRAP:Trace/breakpoint trap) UART_EndRxTransfer() at stm32l4xx_hal_uart.c:3,810 0x801d6d8 HAL_UART_IRQHandler() at stm32l4xx_hal_uart.c:2,407 0x801c478 USART3_IRQHandler() at stm32l4xx_it.c:384 0x800d99a <signal handler called>() at 0xfffffffd UART_Start_Receive_DMA() at stm32l4xx_hal_uart.c:3,752 0x801d584 HAL_UARTEx_ReceiveToIdle_DMA() at stm32l4xx_hal_uart_ex.c:961 0x801dc2a StartUsart3Reception() at main.c:2,603 0x800a218 commandTaskMain() at main.c:3,385 0x800abe8 pxPortInitialiseStack() at port.c:222 0x8024c90
This is where UART_Start_Receive_DMA() is interrupted (stm32l4xx_hal_uart.c:3,752):
3751 /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */ 3752 ATOMIC_SET_BIT(huart->Instance->CR3, USART_CR3_EIE);
And here is where we are in HAL_UART_IRQHandler():
2401 if ((HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)) ||
2402 ((errorcode & (HAL_UART_ERROR_RTO | HAL_UART_ERROR_ORE)) != 0U))
2403 {
2404 /* Blocking error : transfer is aborted
2405 Set the UART state ready to be able to start again the process,
2406 Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
2407 UART_EndRxTransfer(huart); <<<<<< We are here
A dirty hack workaround is to loop on the UART initialization until it succeeds:
bool success = false;
for (int i = 0;i < 5; i ++) {
MX_USART3_UART_Init();
if (HAL_OK == HAL_UARTEx_ReceiveToIdle_DMA(&huart3, Usart3RxDMABuffer, sizeof(Usart3RxDMABuffer))){
success = true;
break;
}
MX_USART3_UART_DeInit();
vTaskDelay(100);
}This works for me - but I don't like it. I also considered de-initializing the UART receive GPIO prior to calling `HAL_UARTEx_ReceiveToIdle_DMA` and reinitializing immediately after - but that's not really any more palatable to me.
Either I'm doing something wrong with HAL, or there is a fundamental race condition in `HAL_UARTEx_ReceiveToIdle_DMA` if it is called while the UART receive line is seeing receive activity - which seems really fragile.
I would welcome any feedback - particularly if I'm doing something wrong that is creating this condition. Thanks in advance.
2026-03-13 11:19 AM - edited 2026-03-13 11:19 AM
If the RX line is low, you are jumping into the stream mid-character and will not get correct results. It seems correct to return HAL_ERROR in this case. What is your expectation of how it should behave--given that, mid-stream, the peripheral can't know where one character ends and the next begins? Returning HAL_OK along with invalid data seems problematic, no?
The system should be architected so that UART is idle when the peripheral is enabled. If you have no control over the incoming stream, one way is to brute-force enable it and ignore all characters prior to an IDLE event.
2026-03-13 11:29 AM
Thanks. I think you are implicitly acknowledging that
That's really all I need to know - I'll stick with my workaround - which seems to be reliable. In an ideal world I could always guarantee that the UART RX line will be idle when I initialize the UART, but that's not always possible.
2026-03-13 11:37 AM - edited 2026-03-13 11:41 AM
I'm only saying that based on your report. I'm not 100% convinced it isn't another issue. For example, characters received after the previous reception completed and before the next reception starts. I wouldn't expect ORE to get set.
On line 2407, do you know the reason it's hit? Shouldn't be DMAR, so is ORE or RTO set? That would surprise me.
HAL_UARTEx_ReceiveToIdle_DMA in circular mode works reliably (provided it's started with RX high).
Is this happening the first time MX_USART3_UART_Init is called?
2026-03-13 11:50 AM - edited 2026-03-13 11:51 AM
On line 2407 ORE is set. I didn't to think to check for USART_CR3_DMAR - I'm working on a different firmware branch at the moment, but I will make a note to repro. next week and check for DMAR. Thanks.
2026-03-14 6:43 AM
I think you have other things going on. If ORE is set, the peripheral has received 2 characters that haven't been read out. So you probably started a transaction, it completed, then a new transaction was started later on with the overrun error already there. Not ideal. You can clear the overrun flag before starting the new transaction.
2026-03-18 8:46 AM
I confirmed that the problem is the ORE ISR becoming pending after the call to MX_USART3_UART_Init() and before HAL_UARTEx_ReceiveToIdle_DMA() completes. Then when the UART error interrupt is enabled in UART_Start_Receive_DMA() the pending ORE error causes an immediate interrupt which calls UART_EndRxTransfer(), clearing ReceptionType and causing HAL_UARTEx_ReceiveToIdle_DMA() to return HAL_ERROR.
This is the very first time that these two functions are called - there is not a previous transaction. If I clear the ORE flag inside HAL_UARTEx_ReceiveToIdle_DMA() before calling UART_Start_Receive_DMA() it seems to prevent this issue, and I don't think this would have any negative side effects in my application:
HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
HAL_StatusTypeDef status;
/* Check that a Rx process is not already ongoing */
if (huart->RxState == HAL_UART_STATE_READY)
{
if ((pData == NULL) || (Size == 0U))
{
return HAL_ERROR;
}
/* Set Reception type to reception till IDLE Event*/
huart->ReceptionType = HAL_UART_RECEPTION_TOIDLE;
huart->RxEventType = HAL_UART_RXEVENT_TC;
__HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF); <<<<< Added
status = UART_Start_Receive_DMA(huart, pData, Size);