STM32使用HAL庫之Msp回調(diào)函數(shù)
1.問題提出
在STM32的HAL庫使用中,會(huì)發(fā)現(xiàn)庫函數(shù)大都被設(shè)計(jì)成了一對:
HAL_PPP/PPPP_Init
HAL_PPP/PPPP_MspInit
而且HAL_PPP/PPPP_MspInit函數(shù)的defination前面還會(huì)有__weak關(guān)鍵字
上面的PPP/PPPP代表常見外設(shè)的名稱為3個(gè)字符或者4個(gè)字符
怎么理解這個(gè)設(shè)計(jì)呢?
2.問題分析
2.1 結(jié)論
首先說結(jié)論:
-
HAL_PPP/PPPP_Init 是與具體芯片(無論是STM32F4/F1/F7)無關(guān)的設(shè)置
-
HAL_PPP/PPPP_MspInit 是與具體芯片相關(guān)的配置(如STM32F429IGTx)
這樣的設(shè)計(jì)是將不變的東西以庫函數(shù)HAL_PPP/PPPP_Init的形式固定下來,而將需要用戶根據(jù)
芯片進(jìn)行編寫的部分抽象成函數(shù)HAL_PPP/PPPP_MspInit的形式,用戶只需要編寫這部分函數(shù)
即可,這樣做減少了用戶的代碼編寫量
__weak關(guān)鍵字的使用是定義一個(gè)弱函數(shù),這個(gè)函數(shù)的函數(shù)體通常是空的
方便用戶重寫一個(gè)自己的函數(shù)HAL_PPP/PPPP_MspInit,來覆蓋之前庫函數(shù)中定義的函數(shù)帶有
__weak關(guān)鍵字的HAL_PPP/PPPP_MspInit函數(shù),編譯器在編譯的時(shí)候,如果檢查到有重名的
(但不含__weak關(guān)鍵字)HAL_PPP/PPPP_MspInit的函數(shù),此時(shí)就會(huì)默認(rèn)編譯這個(gè)用戶寫的函數(shù)
2.2 實(shí)例分析
下面以串口通信為例進(jìn)行分析:
在編寫串口通信的代碼的時(shí)候,常使用正點(diǎn)原子提供的usart.c&usart.h組合,正點(diǎn)原子在usart.c中
定義了HAL_UART_MspInit作為回調(diào)函數(shù):
void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
// GPIO configuration
GPIO_InitTypeDef GPIO_Initure;
if(huart->Instance==USART1)
{
__HAL_RCC_GPIOA_CLK_ENABLE(); // 使能GPIOA 時(shí)鐘
__HAL_RCC_USART1_CLK_ENABLE(); // 使能USART1 時(shí)鐘
GPIO_Initure.Pin=GPIO_PIN_9; //PA9
GPIO_Initure.Mode=GPIO_MODE_AF_PP; // AF復(fù)用,PP為推挽(push pull)
GPIO_Initure.Pull=GPIO_PULLUP; // 設(shè)置上拉
GPIO_Initure.Speed=GPIO_SPEED_FAST; // 設(shè)置為高速
GPIO_Initure.Alternate=GPIO_AF7_USART1; // 復(fù)用為USART1
HAL_GPIO_Init(GPIOA,&GPIO_Initure); // 初始化PA9
GPIO_Initure.Pin=GPIO_PIN_10; //PA10
HAL_GPIO_Init(GPIOA,&GPIO_Initure); // 初始化PA10
#if EN_USART1_RX
HAL_NVIC_EnableIRQ(USART1_IRQn); // 使能USART1中斷通道
HAL_NVIC_SetPriority(USART1_IRQn,3,3); // 搶占優(yōu)先級3, 子優(yōu)先級3
#endif
}
}
這個(gè)庫同時(shí)提供了一個(gè)調(diào)用串口初始化的接口:void uart_init(u32 bound) // bound為波特率
void uart_init(u32 bound)
{
//UART initialization
UART1_Handler.Instance=USART1; // USART1
UART1_Handler.Init.BaudRate=bound; // 設(shè)置波特率
UART1_Handler.Init.WordLength=UART_WORDLENGTH_8B; // 字長為8位的數(shù)據(jù)格式
UART1_Handler.Init.StopBits=UART_STOPBITS_1; // 一個(gè)停止位
UART1_Handler.Init.Parity=UART_PARITY_NONE; // 無奇偶校驗(yàn)位
UART1_Handler.Init.HwFlowCtl=UART_HWCONTROL_NONE; // 無硬件流控
UART1_Handler.Init.Mode=UART_MODE_TX_RX; // 收發(fā)模式
HAL_UART_Init(&UART1_Handler); // HAL_UART_Init() 會(huì)使能UART1
HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);
// 該函數(shù)會(huì)開啟接收中斷,標(biāo)志位UART_IT_RXNE,并設(shè)置接收緩沖以及接收緩沖的最大接收數(shù)量
}
這樣在main函數(shù)中,首先調(diào)用函數(shù)uart_init()
然后uart_init()函數(shù)就會(huì)去調(diào)用HAL_UART_Init,這個(gè)函數(shù)就是HAL庫中的函數(shù)
跳轉(zhuǎn)到文件stm32f4xx_hal_uart.c,找到函數(shù)HAL_UART_Init的定義:
/**
* @brief Initializes the UART mode according to the specified parameters in
* the UART_InitTypeDef and create the associated handle.
* @param huart: pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @retval HAL status
*/
HAL_StatusTypeDef HAL_UART_Init(UART_HandleTypeDef *huart)
{
/* Check the UART handle allocation */
if(huart == NULL)
{
return HAL_ERROR;
}
/* Check the parameters */
if(huart->Init.HwFlowCtl != UART_HWCONTROL_NONE)
{
/* The hardware flow control is available only for USART1, USART2, USART3 and USART6 */
assert_param(IS_UART_HWFLOW_INSTANCE(huart->Instance));
assert_param(IS_UART_HARDWARE_FLOW_CONTROL(huart->Init.HwFlowCtl));
}
else
{
assert_param(IS_UART_INSTANCE(huart->Instance));
}
assert_param(IS_UART_WORD_LENGTH(huart->Init.WordLength));
assert_param(IS_UART_OVERSAMPLING(huart->Init.OverSampling));
if(huart->gState == HAL_UART_STATE_RESET)
{
/* Allocate lock resource and initialize it */
huart->Lock = HAL_UNLOCKED;
/* Init the low level hardware */
HAL_UART_MspInit(huart);
}
huart->gState = HAL_UART_STATE_BUSY;
/* Disable the peripheral */
__HAL_UART_DISABLE(huart);
/* Set the UART Communication parameters */
UART_SetConfig(huart);
/* In asynchronous mode, the following bits must be kept cleared:
- LINEN and CLKEN bits in the USART_CR2 register,
- SCEN, HDSEL and IREN bits in the USART_CR3 register.*/
huart->Instance->CR2 &= ~(USART_CR2_LINEN | USART_CR2_CLKEN);
huart->Instance->CR3 &= ~(USART_CR3_SCEN | USART_CR3_HDSEL | USART_CR3_IREN);
/* Enable the peripheral */
__HAL_UART_ENABLE(huart);
/* Initialize the UART state */
huart->ErrorCode = HAL_UART_ERROR_NONE;
huart->gState= HAL_UART_STATE_READY;
huart->RxState= HAL_UART_STATE_READY;
return HAL_OK;
}
可以看到函數(shù)HAL_UART_Init中調(diào)用了函數(shù)HAL_UART_MspInit
在庫文件中本身是有一個(gè)同名的使用__weak關(guān)鍵字定義的函數(shù),
/**
* @brief UART MSP Init.
* @param huart: pointer to a UART_HandleTypeDef structure that contains
* the configuration information for the specified UART module.
* @retval None
*/
__weak void HAL_UART_MspInit(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_MspInit could be implemented in the user file
*/
}
由于使用了正點(diǎn)原子的庫,所以編譯器在編譯的時(shí)候就不會(huì)再編譯這個(gè)HAL庫自帶的函數(shù)HAL_UART_MspInit
而是編譯引入的庫函數(shù)HAL_UART_MspInit
3. STM32程序的一般執(zhí)行流程
由上面1.2節(jié)的分析,對于一個(gè)真實(shí)的STM32應(yīng)用程序可以總結(jié)其運(yùn)行一般執(zhí)行(編寫)流程如下:
以一個(gè)真實(shí)的點(diǎn)亮跑馬燈的main.c為例進(jìn)行分析(工程使用HAL庫):
#include "sys.h"
#include "delay.h"
#include "usart.h"
void Delay(__IO uint32_t nCount);
void Delay(__IO uint32_t nCount)
{
while(nCount--){}
}
int main(void)
{
GPIO_InitTypeDef GPIO_Initure;
HAL_Init();
Stm32_Clock_Init(360,25,2,8);
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_Initure.Pin=GPIO_PIN_0|GPIO_PIN_1;
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP;
GPIO_Initure.Pull=GPIO_PULLUP;
GPIO_Initure.Speed=GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
while(1)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
Delay(0x7FFFFF);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
Delay(0x7FFFFF);
}
}
這里插入正點(diǎn)原子的圖進(jìn)行解釋:
一個(gè)項(xiàng)目首先是引導(dǎo)程序先運(yùn)行,匯編函數(shù)會(huì)引導(dǎo)SystemInit函數(shù)進(jìn)行系統(tǒng)初始化的設(shè)置,再HAL庫版本的項(xiàng)目中有這個(gè)函數(shù)的定義,在寄存器版本中通常會(huì)將匯編代碼中引導(dǎo)SystemInit函數(shù)的語句刪掉。然后引導(dǎo)程序會(huì)引導(dǎo)main函數(shù),main函數(shù)被引導(dǎo)完成之后就會(huì)開始執(zhí)行用戶寫的main函數(shù)中的代碼。然后HAL_Init()函數(shù)會(huì)調(diào)用函數(shù)進(jìn)行全局的MSP初始化,然后調(diào)用了正點(diǎn)原子提供的庫函數(shù)Stm32_Clock_init函數(shù),這個(gè)函數(shù)調(diào)用HAL_RCC_Oscconfig和HAL_RCC_ClockConfig函數(shù)進(jìn)行系統(tǒng)時(shí)鐘初始化,使用該函數(shù)需要導(dǎo)入SYSTEM庫(正點(diǎn)原子提供),上面的一系列初始化都是常規(guī)操作,也就是每一個(gè)項(xiàng)目必做的系統(tǒng)的初始化。下面正式進(jìn)入了用戶自己編寫得到邏輯,假設(shè)用戶要使用PPP外設(shè),那么就會(huì)調(diào)用HAL庫中的函數(shù)HAL_PPP_Init,這個(gè)函數(shù)又會(huì)去嘗試調(diào)用用戶自定義的HAL_PPP_MspInit,然后進(jìn)入用戶自己定義的邏輯。
————————————————
原文鏈接:《[STM32] NOTE07-STM32使用HAL庫之Msp回調(diào)函數(shù)理解》
STM32HAL庫中外設(shè)初始化MSP回調(diào)機(jī)制及中斷回調(diào)機(jī)制詳解
我們開始學(xué)習(xí)HAL庫的過程中,一定會(huì)發(fā)現(xiàn)與固件庫開發(fā)中外設(shè)初始化流程和中斷處理機(jī)制不相同,在這里將為大家解答一下心中的譯文。
HAL外設(shè)初始化MSP回調(diào)機(jī)制
在外設(shè)初始化函數(shù)中,HAL_PPP_Init();中需配置外設(shè)的相關(guān)參數(shù),外設(shè)用到的IO和NVIC和時(shí)鐘等放到HAL_PPP_MspInit()回調(diào)函數(shù)中。初始化函數(shù)會(huì)自動(dòng)調(diào)用回調(diào)函數(shù).
HAL庫中斷回調(diào)機(jī)制
HAL庫中中斷處理機(jī)制與固件庫中不同,他是經(jīng)過公共中斷處理函數(shù),自動(dòng)調(diào)用中斷處理回調(diào)函數(shù)。用戶想要再中斷中實(shí)現(xiàn)的邏輯代碼則要放在回調(diào)函數(shù)中,而公共中斷處理函數(shù)會(huì)幫你檢測是否有中斷發(fā)生,并幫你清除中斷標(biāo)志位。文章來源:http://www.zghlxwxcb.cn/news/detail-649622.html
HAL_PPP_IRQHandler();公共中斷處理函數(shù),它會(huì)自動(dòng)調(diào)用中斷處理回調(diào)函數(shù)HAL_PPP_Callback()
用戶要寫在中斷服務(wù)處理函數(shù)中的邏輯代碼要放在回調(diào)函數(shù)中,公共中斷處理函數(shù)會(huì)幫你清除中斷標(biāo)志,并且自動(dòng)調(diào)用回調(diào)函數(shù)
參考原文:《STM32HAL庫中外設(shè)初始化MSP回調(diào)機(jī)制及中斷回調(diào)機(jī)制詳解》文章來源地址http://www.zghlxwxcb.cn/news/detail-649622.html
到了這里,關(guān)于STM32使用HAL庫中外設(shè)初始化MSP回調(diào)機(jī)制及中斷回調(diào)機(jī)制詳解的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!