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) 関数はコールバック メカニズムを使用して割り込み効率を向上させます。(業務コードは割り込みがクローズされた後に処理できるため、割り込み処理に時間がかかりすぎず、プログラムの実行効率に影響を与えません)
HAL_UART_IRQHandler(&huart1) のみが USART1_IRQHandler(void) 関数 (STM32f1xx_it.c にある) で呼び出され、パラメータは uart1 のハンドル huart1 であり、ハンドルは huart1 を介して uart1 のさまざまなレジスタとデータ型にアクセスするものとして理解できます。わからない場合は、UART_HandleTypeDef 構造体の定義を参照してください。
UART_Receive_IT(huart); は HAL_UART_IRQHandler(UART_HandleTypeDef *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)//下一个必须是接收 \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
*/
}