第9章 初識HAL固件庫


本章參考資料:《STM32F76xxx參考手冊》、《STM32F7xx規格書》、《Cortex-M3權威指南》, STM32 HAL庫幫助文檔:《STM32F779xx_User_Manual.chm》。

在上一章中,我們構建了幾個控制GPIO外設的函數,算是實現了函數庫的雛形,但GPIO還有很多功能函數我們沒有實現,而且STM32芯片不僅僅只有GPIO這一個外設。如果我們想要親自完成這個函數庫,工作量是非常巨大的。ST公司提供的HAL軟件庫,包含了STM32芯片所有寄存器的控制操作,我們直接學習如何使用STHAL庫,會極大地方便控制STM32芯片。

9.1  CMSIS標准及庫層次關系

因為基於Cortex系列芯片采用的內核都是相同的,區別主要為核外的片上外設的差異,這些差異卻導致軟件在同內核,不同外設的芯片上移植困難。為了解決不同的芯片廠商生產的Cortex微控制器軟件 的兼容性問題,ARM與芯片廠商建立了CMSIS標准(Cortex MicroController Software Interface Standard)

所謂CMSIS標准,實際是新建了一個軟件抽象層。見 9-1

 

9-1 CMSIS架構

CMSIS標准中最主要的為CMSIS核心層,它包括了:

內核函數層:其中包含用於訪問內核寄存器的名稱、地址定義,主要由ARM公司提供。

設備外設訪問層:提供了片上的核外外設的地址和中斷定義,主要由芯片生產商提供。

可見CMSIS層位於硬件層與操作系統或用戶層之間,提供了與芯片生產商無關的硬件抽象層,可以為接口外設、實時操作系統提供簡單的處理器軟件接口,屏蔽了硬件差異,這對軟件的移植是有極大的好處的。STM32的庫,就是按照CMSIS標准建立的。

9.1.1  庫目錄、文件簡介

STM32 HAL庫可以從官網獲得,也可以直接從本書的配套資料得到。本書講解的例程全部采用1.5.1庫文件。以下內容請大家打開STM32 HAL庫文件配合閱讀。

解壓庫文件后進入其目錄:

“STM32Cube_FW_F7_V1.6.0\

軟件庫各文件夾的內容說明見 9-2

 

9-2 ST HAL 目錄STM32Cube_FW_F7_V1.6.0\

q Documentation:文件夾下是HAL庫幫助文檔,主要講述如何使用驅動庫來編寫自己的應用程序。說得形象一點就是告訴我們:ST公司已經為你寫好了每個外設的驅動了,想知道如何運用這些例子就來向我求救吧。不幸的是,這個幫助文檔是英文的,這對很多英文不好的朋友來說是一個很大的障礙。但這里要告訴大家,英文僅僅是一種工具,絕對不能讓它成為我們學習的障礙。其實這些英文還是很簡單的,我們需要的是拿下它的勇氣。

q Drivers:文件夾下是官方的CMSISI庫,HAL庫,板載外設驅動。

q Middlewares:中間件,包含ST官方的STemWin STM32_AudioSTM32_USB_Device_LibrarySTM32_USB_Host_Library;也有第三方的fatfs文件系統等等。

q Project 文件夾下是用驅動庫寫的針對官方發行demo板的例子和工程模板。

q Utilities:實用的公用組件比如LCD_LOG實用液晶打印調試信息。

q Release_Note.html::庫的版本更新說明。

在使用庫開發時,我們需要把Drivers目錄下的CMSISSTM32F7xx_HAL_Driver內核與外設的庫文件添加到工程中,並查閱庫幫助文檔來了解ST提供的庫函數,這個文檔說明了每一個庫函數的使用方法。

先看看CMSIS文件夾。

STM32Cube_FW_F7_V1.6.0\Drivers\CMSIS\文件夾下內容見 9-3

9-3 CMSIS文件夾內容  目錄:Drivers \CMSIS\

其中DeviceInclude中的文件是我們使用得最多的,先講解這兩個文件夾中的內容。

1. Include文件夾

Include文件夾中包含了 的是位於CMSIS標准的核內設備函數層Cortex-M核通用的頭文件,它們的作用是為那些采用Cortex-M核設計SOC的芯片商設計的芯片外設提供一個進入內核的接口,定義了一些內核相關的寄存器(類似我們前面寫的stm32f767xx.h文件,但定義的是內核部分的寄存器)。這些文件在其它公司的Cortex-M系列芯片也是相同的。至於這些功能是怎樣用源碼實現的,可以不用管它,只需把這些文件加進我們的工程文件即可,有興趣的朋友可以深究,關於內核的寄存器說明,需要查閱《cortex_m7_Technical Reference Manual》及《Cortex®-M7內核編程手冊》文檔,《STM32F76xxx參考手冊》只包含片上外設說明,不包含內核寄存器。

