Skip to main content
MMath.10
Associate III
March 7, 2019
Solved

using HAL_UART_Transmit with safety

  • March 7, 2019
  • 17 replies
  • 4700 views

Hi,

I'm using the USART function HAL_UART_Transmit for debugging.

Most of the time everything works well.

But sometimes the HAL_UART_ErrorCallback is called... And all my program freeze.

Could you help me with these 2 options (first is best for me) :

how can i get back to a functionnal state after a HAL_UART_ErrorCallback call ?

how can i check if UART is ready to send data before using HAL_UART_Transmit ?

Regards

This topic has been closed for replies.
Best answer by Bob S

I'm not sure what you mean be "the visible code" - you have the source for the entire HAL_UART code (i.e. the file in which you found the source for HAL_UART_Transmit_DMA() above). Search that file for all mentions of "HAL_UART_ErrorCallBack()". Functions that start with underscores, like __HAL_DMA_Enable_IT() are actually macros that are defined in the associated .h file (i.e. stm32l0xx_hal_uart.h or something like that).

As an aside - if your editor does not have a "search all files in this project" function, or a "go to the definitiion of this function", or a "show me all the references to this function" - get a new editor or IDE. Or learn how to use these features in the editor that you are using. They will save you SOOOOOO much time.

Not having debugger access on your board makes things more difficult. Time to go back to old school debugging. A first step would be to add code to your HAL_UART_ErrorCallBack() to dump all of the UART and DMA control and status registers to a debug serial port (which I hope you have). Then look and see what kind of error is indicated. If that doesn't help, you will have to (temporarily) change the code in HAL UART source whereever the error callback is called to somehow tell your program what error happened.

17 replies

Bob S
Super User
March 7, 2019

HAL_UART_Transmit() should never call HAL_UART_ErrorCallback(), so something is fishy here. The error callback is only used with interrupt and/or DMA transmit and receive.

Are you using any other UART? Are you ever calling HAL_UART_Transmit_IT() or HAL_UART_Transmit_DMA() or the equivalent "receive" functions?

The error callback function is passed a pointer to a UART handle. Set a breakpoint in HAL_UART_ErrorCallback() and look at which structure is passed. Look at the "instance" pointer and see which UART it points to. You can also use the HAL_UART_GetError() function/macro to get the error code.

MMath.10
MMath.10Author
Associate III
March 8, 2019

Hi,

yes i use HAL_UART_Transmit_DMA for a second USART.

But i use the printf with this code and i think is safe :

int32_t TraceSendBle( const char *strFormat, ...)
{
 char buf[TEMPBUFSIZE];
 va_list vaArgs;
 uint8_t* buffer;
 va_start( vaArgs, strFormat);
 uint16_t bufSize=vsnprintf(buf,TEMPBUFSIZE,strFormat, vaArgs);
 va_end(vaArgs);
 int status=0;
 
 status =circular_queue_add(&MsgTraceQueue,(uint8_t*)buf, bufSize);
 
 if ((status==0 ) && (TracePeripheralReady==SET))
 {
 circular_queue_get(&MsgTraceQueue,&buffer,&bufSize);
 vcom_Trace(buffer, bufSize);
 }
 
 return status;
}
void vcom_Trace( uint8_t *p_data, uint16_t size )
{
 HAL_UART_Transmit_DMA(&huart2,p_data, size);
}

 EDIT:

maybe is due to my change to desactivate low power mode :

static void Trace_TxCpltCallback(void)
{
 int status;
 uint8_t* buffer;
 uint16_t bufSize;
 
 //BACKUP_PRIMASK();
 
 //DISABLE_IRQ(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/
 /* Remove element just sent to UART */
 circular_queue_remove(&MsgTraceQueue);
 //DBG_GPIO_SET(GPIOB, GPIO_PIN_13);
 //DBG_GPIO_RST(GPIOB, GPIO_PIN_13);
 /* Sense if new data to be sent */
 status=circular_queue_sense(&MsgTraceQueue);
 
 if ( status == 0)
 {
 circular_queue_get(&MsgTraceQueue,&buffer,&bufSize);
 //RESTORE_PRIMASK();
 //DBG_GPIO_SET(GPIOB, GPIO_PIN_14);
 //DBG_GPIO_RST(GPIOB, GPIO_PIN_14);
 OutputTrace(buffer, bufSize);
 }
 else
 {
 //DBG_GPIO_SET(GPIOB, GPIO_PIN_12);
 
 //LPM_SetStopMode(LPM_UART_TX_Id , LPM_Enable );
 //TracePeripheralReady = SET;
 //RESTORE_PRIMASK();
 }
}

