cancel
Showing results for 
Search instead for 
Did you mean: 

UART Receive interrupt stuck issue

jeevankumar
Associate II

As per my understanding of stm32 uart driver interrupt code the usual process for receive interrupt is,

1. HAL_UART_Receive_IT:
It checks if huart->RxState is HAL_UART_STATE_READY.
If true, it sets huart->RxState to HAL_UART_STATE_BUSY_RX.

2. When Data is received into DataRegister HAL_UART_IRQHandler calls UART_Receive_IT.

3. UART_Receive_IT:
checks if huart->RxState is HAL_UART_STATE_BUSY_RX.
If true, and when all data bytes are received, it sets huart->RxState to HAL_UART_STATE_READY.

Issue Scenario: Suppose due to some noise on UART line USART_SR_RXNE bit is set and HAL_UART_IRQHandler is triggered, and it calls UART_Receive_IT. Now UART_Receive_IT checks if huart->RxState is HAL_UART_STATE_BUSY_RX , its Not since HAL_UART_Receive_IT was not called by user. In this case the UART_Receive_IT exits without doing anything and HAL_UART_IRQHandler keeps getting triggered repeatedly since USART_SR_RXNE is not cleared and execution is stuck. 

Am I missing something here? or by design stm32 uart interrupt is stuck and system hangs.

I'm using STM32F407IGTx microcontroller. 

Kindly advice.

6 REPLIES 6
TDK
Guru

If you are not actively receiving data (i.e. in HAL_UART_STATE_READY state), then the interrupt is disabled and the interrupt handler never gets called. Receiving characters in this state doesn't cause any code to run, but it will cause an overrun condition which causes problems the next time you try to receive characters.

If you feel a post has answered your question, please click "Accept as Solution".
Karl Yamashita
Lead III

Show your code. It's more than likely something that you're doing incorrectly. 

Probably calling UART_Receive_IT in the wrong place(s);

 

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.

Please don't post screen shots of code..

Use the </> icon in the editor to post enough code to be helpful.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jeevankumar
Associate II

I did some further debugging and found out what is happening. This issue happens whenever there is a noise on uart line. Sequence is as follows:

1. DeInit and Init UART which sets huart->RxState is HAL_UART_STATE_READY.

2. Transmit data using interrupt by calling HAL_UART_Transmit_IT, I see HAL_UART_TxCpltCallback is called and transmission is successful.

3. Next I call HAL_UART_Receive_IT which sets USART_SR_RXNE and wait for HAL_UART_RxCpltCallback, may be due to noise the HAL_UART_RxCpltCallback is not called and reception is unsuccessful, my timeout timer callback is called and I exit. But at this point USART_SR_RXNE is still set since HAL_UART_RxCpltCallback is never called.

4. for next data, I call DeInit and Init UART which sets huart->RxState is HAL_UART_STATE_READY. (Note that init doesn't clear USART_SR_RXNE)

5. Transmit data using interrupt by calling HAL_UART_Transmit_IT, At this point UART HAL_UART_IRQHandler is called. Inside the HAL_UART_IRQHandler first they check for Receive - UART_Receive_IT. In UART_Receive_IT it checks if(huart->RxState == HAL_UART_STATE_BUSY_RX) , since during init huart->RxState is HAL_UART_STATE_READY. the check is not satisfied and it exits UART IRQ handler without doing anything. But still since USART_SR_RXNE bit is not cleared the interrupt keeps repeatedly calling  HAL_UART_IRQHandler without giving time for any other tasks and system hangs.

void UART_driver_Deinit(void)
{
    HAL_UART_DeInit(&huart6);
}

void MX_USART6_UART_Init(uint32_t baudrate)
{
    huart6.Instance = USART6;
    huart6.Init.BaudRate = baudrate;
    huart6.Init.WordLength = UART_WORDLENGTH_8B;
    huart6.Init.StopBits = UART_STOPBITS_1;
    huart6.Init.Parity = UART_PARITY_NONE;
    huart6.Init.Mode = UART_MODE_TX_RX;
    huart6.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart6.Init.OverSampling = UART_OVERSAMPLING_16;
    if (HAL_HalfDuplex_Init(&huart6) != HAL_OK)
    {
        _Error_Handler();
    }
}

static void thrUART(void *argument) {
    uint32_t ErrorCode = 0U;
    static uint32_t PrvErrorCode_uart6 = 0;
    
    OneWire_Init(UART6_SELECT);
    
    for (;;) {
        ErrorCode = osThreadFlagsWait (UART_THREAD_SIGNAL_MASK, osFlagsWaitAny, osWaitForever); // Wait forever until thread flag
        
        if(ErrorCode & UART6_THREAD_SUCCESS)
        {
            uart6_comm_status = UART_NO_ERROR;
        }
        if(ErrorCode & UART6_THREAD_FAILED)
        {
            uart6_comm_status = UART_COMMUNICATION_ERROR;
            if(PrvErrorCode_uart6 != ErrorCode)
            {
                /*Error handling of communication failure*/
                PrvErrorCode_uart6 = ErrorCode;
            }
        }
        
        osThreadYield();
    }
}

int8_t create_Thread_UART (void)
{
    int8_t retvalue = 0;
    
    tid_thrUART = osThreadNew (thrUART, NULL, &Thread_UART_attr);	
    if(tid_thrUART == NULL)
    {
        retvalue = UART_COMMUNICATION_ERROR;
        _Error_Handler(); // Thread object not created, handle failure
    }
    
    return retvalue;
}

/*
    Return value:
    -2 = Transfer not complete due to Wait timer expired
    -1 = Transfer not complete due to failed callback
    0 = Transfer complete
*/
int32_t UART6_WriteBuf (uint8_t *buf, uint32_t len) {
    uart6_comm_status = UART_COMMUNICATION_ERROR;
    if(HAL_UART_Transmit_IT(&huart6, buf, len) == HAL_OK)
    {
        // Wait for the transfer complete and exit the wait if transfer timed out
        while(uart6_comm_status != UART_NO_ERROR && uart6_comm_status != UART_TIMEOUT_ERROR);
    }
    
    return uart6_comm_status;
}

int32_t UART6_ReadBuf (uint8_t *buf, uint32_t len) {
    
    uart6_comm_status = UART_COMMUNICATION_ERROR;
    if(HAL_UART_Receive_IT(&huart6, buf, len) == HAL_OK)
    {
        uart_callingThreadID = osThreadGetId(); //Get the caller ID to return from callback
        osThreadFlagsWait (UART6_THREAD_SUCCESS, osFlagsWaitAll, UART_WAIT_DELAY); // Wait forever until thread flag
    }
    
    return uart6_comm_status;
}

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if(huart->Instance==USART6)
    {
        /* Peripheral clock enable */
        __HAL_RCC_USART6_CLK_ENABLE();
        
        __HAL_RCC_GPIOG_CLK_ENABLE();
        /**USART6 GPIO Configuration    
            PG14     ------> USART6_TX 
        */
        GPIO_InitStruct.Pin = OneWire_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF8_USART6;
        HAL_GPIO_Init(OneWire_GPIO_Port, &GPIO_InitStruct);
        
        /* USART6 interrupt Init */
        HAL_NVIC_SetPriority(USART6_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(USART6_IRQn);
    }
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
{
    if(huart->Instance==USART6)
    {
        /* Peripheral clock enable */
        __HAL_RCC_USART6_CLK_DISABLE();
        
        __HAL_RCC_GPIOG_CLK_DISABLE();
        HAL_NVIC_DisableIRQ(USART6_IRQn);
    }
}

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART6)
    {
        if(huart->ErrorCode == 0)
        {
            uart6_comm_status = UART_NO_ERROR;
            osThreadFlagsSet(uart_callingThreadID, UART6_THREAD_SUCCESS);
        }
        else
        {
            osThreadFlagsSet(tid_thrUART, UART6_THREAD_FAILED);
        }
    }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if(huart->Instance == USART6)
    {
        if(huart->ErrorCode == 0)
        {
            uart6_comm_status = UART_NO_ERROR;
        }
        else
        {
            osThreadFlagsSet(tid_thrUART, UART6_THREAD_FAILED);
        }
    }
}

void Uart_timer_callback(void)
{
    uart6_comm_status = UART_TIMEOUT_ERROR; //Force exit the write complete waiting flag
}

Yeah, you wrote your HAL_UART_Receive_IT in the wrong place. 

 

You should call it once before your tasks start to run, to first initialize the UART interrupt.

 

Then in the HAL_UART_RxCpltCallback, you need to copy the byte(s) from the primary buffer to a secondary buffer, set your ready flag and call HAL_UART_Receive_IT. You need to that before you exit the interrupt so that way you don't miss any incoming bytes. Then in your tasks, you check the ready flag and process the data from the secondary buffer while the primary buffer is receiving data.

 

Because you exit the interrupt before you call HAL_UART_RxCpltCallback, your tasks, how many there may be, could be doing other stuff before you enable the interrupt again. If you're looking for a specific amount of bytes like 10 bytes, and you enable the interrupt later, you may have missed a byte or two? So what happens is that HAL_UART_RxCpltCallback is not called because it is still waiting for 2 more bytes. 

 

You're not showing enough code so no telling when you're saving the byte(s) to a buffer and when you're calling UART6_ReadBuf

 

 

 

 
 

 

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.

Thank you, Please find the  UART6_ReadBuf calling code

 

 

static uint8_t reset_slave(void)
{
  uint8_t cmd = 0x0F;
  uint8_t ret_val = 0xFF;
 
  UART_driver_Deinit();
  MX_USART6_UART_Init(9600);
  Start_uart_timer(200);
  UART6_WriteBuf(&cmd, 1);
  UART6_ReadBuf(&cmd, 1);
  Stop_uart_timer();
  if(cmd == 0xE0)
  {
    MX_USART6_UART_Init(115200);
    osDelay(20);
    ret_val = 0x00;
  }
  
  return ret_val;
}