? ? ?
目錄
STM32的數(shù)據(jù)類型
const關(guān)鍵字
static 關(guān)鍵字
volatile關(guān)鍵字
extern關(guān)鍵字
?struct結(jié)構(gòu)體
enum
?typedef
#define
回調(diào)函數(shù)
#ifdef 、#ifndef、#else ?、#if ? ?
嵌入式開(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)框架圖
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類型限定詞加下畫線是為了避免命名沖突。
表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方式。
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)閟um 只能被讀取,不能賦值
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)。
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("%d\n",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。
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)存的讀/寫速度遠(yuǎn)不及CPU中寄存器的讀/寫速度,為了提高數(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)行聲明。
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í)例如下:
?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ī)
};
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;
?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主要有以下三種用法。
? ? ? 1. typedef的基本應(yīng)用
? ? ? 為已知的數(shù)據(jù)類型起一個(gè)簡(jiǎn)單的別名,如上例。
? ? ? 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_InitTypeDef;
? ? ? ?上述語(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;
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è)別名。
#define
? ? ? #define是C語(yǔ)言的預(yù)處理命令,它用于宏定義,用來(lái)將一個(gè)標(biāo)識(shí)符定義為一個(gè)字符串,該標(biāo)識(shí)符稱為宏名,被定義的字符串稱為替換文本,采用宏定義的目的主要是方便程序編寫,一般放在源文件的前面,稱為預(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的使用方式主要有以下兩種。
1.無(wú)參數(shù)宏定義
無(wú)參數(shù)宏定義的一般形式如下:
#define<宏名>字符串>
其中,字符串可以是常數(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è)基地址的定義
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)行任何檢查。
回調(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ù)中編寫應(yīng)用程序就能實(shí)現(xiàn)中斷服務(wù)功能。
#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è)宏是否被定義文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-799330.html
指針相關(guān)內(nèi)容我這里就不在贅述了網(wǎng)上有很多豐富的資料。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-799330.html
到了這里,關(guān)于嵌入式C語(yǔ)言(入門必看)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!