我們寫STM32F7的工程,必須用到其中的四個文件:core_cm7.hcore_cmFunc.hcorecmInstr.hcore_cmSimd.h,其它的文件是屬於其它內核的,還有幾個文件是DSP函數庫使用的頭文件。

core_cm7.c文件有一些與編譯器相關條件編譯語句,用於屏蔽不同編譯器的差異。里面包含了一些跟編譯器相關的信息,如:“__CC_ARM ”(本書采用的RVMDKKEIL),“__GNUC__ ”(GNU編譯器)、“ICC Compiler” (IAR編譯器)。這些不同的編譯器對於C嵌入匯編或內聯函數關鍵字的語法不一樣,這段代碼統一使用“__ASM__INLINE”宏來定義,而在不同的編譯器下,宏自動更改到相應的值,實現了差異屏蔽,見代碼清單9-1

代碼清單9-1core_cm3.c文件中對編譯器差異的屏蔽

1 #if   defined ( __CC_ARM )
 2 #define __ASM            __asm     /*!< asm keyword for ARM Compiler */
 3 #define __INLINE         __inline  /*!< inline keyword for ARM Compiler*/
 4 #define __STATIC_INLINE  static __inline
 5 
 6 #elif defined ( __GNUC__ )
 7 #define __ASM            __asm  /*!< asm keyword for GNU Compiler */
 8 #define __INLINE         inline /*!< inline keyword for GNU Compiler */
 9 #define __STATIC_INLINE  static inline
10 
11 #elif defined ( __ICCARM__ )
12 #define __ASM            __asm  /*!< asm keyword for IAR Compiler */
13 /*!< inline keyword for IAR Compiler. */
14 #define __STATIC_INLINE  static inline
15 #define __INLINE         inline
16 
17 #elif defined ( __TMS470__ )
18 #define __ASM            __asm  /*!< asm keyword for TI CCS Compiler */
19 #define __STATIC_INLINE  static inline
20 
21 #elif defined ( __TASKING__ )
22 #define __ASM            __asm /*!< asm keyword for TASKING Compiler */
23 #define __INLINE         inline /*!< inline keyword for TASKING Compiler */
24 #define __STATIC_INLINE  static inline
25 
26 #elif defined ( __CSMC__ )
27 #define __packed
28 #define __ASM            _asm /*!< asm keyword for COSMIC Compiler */
29 /*use -pc99 on compile line !< inline keyword for COSMIC Compiler   */
30 #define __INLINE         inline
31 #define __STATIC_INLINE  static inline
32 
33 #endif

  

較重要的是在core_cm7.c文件中包含了stdint.h”  這個頭文件,這是一個ANSI C 文件,是獨立於處理器之外的,就像我們熟知的C語言頭文件 “stdio.h” 文件一樣。位於RVMDK這個軟件的安裝目錄下,主要作用是提供一些類型定義。見代碼清單9-2

代碼清單9-2stdint.c文件中的類型定義

1. /* exact-width signed integer types */  
2. typedef   signed          char int8_t;   
3. typedef   signed short     int int16_t;   
4. typedef   signed           int int32_t;   
5. typedef   signed       __int64 int64_t;   
6.   
7. /* exact-width unsigned integer types */  
8. typedef unsigned          char uint8_t;   
9. typedef unsigned short     int uint16_t;   
10. typedef unsigned           int uint32_t;   
11. typedef unsigned       __int64 uint64_t;  

  -

這些新類型定義屏蔽了在不同芯片平台時,出現的諸如int的大小是16位,還是32位的差異。所以在我們以后的程序中,都將使用新類型如uint8_t uint16_t等。

在稍舊版的程序中還經常會出現如u8u16u32這樣的類型,分別表示的無符號的8位、16位、32位整型。初學者碰到這樣的舊類型感覺一頭霧水,它們定義的位置在Stm32f767xx.h文件中。建議在以后的新程序中盡量使用uint8_t uint16_t類型的定義。

