??說到STM32的HAL庫,就不得不提STM32CubeMX,其作為一個可視化的配置工具,對于開發者來說,確實大大節省了開發時間。STM32CubeMX就是以HAL庫為基礎的,且目前僅支持HAL庫!首先看一下,官方給出的HAL庫的包含結構:
stm32f2xx.h主要包含STM32同系列芯片的不同具體型號的定義,是否使用HAL庫等的定義,接著,其會根據定義的芯片信號包含具體的芯片型號的頭文件:#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.c/h 主要實現HAL庫的初始化、系統滴答相關函數、及CPU的調試模式配置stm32f2xx_hal_conf.h :該文件是一個用戶級別的配置文件,用來實現對HAL庫的裁剪,其位于用戶文件目錄,不要放在庫目錄中。??接下來對于HAL庫的源碼文件進行一下說明,HAL庫文件名均以stm32f2xx_hal開頭,后面加上_外設或者模塊名(如:stm32f2xx_hal_adc.c):
庫文件: stm32f2xx_hal_ppp.c/.h // 主要的外設或者模塊的驅動源文件,包含了該外設的通用API stm32f2xx_hal_ppp_ex.c/.h // 外圍設備或模塊驅動程序的擴展文件。這組文件中包含特定型號或者系列的芯片的特殊API。以及如果該特定的芯片內部有不同的實現方式,則該文件中的特殊API將覆蓋_ppp中的通用API。 stm32f2xx_hal.c/.h // 此文件用于HAL初始化,并且包含DBGMCU、重映射和基于systick的時間延遲等相關的API 其他庫文件用戶級別文件: stm32f2xx_hal_msp_template.c // 只有.c沒有.h。它包含用戶應用程序中使用的外設的MSP初始化和反初始化(主程序和回調函數)。使用者復制到自己目錄下使用模板。 stm32f2xx_hal_conf_template.h // 用戶級別的庫配置文件模板。使用者復制到自己目錄下使用 system_stm32f2xx.c // 此文件主要包含SystemInit()函數,該函數在剛復位及跳到main之前的啟動過程中被調用。 **它不在啟動時配置系統時鐘(與標準庫相反)**。 時鐘的配置在用戶文件中使用HAL API來完成。 startup_stm32f2xx.s // 芯片啟動文件,主要包含堆棧定義,終端向量表等 stm32f2xx_it.c/.h // 中斷處理函數的相關實現 main.c/.h //注意: 1. 目前LL庫是和HAL庫捆綁發布的,所以在HAL庫源碼中,還有一些名為 stm32f2xx_ll_ppp的源碼文件,這些文件就是新增的LL庫文件。
??HAL庫最大的特點就是對底層進行了抽象:
??HAL庫在結構上,對每個外設抽象成了一個稱為ppp_HandleTypeDef的結構體,其中ppp就是每個外設的名字。所有的函數都是工作在ppp_HandleTypeDef指針之下。 ??1. 多實例支持:每個外設/模塊實例都有自己的句柄。 因此,實例資源是獨立的 ??2. 外圍進程相互通信:該句柄用于管理進程例程之間的共享數據資源。 下面,以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可能出現的所有定義,對于用戶想要使用ADC只要定義一個ADC_HandleTypeDef的變量,給每個變量賦好值,對應的外設就抽象完了。接下來就是具體使用了。 ??當然,對于那些共享型外設或者說系統外設來說,他們不需要進行以上這樣的抽象,例如以下外設: ??- GPIO ??- SYSTICK ??- NVIC ??- RCC ??- Flash 以GPIO為例,對于HAL_GPIO_Init() 函數,其只需要GPIO 地址以及其初始化參數即可
??HAL庫對所有的函數模型也進行了統一。在HAL庫中,支持三種編程模式:輪詢模式、中斷模式、DMA模式(如果外設支持)。其分別對應如下三種類型的函數(以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庫的源碼中,到處可見一些以__weak開頭的函數,而且這些函數,有些已經被實現了,比如:
有些則沒有被實現,例如:
__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關鍵字的函數表示,就可以有用戶自己來實現,如果,外部反現了同名函數,且不帶__weak關鍵字,那么連接器就會采用外部實現的同名函數。HAL庫包含如下三種用戶回調函數(PPP為外設名): ??1. 外設系統級初始化/解除初始化回調函數:HAL_PPP_MspInit()和 HAL_PPP_MspDeInit。例如:__weak void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi)。在HAL_PPP_Init() 函數中被調用,用來初始化底層相關的設備(GPIOs, clock, DMA, interrupt)
??2. 處理完成回調函數:HAL_PPP_ProcessCpltCallback(Process指具體某種處理,如UART的Tx),例如:__weak void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)。當外設或者DMA工作完成后時,觸發中斷,該回調函數會在外設中斷處理函數或者DMA的中斷處理函數中被調用
??3. 錯誤處理回調函數:HAL_PPP_ErrorCallback例如:__weak void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi)。當外設或者DMA出現錯誤時,觸發終端,該回調函數會在外設中斷處理函數或者DMA的中斷處理函數中被調用
??至此,HAL庫的總體結構就介紹完了!具體的每個文件的詳細說明,官方源碼注釋很詳細!
新聞熱點
疑難解答