本章參考資料:《STM32F76xxx參考手冊》、《STM32F7xx規格書》、《Cortex-M3權威指南》, STM32 HAL庫幫助文檔:《STM32F779xx_User_Manual.chm》。
在上一章中,我們構建了幾個控制GPIO外設的函數,算是實現了函數庫的雛形,但GPIO還有很多功能函數我們沒有實現,而且STM32芯片不僅僅只有GPIO這一個外設。如果我們想要親自完成這個函數庫,工作量是非常巨大的。ST公司提供的HAL軟件庫,包含了STM32芯片所有寄存器的控制操作,我們直接學習如何使用ST的HAL庫,會極大地方便控制STM32芯片。
因為基於Cortex系列芯片采用的內核都是相同的,區別主要為核外的片上外設的差異,這些差異卻導致軟件在同內核,不同外設的芯片上移植困難。為了解決不同的芯片廠商生產的Cortex微控制器軟件 的兼容性問題,ARM與芯片廠商建立了CMSIS標准(Cortex MicroController Software Interface Standard)。
所謂CMSIS標准,實際是新建了一個軟件抽象層。見圖 9-1。
CMSIS標准中最主要的為CMSIS核心層,它包括了:
q 內核函數層:其中包含用於訪問內核寄存器的名稱、地址定義,主要由ARM公司提供。
q 設備外設訪問層:提供了片上的核外外設的地址和中斷定義,主要由芯片生產商提供。
可見CMSIS層位於硬件層與操作系統或用戶層之間,提供了與芯片生產商無關的硬件抽象層,可以為接口外設、實時操作系統提供簡單的處理器軟件接口,屏蔽了硬件差異,這對軟件的移植是有極大的好處的。STM32的庫,就是按照CMSIS標准建立的。
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_Audio、STM32_USB_Device_Library、STM32_USB_Host_Library;也有第三方的fatfs文件系統等等。
q Project :文件夾下是用驅動庫寫的針對官方發行demo板的例子和工程模板。
q Utilities:實用的公用組件比如LCD_LOG實用液晶打印調試信息。
q Release_Note.html::庫的版本更新說明。
在使用庫開發時,我們需要把Drivers目錄下的CMSIS、STM32F7xx_HAL_Driver內核與外設的庫文件添加到工程中,並查閱庫幫助文檔來了解ST提供的庫函數,這個文檔說明了每一個庫函數的使用方法。
先看看CMSIS文件夾。
STM32Cube_FW_F7_V1.6.0\Drivers\CMSIS\文件夾下內容見圖 9-3。
圖 9-3 CMSIS文件夾內容 目錄:Drivers \CMSIS\
其中Device與Include中的文件是我們使用得最多的,先講解這兩個文件夾中的內容。
1. Include文件夾
在Include文件夾中包含了 的是位於CMSIS標准的核內設備函數層的Cortex-M核通用的頭文件,它們的作用是為那些采用Cortex-M核設計SOC的芯片商設計的芯片外設提供一個進入內核的接口,定義了一些內核相關的寄存器(類似我們前面寫的stm32f767xx.h文件,但定義的是內核部分的寄存器)。這些文件在其它公司的Cortex-M系列芯片也是相同的。至於這些功能是怎樣用源碼實現的,可以不用管它,只需把這些文件加進我們的工程文件即可,有興趣的朋友可以深究,關於內核的寄存器說明,需要查閱《cortex_m7_Technical Reference Manual》及《Cortex®-M7內核編程手冊》文檔,《STM32F76xxx參考手冊》只包含片上外設說明,不包含內核寄存器。
我們寫STM32F7的工程,必須用到其中的四個文件:core_cm7.h、core_cmFunc.h、corecmInstr.h、core_cmSimd.h,其它的文件是屬於其它內核的,還有幾個文件是DSP函數庫使用的頭文件。
core_cm7.c文件有一些與編譯器相關條件編譯語句,用於屏蔽不同編譯器的差異。里面包含了一些跟編譯器相關的信息,如:“__CC_ARM ”(本書采用的RVMDK、KEIL),“__GNUC__ ”(GNU編譯器)、“ICC Compiler” (IAR編譯器)。這些不同的編譯器對於C嵌入匯編或內聯函數關鍵字的語法不一樣,這段代碼統一使用“__ASM、__INLINE”宏來定義,而在不同的編譯器下,宏自動更改到相應的值,實現了差異屏蔽,見代碼清單9-1
代碼清單9-1:core_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。
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等。
在稍舊版的程序中還經常會出現如u8、u16、u32這樣的類型,分別表示的無符號的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,如有需要可以修改這個文件的內容,設置成自己所需的時鍾頻率。
q 啟動文件
文件目錄: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。
STM32F7xx_HAL_Driver文件夾下有inc(include的縮寫)跟src(source的簡寫)這兩個文件夾,這里的文件屬於CMSIS之外的的、芯片片上外設部分。src里面是每個設備外設的驅動源程序,inc則是相對應的外設頭文件。src及inc文件夾是ST的HAL庫的主要內容,甚至不少人直接認為ST的HAL庫就是指這些文件,可見其重要性。
在src 和inc文件夾里的就是ST公司針對每個STM32外設而編寫的庫函數文件,每個外設對應一個 .c 和 .h 后綴的文件。我們把這類外設文件統稱為:stm32f7xx_hal_ppp.c 或stm32f7xx_hal_ppp.h文件,PPP表示外設名稱。如在上一章中我們自建的stm32f7xx_hal_gpio.c及stm32f7xx_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.c和inc文件夾中和stm32f7xx_it.h、stm32f7xx_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。
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 */
在ST的HAL庫函數中,一般會包含輸入參數檢查,即上述代碼中的“assert_param”宏,當參數不符合要求時,會調用“assert_failed”函數,這個函數默認是空的。
實際開發中使用斷言時,先通過定義USE_FULL_ASSERT宏來使能斷言,然后定義“assert_failed”函數,通常我們會讓它調用printf函數輸出錯誤說明。使能斷言后,程序運行時會檢查函數的輸入參數,當軟件經過測試,可發布時,會取消USE_FULL_ASSERT宏來去掉斷言功能,使程序全速運行。
前面向大家簡單介紹了各個庫文件的作用,庫文件是直接包含進工程即可,絲毫不用修改,而有的文件就要我們在使用的時候根據具體的需要進行配置。接下來從整體上把握一下各個文件在庫工程中的層次或關系,這些文件對應到CMSIS標准架構上。見圖 9-6。
圖 9-6描述了STM32庫各文件之間的調用關系,這個圖省略了DSP核和實時系統層部分的文件關系。在實際的使用庫開發工程的過程中,我們把位於CMSIS層的文件包含進工程,除了特殊系統時鍾需要修改system_stm32f7xx.c,其它文件絲毫不用修改,也不建議修改。
對於位於用戶層的幾個文件,就是我們在使用庫的時候,針對不同的應用對庫文件進行增刪(用條件編譯的方法增刪)和改動的文件。
9.2 使幫助文檔
我堅信,授之以魚不如授之以漁。官方資料是所有關於STM32知識的源頭,所以在本小節介紹如何使用官方資料。官方的幫助手冊,是最好的教程,幾乎包含了所有在開發過程中遇到的問題。這些資料已整理到了本書附錄資料中。
9.2.1 常用官方資料
q 《STM32F76xxx參考手冊》
這個文件全方位介紹了STM32芯片的各種片上外設,它把STM32的時鍾、存儲器架構、及各種外設、寄存器都描述得清清楚楚。當我們對STM32的外設感到困惑時,可查閱這個文檔。以直接配置寄存器方式開發的話,查閱這個文檔寄存器部分的頻率會相當高,但這樣效率太低了。
q 《STM32F7xx規格書》
本文檔相當於STM32的datasheet,包含了STM32芯片所有的引腳功能說明及存儲器架構、芯片外設架構說明。后面我們使用STM32其它外設時,常常需要查找這個手冊,了解外設對應到STM32的哪個GPIO引腳。
q 《Cortex®-M7內核編程手冊》
本文檔由ST公司提供,主要講解STM32內核寄存器相關的說明,例如系統定時器、中斷等寄存器。這部分的內容是《STM32F76xxx參考手冊》沒涉及到的內核部分的補充。相對來說,本文檔雖然介紹了內核寄存器,但不如以下兩個文檔詳細,要了解內核時,可作為以下兩個手冊的配合資料使用。
q 《Cortex-M3權威指南》、《cortex_m7_Technical Reference Manual》。
這兩個手冊是由ARM公司提供的,它詳細講解了Cortex內核的架構和特性,要深入了解Cortex-M內核,這是首選,經典中的經典,其中Cortex-M3版本有中文版,方便學習。因為Cortex-M7內核與Cortex-M3內核大部分相同,可用它來學習,而Cortex-M7新增的特性,則必須參考《cortex_m7_Technical Reference Manual》文檔了,目前只有英文版。
q 《STM32F779xx_User_Manual.chm》
這個就是本章提到的庫的幫助文檔,在使用庫函數時,我們最好通過查閱此文件來了解HAL庫提供了哪些外設、函數原型或庫函數的調用的方法。也可以直接閱讀源碼里面的函數的函數說明。
9.2.2 初識庫函數
所謂庫函數,就是STM32的庫文件中為我們編寫好的函數接口,我們只要調用這些庫函數,就可以對STM32進行配置,達到控制目的。我們可以不知道庫函數是如何實現的,但我們調用函數必須要知道函數的功能、可傳入的參數及其意義、和函數的返回值。
於是,有讀者就問那么多函數我怎么記呀?我的回答是:會查就行了,哪個人記得了那么多。所以我們學會查閱庫幫助文檔 是很有必要的。
打開庫幫助文檔《STM32F779xx_User_Manual.chm》見圖 9-7
層層打開文檔的目錄標簽:
標簽目錄:Modules\STM32F7xx_HAL_Driver
可看到STM32F7xx_HAL_Driver標簽下有很多外設驅動文件的名字HAL、ADC、BKP、CAN等標簽。
我們試着查看GPIO的“位設置函數GPIO_SetBits”看看,打開標簽:
標簽目錄:Modules\STM32F4xx_StdPeriph_Driver\GPIO\Functions\GPIO_SetBits 見圖 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口的電平高低。
其中輸入的參數 GPIOx為ST的HAL庫中定義的自定義數據類型,這兩個傳入參數均為結構體指針。初學時,我們並不知道如GPIO_TypeDef這樣的類型是什么意思,可以點擊函數原型中帶下划線的 GPIO_TypeDef 就可以查看這個類型的聲明了。
就這樣初步了解了一下庫函數,讀者就可以發現STM32的庫是寫得很優美的。每個函數和數據類型都符合見名知義的原則,當然,這樣的名稱寫起來特別長,而且對於我們來說要輸入這么長的英文,很容易出錯,所以在開發軟件的時候,在用到庫函數的地方,直接把庫幫助文檔中的函數名稱復制粘貼到工程文件就可以了。而且,配合MDK軟件的代碼自動補全功能,可以減少輸入量。
有的用戶覺得使用庫文檔麻煩,也可以直接查閱STM32 HAL庫的源碼,庫幫助文檔的說明都是根據源碼生成的,所以直接看源碼也可以了解函數功能。