嵌入式開(kāi)發(fā)中既有底層硬件的開(kāi)發(fā)又涉及上層應(yīng)用的開(kāi)發(fā),即涉及系統(tǒng)的硬件和軟件,C語(yǔ)言既具有匯編語(yǔ)言操作底層的優(yōu)勢(shì),又具有高級(jí)語(yǔ)言功能性強(qiáng)的特點(diǎn),當(dāng)之無(wú)愧地成為嵌入式開(kāi)發(fā)的主流語(yǔ)言。在 STM32開(kāi)發(fā)過(guò)程中,不論是基于寄存器開(kāi)發(fā)還是基于庫(kù)開(kāi)發(fā),深入理解和掌握嵌入式C語(yǔ)言的函數(shù)、指針、結(jié)構(gòu)體是學(xué)習(xí)STM32的關(guān)鍵。嵌入式C語(yǔ)言的結(jié)構(gòu)特點(diǎn)如下。
(1)程序總是從main函數(shù)開(kāi)始執(zhí)行,語(yǔ)句以分號(hào)“;”結(jié)束,采用/ … /或//做注釋。
(2)函數(shù)是C語(yǔ)言的基本結(jié)構(gòu),每個(gè)C語(yǔ)言程序均由一個(gè)或多個(gè)功能函數(shù)組成。
(3) 函數(shù)由兩部分組成:說(shuō)明部分和函數(shù)體。
函數(shù)名(參數(shù))
{
[說(shuō)明部分];
函數(shù)體;
}
(4)一個(gè)C語(yǔ)言程序包含若干個(gè)源程序文件(.c文件)和頭文件(.h文件),其中.h頭文件主要由預(yù)處理命令(包括文件、宏定義、條件編譯等)和數(shù)據(jù)聲明(全局變量、函數(shù)等聲明)組成;c源文件主要是功能函數(shù)的實(shí)現(xiàn)文件。
(5)采用外設(shè)功能模塊化設(shè)計(jì)方法,一個(gè)外設(shè)功能模塊包括一個(gè)源文件(.c文件)和一個(gè)頭文件(.h文件),.c文件用于具體外設(shè)功能模塊函數(shù)的實(shí)現(xiàn),.h頭文件用于對(duì)該外設(shè)功能模塊參數(shù)及功能函數(shù)的聲明。嵌入式系統(tǒng)開(kāi)發(fā)多采用模塊化、層次化的設(shè)計(jì)思想,系統(tǒng)層次架構(gòu)清晰,便于協(xié)同開(kāi)發(fā)。圖1為嵌入式系統(tǒng)的軟件基本結(jié)構(gòu)框圖。
圖1 嵌入式系統(tǒng)的軟件基本結(jié)構(gòu)框架圖
1 STM32的數(shù)據(jù)類型
數(shù)據(jù)是嵌入式C語(yǔ)言的基本操作對(duì)象,數(shù)據(jù)類型是指數(shù)據(jù)在計(jì)算機(jī)內(nèi)存中的存儲(chǔ)方式,如基本數(shù)據(jù)類型中的整型(存放整數(shù))、浮點(diǎn)型(存放實(shí)數(shù))、字符型(存放字符)、指針(存放地址)以及派生出的復(fù)合數(shù)據(jù)類型(如數(shù)組、結(jié)構(gòu)體、共用體、枚舉類型)。嵌入式C語(yǔ)言的數(shù)據(jù)類型如圖2所示。
圖二 嵌入式C語(yǔ)言的數(shù)據(jù)類型
由于不同CPU定義的數(shù)據(jù)類型的長(zhǎng)度不同,因此ARM公司聯(lián)合其他半導(dǎo)體廠商制定了統(tǒng)一的CMSIS 軟件標(biāo)準(zhǔn),這個(gè)標(biāo)準(zhǔn)中預(yù)先定義了相關(guān)的數(shù)據(jù)類型,ST公司也為開(kāi)發(fā)人員提供了基于C語(yǔ)言的標(biāo)準(zhǔn)外設(shè)庫(kù),其定義的數(shù)據(jù)類型如表1所示,相關(guān)源代碼請(qǐng)參考STM32標(biāo)準(zhǔn)外設(shè)庫(kù)v3.5.0的stdint.h頭文件。stm32f10x.h頭文件還對(duì)標(biāo)準(zhǔn)外設(shè)庫(kù)之前版本所使用的數(shù)據(jù)類型進(jìn)行了說(shuō)明,v3.5.0版本已不再使用這些舊的數(shù)據(jù)類型,為了兼容以前的版本,新版本對(duì)其進(jìn)行了兼容說(shuō)明,如圖3所示。
表1 STM32定義的數(shù)據(jù)類型
圖3 STM32標(biāo)準(zhǔn)外設(shè)庫(kù)數(shù)據(jù)類型兼容說(shuō)明
圖3中的_I、_O以及_IO為IO類型限定詞,內(nèi)核頭文件 core_cm3.h定義了標(biāo)準(zhǔn)外設(shè)庫(kù)所使用的IO類型限定詞,如表2所示。注意,IO類型限定詞加下畫(huà)線是為了避免命名沖突。表1的數(shù)據(jù)類型與表2中的IO類型限定詞相結(jié)合,在標(biāo)準(zhǔn)外設(shè)庫(kù)中常用來(lái)定義寄存器和結(jié)構(gòu)體變量,圖4為stm32f10x.h頭文件中相關(guān)外設(shè)的寄存器定義。
表2 STM32的IO類型限定詞
圖4 stm32f10x.h頭文件中相關(guān)外設(shè)的寄存器定義
結(jié)合表2和圖3,可以看出同一數(shù)據(jù)類型有多種表示方式,如無(wú)符號(hào)8位整型數(shù)據(jù)有unsigned char、uint8_t、u8三種表示方式,在不同的ST標(biāo)準(zhǔn)外設(shè)庫(kù)版本中這三種表示方式都可以表示無(wú)符號(hào)8位整型數(shù)據(jù),初學(xué)者應(yīng)了解這三種表達(dá)方式,最新的v3.5.0版本采用 CMSIS軟件標(biāo)準(zhǔn)的C99標(biāo)準(zhǔn),即 uint8_t方式。
2 CONST關(guān)鍵字
**const關(guān)鍵字用于定義只讀的變量,其值在編譯時(shí)不能被改變,注意,const關(guān)鍵字定義的是變量而不是常量。**使用 const關(guān)鍵字是為了在編譯時(shí)防止變量的值被誤修改,同時(shí)提高程序的安全性和可靠性,一般放在頭文件中或者文件的開(kāi)始部分。在C99標(biāo)準(zhǔn)中,const關(guān)鍵字定義的變量是全局變量。const 關(guān)鍵字與#definc關(guān)鍵字存在區(qū)別,#define關(guān)鍵字只是簡(jiǎn)單的文本替換,而const關(guān)鍵字定義的變量是存儲(chǔ)在靜態(tài)存儲(chǔ)器中的。使用#define關(guān)鍵字定義常量的形式為
#define PI3.14159
使用該方式定義后,無(wú)論在何處使用PI,都會(huì)被預(yù)處理器以3.14159替代,編譯器不對(duì)PI進(jìn)行類型檢查,若使用不慎,則很可能由預(yù)處理引入錯(cuò)誤,且這類錯(cuò)誤很難發(fā)現(xiàn)。用const聲明變量的方式雖然增加了分配空間,但可以很好地消除預(yù)處理引入的錯(cuò)誤,并提供了良好的類型檢查形式,保證安全性。利用 const關(guān)鍵字進(jìn)行編程時(shí)需要注意以下三點(diǎn)。**(1)使用const關(guān)鍵字聲明的變量,只能讀取,不能被賦值。**如:
const uint8t sum = 3.14;
uint8_t abs=0;
...
sum= abs;//非法,將導(dǎo)致編譯錯(cuò)誤,因?yàn)?span id="iye34f8" class="hljs-built_in">sum 只能被讀取,不能賦值
abs- sum: //合法
(2) const關(guān)鍵詞修飾的變量在聲明時(shí)必須初始化,上述語(yǔ)句表示 sum值是3.14,且sum值在編譯時(shí)不能修改,若在編譯過(guò)程中直接修改sum值,則編譯器會(huì)提示出錯(cuò)。(3)函數(shù)的形參聲明為const,則意味著所傳遞的指針指向的內(nèi)容只能讀,不能被修改。如C語(yǔ)言的標(biāo)準(zhǔn)函數(shù)庫(kù)中用于統(tǒng)計(jì)字符串長(zhǎng)度的函數(shù) int strlen(const char*str)。
3 static關(guān)鍵字
在嵌入式C語(yǔ)言中,static關(guān)鍵字可以用來(lái)修飾變量,使用static關(guān)鍵字修飾的變量,稱為靜態(tài)變量。靜態(tài)變量的存儲(chǔ)方式與全局變量一樣,都是靜態(tài)存儲(chǔ)方式。全局變量的作用范圍是整個(gè)源程序,當(dāng)一個(gè)源程序由多個(gè)源文件組成時(shí),全局變量在各個(gè)源文件中都是有效的,即一個(gè)全局變量定義在某個(gè)源文件中,若想在另一個(gè)源文件中使用該全局變量,則只需要在該源文件中通過(guò) extern關(guān)鍵字聲明該全局變量就可以使用了。若在該全局變量前加上關(guān)鍵字static,則該全局變量被定義成一個(gè)靜態(tài)全局變量,其作用范圍只在定義該變量的源文件內(nèi)有效,其他源文件不能引用該全局變量,這樣就避免了在其他源文件中因引用相同名字的變量而引發(fā)的錯(cuò)誤,有利于模塊化程序設(shè)計(jì)。利用static關(guān)鍵字進(jìn)行編程時(shí)需要注意以下要點(diǎn)。(1)static關(guān)鍵字不僅可以用來(lái)修飾變量,而且可以用來(lái)修飾函數(shù)。模塊化程序設(shè)計(jì)中,若用static聲明一個(gè)函數(shù),則該函數(shù)只能被該模塊內(nèi)的其他函數(shù)調(diào)用,例如:
#include "stm32f1xx_hal .h”
static void DMA_SetConfig (DMA_HandleTypeDef *hdma,uint32_t SrcAddress,uint32_t DstAddress, uint32_t DataLength);
...
HAL_statusTypeDef HAL_DMA_start_IT(DMA_HandleTypeDef *hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
{
HAL_StatusTypeDef status- HAL_OK;”
.... ...
if(HAL_DMA_STATE_REA.DY m- hdma->state)
{
DMA_Setconfig(hdma, SrcAddress, DstAddress, DataLength);
... ...
}
... ...
}
上述代碼為DMA模塊的源文件stm32f1xx_hal_dma.c,若利用static將DMA_SetConfig()函數(shù)聲明為一個(gè)靜態(tài)函數(shù),則 DMA_SetConfig)函數(shù)只能被stm32flxx_hal_dma.c中的其他函數(shù)調(diào)用,而不能被其他模塊的文件使用,即定義了一個(gè)本地函數(shù),有效避免了因其他模塊的文件定義了同名函數(shù)而引發(fā)的錯(cuò)誤,充分體現(xiàn)了程序的模塊化設(shè)計(jì)思想。(2) static除了用于定義靜態(tài)全局變量,還用于定義靜態(tài)局部變量,保證靜態(tài)局部變量在調(diào)用過(guò)程中不被重新初始化。典型應(yīng)用案例有實(shí)現(xiàn)計(jì)數(shù)統(tǒng)計(jì)功能。
void fun_count()
{
static count_num=0;
//聲明一個(gè)靜態(tài)局部變量,count_num用作計(jì)數(shù)器,初值為0
count_num++;
printf("%dn",count_num) :
}
int main(void)
(
int i=0;
for( i=0;i<=5;i++)
{
fun_count();
}
return 0;
}
在main函數(shù)中每調(diào)用一次 fun_count()函數(shù),靜態(tài)局部變量count_num加1,而不是每次都被初始化為初值0。
4 volatile關(guān)鍵字
嵌入式開(kāi)發(fā)中,常用到volatile關(guān)鍵字,它是一個(gè)類型修飾符,含義為“易變的”。使用方式如下:
volatile char i;
這里使用volatile關(guān)鍵字定義了一個(gè)字符型的變量i,指出i是隨時(shí)可能發(fā)生變化的,每次使用該變量時(shí)都必須從i的地址中讀取。由于內(nèi)存的讀/寫(xiě)速度遠(yuǎn)不及CPU中寄存器的讀/寫(xiě)速度,為了提高數(shù)據(jù)信息的存取速度,一方面在硬件上引入高速緩存Cache,另一方面在軟件上使用編譯器對(duì)程序進(jìn)行優(yōu)化,將變量的值提前從內(nèi)存讀取到CPU的寄存器中,以后用到該變量時(shí),直接從速度較快的寄存器中讀取,這樣有利于提高運(yùn)算速度,但同時(shí)也可能存在風(fēng)險(xiǎn),如該變量在內(nèi)存中的值有可能被程序的其他部分(如其他線程)修改或覆蓋,而寄存器中存放的仍是之前的值,這就導(dǎo)致應(yīng)用程序讀取的值和實(shí)際變量值不一致;也有可能是寄存器中的值發(fā)生了改變,而內(nèi)存中該變量的值沒(méi)有被修改,同樣也會(huì)導(dǎo)致不一致的情況發(fā)生。因此,為防止由于編譯器對(duì)程序進(jìn)行優(yōu)化導(dǎo)致讀取錯(cuò)誤數(shù)據(jù),使用 volatile關(guān)鍵詞進(jìn)行定義。簡(jiǎn)單地說(shuō),使用volatile關(guān)鍵字就是不讓編譯器進(jìn)行優(yōu)化,即每次讀取或者修改值時(shí),都必須重新從內(nèi)存中讀取或者修改,而不是使用保存在寄存器的備份。舉個(gè)簡(jiǎn)單的例子:大學(xué)里的獎(jiǎng)/助學(xué)金的發(fā)放一般都是直接轉(zhuǎn)給學(xué)校,學(xué)校再發(fā)給每名學(xué)生,學(xué)校財(cái)務(wù)處都登記了每名學(xué)生的銀行卡號(hào),但不可避免地會(huì)有一些學(xué)生因各種原因丟失銀行卡或不再使用這張銀行卡,而沒(méi)來(lái)得及去財(cái)務(wù)處重新登記,從而影響?yīng)?助學(xué)金的發(fā)放,這里,學(xué)生就是變量的原始地址,而財(cái)務(wù)處的銀行卡號(hào)就是變量在寄存器中的備份,使用 volatile關(guān)鍵字來(lái)定義學(xué)生這個(gè)變量,這樣每次發(fā)放獎(jiǎng)/助學(xué)金時(shí)都去找學(xué)生這個(gè)變量的原始地址,而不是直接轉(zhuǎn)到財(cái)務(wù)處保存的銀行卡上,進(jìn)而避免錯(cuò)誤的發(fā)生。const關(guān)鍵字的含義為“只讀”,volatile關(guān)鍵字的含義為“易變的”,但volatile關(guān)鍵字解釋為“直接存取原始內(nèi)存地址”更為合適,使用 volatile關(guān)鍵字定義變量后,該變量就不會(huì)因外因而發(fā)生變化了。一般來(lái)說(shuō),volatile 關(guān)鍵字常用在以下場(chǎng)合。
(1)中斷服務(wù)程序中修改的、供其他程序檢測(cè)的變量需要使用volatile關(guān)鍵字。
(2)多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志應(yīng)添加 volatile關(guān)鍵字。
(3)外設(shè)寄存器地址映射的硬件寄存器通常要用volatile關(guān)鍵字進(jìn)行聲明。
5 extern關(guān)鍵字
extern關(guān)鍵字用于指明此函數(shù)或變量定義在其他文件中,提示編譯器遇到此函數(shù)或變量時(shí)到其他模塊中尋找其定義。這樣,extern關(guān)鍵字聲明的函數(shù)或變量就可以在本模塊或其他模塊中使用,因此,使用extern關(guān)鍵字是一個(gè)聲明而不是重新定義。使用方法如下:
extern int a;
extern int funA():
解析:第一條語(yǔ)句僅僅是變量a的聲明,而不是定義變量a,并未為a分配內(nèi)存空間,變量a作為全局變量只能被定義一次。第二條語(yǔ)句聲明函數(shù)funA(),此函數(shù)已在其他文件中定義。STM32中,extern關(guān)鍵字還有一個(gè)重要作用,即與"C一起連用,即 extern "c",進(jìn)行鏈接指定。例如,stm32f10x.h頭文件中有如下代碼。
#ifndef _STM32F10× H
#define _STM32F10x_H
#ifdef .epluspius
extern "C"{
#endif
...
#ifdef _eplusplus
}
"endif
這段代碼的含義是,若沒(méi)有定義_STM32F10x_H,則定義_STM32F10x H,若已經(jīng)定義_cplusplus,則執(zhí)行 extern "C"中語(yǔ)句,extern "C"是告訴C++編譯器括號(hào)中的程序代碼是按照C語(yǔ)言的文件格式進(jìn)行編譯的,_cplusplus是C++編譯器中自定義的宏,plus是“+”的意思。C+H+支持函數(shù)重載,即在編譯時(shí)會(huì)將函數(shù)名與參數(shù)聯(lián)合起來(lái)生成一個(gè)新的中間函數(shù)名稱,而C語(yǔ)言不支持函數(shù)重載,這就導(dǎo)致在C++環(huán)境下使用C函數(shù)會(huì)出現(xiàn)鏈接時(shí)找不到對(duì)應(yīng)函數(shù)的情況,這時(shí)就需要使用extern "C"進(jìn)行鏈接指定,告知編譯器此時(shí)采用的是C語(yǔ)言定義的函數(shù),需要使用C語(yǔ)言 的命名規(guī)則來(lái)處理函數(shù),不要生成用于鏈接的中間函數(shù)名。 一般將函數(shù)聲明存放在頭文件中,當(dāng)函數(shù)有可能被C語(yǔ)言或C+使用時(shí),將函數(shù)聲明存放在 extern "C"中以免出現(xiàn)編譯錯(cuò)誤,完整的使用方法如下:
#ifdef__cplusplus
extern "C"{
#endif
//函數(shù)聲明
#ifdef_Cplusplus
}
#endif
STM32中很多頭文件都采用這樣的用法,如標(biāo)準(zhǔn)外設(shè)庫(kù)中的 stm32f1 0x_adc.h ,stm32f10x can.h、 stm32f1Ox_gpio.h 等。利用extern 關(guān)鍵字進(jìn)行編程時(shí)需要注意以下要點(diǎn)。嵌入式開(kāi)發(fā)一般采用模塊化設(shè)計(jì)思想,因此,為保證全局變量和功能函數(shù)的使用,extern關(guān)鍵字一般用在.h頭文件中對(duì)某個(gè)模塊提供給其他模塊調(diào)用的外部函數(shù)及變量進(jìn)行聲明,實(shí)際編程中只需要將該.h頭文件包含進(jìn)該模塊對(duì)應(yīng)的.c文件中,即在該模塊的.c文件中加入代碼#include "xxx.h”。實(shí)例如下:
6 struct結(jié)構(gòu)體
**struct用于定義結(jié)構(gòu)體類型,其作用是將不同數(shù)據(jù)類型的數(shù)據(jù)組合在一起,構(gòu)造出一個(gè)新的數(shù)據(jù)類型。**struct一般用法如下:
struct 結(jié)構(gòu)體名
{
數(shù)據(jù)類型成員名1;
數(shù)據(jù)類型成員名2;
數(shù)據(jù)類型成員名n;
};
struct Student{ //聲明結(jié)構(gòu)體
char name[20]; //姓名
int num; //學(xué)號(hào)
float score; //成績(jī)
};
7 enum
有時(shí)一個(gè)變量會(huì)有幾種可能的取值,如一個(gè)星期有7天、每學(xué)期開(kāi)設(shè)的課程、12種不同的顏色(紅、橙、黃、綠、青、藍(lán)、紫、灰、粉、黑、白、棕)等,C語(yǔ)言提供了一種enum枚舉類型,用來(lái)將變量或?qū)ο蟮乃锌赡艿闹狄灰涣谐?變量取值只限于列舉出來(lái)的值。enum枚舉類型的用法如下:
enum枚舉名
{
枚舉成員1,
枚舉成員2,
...
枚舉成員n;
}枚舉變量;
enum枚舉類型是一個(gè)集合,將所有可能的取值用花括號(hào)括住,花括號(hào)中的各枚舉成員之間用逗號(hào)隔開(kāi),最后一個(gè)枚舉成員后省略逗號(hào)。enum枚舉類型以分號(hào)結(jié)束,這里的枚舉變量可以省略,在后面需要時(shí)再根據(jù)枚舉名進(jìn)行定義。例如,利用enum枚舉類型列舉幾種常見(jiàn)的顏色。
enum Color
{
RED,
GREEN,
BLACK,
YELLOw
};
上述名為 Color的枚舉類型只有4個(gè)成員:RED、GREEN、BLACK、YELLOW,即意味著Color類型變量的取值只能取這4種顏色中的某一種顏色。例如,利用enum定義一個(gè) Weekdays枚舉類型名,包括7個(gè)枚舉成員:從星期一到星期日,并定義枚舉變量 Mydays 與 Olddays.
enumweekdays
{
Monday=1,
Tuesday,
wednesday,
Thursday,
Friday,
Saturday,
sunday
}Mydays.olddays;
注意:enum枚舉類型具有自動(dòng)編號(hào)功能,第一個(gè)枚舉成員的默認(rèn)值為整型的0,后續(xù)枚舉成員的值在前一個(gè)成員值上自動(dòng)加1,也可以自定義枚舉成員的值,若把第一個(gè)枚舉成員的值定義為1,則第二枚舉成員的值就為2,依此類推,如上述例子中 Friday 的值為5。因此,enum枚舉類型中的枚舉成員的值是常量而不是變量,不能在程序中用賦值語(yǔ)句再對(duì)它賦值,但可以將枚舉值賦給枚舉變量。例如,以下兩條語(yǔ)句是正確的。
Mydays=Thursday;
olddays=Friday;
但以下兩條語(yǔ)句是錯(cuò)誤的。
Tuesday=o;
Mydays=1;
8 typedef
typedef用于為復(fù)雜的聲明定義一個(gè)簡(jiǎn)單的別名,它不是一個(gè)真正意義上的新類型。在編程中使用 typedef的目的一般有兩個(gè):
①為變量起一個(gè)容易記憶且意義明確的新名稱;
②簡(jiǎn)化一些比較復(fù)雜的類型聲明。
其基本格式如下: typedef類型名自定義的別名; 例如:
typedef signed char int8_t;//為數(shù)據(jù)類型signed char起別名int8_t
typedef signed int int32_t;//為數(shù)據(jù)類型signed int起別名int32_t
STM32開(kāi)發(fā)中,typedef主要有以下三種用法。
8.1 typedef的基本應(yīng)用
為已知的數(shù)據(jù)類型起一個(gè)簡(jiǎn)單的別名,如上例。
8.2 typedef 與結(jié)構(gòu)體struct結(jié)合使用
該用法用于自定義數(shù)據(jù)類型。如 stm32f10x_gpio.h頭文件中的GPIO初始化結(jié)構(gòu)體GPIO_InitTypeDef。
typedef struct
{
uint16_t GPIO_ Pin;
GPIOSpeed_TypeDef GPIO_Speed;
GPIOMode TypeDef GPIO_Mode;
}IGPIo_InitType
上述語(yǔ)句利用 struct創(chuàng)建了一個(gè)新的結(jié)構(gòu)體,這個(gè)新結(jié)構(gòu)體有三個(gè)成員GPIO_Pin、GPIO_Speed和 GPIO_Mode,同時(shí)又使用 typedef為這個(gè)新建的結(jié)構(gòu)體定義一個(gè)新的名稱GPIO_InitTypeDef,在應(yīng)用時(shí)就可以直接使用GPIO_InitTypeDef 來(lái)定義變量。例如:
GPIO_InitTypeDef GPIO_ InitStrueture;
上述語(yǔ)句利用 GPIO_InitTypeDef結(jié)構(gòu)體定義了一個(gè)變GPIO_InitStructure,引用三個(gè)成員的方法如下:
GPIO InitStructure.GPIO_Pin;
GPIO_InitStructure.GPIO_Speed;
GPIO InitStructure.GPIO Mode;
8.3 typedef 與enum結(jié)合使用
利用typedef關(guān)鍵字將枚舉類型定義成別名,并利用該別名進(jìn)行變量聲明,STM32標(biāo)準(zhǔn)外設(shè)庫(kù)v3.5.0版本中有很多enum和 typedef結(jié)合使用的應(yīng)用。stm32f10x_gpio.h頭文件中的代碼如下。
Typedef enum
{
GPIO Speed_1OMHz=1,
GPIo_Speed_2MHz,
GPIOSpeed_50MHz;
}GPIOSpeed_TypeDef;
該例中enum枚舉類型共有三個(gè)成員:GPIO Speed_10MHz、GPIO Speed_2MHz和GPIO_Speed_50MHz,并將第一個(gè)枚舉成員GPIO_Speed_10MHz賦值為1,enum枚舉類型會(huì)將枚舉成員的賦值在第一個(gè)枚舉成員賦值的基礎(chǔ)上加1,因此GPIO_Speed_2MHz 默認(rèn)值為2,GPIO_Speed_50MHz默認(rèn)值為3。同時(shí),利用typedef關(guān)鍵字將此枚舉類型定義一個(gè)別名GPIOSpeed TypeDef,這里省略了枚舉類型的枚舉名,只用 typedef為枚舉類型定義一個(gè)別名。
9 #define
#define是C語(yǔ)言的預(yù)處理命令,它用于宏定義,用來(lái)將一個(gè)標(biāo)識(shí)符定義為一個(gè)字符串,該標(biāo)識(shí)符稱為宏名,被定義的字符串稱為替換文本,采用宏定義的目的主要是方便程序編寫(xiě),一般放在源文件的前面,稱為預(yù)處理部分。所謂預(yù)處理是指在編譯前所做的工作。預(yù)處理是C語(yǔ)言的一個(gè)重要功能,由預(yù)處理程序負(fù)責(zé)完成,程序編譯時(shí),系統(tǒng)將自動(dòng)引用預(yù)處理程序?qū)υ闯绦蛑械念A(yù)處理部分進(jìn)行處理,處理完畢后自動(dòng)進(jìn)入對(duì)源程序的編譯。STM32標(biāo)準(zhǔn)外設(shè)庫(kù)中,#define的使用方式主要有以下兩種。
**9.1 **無(wú)參數(shù)宏定義
無(wú)參數(shù)宏定義的一般形式如下:
其中,字符串可以是常數(shù)、字符串和表達(dá)式等。例如:#define UINT8_MAX 255 該語(yǔ)句表示定義了宏名UINT8_MAX,它代表255,例如:#define_IO volatile; 該語(yǔ)句表示定義宏名_IO,代表 volatile,若以后程序中再需要用到 volatile,則可以使用IO。例如:#define RCC AHBPeriph_DMA1 ((uint32_t)0x00000001) 該語(yǔ)句表示定義RCC_AHBPeriph_DMA1宏名,代表32位的無(wú)符號(hào)數(shù)據(jù)0x00000001.
STM32中有很多此類用法,如標(biāo)準(zhǔn)外設(shè)庫(kù) v3.5.0的 stm32f1 0x_rcc.h文件中APB2_peripheral外設(shè)基地址的定義,如圖5所示。
圖5 APB2_peripheral各外設(shè)基地址的定義
****9.2 帶參數(shù)的宏定義
宏定義格式如下:
#define<宏名>(參數(shù)1,參數(shù)2,…,參數(shù)n)<替換列表>
例如:
define SUM(x,y) (x+y)
…
a=SUM(2,2):
其中,a的結(jié)果是4,將 SUM(X,y)定義為x+y,預(yù)編譯時(shí)會(huì)將SUM(x,y)替換為xty。例如:
#define IsGPIO_SPEED(SPEED)(((SPEED) = GP1o_Speed_10MHz)||((SPEED)==GPIO_Speed_ 2MHz)||((SPEED)==GP10_Speed_50MHz))
使用宏定義#define 將 IS_GPIO_SPEED(SPEED)替換為 GPIO_Speed_10MHz、GPIO_Speed_2MHz或者GPIO_Speed_50MHz。注意:帶參數(shù)的宏定義同樣也只是進(jìn)行簡(jiǎn)單的字符替換,替換是在編譯前進(jìn)行的,展開(kāi)并不分配內(nèi)存單元,不進(jìn)行值的傳遞處理,因此替換不會(huì)占用運(yùn)行時(shí)間,只占用編譯時(shí)間,因此該方式可以提高運(yùn)行效率。#define與 typedef的區(qū)別為:typedef是在編譯階段處理的,具有類型檢查的功能,而#define是在預(yù)處理階段處理的,即在編譯前,只進(jìn)行簡(jiǎn)單的字符串替換,而不進(jìn)行任何檢查。
10 回調(diào)函數(shù)
回調(diào)函數(shù)是一個(gè)通過(guò)函數(shù)指針調(diào)用的函數(shù)。操作系統(tǒng)中的某些函數(shù)常需要調(diào)用用戶定義的函數(shù)來(lái)實(shí)現(xiàn)其功能,由于與常用的用戶程序調(diào)用系統(tǒng)函數(shù)的調(diào)用方向相反,因此將這種調(diào)用稱為回調(diào)(Callback),而被系統(tǒng)函數(shù)調(diào)用的函數(shù)就稱為回調(diào)函數(shù)。STM32的HAL庫(kù)在stm32flxx_hal_xxx.c文件中定義了相應(yīng)的回調(diào)函數(shù),并由中斷觸發(fā),其實(shí)質(zhì)是中斷處理程序。如 stm32flxx_hal_gpio.c代碼中通過(guò)GPIO中斷處理函數(shù)voidHAL _GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)調(diào)用相應(yīng)的回調(diào)函數(shù)HAL_GPIO_EXTICallback(GPIO_Pin),開(kāi)發(fā)人員只需要在回調(diào)函數(shù)中編寫(xiě)應(yīng)用程序就能實(shí)現(xiàn)中斷服務(wù)功能。
**11 ** #ifdef 、#ifndef、#else 、#if
*#define 定義一個(gè)預(yù)處理宏 *
*#undef 取消宏的義 *
*#if 編譯預(yù)處理中的條件命令,相當(dāng)于C語(yǔ)法中的if語(yǔ)句 *
*#ifdef 判斷某個(gè)宏是否被定義,若已定義,執(zhí)行隨后的語(yǔ)句 *
*#ifndef 與#ifdef相反,判斷某個(gè)宏是否未被定義*
*#elif 若#if, #ifdef, #ifndef或前面的#elif條件不滿足,則執(zhí)行#elif之后的語(yǔ)句,相當(dāng)于C語(yǔ)法中的else-if *
*#else 與#if, #ifdef, #ifndef對(duì)應(yīng), 若這些條件不滿足,則執(zhí)行#else之后的語(yǔ)句,相當(dāng)于C語(yǔ)法中的else *
*#endif 與#if, #ifdef, #ifndef這些條件命令的結(jié)束標(biāo)志. *
defined 與#if, #elif配合使用,判斷某個(gè)宏是否被定義
-
嵌入式
+關(guān)注
關(guān)注
5082文章
19104瀏覽量
304791 -
硬件
+關(guān)注
關(guān)注
11文章
3312瀏覽量
66200 -
軟件
+關(guān)注
關(guān)注
69文章
4921瀏覽量
87392 -
C語(yǔ)言
+關(guān)注
關(guān)注
180文章
7604瀏覽量
136683 -
程序
+關(guān)注
關(guān)注
117文章
3785瀏覽量
81001
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論