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访问到uart1的各种寄存器和数据类型,不理解的话,可以去看UART_HandleTypeDef结构体的定义。

在HAL_UART_IRQHandler(UART_HandleTypeDef *huart)函数内又调用了UART_Receive_IT(huart);(回调函数在这个函数中被调用)看注释理解该函数的作用

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);#else       /*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)//下一个必须是接收 \nUART1_RX_STA|=0x8000;elseUART1_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!