Bob S
Super User
March 8, 2019

As I mentioned in my first post - Add code to the error callback, set a breakpoint and see what the error is. Look back through the call stack to see exactly which line (in the HAL_UART code) generated the error. Until you do that, everything else is just a guess.

MMath.10
MMath.10Author
Associate III
March 8, 2019

error code is 0, i don't understand...

is use this code to have it :

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
 /* Initialization Error */
 advanten_error_code = huart->ErrorCode;
 Error_Handler();
}

Today i work on my watchdog which permit to restart but i prefer to found the source of the problem.

Bob S
Super User
March 8, 2019

Hmmmm... I don't know which CPU you are using, but looking at the L4xx HAL code, there are cases where the error callback is called with the error still at zero. So... set a breakpoint in the error callback, look at the call stack and figure out which path led to the error.

Bob S
Bob SBest answer
Super User
March 11, 2019

I'm not sure what you mean be "the visible code" - you have the source for the entire HAL_UART code (i.e. the file in which you found the source for HAL_UART_Transmit_DMA() above). Search that file for all mentions of "HAL_UART_ErrorCallBack()". Functions that start with underscores, like __HAL_DMA_Enable_IT() are actually macros that are defined in the associated .h file (i.e. stm32l0xx_hal_uart.h or something like that).

As an aside - if your editor does not have a "search all files in this project" function, or a "go to the definitiion of this function", or a "show me all the references to this function" - get a new editor or IDE. Or learn how to use these features in the editor that you are using. They will save you SOOOOOO much time.

Not having debugger access on your board makes things more difficult. Time to go back to old school debugging. A first step would be to add code to your HAL_UART_ErrorCallBack() to dump all of the UART and DMA control and status registers to a debug serial port (which I hope you have). Then look and see what kind of error is indicated. If that doesn't help, you will have to (temporarily) change the code in HAL UART source whereever the error callback is called to somehow tell your program what error happened.

MMath.10
MMath.10Author
Associate III
March 11, 2019

Thx Bob,

I use sw4stm32 with Eclipse and I easily find what I'm looking for if I know what I'm looking for. ^^

At first I read this and i'm not really happy with this part ,

 /* Non Blocking error : transfer could go on. 
 Error is notified to user through user error callback */
 HAL_UART_ErrorCallback(huart);
 huart->ErrorCode = HAL_UART_ERROR_NONE;

some error is not blocking and my error callback look like this, waiting the wadchdog reset the stm32...

void Error_Handler(void)
{
 PRINTF("Error_Handler %d\n\r",advanten_error_code);
 while (1)
 {
 ;
 }
}

Adding some error code find the error exactly here :

 /* Non Blocking error : transfer could go on. 
 Error is notified to user through user error callback */
 	 huart->ErrorCode = 0x40;
 HAL_UART_ErrorCallback(huart);
 huart->ErrorCode = HAL_UART_ERROR_NONE;
 }

So i had a non blocking error.

I had this part in the handler

void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
 /* Initialization Error */
 if (huart->ErrorCode == 0x40)
 {
 PRINTF("HAL_UART_ErrorCallback not blocking\n\r");
 }
 else
 {
 advanten_error_code = huart->ErrorCode;
 Error_Handler();
 }
}

Thanks

Bob S
Super User
March 12, 2019

