资讯详情

STM32CubeMX-HAL库-UART串口接收中断回调函数代码分析

CubeMx中HAL在学习中,库函数的调用不同于库函数的调用CubeMx不懂串口通信HAL库中的回调函数是如何调用的,所以查看每个定义,参考别人写的博客,终于明白了HAL库中断调用不同于库函数。写这个博客是为了加深他们的理解,第二个是希望不理解HAL库中回调函数调用机制的朋友有帮助。

参考工程代码:【STM32】-CubeMX-HAL库-UART-串口通信-STM32F103C8T6-收发测试

在库函数中,UART串口中断时,我们直接将业务代码写在上面void USART1_IRQHandler(void)中,如下图:

void USART1_IRQHandler(void)                 ///串口1中断服务程序  {  u8 Res;   if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  ///接收中断(接收到的数据必须是0x0d 0x0a结尾)   {   Res =USART_ReceiveData(USART1); ////读取收到的数据      if((USART_RX_STA&0x8000)==0)//未完成接收    {    if(USART_RX_STA&0x4000)//收到0x0d     {     if(Res!=0x0a)USART_RX_STA=0./接收错误,重新开始     else USART_RX_STA|=0x8000; //接收完成了      }    else ///还没收到0X0D     {      if(Res==0x0d)USART_RX_STA|=0x4000;     else      {      USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;      USART_RX_STA  ;      if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0.//接收数据错误,重新开始接收         }        }    }            }   }   

对于CubeMX生成的代码,USART1_IRQHandler(void)为了提高中断效率,函数采用了回调机制。(中断关闭后可以处理业务代码,这样中断处理就不会占用太多时间来影响程序的执行效率)

USART1_IRQHandler(void)只调用函数HAL_UART_IRQHandler(&huart1)(可以在STM32f1xx_it.c中找到),参数为uart1的句柄huart1.句柄可以理解为通过huart1访问到uart如果不了解各种寄存器和数据类型,可以去看看。UART_HandleTypeDef结构体的定义。

static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart) {     用这个指针指向我们接收数据的变量或数组,在收发试验例程中定义char Res   uint8_t  *pdata8bits;   uint16_t *pdata16bits;    /* Check that a Rx process is ongoing */   if (huart->RxState == HAL_UART_STATE_BUSY_RX)   {     if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))     {       pdata8bits  = NULL;               pdata16bits = (uint16_t *) huart->pRxBuffPtr;//指向Res,相当于pdata16bits=&Res         //参考具体原因HAL_UART_Receive_IT(&huart1, &Res, 1);       *pdata16bits = (uint16_t)(huart->Instance->DR & (uint16_t)0x01FF);       huart->pRxBuffPtr  = 2U;     }     else     {       pdata8bits = (uint8_t *) huart->pRxBuffPtr;//指向Res,相当于pdata8bits=&Res       pdata16bits  = NULL;        if ((huart->Init.WordLength == UART_WORDLENGTH_9B) || ((huart->Init.WordLength == UART_WORDLENGTH_8B) && (huart->Init.Parity == UART_PARITY_NONE)))       {         ////指针操作 相当于Res= (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);         *pdata8bits = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);       }       else       {         ////指针操作 相当于Res= (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);         *pdata8bits = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);       }       huart->pRxBuffPtr  = 1U;     }      if (--huart->RxXferCount == 0U)//关闭中断,准备回调,保存串口接收到的数据     {       /* Disable the UART Data Register not empty Interrupt */       __HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);        /* Disable the UART Parity Error Interrupt */       __HAL_UART_DISABLE_IT(huart, UART_IT_PE);        /* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */       __HAL_UART_DISABLE_IT(huart, UART_IT_ERR);        /* Rx process is completed, restore huart->RxState to Ready */       huart->RxState = HAL_UART_STATE_READY;        /* Check current reception Mode :          If Reception till IDLE event has been selected : */       if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)       {         /* Set reception type to Standard */         huart->ReceptionType = HAL_UART_RECEPTION_STANDARD;          /* Disable IDLE interrupt */         CLEAR_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);          /* Check if IDLE flag is set */         if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE))         {           /* Clear IDLE flag in ISR */           __HAL_UART_CLEAR_IDLEFLAG(huart);         }  #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       }       else       {        /* Standard reception API called */ #if (USE_HAL_UART_REGISTER_CALLBACKS == 1)            /*Call registered Rx complete callback*/        huart->RxCpltCallback(huart); #ese
       /*Call legacy weak Rx complete callback*/
       HAL_UART_RxCpltCallback(huart);//正常情况下会执行这一条语句
        //我们可以自己定义这个函数内部的具体操作
#endif /* USE_HAL_UART_REGISTER_CALLBACKS */
      }

      return HAL_OK;
    }
    return HAL_OK;
  }
  else
  {
    return HAL_BUSY;
  }
}

