新手在入門 STM32 的時(shí)候,一般大多數(shù)都會(huì)選用標(biāo)準(zhǔn)庫和 HAL 庫,而極少部分人會(huì)通過直接配置寄存器進(jìn)行開發(fā)。
對(duì)于剛?cè)腴T的朋友,可能沒法直觀了解這些不同開發(fā)發(fā)方式之間的區(qū)別,本文試圖以一種非常直白的方式,用自己的理解去將這些東西表述出來。
配置寄存器
不少先學(xué)了 51單片機(jī)的朋友可能會(huì)知道,會(huì)有一小部分人或教程是通過匯編語言直接操作寄存器實(shí)現(xiàn)功能的,這種方法到了 STM32 就變得不太容易行得通了。
因?yàn)?STM32 的寄存器數(shù)量是 51單片機(jī)的十?dāng)?shù)倍,如此多的寄存器根本無法全部記憶,開發(fā)時(shí)需要經(jīng)常的翻查芯片的數(shù)據(jù)手冊(cè),此時(shí)直接操作寄存器就變得非常的費(fèi)力了。也有人喜歡去直接操作寄存器,因?yàn)檫@樣更接近原理,代碼更少,知其然也知其所以然。
標(biāo)準(zhǔn)庫
上面也提到了,STM32 有非常多的寄存器,而導(dǎo)致了開發(fā)困難,所以為此 ST 公司就為每款芯片都編寫了一份庫文件,也就是工程文件里 stm32F1xx..... 之類的。在這些 .c 與 .h 文件中,包括一些常用量的宏定義,把一些外設(shè)也通過結(jié)構(gòu)體變量封裝起來,如 GPIO、時(shí)鐘等。
所以我們只需要配置結(jié)構(gòu)體變量成員就可以修改外設(shè)的配置寄存器,從而選擇不同的功能。也是目前最多人使用的方式,也是學(xué)習(xí) STM32 接觸最多的一種開發(fā)方式,我也就不多闡述了。
HAL庫
HAL 庫是 ST 公司目前主推的開發(fā)方式,全稱就是 Hardware Abstraction Layer(抽象印象層),簡單來說就是弱化了開發(fā)者對(duì)硬件底層知識(shí)的依賴。
同樣的功能,標(biāo)準(zhǔn)庫可能要用幾句話,HAL 庫只需用一句話就夠了。并且 HAL 庫也很好地解決了程序移植的問題。不同型號(hào)的 STM32 芯片它的標(biāo)準(zhǔn)庫是不一樣的,例如在F4 上開發(fā)的程序移植到 F3 上是不能通用的,而使用 HAL 庫,只要使用的是相同的外設(shè),程序基本可以完全復(fù)制粘貼。注意是相同外設(shè),意思也就是不能無中生有。例如 F7 比 F3 要多幾個(gè)定時(shí)器,不能明明沒有這個(gè)定時(shí)器卻非要配置,但其實(shí)這種情況不多,絕大多數(shù)都可以直接復(fù)制粘貼。
而且使用 ST 公司研發(fā)的 STMcube 軟件,可以通過圖形化的配置功能,直接生成整個(gè)適用于HAL庫的工程文件,可以說是方便至極。但是方便的同時(shí)也造成了它執(zhí)行效率偏低。
綜合上面說的,其實(shí)筆者還是強(qiáng)烈推薦 HAL 庫的,理由:
- ST 公司已經(jīng)停止更新標(biāo)準(zhǔn)庫,公司主打 HAL 庫的目的已經(jīng)非常明顯了;
- 模塊化的 HAL 庫是趨勢(shì),低效的短板會(huì)被硬件的增強(qiáng)所彌補(bǔ)。
當(dāng)然底層的基本原理是必須要懂的,HAL 庫也不是萬能的,結(jié)合對(duì)底層的理解相信一定會(huì)讓你的開發(fā)水準(zhǔn)大大提高。
HAL庫與標(biāo)準(zhǔn)庫的區(qū)別
1 句柄
在STM32的標(biāo)準(zhǔn)庫中,假設(shè)我們要初始化一個(gè)外設(shè)(這里以 USART 為例) 我們首先要初始化他們的各個(gè)寄存器。
在標(biāo)準(zhǔn)庫中,這些操作都是利用固件庫結(jié)構(gòu)體變量+固件庫 Init 函數(shù)實(shí)現(xiàn)的:
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字長為8位數(shù)據(jù)格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一個(gè)停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//無奇偶校驗(yàn)位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//無硬件數(shù)據(jù)流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收發(fā)模式
USART_Init(USART3, &USART_InitStructure); //初始化串口1
可以看到,要初始化一個(gè)串口,需要對(duì)六個(gè)位置進(jìn)行賦值,然后引用 Init 函數(shù),并且USART_InitStructure 并不是一個(gè)全局結(jié)構(gòu)體變量,而是只在函數(shù)內(nèi)部的局部變量,初始化完成之后,USART_InitStructure 就失去了作用。
而在HAL庫中,同樣是 USART 初始化結(jié)構(gòu)體變量,我們要定義為全局變量。
UART_HandleTypeDef UART1_Handler;
結(jié)構(gòu)體成員:
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;
我們發(fā)現(xiàn),與標(biāo)準(zhǔn)庫不同的是,該成員不僅包含了之前標(biāo)準(zhǔn)庫就有的六個(gè)成員(波特率,數(shù)據(jù)格式等),還包含過采樣、(發(fā)送或接收的)數(shù)據(jù)緩存、數(shù)據(jù)指針、串口 DMA 相關(guān)的變量、各種標(biāo)志位等等,要在整個(gè)項(xiàng)目流程中都要設(shè)置的各個(gè)成員。
該UART1_Handler就被稱為串口的句柄 它被貫穿整個(gè) USART 收發(fā)的流程,比如開啟中斷:
HAL_UART_Receive_IT(&UART1_Handler, (u8 *)aRxBuffer, RXBUFFERSIZE);
比如后面要講到的 MSP 與 Callback 回調(diào)函數(shù):
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
在這些函數(shù)中,只需要調(diào)用初始化時(shí)定義的句柄 UART1_Handler 就好。
2 MSP函數(shù)
MCU Specific Package 單片機(jī)的具體方案。
MSP 是指和 MCU 相關(guān)的初始化,引用一下正點(diǎn)原子的解釋,個(gè)人覺得說地很明白:
“我們要初始化一個(gè)串口,首先要設(shè)置和 MCU 無關(guān)的東西,例如波特率,奇偶校驗(yàn),停止位等,這些參數(shù)設(shè)置和 MCU 沒有任何關(guān)系,可以使用 STM32F1,也可以是 STM32F2/F3/F4/F7 上的串口。而一個(gè)串口設(shè)備它需要一個(gè) MCU 來承載,例如用 STM32F4 來做承載,PA9 做為發(fā)送,PA10 做為接收,MSP 就是要初始化 STM32F4 的 PA9,PA10,配置這兩個(gè)引腳。所以 HAL驅(qū)動(dòng)方式的初始化流程就是:HAL_USART_Init()—>HAL_USART_MspInit(),先初始化與 MCU無關(guān)的串口協(xié)議,再初始化與 MCU 相關(guān)的串口引腳。在 STM32 的 HAL 驅(qū)動(dòng)中HAL_PPP_MspInit()作為回調(diào),被 HAL_PPP_Init() 函數(shù)所調(diào)用。當(dāng)我們需要移植程序到 STM32F1 平臺(tái)的時(shí)候,我們只需要修改 HAL_PPP_MspInit 函數(shù)內(nèi)容而不需要修改 HAL_PPP_Init 入口參數(shù)內(nèi)容。
”
在 HAL 庫中,幾乎每初始化一個(gè)外設(shè)就需要設(shè)置該外設(shè)與單片機(jī)之間的聯(lián)系,比如IO口,是否復(fù)用等等。可見,HAL 庫相對(duì)于標(biāo)準(zhǔn)庫多了MSP函數(shù)之后,移植性非常強(qiáng),但與此同時(shí)卻增加了代碼量和代碼的嵌套層級(jí)。可以說各有利弊。
同樣,MSP 函數(shù)又可以配合句柄,達(dá)到非常強(qiáng)的移植性:
void HAL_UART_MspInit(UART_HandleTypeDef *huart);
3 Callback函數(shù)
類似于 MSP 函數(shù),個(gè)人認(rèn)為 Callback 函數(shù)主要幫助用戶應(yīng)用層的代碼編寫。還是以 USART 為例,在標(biāo)準(zhǔn)庫中,串口中斷了以后,我們要先在中斷中判斷是否是接收中斷,然后讀出數(shù)據(jù),順便清除中斷標(biāo)志位,然后再是對(duì)數(shù)據(jù)的處理,這樣如果我們?cè)谝粋€(gè)中斷函數(shù)中寫這么多代碼,就會(huì)顯得很混亂:
void USART3_IRQHandler(void) //串口1中斷服務(wù)程序
{
u8 Res;
//接收中斷(接收到的數(shù)據(jù)必須是0x0d 0x0a結(jié)尾)
if(USART_GetITStatus(USART3, USART_IT_RXNE) != RESET)
{
//讀取接收到的數(shù)據(jù)
Res =USART_ReceiveData(USART3);
/*數(shù)據(jù)處理區(qū)*/
}
}
而在HAL庫中,進(jìn)入串口中斷后,直接由 HAL 庫中斷函數(shù)進(jìn)行托管:
void USART1_IRQHandler(void)
{
//調(diào)用HAL庫中斷處理公用函數(shù)
HAL_UART_IRQHandler(&UART1_Handler);
/***************省略無關(guān)代碼****************/
}
HAL_UART_IRQHandler 這個(gè)函數(shù)完成了判斷是哪個(gè)中斷(接收?發(fā)送?或者其他?),然后讀出數(shù)據(jù),保存至緩存區(qū),順便清除中斷標(biāo)志位等等操作。比如我提前設(shè)置了,串口每接收五個(gè)字節(jié),我就要對(duì)這五個(gè)字節(jié)進(jìn)行處理。在一開始我定義了一個(gè)串口接收緩存區(qū):
/*HAL庫使用的串口接收緩沖,處理邏輯由HAL庫控制,
接收完這個(gè)數(shù)組就會(huì)調(diào)用HAL_UART_RxCpltCallback進(jìn)行處理這個(gè)數(shù)組*/
/*RXBUFFERSIZE=5*/
u8 aRxBuffer[RXBUFFERSIZE];
在初始化中,我在句柄里設(shè)置好了緩存區(qū)的地址,緩存大?。ㄎ鍌€(gè)字節(jié))
/* 該代碼在HAL_UART_Receive_IT函數(shù)中,初始化時(shí)會(huì)引用 */
huart- >pRxBuffPtr = pData; //aRxBuffer
huart- >RxXferSize = Size; //RXBUFFERSIZE
huart- >RxXferCount = Size; //RXBUFFERSIZE
則在接收數(shù)據(jù)中,每接收完五個(gè)字節(jié),HAL_UART_IRQHandler 才會(huì)執(zhí)行一次Callback 函數(shù):
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
在這個(gè)Callback回調(diào)函數(shù)中,我們只需要對(duì)這接收到的五個(gè)字節(jié)(保存在 aRxBuffer[] 中)進(jìn)行處理就好了,完全不用再去手動(dòng)清除標(biāo)志位等操作。
所以說 Callback 函數(shù)是一個(gè)應(yīng)用層代碼的函數(shù),我們?cè)谝婚_始只設(shè)置句柄里面的各個(gè)參數(shù),然后就等著 HAL 庫把自己安排好的代碼送到手中就可以了~
綜上 ,就是HAL庫的三個(gè)與標(biāo)準(zhǔn)庫不同的地方之個(gè)人見解。
個(gè)人覺得從這三個(gè)小點(diǎn)就可以看出 HAL 庫的可移植性之強(qiáng)大,并且用戶可以完全不去理會(huì)底層各個(gè)寄存器的操作,代碼也更有邏輯性。但由此帶來的是復(fù)雜的代碼量,極慢的編譯速度,略微低下的效率??丛趺慈∩崃恕?/p>
HAL庫的結(jié)構(gòu)
說到 STM32 的 HAL庫,就不得不提 STM32CubeMX,其作為一個(gè)可視化的配置工具,對(duì)于開發(fā)者來說,確實(shí)大大節(jié)省了開發(fā)時(shí)間。另外 STM32CubeIDE 集成了STM32CubeMX 的功能,是一個(gè)集配置與編譯于一體的軟件,可以嘗試一下。軟件更新頻率很高,持續(xù)優(yōu)化某些 bug 及性能問題。
上面兩個(gè)開發(fā)軟件是以 HAL 庫為基礎(chǔ)的,且目前僅支持 HAL 庫及LL庫!
首先看一下,官方給出的HAL庫的文件結(jié)構(gòu):
下圖是STM32庫文件結(jié)構(gòu)。
stm32f2xx.h 主要包含 STM32 同系列芯片的不同具體型號(hào)的定義,是否使用 HAL 庫等的定義,接著,其會(huì)根據(jù)定義的芯片信號(hào)包含具體的芯片型號(hào)的頭文件:
#if defined(STM32F205xx)
#include "stm32f205xx.h"
#elif defined(STM32F215xx)
#include "stm32f215xx.h"
#elif defined(STM32F207xx)
#include "stm32f207xx.h"
#elif defined(STM32F217xx)
#include "stm32f217xx.h"
#else
#error "Please select first the target STM32F2xx device used in your application (in stm32f2xx.h file)"
#endif
緊接著,其會(huì)包含 stm32f2xx_hal.h。
- stm32f2xx_hal.h:stm32f2xx_hal.c/h 主要實(shí)現(xiàn) HAL 庫的初始化、系統(tǒng)滴答時(shí)鐘相關(guān)的函數(shù)、及 CPU 的調(diào)試模式配置
- stm32f2xx_hal_conf.h :該文件是一個(gè)用戶級(jí)別的配置文件,用來實(shí)現(xiàn)對(duì) HAL 庫的裁剪,其位于用戶文件目錄,不要放在庫目錄中。
接下來對(duì)于HAL庫的源碼文件進(jìn)行一下說明,HAL 庫文件名均以 stm32f2xx_hal 開頭,后面加上_外設(shè)或者模塊名(如:stm32f2xx_hal_adc.c):
庫文件:
stm32f2xx_hal_ppp.c/.h // 主要的外設(shè)或者模塊的驅(qū)動(dòng)源文件,包含了該外設(shè)的通用API
stm32f2xx_hal_ppp_ex.c/.h // 外圍設(shè)備或模塊驅(qū)動(dòng)程序的擴(kuò)展文件。這組文件中包含特定型號(hào)或者系列的芯片的特殊API。以及如果該特定的芯片內(nèi)部有不同的實(shí)現(xiàn)方式,則該文件中的特殊API將覆蓋_ppp中的通用API。
stm32f2xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的時(shí)間延遲等相關(guān)的API
其他庫文件
用戶級(jí)別文件:
stm32f2xx_hal_msp_template.c // 只有.c沒有.h。它包含用戶應(yīng)用程序中使用的外設(shè)的MSP初始化和反初始化(主程序和回調(diào)函數(shù))。使用者復(fù)制到自己目錄下使用模板。
stm32f2xx_hal_conf_template.h // 用戶級(jí)別的庫配置文件模板。使用者復(fù)制到自己目錄下使用
system_stm32f2xx.c // 此文件主要包含SystemInit()函數(shù),該函數(shù)在剛復(fù)位及跳到main之前的啟動(dòng)過程中被調(diào)用。**它不在啟動(dòng)時(shí)配置系統(tǒng)時(shí)鐘(與標(biāo)準(zhǔn)庫相反)**。時(shí)鐘的配置在用戶文件中使用HAL API來完成。
startup_stm32f2xx.s // 芯片啟動(dòng)文件,主要包含堆棧定義,終端向量表等
stm32f2xx_it.c/.h // 中斷處理函數(shù)的相關(guān)實(shí)現(xiàn)
main.c/.h
根據(jù) HAL 庫的命名規(guī)則,其 API 可以分為以下三大類:
- 初始化/反初始化函數(shù):HAL_PPP_Init(), HAL_PPP_DeInit()
- IO 操作函數(shù):HAL_PPP_Read(), HAL_PPP_Write(),HAL_PPP_Transmit(), HAL_PPP_Receive()
- 控制函數(shù):HAL_PPP_Set (), HAL_PPP_Get ().
- 狀態(tài)和錯(cuò)誤:HAL_PPP_GetState (), HAL_PPP_GetError ().
“注意:目前 LL 庫是和 HAL 庫捆綁發(fā)布的,所以在 HAL 庫源碼中,還有一些名為 stm32f2xx_ll_ppp 的源碼文件,這些文件就是新增的LL庫文件。使用 CubeMX 生產(chǎn)項(xiàng)目時(shí),可以選擇LL庫。
”
HAL 庫最大的特點(diǎn)就是對(duì)底層進(jìn)行了抽象。在此結(jié)構(gòu)下,用戶代碼的處理主要分為三部分:
- 處理外設(shè)句柄,實(shí)現(xiàn)用戶功能
- 處理MSP
- 處理各種回調(diào)函數(shù),外設(shè)句柄定義
HAL 庫在結(jié)構(gòu)上,對(duì)每個(gè)外設(shè)抽象成了一個(gè)稱為 ppp_HandleTypeDef 的結(jié)構(gòu)體,其中 ppp 就是每個(gè)外設(shè)的名字。所有的函數(shù)都是工作在 ppp_HandleTypeDef 指針之下。
每個(gè)外設(shè)/模塊實(shí)例都有自己的句柄。因此,實(shí)例資源是獨(dú)立的。
外圍進(jìn)程相互通信:該句柄用于管理進(jìn)程例程之間的共享數(shù)據(jù)資源。
下面,以ADC為例,
/**
* @brief ADC handle Structure definition
*/
typedef struct
{
ADC_TypeDef *Instance; /*!< Register base address */
ADC_InitTypeDef Init; /*!< ADC required parameters */
__IO uint32_t NbrOfCurrentConversionRank; /*!< ADC number of current conversion rank */
DMA_HandleTypeDef *DMA_Handle; /*!< Pointer DMA Handler */
HAL_LockTypeDef Lock; /*!< ADC locking object */
__IO uint32_t State; /*!< ADC communication state */
__IO uint32_t ErrorCode; /*!< ADC Error code */
}ADC_HandleTypeDef;
從上面的定義可以看出,ADC_HandleTypeDef 中包含了 ADC 可能出現(xiàn)的所有定義,對(duì)于用戶想要使用 ADC 只要定義一個(gè) ADC_HandleTypeDef 的變量,給每個(gè)變量賦好值,對(duì)應(yīng)的外設(shè)就抽象完了。接下來就是具體使用了。
當(dāng)然,對(duì)于那些共享型外設(shè)或者說系統(tǒng)外設(shè)來說,他們不需要進(jìn)行以上這樣的抽象,這些部分與原來的標(biāo)準(zhǔn)外設(shè)庫函數(shù)基本一樣。例如以下外設(shè):
- GPIO
- SYSTICK
- NVIC
- RCC
- FLASH
以 GPIO 為例,對(duì)于 HAL_GPIO_Init() 函數(shù),其只需要 GPIO 地址以及其初始化參數(shù)即可。
1 三種編程方式
HAL 庫對(duì)所有的函數(shù)模型也進(jìn)行了統(tǒng)一。在HAL 庫中,支持三種編程模式:輪詢模式、中斷模式、DMA模式(如果外設(shè)支持)。其分別對(duì)應(yīng)如下三種類型的函數(shù)(以ADC為例):
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
HAL_StatusTypeDef HAL_ADC_Start_DMA(ADC_HandleTypeDef* hadc, uint32_t* pData, uint32_t Length);
HAL_StatusTypeDef HAL_ADC_Stop_DMA(ADC_HandleTypeDef* hadc);
其中,帶 _IT 的表示工作在中斷模式下;帶 _DMA 的工作在 DMA 模式下(注意:DMA 模式下也是可以開啟中斷的);什么都沒帶的就是輪詢模式(沒有開啟中斷的)。至于使用者使用何種方式,就看自己的選擇了。
此外,新的HAL庫架構(gòu)下統(tǒng)一采用宏的形式對(duì)各種中斷等進(jìn)行配置(原來標(biāo)準(zhǔn)外設(shè)庫一般都是各種函數(shù))。針對(duì)每種外設(shè)主要由以下宏:
- __HAL_PPP_ENABLE_IT(HANDLE, INTERRUPT):使能一個(gè)指定的外設(shè)中斷
- __HAL_PPP_DISABLE_IT(HANDLE, INTERRUPT):失能一個(gè)指定的外設(shè)中斷
- __HAL_PPP_GET_IT (HANDLE, __ INTERRUPT __):獲得一個(gè)指定的外設(shè)中斷狀態(tài)
- __HAL_PPP_CLEAR_IT (HANDLE, __ INTERRUPT __):清除一個(gè)指定的外設(shè)的中斷狀態(tài)
- __HAL_PPP_GET_FLAG (HANDLE, FLAG):獲取一個(gè)指定的外設(shè)的標(biāo)志狀態(tài)
- __HAL_PPP_CLEAR_FLAG (HANDLE, FLAG):清除一個(gè)指定的外設(shè)的標(biāo)志狀態(tài)
- __HAL_PPP_ENABLE(HANDLE) :使能外設(shè)
- __HAL_PPP_DISABLE(HANDLE) :失能外設(shè)
- __HAL_PPP_XXXX (HANDLE, PARAM) :指定外設(shè)的宏定義
- _HAL_PPP_GET IT_SOURCE (HANDLE, __ INTERRUPT __) 檢查中斷源
2 三大回調(diào)函數(shù)
在 HAL 庫的源碼中,到處可見一些以__weak開頭的函數(shù),而且這些函數(shù),有些已經(jīng)被實(shí)現(xiàn)了,比如:
有些則沒有被實(shí)現(xiàn),例如:
__weak void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
/* Prevent unused argument(s) compilation warning */
UNUSED(hspi);
/* NOTE : This function should not be modified, when the callback is needed,the HAL_SPI_TxCpltCallback should be implemented in the user file
*/
}
所有帶有 __weak 關(guān)鍵字的函數(shù)表示,就可以由用戶自己來實(shí)現(xiàn)。如果出現(xiàn)了同名函數(shù),且不帶 __weak 關(guān)鍵字,那么連接器就會(huì)采用外部實(shí)現(xiàn)的同名函數(shù)。通常來說,HAL 庫負(fù)責(zé)整個(gè)處理和 MCU 外設(shè)的處理邏輯,并將必要部分以回調(diào)函數(shù)的形式給出到用戶,用戶只需要在對(duì)應(yīng)的回調(diào)函數(shù)中做修改即可。
HAL 庫包含如下三種用戶級(jí)別回調(diào)函數(shù)(PPP為外設(shè)名):
- 外設(shè)系統(tǒng)級(jí)初始化/解除初始化回調(diào)函數(shù)(用戶代碼的第二大部分:對(duì)于 MSP 的處理):
HAL_PPP_MspInit()
和HAL_PPP_MspDeInit
例如:__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)
。在 HAL_PPP_Init() 函數(shù)中被調(diào)用,用來初始化底層相關(guān)的設(shè)備(GPIOs, clock, DMA, interrupt)。 - 處理完成回調(diào)函數(shù):
HAL_PPP_ProcessCpltCallback
(Process指具體某種處理,如UART的Tx),例如:__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
。當(dāng)外設(shè)或者DMA工作完成后時(shí),觸發(fā)中斷,該回調(diào)函數(shù)會(huì)在外設(shè)中斷處理函數(shù)或者 DMA 的中斷處理函數(shù)中被調(diào)用。 - 錯(cuò)誤處理回調(diào)函數(shù):
HAL_PPP_ErrorCallback
例如:__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
。當(dāng)外設(shè)或者 DMA 出現(xiàn)錯(cuò)誤時(shí),觸發(fā)終端,該回調(diào)函數(shù)會(huì)在外設(shè)中斷處理函數(shù)或者DMA的中斷處理函數(shù)中被調(diào)用。
-
寄存器
+關(guān)注
關(guān)注
31文章
5336瀏覽量
120229 -
STM32
+關(guān)注
關(guān)注
2270文章
10895瀏覽量
355724 -
HAL
+關(guān)注
關(guān)注
2文章
70瀏覽量
12607
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論