core_cm7.c跟啟動文件一樣都是底層文件,都是由ARM公司提供的,遵守CMSIS標准,即所有CM7芯片的庫都帶有這個文件,這樣軟件在不同的CM7芯片的移植工作就得以簡化。

2. Device文件夾

Device文件夾下的是具體芯片直接相關的文件,包含啟動文件、芯片外設寄存器定義、系統時鍾初始化功能的一些文件,這是由ST公司提供的。

q system_stm32f7xx.c文件

文件目錄:\ Drivers \CMSIS\Device\ST\STM32F7xx\Source\Templates

這個文件包含了STM32芯片上電后初始化系統時鍾、擴展外部存儲器用的函數,例如我們前兩章提到供啟動文件調用的“SystemInit”函數,用於上電后初始化時鍾,該函數的定義就存儲在system_stm32f7xx.c文件。STM32F767系列的芯片,調用庫的這個SystemInit函數后,系統時鍾被初始化為216MHz,如有需要可以修改這個文件的內容,設置成自己所需的時鍾頻率。

啟動文件

文件目錄:Drivers \CMSIS\Device\ST\STM32F7xx\Source\Templates

在這個目錄下,還有很多文件夾,如ARM”、“gcc”、“iar”等,這些文件夾下包含了對應編譯平台的匯編啟動文件,在實際使用時要根據編譯平台來選擇。我們使用的MDK啟動文件在“ARM”文件夾中。其中的“strartup_STM32F767xx.s”即為STM32F767芯片的啟動文件,前面兩章工程中使用的啟動文件就是從這里復制過去的。如果使用其它型號的芯片,要在此處選擇對應的啟動文件,如STM32F779型號使用“startup_stm32f779xx.s”文件。

q stm32f767xx.h文件

文件目錄:Drivers \CMSIS\Device\ST\STM32F7xx\Include

stm32f767xx.h 這個文件非常重要,是一個STM32芯片底層相關的文件。它是我們前兩章自己定義的“stm32f767xx.h”文件的完整版,包含了STM32中所有的外設寄存器地址和結構體類型定義,在使用到STM32 HAL庫的地方都要包含這個頭文件。

CMSIS文件夾中的主要內容就是這樣,接下來我們看看STM32F7xx_HAL_Driver文件夾。

3. STM32F10x_StdPeriph_Driver文件夾

文件目錄:Drivers\STM32F7xx_HAL_Driver

進入Drivers目錄下的STM32F7xx_HAL_Driver文件夾,見 9-4

                                                  

9-4 外設驅動

STM32F7xx_HAL_Driver文件夾下有incinclude的縮寫)跟srcsource的簡寫)這兩個文件夾,這里的文件屬於CMSIS之外的的、芯片片上外設部分。src里面是每個設備外設的驅動源程序,inc則是相對應的外設頭文件。srcinc文件夾是STHAL庫的主要內容,甚至不少人直接認為STHAL庫就是指這些文件,可見其重要性。

src inc文件夾里的就是ST公司針對每個STM32外設而編寫的庫函數文件,每個外設對應一個 .c  .h 后綴的文件。我們把這類外設文件統稱為:stm32f7xx_hal_ppp.c stm32f7xx_hal_ppp.h文件,PPP表示外設名稱。如在上一章中我們自建的stm32f7xx_hal_gpio.cstm32f7xx_hal_gpio.h文件,就屬於這一類。

如針對模數轉換(ADC)外設,在src文件夾下有一個stm32f7xx_hal_adc.c源文件,在inc文件夾下有一個stm32f7xx_hal_adc.h頭文件,若我們開發的工程中用到了STM32內部的ADC,則至少要把這兩個文件包含到工程里。見 9-5

 

9-5 驅動的源文件及頭文件

4. stm32f7xx_it.c stm32f7xx_hal_conf.h文件

文件目錄:STM32Cube_FW_F7_V1.6.0\Projects\STM32F767ZI-Nucleo\Templates

在這個文件目錄下,存放了官方的一個庫工程模板,我們在用庫建立一個完整的工程時,還需要添加這個目錄下src文件夾中stm32f7xx_it.cinc文件夾中和stm32f7xx_it.hstm32f7xx_hal_conf.h這三個文件。

stm32f7xx_it.c:這個文件是專門用來編寫中斷服務函數的,在我們修改前,這個文件已經定義了一些系統異常(特殊中斷)的接口,其它普通中斷服務函數由我們自己添加。但是我們怎么知道這些中斷服務函數的接口如何寫?是不是可以自定義呢?答案當然不是的,這些都有可以在匯編啟動文件中找到,在學習中斷和啟動文件的時候我們會詳細介紹

