最近筆者開始學習STM32的HAL庫,由於以前一直用標准庫進行開發,於是發現了HAL庫幾點好玩的地方,在此分享。
1.句柄
在STM32的標准庫中,假設我們要初始化一個外設(這里以USART為例)
我們首先要初始化他們的各個寄存器。在標准庫中,這些操作都是利用固件庫結構體變量+固件庫Init函數實現的:
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數據格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數據流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發模式
USART_Init(USART3, &USART_InitStructure); //初始化串口1
可以看到,要初始化一個串口,需要對六個位置進行賦值,然后引用Init函數,並且USART_InitStructure並不是一個全局結構體變量,而是只在函數內部的局部變量,初始化完成之后,USART_InitStructure就失去了作用。
而在HAL庫中,同樣是USART初始化結構體變量,我們要定義為全局變量。
UART_HandleTypeDef UART1_Handler;
- 1
右鍵查看結構體成員
typedef struct
{
USART_TypeDef *Instance; /*!< UART registers base address */
UART_InitTypeDef Init; /*!< UART communication parameters */
uint8_t *pTxBuffPtr; /*!< Pointer to UART Tx transfer Buffer */
uint16_t TxXferSize; /*!< UART Tx Transfer size */
uint16_t TxXferCount; /*!< UART Tx Transfer Counter */
uint8_t *pRxBuffPtr; /*!< Pointer to UART Rx transfer Buffer */
uint16_t RxXferSize; /*!< UART Rx Transfer size */
uint16_t RxXferCount; /*!< UART Rx Transfer Counter */
DMA_HandleTypeDef *hdmatx; /*!< UART Tx DMA Handle parameters */
DMA_HandleTypeDef *hdmarx; /*!< UART Rx DMA Handle parameters */
HAL_LockTypeDef Lock; /*!< Locking object */
__IO HAL_UART_StateTypeDef State; /*!< UART communication state */
__IO uint32_t ErrorCode; /*!< UART Error code */
}UART_HandleTypeDef;
我們發現,與標准庫不同的是,該成員不僅包含了之前標准庫就有的六個成員(波特率,數據格式等),還包含過采樣、(發送或接收的)數據緩存、數據指針、串口 DMA 相關的變量、各種標志位等等要在整個項目流程中都要設置的各個成員。
該 UART1_Handler 就被稱為串口的句柄
它被貫穿整個USART收發的流程,比如開啟中斷:
HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);
比如后面要講到的MSP與Callback回調函數:
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
在這些函數中,只需要調用初始化時定義的句柄UART1_Handler就好。
2.MSP函數
MCU Specific Package 單片機的具體方案
MSP是指和MCU相關的初始化,引用一下正點原子的解釋,個人覺得說的很明白:
我們要初始化一個串口,首先要設置和 MCU 無關的東西,例如波特率,奇偶校驗,停止
位等,這些參數設置和 MCU 沒有任何關系,可以使用 STM32F1,也可以是 STM32F2/F3/F4/F7
上的串口。而一個串口設備它需要一個 MCU 來承載,例如用 STM32F4 來做承載,PA9 做為發
送,PA10 做為接收,MSP 就是要初始化 STM32F4 的 PA9,PA10,配置這兩個引腳。所以 HAL
驅動方式的初始化流程就是:HAL_USART_Init()—>HAL_USART_MspInit() ,先初始化與 MCU
無關的串口協議,再初始化與 MCU 相關的串口引腳。在 STM32 的 HAL 驅動中
HAL_PPP_MspInit()作為回調,被 HAL_PPP_Init()函數所調用。當我們需要移植程序到 STM32F1
平台的時候,我們只需要修改 HAL_PPP_MspInit 函數內容而不需要修改 HAL_PPP_Init 入口參
數內容。
在HAL庫中,幾乎每初始化一個外設就需要設置該外設與單片機之間的聯系,比如IO口,是否復用等等,可見,HAL庫相對於標准庫多了MSP函數之后,移植性非常強,但與此同時卻增加了代碼量和代碼的嵌套層級。可以說各有利弊。
同樣,MSP函數又可以配合句柄,達到非常強的移植性:
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
入口參數僅僅需要一個串口句柄,這樣有能看出句柄的方便。
3.Callback函數
類似於MSP函數,個人認為Callback函數主要幫助用戶應用層的代碼編寫。
還是以USART為例,在標准庫中,串口中斷了以后,我們要先在中斷中判斷是否是接收中斷,然后讀出數據,順便清除中斷標志位,然后再是對數據的處理,這樣如果我們在一個中斷函數中寫這么多代碼,就會顯得很混亂:
void USART3_IRQHandler(void) //串口1中斷服務程序
{
u8 Res;
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET) //接收中斷(接收到的數據必須是0x0d 0x0a結尾)
{
Res =USART_ReceiveData(USART3); //讀取接收到的數據
/*數據處理區*/
}
}
}
而在HAL庫中,進入串口中斷后,直接由HAL庫中斷函數進行托管:
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&UART1_Handler); //調用HAL庫中斷處理公用函數
/***************省略無關代碼****************/
}
HAL_UART_IRQHandler這個函數完成了判斷是哪個中斷(接收?發送?或者其他?),然后讀出數據,保存至緩存區,順便清除中斷標志位等等操作。
比如我提前設置了,串口每接收五個字節,我就要對這五個字節進行處理。
在一開始我定義了一個串口接收緩存區:
/*HAL庫使用的串口接收緩沖,處理邏輯由HAL庫控制,接收完這個數組就會調用HAL_UART_RxCpltCallback進行處理這個數組*/
/*RXBUFFERSIZE=5*/
u8 aRxBuffer[RXBUFFERSIZE];
在初始化中,我在句柄里設置好了緩存區的地址,緩存大小(五個字節)
/*該代碼在HAL_UART_Receive_IT函數中,初始化時會引用*/
huart->pRxBuffPtr = pData;//aRxBuffer
huart->RxXferSize = Size;//RXBUFFERSIZE
huart->RxXferCount = Size;//RXBUFFERSIZE
則在接收數據中,每接收完五個字節,HAL_UART_IRQHandler才會執行一次Callback函數:
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
在這個Callback回調函數中,我們只需要對這接收到的五個字節(保存在aRxBuffer[]中)進行處理就好了,完全不用再去手動清除標志位等操作。
所以說Callback函數是一個應用層代碼的函數,我們在一開始只設置句柄里面的各個參數,然后就等着HAL庫把自己安排好的代碼送到手中就可以了~
綜上,就是HAL庫的三個與標准庫不同的地方之個人見解。
個人覺得從這三個小點就可以看出HAL庫的可移植性之強大,並且用戶可以完全不去理會底層各個寄存器的操作,代碼也更有邏輯性。但與此帶來的是復雜的代碼量,極慢的編譯速度,略微低下的效率。看怎么取舍了。
轉自:https://blog.csdn.net/weixin_43186792/article/details/88759321?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3.control
