第9章 初識STM32固件庫
全套200集視頻教程和1000頁PDF教程請到秉火論壇下載:www.firebbs.cn
野火視頻教程優酷觀看網址:http://i.youku.com/firege
本章參考資料:《STM32F4xx參考手冊》、《STM32F4xx規格書》、《Cortex-M3權威指南》, STM32標准庫幫助文檔:《stm32f4xx_dsp_stdperiph_lib_um.chm》。
在上一章中,我們構建了幾個控制GPIO外設的函數,算是實現了函數庫的雛形,但GPIO還有很多功能函數我們沒有實現,而且STM32芯片不僅僅只有GPIO這一個外設。如果我們想要親自完成這個函數庫,工作量是非常巨大的。ST公司提供的標准軟件庫,包含了STM32芯片所有寄存器的控制操作,我們直接學習如何使用ST標准庫,會極大地方便控制STM32芯片。
9.1 CMSIS標准及庫層次關系
因為基於Cortex系列芯片采用的內核都是相同的,區別主要為核外的片上外設的差異,這些差異卻導致軟件在同內核,不同外設的芯片上移植困難。為了解決不同的芯片廠商生產的Cortex微控制器軟件的兼容性問題,ARM與芯片廠商建立了CMSIS標准(Cortex MicroController Software Interface Standard)。
所謂CMSIS標准,實際是新建了一個軟件抽象層。見圖 91。
圖 91 CMSIS架構
CMSIS標准中最主要的為CMSIS核心層,它包括了:
內核函數層:其中包含用於訪問內核寄存器的名稱、地址定義,主要由ARM公司提供。
設備外設訪問層:提供了片上的核外外設的地址和中斷定義,主要由芯片生產商提供。
可見CMSIS層位於硬件層與操作系統或用戶層之間,提供了與芯片生產商無關的硬件抽象層,可以為接口外設、實時操作系統提供簡單的處理器軟件接口,屏蔽了硬件差異,這對軟件的移植是有極大的好處的。STM32的庫,就是按照CMSIS標准建立的。
9.1.1 庫目錄、文件簡介
STM32標准庫可以從官網獲得,也可以直接從本書的配套資料得到。本書講解的例程全部采用1.5.1庫文件。以下內容請大家打開STM32標准庫文件配合閱讀。
解壓庫文件后進入其目錄:
"STM32F4xx_DSP_StdPeriph_Lib_V1.5.1\"
軟件庫各文件夾的內容說明見圖 92。
圖 92 ST標准庫 目錄:STM32F4xx_DSP_StdPeriph_Lib_V1.5.1\
Libraries:文件夾下是驅動庫的源代碼及啟動文件。
Project :文件夾下是用驅動庫寫的例子和工程模板。
Utilities:包含了基於ST官方實驗板的例程,以及第三方軟件庫,如emwin圖形軟件庫、fatfs文件系統。
MCD-ST Liberty…:庫文件的License說明。
Release_Note.html::庫的版本更新說明。
stm32f4xx_dsp_stdperiph…: 庫幫助文檔,這是一個已經編譯好的HTML文件,主要講述如何使用驅動庫來編寫自己的應用程序。說得形象一點,這個HTML就是告訴我們:ST公司已經為你寫好了每個外設的驅動了,想知道如何運用這些例子就來向我求救吧。不幸的是,這個幫助文檔是英文的,這對很多英文不好的朋友來說是一個很大的障礙。但這里要告訴大家,英文僅僅是一種工具,絕對不能讓它成為我們學習的障礙。其實這些英文還是很簡單的,我們需要的是拿下它的勇氣。
在使用庫開發時,我們需要把libraries目錄下的庫函數文件添加到工程中,並查閱庫幫助文檔來了解ST提供的庫函數,這個文檔說明了每一個庫函數的使用方法。
進入Libraries文件夾看到,關於內核與外設的庫文件分別存放在CMSIS和STM32F4xx_StdPeriph_Driver文件夾中。
先看看CMSIS文件夾。
STM32F4xx_DSP_StdPeriph_Lib_V1.5.1\Libraries\CMSIS\文件夾下內容見圖 93。
圖 93 CMSIS文件夾內容目錄:Libraries\CMSIS\
其中Device與Include中的文件是我們使用得最多的,先講解這兩個文件夾中的內容。
1. Include文件夾
在Include文件夾中包含了的是位於CMSIS標准的核內設備函數層的Cortex-M核通用的頭文件,它們的作用是為那些采用Cortex-M核設計SOC的芯片商設計的芯片外設提供一個進入內核的接口,定義了一些內核相關的寄存器(類似我們前面寫的stm32f4xx.h文件,但定義的是內核部分的寄存器)。這些文件在其它公司的Cortex-M系列芯片也是相同的。至於這些功能是怎樣用源碼實現的,可以不用管它,只需把這些文件加進我們的工程文件即可,有興趣的朋友可以深究,關於內核的寄存器說明,需要查閱《cortex_m4_Technical Reference Manual》及《Cortex™-M4內核參考手冊》文檔,《STM32參考手冊》只包含片上外設說明,不包含內核寄存器。
我們寫STM32F4的工程,必須用到其中的四個文件:core_cm4.h、core_cmFunc.h、corecmInstr.h、core_cmSimd.h,其它的文件是屬於其它內核的,還有幾個文件是DSP函數庫使用的頭文件。
core_cm4.c文件有一些與編譯器相關條件編譯語句,用於屏蔽不同編譯器的差異。里面包含了一些跟編譯器相關的信息,如:"__CC_ARM "(本書采用的RVMDK、KEIL),"__GNUC__ "(GNU編譯器)、"ICC Compiler" (IAR編譯器)。這些不同的編譯器對於C嵌入匯編或內聯函數關鍵字的語法不一樣,這段代碼統一使用"__ASM、__INLINE"宏來定義,而在不同的編譯器下,宏自動更改到相應的值,實現了差異屏蔽,見代碼清單91。
代碼清單91: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_cm4.c文件中包含了"stdint.h"這個頭文件,這是一個ANSI C 文件,是獨立於處理器之外的,就像我們熟知的C語言頭文件"stdio.h"文件一樣。位於RVMDK這個軟件的安裝目錄下,主要作用是提供一些類型定義。見代碼清單92。
代碼清單92:stdint.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等。
在稍舊版的程序中還經常會出現如u8、u16、u32這樣的類型,分別表示的無符號的8位、16位、32位整型。初學者碰到這樣的舊類型感覺一頭霧水,它們定義的位置在STM32f4xx.h文件中。建議在以后的新程序中盡量使用uint8_t 、uint16_t類型的定義。
core_cm4.c跟啟動文件一樣都是底層文件,都是由ARM公司提供的,遵守CMSIS標准,即所有CM4芯片的庫都帶有這個文件,這樣軟件在不同的CM4芯片的移植工作就得以簡化。
2. Device文件夾
在Device文件夾下的是具體芯片直接相關的文件,包含啟動文件、芯片外設寄存器定義、系統時鍾初始化功能的一些文件,這是由ST公司提供的。
system_stm32f4xx.c文件
文件目錄:\Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates
這個文件包含了STM32芯片上電后初始化系統時鍾、擴展外部存儲器用的函數,例如我們前兩章提到供啟動文件調用的"SystemInit"函數,用於上電后初始化時鍾,該函數的定義就存儲在system_stm32f4xx.c文件。STM32F429系列的芯片,調用庫的這個SystemInit函數后,系統時鍾被初始化為180MHz,如有需要可以修改這個文件的內容,設置成自己所需的時鍾頻率。
啟動文件
文件目錄:Libraries\CMSIS\Device\ST\STM32F4xx\Source\Templates
在這個目錄下,還有很多文件夾,如"ARM"、"gcc_ride7"、"iar"等,這些文件夾下包含了對應編譯平台的匯編啟動文件,在實際使用時要根據編譯平台來選擇。我們使用的MDK啟動文件在"ARM"文件夾中。其中的"strartup_stm32f429_439xx.s"即為STM32F429芯片的啟動文件,前面兩章工程中使用的啟動文件就是從這里復制過去的。如果使用其它型號的芯片,要在此處選擇對應的啟動文件,如STM32F446型號使用"startup_stm32f446xx.s"文件。
stm32f4xx.h文件
文件目錄: Libraries\CMSIS\Device\ST\STM32F4xx\Include
stm32f4xx.h 這個文件非常重要,是一個STM32芯片底層相關的文件。它是我們前兩章自己定義的"stm32f4xx.h"文件的完整版,包含了STM32中所有的外設寄存器地址和結構體類型定義,在使用到STM32標准庫的地方都要包含這個頭文件。
CMSIS文件夾中的主要內容就是這樣,接下來我們看看STM32F4xx_StdPeriph_Driver文件夾。
3. STM32F10x_StdPeriph_Driver文件夾
文件目錄:Libraries\STM32F4xx_StdPeriph_Driver
進入libraries目錄下的STM32F4xx_StdPeriph_Driver文件夾,見圖 94。
圖 94 外設驅動
STM32F4xx_StdPeriph_Driver文件夾下有inc(include的縮寫)跟src(source的簡寫)這兩個文件夾,這里的文件屬於CMSIS之外的的、芯片片上外設部分。src里面是每個設備外設的驅動源程序,inc則是相對應的外設頭文件。src及inc文件夾是ST標准庫的主要內容,甚至不少人直接認為ST標准庫就是指這些文件,可見其重要性。
在src 和inc文件夾里的就是ST公司針對每個STM32外設而編寫的庫函數文件,每個外設對應一個 .c 和 .h 后綴的文件。我們把這類外設文件統稱為:stm32f4xx_ppp.c 或stm32f4xx_ppp.h文件,PPP表示外設名稱。如在上一章中我們自建的stm32f4xx_gpio.c及stm32f4xx_gpio.h文件,就屬於這一類。
如針對模數轉換(ADC)外設,在src文件夾下有一個stm32f4xx_adc.c源文件,在inc文件夾下有一個stm32f4xx_adc.h頭文件,若我們開發的工程中用到了STM32內部的ADC,則至少要把這兩個文件包含到工程里。見圖 95。
圖 95 驅動的源文件及頭文件
這兩個文件夾中,還有一個很特別的misc.c文件,這個文件提供了外設對內核中的NVIC(中斷向量控制器)的訪問函數,在配置中斷時,我們必須把這個文件添加到工程中。
4. stm32f4xx_it.c、 stm32f4xx_conf.h文件
文件目錄:STM32F4xx_DSP_StdPeriph_Lib_V1.5.1\Project\STM32F4xx_StdPeriph_Templates
在這個文件目錄下,存放了官方的一個庫工程模板,我們在用庫建立一個完整的工程時,還需要添加這個目錄下的stm32f4xx_it.c、stm32f4xx_it.h、stm32f4xx_conf.h這三個文件。
stm32f4xx_it.c:這個文件是專門用來編寫中斷服務函數的,在我們修改前,這個文件已經定義了一些系統異常(特殊中斷)的接口,其它普通中斷服務函數由我們自己添加。但是我們怎么知道這些中斷服務函數的接口如何寫?是不是可以自定義呢?答案當然不是的,這些都有可以在匯編啟動文件中找到,在學習中斷和啟動文件的時候我們會詳細介紹
stm32f4xx_conf.h:這個文件被包含進stm32f4xx.h 文件。ST標准庫支持所有STM32F4型號的芯片,但有的型號芯片外設功能比較多,所以使用這個配置文件根據芯片型號增減ST庫的外設文件。見代碼清單93,針對STM32F429和STM32F427型號芯片的差異,它們實際包含不一樣的頭文件,我們通過宏來指定芯片的型號。
代碼清單93 stm32f4xx_conf.h文件配置軟件庫
1
2 #if defined (STM32F429_439xx) || defined(STM32F446xx)
3 #include "stm32f4xx_cryp.h"
4 #include "stm32f4xx_hash.h"
5 #include "stm32f4xx_rng.h"
6 #include "stm32f4xx_can.h"
7 #include "stm32f4xx_dac.h"
8 #include "stm32f4xx_dcmi.h"
9 #include "stm32f4xx_dma2d.h"
10 #include "stm32f4xx_fmc.h"
11 #include "stm32f4xx_ltdc.h"
12 #include "stm32f4xx_sai.h"
13 #endif /* STM32F429_439xx || STM32F446xx */
14
15 #if defined (STM32F427_437xx)
16 #include "stm32f4xx_cryp.h"
17 #include "stm32f4xx_hash.h"
18 #include "stm32f4xx_rng.h"
19 #include "stm32f4xx_can.h"
20 #include "stm32f4xx_dac.h"
21 #include "stm32f4xx_dcmi.h"
22 #include "stm32f4xx_dma2d.h"
23 #include "stm32f4xx_fmc.h"
24 #include "stm32f4xx_sai.h"
25 #endif /* STM32F427_437xx */
26
27 #if defined (STM32F40_41xxx)
stm32f4xx_conf.h這個文件還可配置是否使用"斷言"編譯選項,見代碼清單 94。
代碼清單 94 斷言配置
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標准庫的函數中,一般會包含輸入參數檢查,即上述代碼中的"assert_param"宏,當參數不符合要求時,會調用"assert_failed"函數,這個函數默認是空的。
實際開發中使用斷言時,先通過定義USE_FULL_ASSERT宏來使能斷言,然后定義"assert_failed"函數,通常我們會讓它調用printf函數輸出錯誤說明。使能斷言后,程序運行時會檢查函數的輸入參數,當軟件經過測試,可發布時,會取消USE_FULL_ASSERT宏來去掉斷言功能,使程序全速運行。
9.1.2 庫各文件間的關系
前面向大家簡單介紹了各個庫文件的作用,庫文件是直接包含進工程即可,絲毫不用修改,而有的文件就要我們在使用的時候根據具體的需要進行配置。接下來從整體上把握一下各個文件在庫工程中的層次或關系,這些文件對應到CMSIS標准架構上。見圖 96。
圖 96 庫各文件關系
圖 96描述了STM32庫各文件之間的調用關系,這個圖省略了DSP核和實時系統層部分的文件關系。在實際的使用庫開發工程的過程中,我們把位於CMSIS層的文件包含進工程,除了特殊系統時鍾需要修改system_stm32f4xx.c,其它文件絲毫不用修改,也不建議修改。
對於位於用戶層的幾個文件,就是我們在使用庫的時候,針對不同的應用對庫文件進行增刪(用條件編譯的方法增刪)和改動的文件。
9.2 使幫助文檔
我堅信,授之以魚不如授之以漁。官方資料是所有關於STM32知識的源頭,所以在本小節介紹如何使用官方資料。官方的幫助手冊,是最好的教程,幾乎包含了所有在開發過程中遇到的問題。這些資料已整理到了本書附錄資料中。
9.2.1 常用官方資料
《STM32F4xx參考手冊》
這個文件全方位介紹了STM32芯片的各種片上外設,它把STM32的時鍾、存儲器架構、及各種外設、寄存器都描述得清清楚楚。當我們對STM32的外設感到困惑時,可查閱這個文檔。以直接配置寄存器方式開發的話,查閱這個文檔寄存器部分的頻率會相當高,但這樣效率太低了。
《STM32F4xx規格書》
本文檔相當於STM32的datasheet,包含了STM32芯片所有的引腳功能說明及存儲器架構、芯片外設架構說明。后面我們使用STM32其它外設時,常常需要查找這個手冊,了解外設對應到STM32的哪個GPIO引腳。
《Cortex™-M4內核參考手冊》
本文檔由ST公司提供,主要講解STM32內核寄存器相關的說明,例如系統定時器、中斷等寄存器。這部分的內容是《STM32F4xx參考手冊》沒涉及到的內核部分的補充。相對來說,本文檔雖然介紹了內核寄存器,但不如以下兩個文檔詳細,要了解內核時,可作為以下兩個手冊的配合資料使用。
《Cortex-M3權威指南》、《cortex_m4_Technical Reference Manual》。
這兩個手冊是由ARM公司提供的,它詳細講解了Cortex內核的架構和特性,要深入了解Cortex-M內核,這是首選,經典中的經典,其中Cortex-M3版本有中文版,方便學習。因為Cortex-M4內核與Cortex-M3內核大部分相同,可用它來學習,而Cortex-M4新增的特性,則必須參考《cortex_m4_Technical Reference Manual》文檔了,目前只有英文版。
《stm32f4xx_dsp_stdperiph_lib_um.chm》
這個就是本章提到的庫的幫助文檔,在使用庫函數時,我們最好通過查閱此文件來了解標准庫提供了哪些外設、函數原型或庫函數的調用的方法。也可以直接閱讀源碼里面的函數的函數說明。
9.2.2 初識庫函數
所謂庫函數,就是STM32的庫文件中為我們編寫好的函數接口,我們只要調用這些庫函數,就可以對STM32進行配置,達到控制目的。我們可以不知道庫函數是如何實現的,但我們調用函數必須要知道函數的功能、可傳入的參數及其意義、和函數的返回值。
於是,有讀者就問那么多函數我怎么記呀?我的回答是:會查就行了,哪個人記得了那么多。所以我們學會查閱庫幫助文檔是很有必要的。
打開庫幫助文檔《stm32f4xx_dsp_stdperiph_lib_um.chm》見圖 97
圖 97 庫幫助文檔
層層打開文檔的目錄標簽:
標簽目錄:Modules\STM32F4xx_StdPeriph_Driver\
可看到STM32F4xx _StdPeriph_Driver標簽下有很多外設驅動文件的名字MISC、ADC、BKP、CAN等標簽。
我們試着查看GPIO的"位設置函數GPIO_SetBits"看看,打開標簽:
標簽目錄:Modules\STM32F4xx_StdPeriph_Driver\GPIO\Functions\GPIO_SetBits 見圖 98。
圖 98 庫幫助文檔的函數說明
利用這個文檔,我們即使沒有去看它的具體源代碼,也知道要怎么利用它了。
如GPIO_SetBits,函數的原型為void GPIO_SetBits(GPIO_TypeDef * GPIOx , uint16_t GPIO_Pin)。它的功能是:輸入一個類型為GPIO_TypeDef的指針GPIOx參數,選定要控制的GPIO端口;輸入GPIO_Pin_x宏,其中x指端口的引腳號,指定要控制的引腳。
其中輸入的參數 GPIOx為ST標准庫中定義的自定義數據類型,這兩個傳入參數均為結構體指針。初學時,我們並不知道如GPIO_TypeDef這樣的類型是什么意思,可以點擊函數原型中帶下划線的 GPIO_TypeDef 就可以查看這個類型的聲明了。
就這樣初步了解了一下庫函數,讀者就可以發現STM32的庫是寫得很優美的。每個函數和數據類型都符合見名知義的原則,當然,這樣的名稱寫起來特別長,而且對於我們來說要輸入這么長的英文,很容易出錯,所以在開發軟件的時候,在用到庫函數的地方,直接把庫幫助文檔中的函數名稱復制粘貼到工程文件就可以了。而且,配合MDK軟件的代碼自動補全功能,可以減少輸入量。
有的用戶覺得使用庫文檔麻煩,也可以直接查閱STM32標准庫的源碼,庫幫助文檔的說明都是根據源碼生成的,所以直接看源碼也可以了解函數功能。
9.3 每課一問
1. 打開ST標准庫,查看它的各個文件,對比一下ST標准庫與我們上一章中工程里的同名文件,查看有何差異。