stm32f7xx_hal_conf.h:這個文件被包含進stm32f767xx.h 文件。ST標准庫支持所有STM32F7型號的芯片,但有的型號芯片外設功能比較多,所以使用這個配置文件根據芯片型號增減ST庫的外設文件。見代碼清單9-3

代碼清單9-3 stm32f7xx_hal_conf.h文件配置軟件庫

1 /* Includes ---------------------------------*/
 2 /**
 3   * @brief Include module's header file
 4   */
 5
 6 #ifdef HAL_RCC_MODULE_ENABLED
 7 #include "stm32f7xx_hal_rcc.h"
 8 #endif /* HAL_RCC_MODULE_ENABLED */
 9
10 #ifdef HAL_GPIO_MODULE_ENABLED
11 #include "stm32f7xx_hal_gpio.h"
12 #endif /* HAL_GPIO_MODULE_ENABLED */
13
14 #ifdef HAL_DMA_MODULE_ENABLED
15 #include "stm32f7xx_hal_dma.h"
16 #endif /* HAL_DMA_MODULE_ENABLED */
17
18 #ifdef HAL_CORTEX_MODULE_ENABLED
19 #include "stm32f7xx_hal_cortex.h"
20 #endif /* HAL_CORTEX_MODULE_ENABLED */
21
22 #ifdef HAL_ADC_MODULE_ENABLED
23 #include "stm32f7xx_hal_adc.h"
24 #endif /* HAL_ADC_MODULE_ENABLED */
25
26 #ifdef HAL_CAN_MODULE_ENABLED
27 #include "stm32f7xx_hal_can.h"
28 #endif /* HAL_CAN_MODULE_ENABLED */
29 

  

stm32f7xx_hal_conf.h這個文件還可配置是否使用“斷言”編譯選項,見代碼清單 9-4

代碼清單 9-4 斷言配置

1 #ifdef  USE_FULL_ASSERT
 2
 3 /**
 4   * @brief  The assert_param macro is used for  parameters check.
 5   * @param  expr: If expr is false, it calls assert_failed function
 6   *   which reports the name of the source file and the source
 7   *   line number of the call that failed.
 8   *   If expr is true, it returns no value.
 9   * @retval None
10   */
11 #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
12 /* Exported functions ---------------------------------- */
13 void assert_failed(uint8_t* file, uint32_t line);
14 #else
15 #define assert_param(expr) ((void)0)
16 #endif /* USE_FULL_ASSERT */

  

STHAL庫函數中,一般會包含輸入參數檢查,即上述代碼中的“assert_param”宏,當參數不符合要求時,會調用“assert_failed”函數,這個函數默認是空的。

實際開發中使用斷言時,先通過定義USE_FULL_ASSERT宏來使能斷言,然后定義assert_failed”函數,通常我們會讓它調用printf函數輸出錯誤說明。使能斷言后,程序運行時會檢查函數的輸入參數,當軟件經過測試,可發布時,會取消USE_FULL_ASSERT宏來去掉斷言功能,使程序全速運行。

9.1.2  庫各文件間的關系

前面向大家簡單介紹了各個庫文件的作用,庫文件是直接包含進工程即可,絲毫不用修改,而有的文件就要我們在使用的時候根據具體的需要進行配置。接下來從整體上把握一下各個文件在庫工程中的層次或關系,這些文件對應到CMSIS標准架構上。見 9-6


 

9-6 庫各文件關系

9-6描述了STM32庫各文件之間的調用關系,這個圖省略了DSP核和實時系統層部分的文件關系。在實際的使用庫開發工程的過程中,我們把位於CMSIS層的文件包含進工程,除了特殊系統時鍾需要修改system_stm32f7xx.c,其它文件絲毫不用修改,也不建議修改。

對於位於用戶層的幾個文件,就是我們在使用庫的時候,針對不同的應用對庫文件進行增刪(用條件編譯的方法增刪)和改動的文件。

9.2  使幫助文檔

我堅信,授之以魚不如授之以漁。官方資料是所有關於STM32知識的源頭,所以在本小節介紹如何使用官方資料。官方的幫助手冊,是最好的教程,幾乎包含了所有在開發過程中遇到的問題。這些資料已整理到了本書附錄資料中。

9.2.1  常用官方資料

STM32F76xxx參考手冊》

