cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4 HAL UART DMA does not recover after error

Louis Cloete work
Associate III

The exact issue I am experiencing with the STM32L4 series (both L476 and L412), is described here: https://stackoverflow.com/a/71297149/10909834

The solution I came to independently is to reinitialize the uart. I used HAL_UART_DeInit() followed by HAL_UART_Init() calls, the poster of the SO answer called the MX_USARTx_UART_Init() function again.

I would like to know what happens during the procedure that recovers the uart. What is the minimum I need to do to recover?

I did implement the error callbacks, and called HAL_UART_Abort_IT() in it, with a HAL_UARTEx_ReceiveToIdle() call in the HAL_UART_AbortCplt() callback. The uart handle got stuck in DMA error state though, so I added a check for that in the main loop and just reinitialized the whole thing when I got in that state.

Do anybody understand what's going on and what is the correct procedure to elegantly recover the DMA and/or UART?

5 REPLIES 5
Karl Yamashita
Lead III

I've tested where I have the STM32 set for a baud rate of 115200kbs and Docklight set for 57600kbs. What I get is an HAL_UART_ERRROR_FE when I call HAL_UART_GetError().

So, for this test I just set an error flag. Then in a polling routine I check for the error flag and try to enable the DMA interrupt again. So far it recovers when initially using the wrong baud rate and then switching to the correct baud rate.

//#define  HAL_UART_ERROR_NONE             (0x00000000U)    /*!< No error                */
//#define  HAL_UART_ERROR_PE               (0x00000001U)    /*!< Parity error            */
//#define  HAL_UART_ERROR_NE               (0x00000002U)    /*!< Noise error             */
//#define  HAL_UART_ERROR_FE               (0x00000004U)    /*!< Frame error             */
//#define  HAL_UART_ERROR_ORE              (0x00000008U)    /*!< Overrun error           */
//#define  HAL_UART_ERROR_DMA              (0x00000010U)    /*!< DMA transfer error      */
//#define  HAL_UART_ERROR_RTO              (0x00000020U)    /*!< Receiver Timeout error  */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
	uint32_t errorCode;
 
	if(huart->Instance == uartDMA_RXMsg.huart->Instance)
	{
		errorCode = HAL_UART_GetError(huart); // for incorrect baud rate HAL_UART_ERROR_FE is returned.
		if(errorCode !=  HAL_UART_ERROR_NONE)
		{
			uartDMA_RXMsg.uart_dma_rxIntErrorFlag = true;
		}
	}
}
 
// called from a polling routine 
void UART_DMA_CheckRxInterruptErrorFlag(UART_DMA_RxQueueStruct *msg)
{
	if(msg->uart_dma_rxIntErrorFlag)
	{
		msg->uart_dma_rxIntErrorFlag = false;
		UART_DMA_EnableRxInterrupt(msg);
	}
}
 
// enable UART DMA interrupt. 
void UART_DMA_EnableRxInterrupt(UART_DMA_RxQueueStruct *msg)
{
	if(HAL_UARTEx_ReceiveToIdle_DMA(msg->huart, msg->queue[msg->ptr.iIndexIN].data, UART_DMA_MESSAGE_SIZE) != HAL_OK)
	{
		msg->uart_dma_rxIntErrorFlag = true;
	}
}
 
 

Tips and Tricks with TimerCallback https://www.youtube.com/@eebykarl
If you find my solution useful, please click the Accept as Solution so others see the solution.

I'll try to get a minimal reproducible example for the L476 Nucleo64 board.

Bob S
Principal

The stock HAL code halts DMA RX on error. You should just need to re-start RX with HAL_UART_Receive_DMA(). Might be able to do that from the error callback, though I've not tried. And you need to figure out where in your RX buffer to restart the DMA, and I'm not sure you can start it in the middle of a buffer as the HAL call expects the buffer pointer to be the beginning of the buffer.

I ended up hacking the HAL code so errors get reported but RX continues. I've seen other people disable the error interrupt bits (OV, FE, etc.) immediately after the HAL_UART_Receive_DMA() call so that the HAL UART interrupt handler doesn't react to those interrupts. But then you don't get notified of those errors. All depends what it important to you.

Or, as others on this forum often say - don't use HAL if it doesn't do work for you.

I think it should be possible to restart DMA reception, then set the DMA counter to put the data at an offset in the buffer. I'll try that.

Guenael Cadier
ST Employee

Dear @Community member​ 

When implementing you recovery mechanism (error callback calling HAL_UART_Abort_IT(), then calling HAL_UARTEx_ReceiveToIdle() in the HAL_UART_AbortCplt() callback), could you try to make sure ORE bit is cleared in ISR when restarting reception ?

For instance, you could systematically clear ORE bit by writing 1 in ORE bit in ICR just before calling the HAL_UARTEx_ReceiveToIdle().

(this could be done by calling __HAL_UART_CLEAR_OREFLAG(__HANDLE__) macro for example).

ORE bit at 1 could prevent further reception, I think.

Just in case ...

And if you succeed in getting a minimal example allowing to reproduce issue on L476, that could be shared, I'm interested.

Regards