Almost, but not quite. That "if" statement you show is indeed the "if" that is causing your error callback to be called. However, if you really are getting the "non blocking" callback, that "if" statement is evaluating to FALSE (i.e. the code goes to the "else" clause"). At least that is what happens in the 32L4xx HAL code. So this means that ORE is "reset" (no overrun error) AND that DMA is DIS-abled (that is a big clue here).

So go back to my initial debug suggestions. In you callback, what does the "huart" point to? Which of your UART structures? I don't know that you ever answered that question. Read the control and status registers for that uart. What do you see? Looking at those register values, can you look at the HAL_UART_IRQHandler() code and verify how it got to calling the error callback? Post the values here if you can't see what is going on.

Hmmmm.... actually, since you cannot run the debugger on this hardware and single-step through the HAL_UART_IRQHandler(), you will need to modify the code. HAL_UART_IRQHandler() reads the UART's ISR, CR1 and CR3 registers, and then clears some of the error/interrupt flags as it processes things (via __HAL_UIART_CLEAR_FLAG). You will need to change this to store a copy of the ISR register in some global variable where your error callback function can read it, so you can see the un-modified version. At a quick glance, it don't THINK it alters the contents of CR1 or CR3, so you should be able to read those directly from the USART peripheral. Though if you save the ISR to a global variable, you may as well save the CR1 and CR3 values as well.

MMath.10
MMath.10Author
Associate III
March 12, 2019

"if you really are getting the "non blocking" callback, that "if" statement is evaluating to FALSE "

=> yes

So this means that ORE is "reset" (no overrun error) AND that DMA is DIS-abled (that is a big clue here)

=> OK, DMA disabled is odd, maybe the error come from the receive function?

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
 if (HAL_UART_ERROR_NONE == huart->ErrorCode) {
 CMD_GetChar(&charRx);
 }
 HAL_UART_Receive_IT(huart, &charRx,1);
}

The huart is defined here,

used in DMA mode for TX and IT mode for RX

void vcom_Init( void (*TxCb)(void) )
{
 
 /*Record Tx complete for DMA*/
 TxCpltCallback=TxCb;
 /*## Configure the UART peripheral ######################################*/
 /* Put the USART peripheral in the Asynchronous mode (UART Mode) */
 /* UART1 configured as follow:
 - Word Length = 8 Bits
 - Stop Bit = One Stop bit
 - Parity = ODD parity
 - BaudRate = 921600 baud
 - Hardware flow control disabled (RTS and CTS signals) */
 huart2.Instance = USART2;
 huart2.Init.BaudRate = 115200;
 huart2.Init.WordLength = UART_WORDLENGTH_8B;
 huart2.Init.StopBits = UART_STOPBITS_1;
 huart2.Init.Parity = UART_PARITY_NONE;
 huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
 huart2.Init.Mode = UART_MODE_TX_RX;
 huart2.Init.OverSampling = UART_OVERSAMPLING_16;
 huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
 huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
 
 if(HAL_UART_Init(&huart2) != HAL_OK)
 {
 /* Initialization Error */
 advanten_error_code = 1;
 Error_Handler();
 }
 
}

HAL_UART_IRQHandler() called the error callback where i show you :

/* Call UART Error Call back function if need be --------------------------*/
 if(huart->ErrorCode != HAL_UART_ERROR_NONE)
 {
 /* UART in mode Receiver ---------------------------------------------------*/
 if(((isrflags & USART_ISR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
 {
 UART_Receive_IT(huart);
 }
 
 /* If Overrun error occurs, or if any error occurs in DMA mode reception,
 consider error as blocking */
 if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) ||
 (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)))
 { 
 /* Blocking error : transfer is aborted
 Set the UART state ready to be able to start again the process,
 Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
 UART_EndRxTransfer(huart);
 
 /* Disable the UART DMA Rx request if enabled */
 if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
 {
 CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
 
 /* Abort the UART DMA Rx channel */
 if(huart->hdmarx != NULL)
 {
 /* Set the UART DMA Abort callback : 
 will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
 huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;
 
 /* Abort DMA RX */
 if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
 {
 /* Call Directly huart->hdmarx->XferAbortCallback function in case of error */
 huart->hdmarx->XferAbortCallback(huart->hdmarx);
 }
 }
 else
 {
 /* Call user error callback */
 HAL_UART_ErrorCallback(huart);
 }
 }
 else
 {
 /* Call user error callback */
 HAL_UART_ErrorCallback(huart);
 }
 }
 else
 {
 /* Non Blocking error : transfer could go on. 
 Error is notified to user through user error callback */
 	huart->ErrorCode = 0x40;
 HAL_UART_ErrorCallback(huart);
 huart->ErrorCode = HAL_UART_ERROR_NONE;
 }
 }
 return;
 
 } /* End if some error occurs */