這個文件全方位介紹了STM32芯片的各種片上外設,它把STM32的時鍾、存儲器架構、及各種外設、寄存器都描述得清清楚楚。當我們對STM32的外設感到困惑時,可查閱這個文檔。以直接配置寄存器方式開發的話,查閱這個文檔寄存器部分的頻率會相當高,但這樣效率太低了。

STM32F7xx規格書》

本文檔相當於STM32datasheet,包含了STM32芯片所有的引腳功能說明及存儲器架構、芯片外設架構說明。后面我們使用STM32其它外設時,常常需要查找這個手冊,了解外設對應到STM32的哪個GPIO引腳。

Cortex®-M7內核編程手冊》

本文檔由ST公司提供,主要講解STM32內核寄存器相關的說明,例如系統定時器、中斷等寄存器。這部分的內容是《STM32F76xxx參考手冊》沒涉及到的內核部分的補充。相對來說,本文檔雖然介紹了內核寄存器,但不如以下兩個文檔詳細,要了解內核時,可作為以下兩個手冊的配合資料使用。

Cortex-M3權威指南》、《cortex_m7_Technical Reference Manual》。

這兩個手冊是由ARM公司提供的,它詳細講解了Cortex內核的架構和特性,要深入了解Cortex-M內核,這是首選,經典中的經典,其中Cortex-M3版本有中文版,方便學習。因為Cortex-M7內核與Cortex-M3內核大部分相同,可用它來學習,而Cortex-M7新增的特性,則必須參考《cortex_m7_Technical Reference Manual》文檔了,目前只有英文版。

STM32F779xx_User_Manual.chm

這個就是本章提到的庫的幫助文檔,在使用庫函數時,我們最好通過查閱此文件來了解HAL庫提供了哪些外設、函數原型或庫函數的調用的方法。也可以直接閱讀源碼里面的函數的函數說明。

9.2.2  初識庫函數

所謂庫函數,就是STM32的庫文件中為我們編寫好的函數接口,我們只要調用這些庫函數,就可以對STM32進行配置,達到控制目的。我們可以不知道庫函數是如何實現的,但我們調用函數必須要知道函數的功能可傳入的參數及其意義、和函數的返回值

於是,有讀者就問那么多函數我怎么記呀?我的回答是:會查就行了,哪個人記得了那么多。所以我們學會查閱庫幫助文檔 是很有必要的。

打開庫幫助文檔STM32F779xx_User_Manual.chm 9-7

 圖 9-7 庫幫助文檔

層層打開文檔的目錄標簽:

標簽目錄:Modules\STM32F7xx_HAL_Driver

可看到STM32F7xx_HAL_Driver標簽下有很多外設驅動文件的名字HALADCBKPCAN等標簽。

我們試着查看GPIO的“位設置函數GPIO_SetBits”看看,打開標簽:

標簽目錄:Modules\STM32F4xx_StdPeriph_Driver\GPIO\Functions\GPIO_SetBits     9-8

 


9-8 庫幫助文檔的函數說明

利用這個文檔,我們即使沒有去看它的具體源代碼,也知道要怎么利用它了。

HAL_GPIO_WritePin,函數的原型為void HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)。它的功能是:輸入一個類型為GPIO_TypeDef的指針GPIOx參數,選定要控制的GPIO端口;輸入GPIO_PIN_x宏,其中x指端口的引腳號,指定要控制的引腳;輸入GPIO_PIN_RESET或者GPIO_PIN_SET設置IO口的電平高低。

其中輸入的參數  GPIOxSTHAL庫中定義的自定義數據類型,這兩個傳入參數均為結構體指針。初學時,我們並不知道如GPIO_TypeDef這樣的類型是什么意思,可以點擊函數原型中帶下划線的  GPIO_TypeDef  就可以查看這個類型的聲明了。

就這樣初步了解了一下庫函數,讀者就可以發現STM32的庫是寫得很優美的。每個函數和數據類型都符合見名知義的原則,當然,這樣的名稱寫起來特別長,而且對於我們來說要輸入這么長的英文,很容易出錯,所以在開發軟件的時候,在用到庫函數的地方,直接把庫幫助文檔中的函數名稱復制粘貼到工程文件就可以了。而且,配合MDK軟件的代碼自動補全功能,可以減少輸入量。

有的用戶覺得使用庫文檔麻煩,也可以直接查閱STM32 HAL庫的源碼,庫幫助文檔的說明都是根據源碼生成的,所以直接看源碼也可以了解函數功能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM