STM32的四種庫:STM32Snippets、Standard Peripheral Library、STM32Cube LL、STM32Cube HAL。
1STM32Snippets什么是STM32Snippets?STM32Snippets可翻譯為“代碼片段”、“裁剪”,其實他就是我們常說的“寄存器”開發(fā)STM32的底層驅(qū)動代碼。比如配置ADC引腳的代碼片段:
__INLINE void ConfigureGPIOforADC(void)
{
/* (1) Enable the peripheral clock of GPIOA, GPIOB and GPIOC */
/* (2) Select analog mode for PA1 */
/* (3) Select analog mode for PB1 */
/* (4) Select analog mode for PC0 */
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | RCC_AHBENR_GPIOCEN; /* (1) */
GPIOA->MODER |= GPIO_MODER_MODER1; /* (2) */
GPIOB->MODER |= GPIO_MODER_MODER1; /* (3) */
GPIOC->MODER |= GPIO_MODER_MODER0; /* (4) */
}
STM32Snippets是 高度優(yōu)化的示例代碼集合 ,使用符合CMSIS的直接寄存器訪問來減少代碼開銷,從而在各種應(yīng)用程序中最大化STM32 MCUs的性能。
STM32Snippets主要針對底層開發(fā)人員,或者從51轉(zhuǎn)過來,直接操作寄存器開發(fā)的人員。 是沒有經(jīng)過封裝,可見底層寄存器的一套示例代碼。每個STM32系列的100多個片段演示了如何以最小的內(nèi)存占用有效地使用STM32外圍設(shè)備。
STM32Snippets目前官方只提供: STM32F0和L0的示例代碼包 。
提供的示例代碼有點類似標(biāo)準(zhǔn)外設(shè)庫風(fēng)格,給大家看下F0的代碼包:

?
2Standard Peripheral Library
Standard Peripheral Library:簡寫SPL,也叫標(biāo)準(zhǔn)外設(shè)庫 。
相信學(xué)習(xí)過STM32的朋友,對標(biāo)準(zhǔn)外設(shè)庫都不陌生,是一組外圍設(shè)備的C語言代碼集合。 (因為現(xiàn)在ST官方主推STM32CubeMX,所以停止了對SPL的更新) 標(biāo)準(zhǔn)外設(shè)庫是在寄存器的基礎(chǔ)上進行了一次簡單封裝,主要是面向過程的嵌入式系統(tǒng)開發(fā)人員。

?
目前標(biāo)準(zhǔn)外設(shè)庫 支持STM32F0、F1、F2、F3、F4、L1 ,不支持F7、H7、 MP1、L0、L4、L5、G0、G4等后面推出的系列。所以,不要再問: 在哪里下載L0的標(biāo)準(zhǔn)外設(shè)庫了 。
3STM32Cube LL 和 HAL
LL:**** Low-Layer,底層庫
HAL: Hardware Abstraction Layer,硬件抽象層庫
STM32Cube HAL和LL配合STM32CubeMX工具對STM32進行開發(fā)。也是目前ST官方主推的一套開發(fā)STM32的庫。HAL針對的是具有一定嵌入式基礎(chǔ)的開發(fā)人員,HAL具有很好的移植性。LL庫相對HAL,具有簡單的結(jié)構(gòu),針對之前從事SPL(標(biāo)準(zhǔn)外設(shè)庫),或寄存器開發(fā)的人員。

?
STM32Cube HAL和LL是目前官方主推,并重點維護和更新的庫,也建議大家都學(xué)習(xí)一下。而且新出來的型號(如L5、G4等系列)沒有標(biāo)準(zhǔn)外設(shè)庫,只有STM32Cube HAL和LL庫了。
4四種庫對比
來自官方的對比信息,包含可移植性、優(yōu)化、難易程度等。

?
其中: Portability: 可移植性; Optimization : 優(yōu)化; Easy: 難易程度; Hardware coverage: 硬件覆蓋。
定位:

?
支持器件:

?
最近新增了STM32Cube MP1,官方?jīng)]有統(tǒng)計上。
庫之間的轉(zhuǎn)移:

