解决STM32串口溢出中断问题的方法

应用原理

STM32单片机与传感器通过串口通信,单片机串口采用中断方式接收传感器数据,同时单片机控制传感器上电。

问题描述

在一批设备中,有些设备开机能够正常读取传感器数据,有一小部分读取不到传感器的数据,出现了异常情况。

问题排查

1.确定传感器是否正常发数据

示波器测量传感器的TX,波形正常。

2.在线调试

在线调试模式下,在串口接收中断中打断点,发现异常的设备无法进入串口中断。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  uint8_t res;
	if(huart->Instance == USART4)//
	{
		USART4_RX_Buff[Usart4_RX_Cnt] = RX4_Buffer;
		if(USART4_RX_Buff[0]==0xff)
		{
		  Usart4_RX_Cnt++;
			if(Usart4_RX_Cnt>8) 
			{
				Usart4_RX_Cnt = 0;
				Usart4_RX_Flag = 1;
			}
		}
		else
		{
		 Usart4_RX_Cnt = 0;
		 Usart4_RX_Flag = 0;
		}	 
	 HAL_UART_Receive_IT(&huart4,&RX4_Buffer,1);//再次开启接收中断 
	}
}

这个时候查看串口相关参数,ErrorCode的值是8.

在stm32g0xx_hal_uart.h文件中,ErrorCode值为8表示Overrun error(溢出错误)。

/** @defgroup UART_Error_Definition   UART Error Definition
  * @{
  */
#define  HAL_UART_ERROR_NONE             ((uint32_t)0x00000000U)    /*!< No error                */
#define  HAL_UART_ERROR_PE               ((uint32_t)0x00000001U)    /*!< Parity error            */
#define  HAL_UART_ERROR_NE               ((uint32_t)0x00000002U)    /*!< Noise error             */
#define  HAL_UART_ERROR_FE               ((uint32_t)0x00000004U)    /*!< Frame error             */
#define  HAL_UART_ERROR_ORE              ((uint32_t)0x00000008U)    /*!< Overrun error           */
#define  HAL_UART_ERROR_DMA              ((uint32_t)0x00000010U)    /*!< DMA transfer error      */
#define  HAL_UART_ERROR_RTO              ((uint32_t)0x00000020U)    /*!< Receiver Timeout error  */

查看程序,发现使用STM32CubeMX配置软件时USART4的溢出中断配置为enable(默认配置,没有动),下面是窗口中断代码。

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
 uint32_t isrflags   = READ_REG(huart->Instance->ISR);
  uint32_t cr1its     = READ_REG(huart->Instance->CR1);
  uint32_t cr3its     = READ_REG(huart->Instance->CR3);

  uint32_t errorflags;
  uint32_t errorcode;

  /* If no error occurs */
  errorflags = (isrflags & (uint32_t)(USART_ISR_PE | USART_ISR_FE | USART_ISR_ORE | USART_ISR_NE | USART_ISR_RTOF));
  if (errorflags == 0U)
  {
    /* UART in mode Receiver ---------------------------------------------------*/
    if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
            || ((cr3its & USART_CR3_RXFTIE) != 0U)))
    {
      if (huart->RxISR != NULL)
      {
        huart->RxISR(huart);
      }
      return;
    }
  }

  /* If some errors occur */
  if ((errorflags != 0U)
      && ((((cr3its & (USART_CR3_RXFTIE | USART_CR3_EIE)) != 0U)
           || ((cr1its & (USART_CR1_RXNEIE_RXFNEIE | USART_CR1_PEIE | USART_CR1_RTOIE)) != 0U))))
  {
    /* UART parity error interrupt occurred -------------------------------------*/
    if (((isrflags & USART_ISR_PE) != 0U) && ((cr1its & USART_CR1_PEIE) != 0U))
    {
      __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_PEF);

      huart->ErrorCode |= HAL_UART_ERROR_PE;
    }

    /* UART frame error interrupt occurred --------------------------------------*/
    if (((isrflags & USART_ISR_FE) != 0U) && ((cr3its & USART_CR3_EIE) != 0U))
    {
      __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_FEF);

      huart->ErrorCode |= HAL_UART_ERROR_FE;
    }

    /* UART noise error interrupt occurred --------------------------------------*/
    if (((isrflags & USART_ISR_NE) != 0U) && ((cr3its & USART_CR3_EIE) != 0U))
    {
      __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_NEF);

      huart->ErrorCode |= HAL_UART_ERROR_NE;
    }

    /* UART Over-Run interrupt occurred -----------------------------------------*/
    if (((isrflags & USART_ISR_ORE) != 0U)
        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U) ||
            ((cr3its & (USART_CR3_RXFTIE | USART_CR3_EIE)) != 0U)))
    {
      __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF);

      huart->ErrorCode |= HAL_UART_ERROR_ORE;
    }

    /* UART Receiver Timeout interrupt occurred ---------------------------------*/
    if (((isrflags & USART_ISR_RTOF) != 0U) && ((cr1its & USART_CR1_RTOIE) != 0U))
    {
      __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_RTOF);

      huart->ErrorCode |= HAL_UART_ERROR_RTO;
    }
    /*省略若干代码*/
    errorcode = huart->ErrorCode;
      if ((HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)) ||
          ((errorcode & (HAL_UART_ERROR_RTO | HAL_UART_ERROR_ORE)) != 0U))
      {
        /* 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);
         /*省略若干代码*/
        }
}

从上述代码可以看出如果串口一切正常,是能够进入中断接收函数的,进入中断后会先关闭中断,接收中断执行完成后再次打开中断。

 if (errorflags == 0U)
  {
    /* UART in mode Receiver ---------------------------------------------------*/
    if (((isrflags & USART_ISR_RXNE_RXFNE) != 0U)
        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U)
            || ((cr3its & USART_CR3_RXFTIE) != 0U)))
    {
      if (huart->RxISR != NULL)
      {
        huart->RxISR(huart);//进入串口接收中断,进入回调函数
      }
      return;
    }
  }

但是一旦出现溢出错误,在溢出错误中断中关闭的RX中断,因为代码是在正常的串口中断回调函数中再次打开接收中断,所以当溢出错误中断关闭了RX接收中断后,就再也无法进入接收中断了。

出现溢出中断的原因

示波器测量传感器电源和单片机RX波形,发现RX在上电之初有一个下降沿,此时单片机的中断还没打开。

实际测量串口初始化与打开串口中断之间大约有40ms的间隔。

  MX_USART4_UART_Init();
  MX_RTC_Init();
  MX_IWDG_Init();//实际上是这一个操作耗时大约40ms
  /* Initialize interrupts */
  MX_NVIC_Init();
  HAL_UART_Receive_IT(&huart4,&RX4_Buffer,1);//开启接收中断 

出现溢出中断的原因就是串口初始化之后单片机的RX引脚出现了一个下降沿,单片机认为是一个起始位,开始接收数据,但是这个时候串口中断还没打开,所以造成了溢出中断。
STM32的数据手册是这样描述的:

解决方法(软件处理)

  1. 配置串口时,溢出中断配置为disenable,也就是说关闭溢出中断。
  2. 串口的RX引脚配置为上拉模式。
  3. 串口初始化之后立即打开中断。
    以上任意一种方式都可以解决,为了防止出现其他不可控问题,3条处理方式我都使用。
物联沃分享整理
物联沃-IOTWORD物联网 » 解决STM32串口溢出中断问题的方法

发表评论