Here the ISR CR1 and CR3 value in the error callback

ISR = 6010D0

CR1 = 012D

CR3 = 0001

Good news,I managed to fix the problem of the character blocking in the RX buffer.

Make some other test but it works with a wooden cane

EDIT: ISR seems to say there is no error. ORE NF and FE are set to 0

MMath.10
MMath.10Author
Associate III
March 12, 2019

I don't understand why my error code in the callback is 0 before i change it to 0x40.

The first test (lign 2) is verify ErrorCode is != 0...

 /* Call UART Error Call back function if need be --------------------------*/
 if(huart->ErrorCode != HAL_UART_ERROR_NONE)
 {
 /* UART in mode Receiver ---------------------------------------------------*/
 if(((isrflags & USART_ISR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
 {
 UART_Receive_IT(huart);
 }
 
 /* If Overrun error occurs, or if any error occurs in DMA mode reception,
 consider error as blocking */
 if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) ||
 (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)))
 { 
 /* Blocking error : transfer is aborted
 Set the UART state ready to be able to start again the process,
 Disable Rx Interrupts, and disable Rx DMA request, if ongoing */
 UART_EndRxTransfer(huart);
 
 /* Disable the UART DMA Rx request if enabled */
 if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR))
 {
 CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);
 
 /* Abort the UART DMA Rx channel */
 if(huart->hdmarx != NULL)
 {
 /* Set the UART DMA Abort callback : 
 will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */
 huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError;
 
 /* Abort DMA RX */
 if(HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK)
 {
 /* Call Directly huart->hdmarx->XferAbortCallback function in case of error */
 huart->hdmarx->XferAbortCallback(huart->hdmarx);
 }
 }
 else
 {
 /* Call user error callback */
 HAL_UART_ErrorCallback(huart);
 }
 }
 else
 {
 /* Call user error callback */
 HAL_UART_ErrorCallback(huart);
 }
 }
 else
 {
 /* Non Blocking error : transfer could go on. 
 Error is notified to user through user error callback */
 	huart->ErrorCode |= 0x40;
 HAL_UART_ErrorCallback(huart);
 huart->ErrorCode = HAL_UART_ERROR_NONE;
 }
 }
 return;

Bob S
Super User
March 12, 2019

(1) The ISR value that you posted, did you get that by reading the ISR register in your callback function, or did you alter the HAL_UART_ISRHandler() to save its values somewhere? My guess is you read the huart->instance->ISR in your error callback function, but I may be wrong.

(2) This is basic debugging - Follow/Simulate (in your head or on paper) the HAL_UART_ISRHandler() code from the beginning of the function, presuming that the interrupt is caused by receiving a character (which your previous posts point to) along with some kind of error except overrun. Look at what happens in each function called from HAL_UART_ISRHandler() before it calls the error callback. Hint - do any of them modify huart->ErrorCode?

MMath.10
MMath.10Author
Associate III
March 14, 2019

1- yes in the callback

value in the handler :

Error = 0008

Isr = 6110F8

Cr1 = 012D

CR3 = 0001

My Rx handler is not fast enough if i understand well? a character stay in the rx buffer before i read it?

  

MMath.10
MMath.10Author
Associate III
March 15, 2019

Hi @Bob S​ 

i found the source of the problem, i use these function when i receive char on uart.

LORA_Init( &LoRaMainCallbacks, &LoRaParamInit);
 LORA_Join();

problem solved by doing one thing at a time

regards