表格中左右两边的操作为什么等价呢?

pdata8bits = (uint8_t *) huart->pRxBuffPtr; pdata8bits=&Res 
*pdata8bits = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF); Res= (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);

可以从UART_Start_Receive_IT函数中找到答案

(UART_Start_Receive_IT被main函数中的HAL_UART_Receive_IT调用)

//函数参数相当于(&huart1, &Res, 1)
HAL_StatusTypeDef UART_Start_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
{
  //函数参数相当于(&huart1, &Res, 1)
  huart->pRxBuffPtr = pData;//pData==&Res 
  //在UART_Receive_IT函数中 pdata8bits = (uint8_t *) huart->pRxBuffPtr;
  //pdata8bits=&Res,其他都是同理,如果不理解,需要回顾一下指针操作。
  huart->RxXferSize = Size;//Size==1
  huart->RxXferCount = Size;

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

  /* Process Unlocked */
  __HAL_UNLOCK(huart);

  /* Enable the UART Parity Error Interrupt */
  __HAL_UART_ENABLE_IT(huart, UART_IT_PE);

  /* Enable the UART Error Interrupt: (Frame error, noise error, overrun error) */
  __HAL_UART_ENABLE_IT(huart, UART_IT_ERR);

  /* Enable the UART Data Register not empty Interrupt */
  __HAL_UART_ENABLE_IT(huart, UART_IT_RXNE);

  return HAL_OK;
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//自定义回调函数 在UART_Receive_IT()中调用
{
	//判断是哪个串口触发的中断  huart1.Instance = USART1;定义在MX_USART1_UART_Init中
	if(huart==&huart1)//huart ->Instance == USART1两种判断条件等价
	{
		if((UART1_RX_STA & 0x8000)==0)//接收未完成&位运算符 &&短路与判断
		{
			if(UART1_RX_STA & 0x4000)//接收到 \r
			{
				if(Res==0x0a)//下一个必须是接收 \n
					UART1_RX_STA|=0x8000;
				else
					UART1_RX_STA=0;
			}
			else //未接收到\r
			{
				if(Res==0x0d)//Receive \r
				{
					UART1_RX_STA|=0x4000;
				}
				else
				{
					UART1_RX_Buffer[UART1_RX_STA&0X3FFF]=Res;
					UART1_RX_STA++;
					if(UART1_RX_STA>UART1_REC_LEN-1) UART1_RX_STA=0;//如果接收数据大于200Byte 重新开始接收
				}
			}
		}
		HAL_UART_Receive_IT(&huart1, &Res, 1);//完成一次接受,再此开启中断
	}
}

        如果我们自己没有定义回调函数的话,系统会调用自带的回调函数,函数类型为__weak,表示弱定义

        如果用户自己定义了函数就优先调用用户定义的回调函数

__weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);
  /* NOTE: This function should not be modified, when the callback is needed,
           the HAL_UART_RxCpltCallback could be implemented in the user file
   */
}

        如果对你有帮助,请点个赞再走,Respect!

标签: cr1mt交流功率固态继电器cr1u系列特殊型固态继电器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台