HAL串口中断接收数据,只能接收一两个字符?附串口中断源码分析

先分析以下源码,记录一下学习成果,如果你急切想解决标题所述问题,可以直接跳到文本。

如果你在HAL库下调试中断接收代码,那么下面这个API函数

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

你肯定不会感到陌生。此API用于接收指定长度字符串,当接收到Size个字符后,会触发中断,调用回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)`

它是一个_weak类型的函数,我们重写该函数执行所需功能即可。

先来分析一下中断接收函数(看注释)

HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{

/*检查一些标志位*/
  /* Check that a Rx process is not already ongoing */
  if (huart->RxState == HAL_UART_STATE_READY)
  {
    if ((pData == NULL) || (Size == 0U))
    {
      return HAL_ERROR;
    }

/*!!!!!!!!!!!!!!!!!!!!*/
//调用时上锁,有操作系统基础的童鞋可能比较容易理解锁的含义
//通俗点就是,现在huart这个串口已经上锁(被占用),其它外设/设备想要使用它时,需要先等锁huart这个锁被解开
    __HAL_LOCK(huart);

    /* Set Reception type to Standard reception */
    huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;

    if (!(IS_LPUART_INSTANCE(huart->Instance)))
    {
      /* Check that USART RTOEN bit is set */
      if (READ_BIT(huart->Instance->CR2, USART_CR2_RTOEN) != 0U)
      {
        /* Enable the UART Receiver Timeout Interrupt */
        SET_BIT(huart->Instance->CR1, USART_CR1_RTOIE);
      }
    }

/*!!!!!!!!!!!!!!!!!!!!!*/
/*调用UART_Start_Receive_IT(huart, pData, Size)*/
    return (UART_Start_Receive_IT(huart, pData, Size));
  }
  else
  {
    return HAL_BUSY;
  }
}

接着分析UART_Start_Receive_IT(huart, pData, Size),该函数引用了
HAL_StatusTypeDef HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)的参数

HAL_StatusTypeDef UART_Start_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{

/*设置一些参数*/
  huart->pRxBuffPtr  = pData;/*串口接收存储目标地址*/
  huart->RxXferSize  = Size; /*串口接收字符的数目*/
  huart->RxXferCount = Size; /*用于记录还剩余多少个字符要接收,当==0时会触发中断*/
  huart->RxISR       = NULL; /*中断回调函数*/

  /* Computation of UART mask to apply to RDR register */
  UART_MASK_COMPUTATION(huart);

  huart->ErrorCode = HAL_UART_ERROR_NONE;
  huart->RxState = HAL_UART_STATE_BUSY_RX;

  /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  /*使能错误中断,发送串口接收错误时(溢出等),会调用相应回调函数*/
  SET_BIT(huart->Instance->CR3, USART_CR3_EIE);

  /* Configure Rx interrupt processing */
  /*设置相应的中断处理函数*/
  if ((huart->FifoMode == UART_FIFOMODE_ENABLE) && (Size >= huart->NbRxDataToProcess))
  {
    /* Set the Rx ISR function pointer according to the data word length */
    if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
    {

/*16位字长接收函数*/
      huart->RxISR = UART_RxISR_16BIT_FIFOEN;
    }
    else
    {
/*8位字长接收函数*/
      huart->RxISR = UART_RxISR_8BIT_FIFOEN;
    }


/*!!!!!!!!!!!!!!!*/
//解锁,UART_Start_Receive_IT()中对它上了锁
    __HAL_UNLOCK(huart);

    /* Enable the UART Parity Error interrupt and RX FIFO Threshold interrupt */
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE);
    SET_BIT(huart->Instance->CR3, USART_CR3_RXFTIE);
  }


//大同小异
  else
  {
    /* Set the Rx ISR function pointer according to the data word length */
    if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
    {
      huart->RxISR = UART_RxISR_16BIT;
    }
    else
    {

/*!!!!!!!!!*/

/*记住这一句!!,等下回用到*/
      huart->RxISR = UART_RxISR_8BIT;
    }

    __HAL_UNLOCK(huart);

    /* Enable the UART Parity Error interrupt and Data Register Not Empty interrupt */
    SET_BIT(huart->Instance->CR1, USART_CR1_PEIE | USART_CR1_RXNEIE_RXFNEIE);
  }
  return HAL_OK;
}

以上函数设置了中断使能,当发生中断时,调用USART1_IRQHandler()

void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */

/*!!!!!!!!!!!!!!!*/
/*BUG1*/
//串口中断里面,不能使用printf(),HAL_UART_Transmit()等函数,否则就会导致中断接收异常,或者卡死!!!!!!!
//	printf("USART1_IRQHandler\r\n");/*注释掉*/
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
	
	
 
 /* HAL_UART_IRQHandler(&huart1);串口中断公共函数已经失能中断*/
 /*因此要重新开启,为下一次接收做准备*/
  HAL_UART_Receive_IT(&huart1, (uint8_t *)&ch,1);
	
 
  /* USER CODE END USART1_IRQn 1 */
}

void USART1_IRQHandler(void)
内部又调用了 HAL_UART_IRQHandler(&huart1);—>主要功能是清除一些中断标志位,和调用中断回调函数。

void HAL_UART_IRQHandler(UART_HandleTypeDef *huart)
{
		
	//printf("HAL_UART_IRQHandler\r\n");
  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)
      {
		/*调用UART_Start_Receive_IT()时,已经设置了对应的RxISR函数指针,如下注释所示*/
		/*      huart->RxISR = UART_RxISR_8BIT;
*/

/*调用该函数,接收定长数据,清除中断标志位,并调用回调函数*/
        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))))
  {
  /*巴拉巴拉,干一堆其它事情,详细可以研究源码*/
    .........
  }



}
static void UART_RxISR_8BIT(UART_HandleTypeDef *huart)
{
  uint16_t uhMask = huart->Mask;
  uint16_t  uhdata;

  /* Check that a Rx process is ongoing */
  if (huart->RxState == HAL_UART_STATE_BUSY_RX)
  {
  /*从RDR寄存器中接收一个字节数据*/
    uhdata = (uint16_t) READ_REG(huart->Instance->RDR);
    /*保存到目标地址中*/
    *huart->pRxBuffPtr = (uint8_t)(uhdata & (uint8_t)uhMask);
    /*地址++,为下一次接收做准备*/
    huart->pRxBuffPtr++;
    /*剩余字符数--,当==0时使能中断*/
    huart->RxXferCount--;

    if (huart->RxXferCount == 0U)
    {
      /* Disable the UART Parity Error Interrupt and RXNE interrupts */

/*!!!!!!!!!!!!!!!!!!!!!!!!!*/
      /*关闭UART奇偶校验错误中断和RXNE中断*/
      
      CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE_RXFNEIE | USART_CR1_PEIE));

      /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
      /*禁用UART错误中断(帧错误,噪声错误,溢出错误)*/
      CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

      /* Rx process is completed, restore huart->RxState to Ready */
      huart->RxState = HAL_UART_STATE_READY;

      /* Clear RxISR function pointer */
      /*清空*/
      huart->RxISR = NULL;

      /* Check current reception Mode :
         If Reception till IDLE event has been selected : */
      if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
      {
        /* Disable IDLE interrupt */
        CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);

#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered Rx Event callback*/
        huart->RxEventCallback(huart, huart->RxXferSize);
#else
        /*Call legacy weak Rx Event callback*/
        HAL_UARTEx_RxEventCallback(huart, huart->RxXferSize);
#endif /* (USE_HAL_UART_REGISTER_CALLBACKS) */
      }
      else
      {
        /* Standard reception API called */
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
        /*Call registered Rx complete callback*/

/*!!!!!!!!!!!!!!!!!!!!!!*/
/*_weak函数被重写,所以调用RxCpltCallback()回调函数*/
        huart->RxCpltCallback(huart);
#else
        /*Call legacy weak Rx complete callback*/
        HAL_UART_RxCpltCallback(huart);
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
      }
      huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;
    }
  }
  else
  {
    /* Clear RXNE interrupt flag */
    __HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
  }
}

HAL库的串口中断接收处理逻辑有点复杂,函数调用太多

回到标题所述问题:
产生中断接收出错,或者只能接收到一两个字符的情况,主要是原因是,在中断函数中(包括:HAL_UART_RxCpltCallback(),void USART1_IRQHandler(void)
, HAL_UART_IRQHandler(&huart1);
等中断相关函数),调用了printf(),HAL_UART_Transmit()等串口发送函数,可能你也是和我一样想在中断里打印一些信息,调试代码,糟心的是恰恰进入了死胡同!!!!
分析HAL_UART_Transmit()函数的源码就会发现,在串口发送函数中也会上锁,__HAL_LOCK(huart);从而导致程序卡死或者发生错误!!。
在这里插入图片描述

在这里插入图片描述

因此只需要把这些地方的串口打印函数全部注释掉即可,或者是将有可能在中断接收期间串口发送的函数注释掉。

卡了很久,记录下来分享一下
如果对你有帮助,点个赞让我知道一下。

如有错漏,请大佬指正

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
在使用STM32HAL库进行串口中断接收数据时,可以按照以下步骤进行操作: 1. 在代码中定义一个接收缓冲区,用于存放接收到的数据。例如,可以使用一个数组来存放接收到的数据,如引用\[1\]中的`uint8_t Rdata`。 2. 注册中断函数。在中断函数中,可以使用`HAL_UART_IRQHandler`函数来清空中断标志,取消中断使能,并调用回调函数。同时,可以将接收到的数据存放到接收缓冲区中,如引用\[3\]中的代码所示。 3. 在主程序中,可以通过调用`HAL_UART_Receive_IT`函数来启动串口接收中断。该函数会在每接收到一个字符时触发一次中断,并将接收到的字符存放到接收缓冲区中。 通过以上步骤,就可以实现STM32HAL库串口中断接收数据的功能。 #### 引用[.reference_title] - *1* *2* [STM32HAL库中断模式串口收发](https://blog.csdn.net/ABCisCOOL/article/details/113977863)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [STM32串口接收中断——基于HAL库](https://blog.csdn.net/a154299/article/details/86652801)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值