HAL庫結(jié)構(gòu)
??說到STM32的HAL庫,就不得不提STM32CubeMX,其作為一個可視化的配置工具,對于開發(fā)者來說,確實大大節(jié)省了開發(fā)時間。STM32CubeMX就是以HAL庫為基礎(chǔ)的,且目前僅支持HAL庫!首先看一下,官方給出的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 //
注意:
1. 目前LL庫是和HAL庫捆綁發(fā)布的,所以在HAL庫源碼中,還有一些名為 stm32f2xx_ll_ppp的源碼文件,這些文件就是新增的LL庫文件。
??HAL庫最大的特點就是對底層進行了抽象:
外設(shè)句柄定義
??HAL庫在結(jié)構(gòu)上,對每個外設(shè)抽象成了一個稱為ppp_HandleTypeDef的結(jié)構(gòu)體,其中ppp就是每個外設(shè)的名字。所有的函數(shù)都是工作在ppp_HandleTypeDef指針之下。
??1. 多實例支持:每個外設(shè)/模塊實例都有自己的句柄。 因此,實例資源是獨立的
??2. 外圍進程相互通信:該句柄用于管理進程例程之間的共享數(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;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
??從上面的定義可以看出,ADC_HandleTypeDef中包含了ADC可能出現(xiàn)的所有定義,對于用戶想要使用ADC只要定義一個ADC_HandleTypeDef的變量,給每個變量賦好值,對應(yīng)的外設(shè)就抽象完了。接下來就是具體使用了。
??當(dāng)然,對于那些共享型外設(shè)或者說系統(tǒng)外設(shè)來說,他們不需要進行以上這樣的抽象,例如以下外設(shè):
??- GPIO
??- SYSTICK
??- NVIC
??- RCC
??- FLASH
以GPIO為例,對于HAL_GPIO_Init() 函數(shù),其只需要GPIO 地址以及其初始化參數(shù)即可
三種編程方式
??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模式下也是開中斷的);什么都沒帶的就是輪詢模式(沒有開啟中斷的)。至于使用者使用何種方式,就看自己的選擇了。
三大回調(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庫包含如下三種用戶回調(diào)函數(shù)(PPP為外設(shè)名):
??1. 外設(shè)系統(tǒng)級初始化/解除初始化回調(diào)函數(shù):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)用
HAL庫移植使用
- 復(fù)制
stm32f2xx_hal_msp_template.c ,參照該模板,依次實現(xiàn)用到的外設(shè)的HAL_PPP_MspInit() 和 HAL_PPP_MspDeInit 。
- 復(fù)制
stm32f2xx_hal_conf_template.h ,用戶可以在此文件中自由裁剪,配置HAL庫。
- 在使用HAL庫時,必須先調(diào)用函數(shù):
HAL_StatusTypeDef HAL_Init(void) (該函數(shù)在stm32f2xx_hal.c 中定義,也就意味著第一點中,必須首先實現(xiàn)HAL_MspInit(void) 和HAL_MspDeInit(void) )
- HAL庫與STD庫不同,HAL庫使用RCC中的函數(shù)來配置系統(tǒng)時鐘,用戶需要單獨寫時鐘配置函數(shù)(STD庫默認在
system_stm32f2xx.c 中)
- 關(guān)于中斷,HAL提供了中斷處理函數(shù),只需要調(diào)用HAL提供的中斷處理函數(shù)。用戶自己的代碼,不建議先寫到中斷中,而應(yīng)該寫到HAL提供的回調(diào)函數(shù)中。
- 對于每一個外設(shè),HAL都提供了回調(diào)函數(shù),回調(diào)函數(shù)用來實現(xiàn)用戶自己的代碼。整個調(diào)用結(jié)構(gòu)由HAL庫自己完成。例如:Uart中,HAL提供了
void HAL_UART_IRQHandler(UART_HandleTypeDef *huart); 函數(shù),用戶只需要觸發(fā)中斷后,用戶只需要調(diào)用該函數(shù)即可,同時,自己的代碼寫在對應(yīng)的回調(diào)函數(shù)中即可!如下:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);
使用了哪種就用哪個回調(diào)函數(shù)即可!
??至此,HAL庫的總體結(jié)構(gòu)就介紹完了!具體的每個文件的詳細說明,官方源碼注釋很詳細!
|