?
STM32標(biāo)準(zhǔn)庫和HAL庫有什么不同?
網(wǎng)上關(guān)于標(biāo)準(zhǔn)庫、HAL庫的描述相信是數(shù)不勝數(shù)。可是一個對于很多剛?cè)腴T的朋友還是沒法很直觀的去真正了解這些不同開發(fā)發(fā)方式彼此之間的區(qū)別,下面以一種非常直白的方式,用自己的理解去將這些東西表述出來。
一、配置寄存器
不少先學(xué)了51的朋友可能會知道,會有一小部分人或是教程是通過匯編語言直接操作寄存器實現(xiàn)功能的,這種方法到了STM32就變得不太容易行得通了。因為STM32的寄存器數(shù)量是51單片機的十?dāng)?shù)倍,如此多的寄存器根本無法全部記憶,開發(fā)時需要經(jīng)常的翻查芯片的數(shù)據(jù)手冊,此時直接操作寄存器就變得非常的費力了。但還是會有很小一部分人,喜歡去直接操作寄存器,因為這樣更接近原理,知其然也知其所以然。
二、標(biāo)準(zhǔn)庫
STM32有非常多的寄存器,而導(dǎo)致了開發(fā)困難,所以為此ST公司就為每款芯片都編寫了一份庫文件,也就是工程文件里stm32F1xx.....之類的。在這些.c .h文件中,包括一些常用量的宏定義,把一些外設(shè)也通過結(jié)構(gòu)體變量封裝起來,如GPIO口時鐘等。所以我們只需要配置結(jié)構(gòu)體變量成員就可以修改外設(shè)的配置寄存器,從而選擇不同的功能。也是目前最多人使用的方式,也是學(xué)習(xí)STM32接觸最多的一種開發(fā)方式。
三、HAL庫
HAL庫是ST公司目前主力推的開發(fā)方式,全稱就是Hardware Abstraction Layer(抽象印象層)。庫如其名,很抽象,一眼看上去不太容易知道其作用是什么。它的出現(xiàn)比標(biāo)準(zhǔn)庫要晚,但其實和標(biāo)準(zhǔn)庫一樣,都是為了節(jié)省程序開發(fā)的時期,而且HAL庫尤其的有效,如果說標(biāo)準(zhǔn)庫把實現(xiàn)功能需要配置的寄存器集成了,那么HAL庫的一些函數(shù)甚至可以做到某些特定功能的集成。也就是說,同樣的功能,標(biāo)準(zhǔn)庫可能要用幾句話,HAL庫只需用一句話就夠了。并且HAL庫也很好的解決了程序移植的問題,不同型號的stm32芯片它的標(biāo)準(zhǔn)庫是不一樣的,例如在F4上開發(fā)的程序移植到F3上是不能通用的,而使用HAL庫,只要使用的是相通的外設(shè),程序基本可以完全復(fù)制粘貼,注意是相通外設(shè),意思也就是不能無中生有。例如F7比F3要多幾個定時器,不能明明沒有這個定時器卻非要配置,但其實這種情況不多,絕大多數(shù)都可以直接復(fù)制粘貼。是而且使用ST公司研發(fā)的STMcube軟件,可以通過圖形化的配置功能,直接生成整個使用HAL庫的工程文件,可以說是方便至極,但是方便的同時也造成了它執(zhí)行效率的低下,在各種論壇帖子真的是被吐槽的數(shù)不勝數(shù)。
四、總結(jié)
綜合上面說的,其實筆者還是強烈推薦HAL庫的,理由有二:
一、 F7系列開始 ST公司就已近開始停止更新標(biāo)準(zhǔn)庫,也就是F7開始包括F7已經(jīng)不能用標(biāo)準(zhǔn)庫了,公司對于主打HAL庫的目的已經(jīng)非常明顯了。
二、追求更方便、追求模塊化向來是世界的潮流,更方便的HAL庫一定會迅速發(fā)展,低效的短板遲早會被硬件高度集成化所彌補 。
當(dāng)然啦,不能只學(xué)習(xí)HAL庫,底層的原理必需是要懂的,這是每個學(xué)有所成的人都公認的事實,HAL庫也不是萬能的,結(jié)合對底層的理解相信一定會讓你的開發(fā)水準(zhǔn)大大提高。
五、STM32 HAL庫與標(biāo)準(zhǔn)庫的區(qū)別
1.句柄
在STM32的標(biāo)準(zhǔn)庫中,假設(shè)我們要初始化一個外設(shè)(這里以USART為例) 我們首先要初始化它們的各個寄存器。
在標(biāo)準(zhǔn)庫中,這些操作都是利用固件庫結(jié)構(gòu)體變量+固件庫Init函數(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;//一個停止位??USART_InitStructure.USART_Parity?=?USART_Parity_No;//無奇偶校驗位??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
可以看到,要初始化一個串口,需要對六個位置進行賦值,然后引用Init函數(shù),并且USART_InitStructure
并不是一個全局結(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_InitTypeDef??????????????Init;?????????????/*!????uint8_t???????????????????????*pTxBuffPtr;??????/*!????uint16_t??????????????????????TxXferSize;???????/*!????uint16_t??????????????????????TxXferCount;??????/*!????uint8_t???????????????????????*pRxBuffPtr;??????/*!????uint16_t??????????????????????RxXferSize;???????/*!????uint16_t??????????????????????RxXferCount;??????/*!?? ???DMA_HandleTypeDef?????????????*hdmatx;??????????/*!? ???DMA_HandleTypeDef?????????????*hdmarx;??????????/*!????HAL_LockTypeDef???????????????Lock;?????????????/*!????__IO?HAL_UART_StateTypeDef????State;????????????/*!????__IO?uint32_t?????????????????ErrorCode;????????/*!?}UART_HandleTypeDef;
我們發(fā)現(xiàn),與標(biāo)準(zhǔn)庫不同的是,該成員不僅包含了之前標(biāo)準(zhǔn)庫就有的六個成員(波特率,數(shù)據(jù)格式等),還包含過采樣、(發(fā)送或接收的)數(shù)據(jù)緩存、數(shù)據(jù)指針、串口 DMA 相關(guān)的變量、各種標(biāo)志位等等要在整個項目流程中都要設(shè)置的各個成員。該UART1_Handler就被稱為串口的句柄 它被貫穿整個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)用初始化時定義的句柄UART1_Handler
就好。2.MSP函數(shù)
MCU Specific Package
單片機的具體方案MSP是指和MCU相關(guān)的初始化,引用一下正點原子的解釋,個人覺得說的很明白:我們要初始化一個串口,首先要設(shè)置和 MCU 無關(guān)的東西,例如波特率,奇偶校驗,停止位等,這些參數(shù)設(shè)置和 MCU 沒有任何關(guān)系,可以使用 STM32F1,也可以是STM32F2/F3/F4/F7
上的串口。而一個串口設(shè)備它需要一個 MCU 來承載,例如用 STM32F4 來做承載,PA9 做為發(fā)送,PA10 做為接收,MSP 就是要初始化 STM32F4 的 PA9,PA10,配置這兩個引腳。所以 HAL驅(qū)動方式的初始化流程就是:HAL_USART_Init()—>HAL_USART_MspInit()
,先初始化與 MCU無關(guān)的串口協(xié)議,再初始化與 MCU 相關(guān)的串口引腳。在 STM32 的 HAL 驅(qū)動中HAL_PPP_MspInit()作為回調(diào),被HAL_PPP_Init()
函數(shù)所調(diào)用。當(dāng)我們需要移植程序到 STM32F1平臺的時候,我們只需要修改 HAL_PPP_MspInit 函數(shù)內(nèi)容而不需要修改 HAL_PPP_Init 入口參數(shù)內(nèi)容。”在HAL庫中,幾乎每初始化一個外設(shè)就需要設(shè)置該外設(shè)與單片機之間的聯(lián)系,比如IO口,是否復(fù)用等等,可見,HAL庫相對于標(biāo)準(zhǔn)庫多了MSP函數(shù)之后,移植性非常強,但與此同時卻增加了代碼量和代碼的嵌套層級。可以說各有利弊。同樣,MSP函數(shù)又可以配合句柄,達到非常強的移植性:void?HAL_UART_MspInit(UART_HandleTypeDef?*huart);
3.Callback函數(shù)
類似于MSP函數(shù),個人認為Callback函數(shù)主要幫助用戶應(yīng)用層的代碼編寫。還是以USART為例,在標(biāo)準(zhǔn)庫中,串口中斷了以后,我們要先在中斷中判斷是否是接收中斷,然后讀出數(shù)據(jù),順便清除中斷標(biāo)志位,然后再是對數(shù)據(jù)的處理,這樣如果我們在一個中斷函數(shù)中寫這么多代碼,就會顯得很混亂:void?USART3_IRQHandler(void)?//串口1中斷服務(wù)程序?{ ?u8?Res; ?if(USART_GetITStatus(USART3,?USART_IT_RXNE)?!=?RESET)??//接收中斷(接收到的數(shù)據(jù)必須是0x0d?0x0a結(jié)尾)??{ ??Res?=USART_ReceiveData(USART3);?//讀取接收到的數(shù)據(jù)???/*數(shù)據(jù)處理區(qū)*/??}? }?
而在HAL庫中,進入串口中斷后,直接由HAL庫中斷函數(shù)進行托管:void?USART1_IRQHandler(void)??????????????????{? ?HAL_UART_IRQHandler(&UART1_Handler);?//調(diào)用HAL庫中斷處理公用函數(shù)??/***************省略無關(guān)代碼****************/? }
HAL_UART_IRQHandler
這個函數(shù)完成了判斷是哪個中斷(接收?發(fā)送?或者其他?),然后讀出數(shù)據(jù),保存至緩存區(qū),順便清除中斷標(biāo)志位等等操作。比如我提前設(shè)置了,串口每接收五個字節(jié),我就要對這五個字節(jié)進行處理。在一開始我定義了一個串口接收緩存區(qū):/*HAL庫使用的串口接收緩沖,處理邏輯由HAL庫控制,接收完這個數(shù)組就會調(diào)用HAL_UART_RxCpltCallback進行處理這個數(shù)組*/?/*RXBUFFERSIZE=5*/?u8?aRxBuffer[RXBUFFERSIZE];
在初始化中,我在句柄里設(shè)置好了緩存區(qū)的地址,緩存大小(五個字節(jié))
/*該代碼在HAL_UART_Receive_IT函數(shù)中,初始化時會引用*/?huart->pRxBuffPtr?=?pData;//aRxBuffer?huart->RxXferSize?=?Size;//RXBUFFERSIZE?huart->RxXferCount?=?Size;//RXBUFFERSIZE
則在接收數(shù)據(jù)中,每接收完五個字節(jié),HAL_UART_IRQHandler
才會執(zhí)行一次Callback函數(shù):void?HAL_UART_RxCpltCallback(UART_HandleTypeDef?*huart);
在這個Callback回調(diào)函數(shù)中,我們只需要對這接收到的五個字節(jié)(保存在aRxBuffer[]中)進行處理就好了,完全不用再去手動清除標(biāo)志位等操作。
所以說Callback函數(shù)是一個應(yīng)用層代碼的函數(shù),我們在一開始只設(shè)置句柄里面的各個參數(shù),然后就等著HAL庫把自己安排好的代碼送到手中就可以了~
綜上,就是HAL庫的三個與標(biāo)準(zhǔn)庫不同的地方之個人見解。 個人覺得從這三個小點就可以看出HAL庫的可移植性之強大,并且用戶可以完全不去理會底層各個寄存器的操作,代碼也更有邏輯性。但與此帶來的是復(fù)雜的代碼量,極慢的編譯速度,略微低下的效率。看怎么取舍了 。
五、HAL庫結(jié)構(gòu)
說到STM32 的HAL庫,就不得不提STM32CubeMX,其作為一個可視化的配置工具,對于開發(fā)者來說,確實大大節(jié)省了開發(fā)時間。
STM32CubeMX 就是以 HAL 庫為基礎(chǔ)的,且目前僅支持 HAL 庫及 LL 庫!首先看一下,官方給出的 HAL 庫的包含結(jié)構(gòu):

?

?
stm32f2xx.h 主要包含STM32同系列芯片的不同具體型號的定義,是否使用HAL庫等的定義,接著,其會根據(jù)定義的芯片信號包含具體的芯片型號的頭文件:
#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
緊接著,其會包含 stm32f2xx_hal.h。stm32f2xx_hal.h:stm32f2xx_hal.c/h 主要實現(xiàn)HAL庫的初始化、系統(tǒng)滴答相關(guān)函數(shù)、及CPU的調(diào)試模式配置
stm32f2xx_hal_conf.h :該文件是一個用戶級別的配置文件,用來實現(xiàn)對HAL庫的裁剪,其位于用戶文件目錄,不要放在庫目錄中。
接下來對于HAL庫的源碼文件進行一下說明,HAL庫文件名均以stm32f2xx_hal開頭,后面加上_外設(shè)或者模塊名(如:stm32f2xx_hal_adc.c):
庫文件: ?stm32f2xx_hal_ppp.c/.h???//?主要的外設(shè)或者模塊的驅(qū)動源文件,包含了該外設(shè)的通用API??stm32f2xx_hal_ppp_ex.c/.h??//?外圍設(shè)備或模塊驅(qū)動程序的擴展文件。這組文件中包含特定型號或者系列的芯片的特殊API。以及如果該特定的芯片內(nèi)部有不同的實現(xiàn)方式,則該文件中的特殊API將覆蓋_ppp中的通用API。??stm32f2xx_hal.c/.h????//?此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的時間延遲等相關(guān)的API??其他庫文件 用戶級別文件: ?stm32f2xx_hal_msp_template.c?//?只有.c沒有.h。它包含用戶應(yīng)用程序中使用的外設(shè)的MSP初始化和反初始化(主程序和回調(diào)函數(shù))。使用者復(fù)制到自己目錄下使用模板。??stm32f2xx_hal_conf_template.h?//?用戶級別的庫配置文件模板。使用者復(fù)制到自己目錄下使用??system_stm32f2xx.c????//?此文件主要包含SystemInit()函數(shù),該函數(shù)在剛復(fù)位及跳到main之前的啟動過程中被調(diào)用。?**它不在啟動時配置系統(tǒng)時鐘(與標(biāo)準(zhǔn)庫相反)**。?時鐘的配置在用戶文件中使用HAL API來完成。??startup_stm32f2xx.s????//?芯片啟動文件,主要包含堆棧定義,終端向量表等??stm32f2xx_it.c/.h????//?中斷處理函數(shù)的相關(guān)實現(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)和錯誤:HAL_PPP_GetState (), HAL_PPP_GetError ().
注意:目前LL庫是和HAL庫捆綁發(fā)布的,所以在HAL庫源碼中,還有一些名為 stm32f2xx_ll_ppp 的源碼文件,這些文件就是新增的LL庫文件。使用CubeMX生產(chǎn)項目時,可以選擇LL庫。”
HAL庫最大的特點就是對底層進行了抽象。在此結(jié)構(gòu)下,用戶代碼的處理主要分為三部分:
處理外設(shè)句柄(實現(xiàn)用戶功能) 處理MSP 處理各種回調(diào)函數(shù) 外設(shè)句柄定義 ??用戶代碼的第一大部分:對于外設(shè)句柄的處理。HAL庫在結(jié)構(gòu)上,對每個外設(shè)抽象成了一個稱為ppp_HandleTypeDef的結(jié)構(gòu)體,其中ppp就是每個外設(shè)的名字。*所有的函數(shù)都是工作在ppp_HandleTypeDef指針之下。多實例支持:每個外設(shè)/模塊實例都有自己的句柄。因此,實例資源是獨立的
外圍進程相互通信:該句柄用于管理進程例程之間的共享數(shù)據(jù)資源。
下面,以ADC為例:
/**? ?*?@brief??ADC?handle?Structure?definition ?*/??typedef?struct?{??ADC_TypeDef???????????????????*Instance;???????????????????/*!??ADC_InitTypeDef???????????????Init;????????????????????????/*!???__IO?uint32_t?????????????????NbrOfCurrentConversionRank;??/*!??DMA_HandleTypeDef?????????????*DMA_Handle;?????????????????/*!??HAL_LockTypeDef???????????????Lock;????????????????????????/*!??__IO?uint32_t?????????????????State;???????????????????????/*!??__IO?uint32_t?????????????????ErrorCode;???????????????????/*!?}ADC_HandleTypeDef;
從上面的定義可以看出,
ADC_HandleTypeDef
中包含了ADC可能出現(xiàn)的所有定義,對于用戶想要使用ADC只要定義一個ADC_HandleTypeDef
的變量,給每個變量賦好值,對應(yīng)的外設(shè)就抽象完了。接下來就是具體使用了。當(dāng)然,對于那些共享型外設(shè)或者說系統(tǒng)外設(shè)來說,他們不需要進行以上這樣的抽象,這些部分與原來的標(biāo)準(zhǔn)外設(shè)庫函數(shù)基本一樣。如以下外設(shè):GPIO、SYSTICK、NVIC、RCC、FLASH。
以GPIO 為例,對于
HAL_GPIO_Init()
函數(shù),其只需要GPIO地址以及其初始化參數(shù)即可。
1. 三種編程方式
HAL庫對所有的函數(shù)模型也進行了統(tǒng)一。在HAL庫中,支持三種編程模式:輪詢模式、中斷模式、DMA模式(如果外設(shè)支持)。其分別對應(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)一采用宏的形式對各種中斷等進行配置(原來標(biāo)準(zhǔn)外設(shè)庫一般都是各種函數(shù))。針對每種外設(shè)主要由以下宏:__HAL_PPP_ENABLE_IT(HANDLE, INTERRUPT):使能一個指定的外設(shè)中斷
__HAL_PPP_DISABLE_IT(HANDLE, INTERRUPT):失能一個指定的外設(shè)中斷
__HAL_PPP_GET_IT (HANDLE, __ INTERRUPT __):獲得一個指定的外設(shè)中斷狀態(tài)
__HAL_PPP_CLEAR_IT (HANDLE, __ INTERRUPT __):清除一個指定的外設(shè)的中斷狀態(tài)
__HAL_PPP_GET_FLAG (HANDLE, FLAG):獲取一個指定的外設(shè)的標(biāo)志狀態(tài)
__HAL_PPP_CLEAR_FLAG (HANDLE, FLAG):清除一個指定的外設(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)被實現(xiàn)了,比如:__weak?HAL_StatusTypeDef?HAL_InitTick(uint32_t?TickPriority)?{ ?/*Configure?the?SysTick?to?have?interrupt?in?1ms?time?basis*/??HAL_SYSTICK_Config(SystemCoreClock/1000U); ?/*Configure?the?SysTick?IRQ?priority?*/??HAL_NVIC_SetPriority(SysTick_IRQn,?TickPriority?,0U); ?/*?Return?function?status?*/??return?HAL_OK; }
有些則沒有被實現(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ù)表示,就可以由用戶自己來實現(xiàn)。如果出現(xiàn)了同名函數(shù),且不帶__weak
關(guān)鍵字,那么連接器就會采用外部實現(xiàn)的同名函數(shù)。通常來說,HAL庫負責(zé)整個處理和MCU外設(shè)的處理邏輯,并將必要部分以回調(diào)函數(shù)的形式給出到用戶,用戶只需要在對應(yīng)的回調(diào)函數(shù)中做修改即可。HAL 庫包含如下三種用戶級別回調(diào)函數(shù)(PPP為外設(shè)名):1.外設(shè)系統(tǒng)級初始化/解除初始化回調(diào)函數(shù)(用戶代碼的第二大部分:對于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)2.處理完成回調(diào)函數(shù):
HAL_PPP_ProcessCpltCallback
(Process指具體某種處理,如UART的Tx),例如:__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
。當(dāng)外設(shè)或者DMA工作完成后時,觸發(fā)中斷,該回調(diào)函數(shù)會在外設(shè)中斷處理函數(shù)或者DMA的中斷處理函數(shù)中被調(diào)用3.錯誤處理回調(diào)函數(shù):
HAL_PPP_ErrorCallback
例如:__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)
。當(dāng)外設(shè)或者DMA出現(xiàn)錯誤時,觸發(fā)終端,該回調(diào)函數(shù)會在外設(shè)中斷處理函數(shù)或者DMA的中斷處理函數(shù)中被調(diào)用。
評論