嵌入式技術(shù)基礎(chǔ)與實(shí)踐課件章語言及_第1頁
嵌入式技術(shù)基礎(chǔ)與實(shí)踐課件章語言及_第2頁
嵌入式技術(shù)基礎(chǔ)與實(shí)踐課件章語言及_第3頁
嵌入式技術(shù)基礎(chǔ)與實(shí)踐課件章語言及_第4頁
嵌入式技術(shù)基礎(chǔ)與實(shí)踐課件章語言及_第5頁
已閱讀5頁,還剩44頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

第5章08C語言及第一個(gè)08C工程本章首先講述了嵌入式中選用C語言編程的優(yōu)越性,講述嵌入式C語言的編程方法,比較了08C語言和標(biāo)準(zhǔn)C的差異,然后闡述了08C的一些特殊用法,接下來通過實(shí)例講述08C語言編程框架,最后結(jié)合我們的實(shí)際開發(fā)經(jīng)驗(yàn),總結(jié)了08C中的一些編程技巧。使用C語言進(jìn)行嵌入式程序開發(fā),它的編程方法和編程手段與PC機(jī)上使用C語言還是有很大差別,只有對(duì)嵌入式體系結(jié)構(gòu)和硬件資源作詳盡了解,才能寫出高質(zhì)量高效率的C語言程序。讀者在實(shí)際學(xué)習(xí)過程中,要多加練習(xí),從實(shí)際編程中體會(huì)嵌入式的C語言編程方法。5.1標(biāo)準(zhǔn)C語言的基本語法

C語言是在70年代初問世的。1978年美國電話電報(bào)公司(AT&T)貝爾實(shí)驗(yàn)室正式發(fā)表了C語言。同時(shí)由B.W.Kernighan和D.M.Ritchit合著了著名的《THECPROGRAMMINGLANGUAGE》,簡(jiǎn)稱為《K&R》,也有人稱之為K&R標(biāo)準(zhǔn)。但是,在《K&R》中并沒有定義一個(gè)完整的標(biāo)準(zhǔn)C語言,后來由美國國家標(biāo)準(zhǔn)學(xué)會(huì)在此基礎(chǔ)上制定了一個(gè)C語言標(biāo)準(zhǔn),于1983年發(fā)表,通常稱之為ANSIC或標(biāo)準(zhǔn)C。本節(jié)簡(jiǎn)要介紹C語言的基本知識(shí),特別是一些和單片機(jī)編程密切相關(guān)的基本知識(shí),未學(xué)過標(biāo)準(zhǔn)C語言的讀者可以通過本節(jié)了解C語言,對(duì)于C語言很熟悉的讀者,可以跳過本節(jié)。

5.1.1數(shù)據(jù)類型

C語言的數(shù)據(jù)類型有基本類型和構(gòu)造類型兩大類?;緮?shù)據(jù)類型如表5-1所示。

注:08C語言的double類型長(zhǎng)度為4字節(jié)。構(gòu)造類型有數(shù)組、結(jié)構(gòu)、聯(lián)合、枚舉、指針和空類型。結(jié)構(gòu)和聯(lián)合是基本數(shù)據(jù)類型的組合。枚舉是一個(gè)被命名為整型常量的集合??疹愋妥止?jié)長(zhǎng)度為0,主要有兩個(gè)用途:一是明確地表示一個(gè)函數(shù)不返回任何值;二是產(chǎn)生一個(gè)同一類型指針(可根據(jù)需要?jiǎng)討B(tài)地分配給其內(nèi)存)。表5-1C語言基本數(shù)據(jù)類型數(shù)據(jù)類型簡(jiǎn)明含義位數(shù)字節(jié)數(shù)值域signedchar有符號(hào)字節(jié)型81-128~+127unsignedchar無符號(hào)字節(jié)型810~255signedshort有符號(hào)短整型162-32768~unsignedshort無符號(hào)短整型1620~65535signedint有符號(hào)短整型162-32768~unsignedint無符號(hào)短整型1620~65535signedlong有符號(hào)長(zhǎng)整型324-2147483648~7unsignedlong無符號(hào)長(zhǎng)整型3240~float浮點(diǎn)型3243.4E-38~3.4E+38double雙精度型6481.7E-312~1.7E+312嵌入式系統(tǒng)學(xué)習(xí)5.1.2運(yùn)算符

C語言的運(yùn)算符與大多數(shù)計(jì)算機(jī)語言基本相同,分為算術(shù)、邏輯、關(guān)系和位運(yùn)算及一些特殊的操作符。表5-2列出了C語言的部分運(yùn)算符及使用方法舉例。

5.1.3

流程控制

在程序設(shè)計(jì)中主要有三種基本控制結(jié)構(gòu):順序結(jié)構(gòu)、選擇結(jié)構(gòu)和循環(huán)結(jié)構(gòu)。(1)順序結(jié)構(gòu)順序結(jié)構(gòu)就是從前向后依次執(zhí)行語句。從整體上看,所有程序的基本結(jié)構(gòu)都是順序結(jié)構(gòu),中間的某個(gè)過程可以是選擇結(jié)構(gòu)或循環(huán)結(jié)構(gòu)。(2)選擇結(jié)構(gòu)在大多數(shù)程序中都會(huì)包含選擇結(jié)構(gòu)。它的作用是,根據(jù)所指定的條件是否滿足,決定從給定的兩組操作選擇其一。在C語言中選擇結(jié)構(gòu)可以用兩種語句來實(shí)現(xiàn):if語句和switch語句。

(3)循環(huán)結(jié)構(gòu)

C語言中的循環(huán)結(jié)構(gòu)常用for循環(huán),while循環(huán)與do...while循環(huán)。

5.1.4

函數(shù)

所謂函數(shù),即子程序,也就是“語句的集合”,就是說把經(jīng)常使用的語句群定義成函數(shù),供其他程序調(diào)用,這樣就可以避免重復(fù)編寫程序的麻煩,也可以縮短程序的長(zhǎng)度。當(dāng)一個(gè)程序太大時(shí),建議將其中的一部分程序改成用函數(shù)的方式調(diào)用較好,因?yàn)榇蟪绦蜻^于繁雜容易出錯(cuò),而小程序容易調(diào)試,也易于閱讀和修改。函數(shù)定義的一般形式如下所示:類型標(biāo)識(shí)符

函數(shù)名(類型參數(shù)1,類型參數(shù)2,類型參數(shù)3,……){說明部分語句}(1)使用函數(shù)的注意事項(xiàng)①函數(shù)定義時(shí)要同時(shí)聲明其類型。②調(diào)用函數(shù)前要先聲明該函數(shù)。③傳給函數(shù)的參數(shù)值,其類型要與函數(shù)原定義一致。④接收函數(shù)返回值的變量,其類型也要與函數(shù)類型一致。嵌入式系統(tǒng)學(xué)習(xí)5.1.4函數(shù)(2)(2)函數(shù)的聲明

voidfunction1(void)此函數(shù)無返回值,也不傳參數(shù)。

voidfunction2(unsignedchari,intj)此函數(shù)無返回值,但需要unsignedchar類型的參數(shù)i和int類型的參數(shù)j。

unsignedcharfunction3(unsignedchari)此函數(shù)有返回值,其類型為unsignedchar。(3)函數(shù)的返回值

return

表達(dá)式;return語句用來立即結(jié)束函數(shù),并返回一確定給調(diào)用程序。如果函數(shù)的類型和return語句中表達(dá)式的值不一致,則以函數(shù)類型為準(zhǔn)。對(duì)數(shù)值型數(shù)據(jù),可以自動(dòng)進(jìn)行類型轉(zhuǎn)換。即函數(shù)類型決定返回值的類型。5.1.5

指針指針是C語言中廣泛使用的一種數(shù)據(jù)類型,運(yùn)用指針是C語言最主要的風(fēng)格之一。指針是一種特殊的數(shù)據(jù)類型,在其它語言中一般沒有。指針是指向變量的地址,實(shí)質(zhì)上指針就是存儲(chǔ)單元的地址。根據(jù)所指的變量類型不同,可以是整型指針(int*)、浮點(diǎn)型指針(float*)、字符型指針(char*)、結(jié)構(gòu)指針(struct*)和聯(lián)合指針(union*)。(1)指針變量的定義其一般形式為:類型說明符*變量名;其中,*表示這是一個(gè)指針變量,變量名即為定義的指針變量名,類型說明符表示本指針變量所指向的變量的數(shù)據(jù)類型。例如:int*point_1;表示point_1是一個(gè)指向整型的指針變量,它的值是一個(gè)整型變量的地址。5.1.5

指針(2)(2)指針變量的賦值指針變量同普通變量一樣,使用之前不僅要定義說明,而且必須賦予具體值。未經(jīng)賦值的指針變量不能使用,否則將造成系統(tǒng)混亂,甚至死機(jī)。指針變量的賦值只能賦予地址。①指針變量初始化的方法inta;int*point=&a;a②給指針賦值的方法inta;int*point;point=&a;將數(shù)值賦給指針將導(dǎo)致錯(cuò)誤,例如:int*point;point=1000;是錯(cuò)誤的。被賦值的指針變量前不能再加“*”說明符,如寫為*point=&a也是錯(cuò)誤的。(3)指針的運(yùn)算①取地址運(yùn)算符&取地址運(yùn)算符&是單目運(yùn)算符,其結(jié)合性為自右至左,其功能是取變量地址。5.1.5

指針(3)②取內(nèi)容運(yùn)算符*取內(nèi)容運(yùn)算符*是單目運(yùn)算符,其結(jié)合性為自右至左,用來表示指針變量所指的變量。在*運(yùn)算符之后跟的變量必須是指針變量。注意:指針運(yùn)算符*和指針變量說明中的指針說明符*并非相同。在指針變量說明中,“*”是類型說明符,表示其后的變量是指針類型。而表達(dá)式中出現(xiàn)的“*”則是一個(gè)運(yùn)算符用以表示指針變量所指的變量。main(){inta=5,*point=&a;printf("%d",*point);}表示指針變量point取得了整型變量a的地址。本語句表示輸出變量a的值。③

加減算術(shù)運(yùn)算對(duì)于指向數(shù)組的指針變量,可以加上或減去一個(gè)整數(shù)n。設(shè)pa是指向數(shù)組a的指針變量,則pa+n,pa-n,pa++,++pa,pa--,--pa運(yùn)算都是合法的。指針變量加或減一個(gè)整數(shù)n的意義是把指針指向的當(dāng)前位置(指向某數(shù)組元素)向前或向后移動(dòng)n個(gè)位置。5.1.5

指針(4)注意:數(shù)組指針變量向前或向后移動(dòng)一個(gè)位置和地址加1或減1在概念上是不同的。因?yàn)閿?shù)組可以有不同的類型,各種類型的數(shù)組元素所占的字節(jié)長(zhǎng)度是不同的。如指針變量加1,即向后移動(dòng)1個(gè)位置表示指針變量指向下一個(gè)數(shù)據(jù)元素的首地址。而不是在原地址基礎(chǔ)上加1。例如:inta[5],*pa;pa=a;/*pa指向數(shù)組a,也是指向a[0]*/pa=pa+2;/*pa指向a[2],即pa的值為&pa[2]*/指針變量的加減運(yùn)算只能對(duì)數(shù)組指針變量進(jìn)行,對(duì)指向其它類型變量的指針變量作加減運(yùn)算是毫無意義的。(4)void指針類型顧名思義,void*為“無類型指針”,即用來定義一個(gè)指針變量,不指定它是指向哪一種類型數(shù)據(jù),但可以把它強(qiáng)制轉(zhuǎn)化成任何一種類型的指針。對(duì)于void*類型的指針變量不能進(jìn)行取內(nèi)容運(yùn)算和加減算術(shù)運(yùn)算,因?yàn)榫幾g器不知道它指向的具體類型。5.1.5

指針(5)眾所周知,如果指針p1和p2的類型相同,那么我們可以直接在p1和p2間互相賦值;如果p1和p2指向不同的數(shù)據(jù)類型,則必須使用強(qiáng)制類型轉(zhuǎn)換運(yùn)算符把賦值運(yùn)算符右邊的指針類型轉(zhuǎn)換為左邊指針的類型。例如:float*p1;int*p2;p1=(float*)p2;而void*則不同,任何類型的指針都可以直接賦值給它,無需進(jìn)行強(qiáng)制類型轉(zhuǎn)換:void*p1;int*p2;p1=p2;但這并不意味著,void*也可以無需強(qiáng)制類型轉(zhuǎn)換地賦給其它類型的指針,也就是說p2=p1這條語句編譯就會(huì)出錯(cuò),而必須將p1強(qiáng)制類型轉(zhuǎn)換成“void*”類型的。因?yàn)椤盁o類型”可以包容“有類型”,而“有類型”則不能包容“無類型”.5.1.6結(jié)構(gòu)體

結(jié)構(gòu)體是由基本數(shù)據(jù)類型構(gòu)成的,并用一個(gè)標(biāo)識(shí)符來命名的各種變量的組合。結(jié)構(gòu)體中可以使用不同的數(shù)據(jù)類型。(1)結(jié)構(gòu)體的說明和結(jié)構(gòu)體變量的定義定義一個(gè)結(jié)構(gòu)體的一般形式為:

在①方式中,僅定義了一個(gè)結(jié)構(gòu)體。如果要使用這種類型的結(jié)構(gòu)體還需要使用變量定義語句。②方式在定義結(jié)構(gòu)體的同時(shí),定義了結(jié)構(gòu)體變量。例如下面左右兩邊的代碼是等價(jià)的。

①struct結(jié)構(gòu)體名

{成員列表};

②struct結(jié)構(gòu)體名{成員列表}結(jié)構(gòu)體變量;或

結(jié)構(gòu)體的使用5.1.6結(jié)構(gòu)體(2)structstudent/*定義一個(gè)student結(jié)構(gòu)體*/{charname[8];intage;charsex[2];};structstudentstudent1;/*定義一個(gè)student結(jié)構(gòu)變量student1*/structstudent{charname[8];intage;charsex[2];}student1;/*定義一個(gè)student結(jié)構(gòu)體,同時(shí)定義一個(gè)student結(jié)構(gòu)變量student1*/結(jié)構(gòu)體是一個(gè)新的數(shù)據(jù)類型,因此結(jié)構(gòu)體變量也可以象其它類型的變量一樣賦值運(yùn)算,不同的是結(jié)構(gòu)體變量以成員作為基本變量。結(jié)構(gòu)體成員的表示方式為:結(jié)構(gòu)體變量.成員名

如果將“結(jié)構(gòu)體變量.成員名”看成一個(gè)整體,則這個(gè)整體的數(shù)據(jù)類型與結(jié)構(gòu)體中該成員的數(shù)據(jù)類型相同,這樣就像前面所講的變量那樣使用。例如:

student1.age=18;5.1.6結(jié)構(gòu)體(3)(3)結(jié)構(gòu)體指針結(jié)構(gòu)體指針是指向結(jié)構(gòu)體的指針。它由一個(gè)加在結(jié)構(gòu)體變量名前的“*”操作符來定義。例如用上面已說明的結(jié)構(gòu)體定義一個(gè)結(jié)構(gòu)體指針如下:structstudent*Pstudent;使用結(jié)構(gòu)體指針對(duì)結(jié)構(gòu)體成員的訪問,與結(jié)構(gòu)體變量對(duì)結(jié)構(gòu)體成員的訪問在表達(dá)方式上有所不同。結(jié)構(gòu)體指針對(duì)結(jié)構(gòu)體成員的訪問表示為:結(jié)構(gòu)體指針名->結(jié)構(gòu)體成員其中"->"是兩個(gè)符號(hào)"-"和">"的組合,好象一個(gè)箭頭指向結(jié)構(gòu)體成員。例如要給上面定義的結(jié)構(gòu)體中name和age賦值,可以用下面語句:strcpy(Pstudent->name,"LiuYuZhang");Pstudent->age=18;結(jié)構(gòu)體的使用5.1.6結(jié)構(gòu)體(4)實(shí)際上,Pstudent->name就是(*Pstudent).name的縮寫形式。需要指出的是結(jié)構(gòu)體指針是指向結(jié)構(gòu)體的一個(gè)指針,即結(jié)構(gòu)體中第一個(gè)成員的首地址,因此在使用之前應(yīng)該對(duì)結(jié)構(gòu)體指針初始化,即分配整個(gè)結(jié)構(gòu)體長(zhǎng)度的字節(jié)空間。這可用下面函數(shù)完成,例如:Pstudent=(structsudent*)malloc(sizeof(structstudent));sizeof(structstudent)自動(dòng)求取student結(jié)構(gòu)體的字節(jié)長(zhǎng)度,malloc()函數(shù)定義了一個(gè)大小為結(jié)構(gòu)體長(zhǎng)度的內(nèi)存區(qū)域,然后將其地址作為結(jié)構(gòu)體指針返回。結(jié)構(gòu)體的使用5.1.7共用體

有時(shí)需要將幾種不同類型的變量存放到同一段內(nèi)存單元中。嵌入式系統(tǒng)為了減少內(nèi)存使用,內(nèi)存單元不同時(shí)刻作不同的用途,即內(nèi)存復(fù)用,共用體數(shù)據(jù)類型就非常適合于這種情況。例如,可把一個(gè)整型變量、一個(gè)字符型變量和一個(gè)長(zhǎng)整型變量放在同一個(gè)地址開始的內(nèi)存單元,如圖5-6所示。以上3個(gè)變量在內(nèi)存中占的字節(jié)數(shù)不同,但都從同一地址開始(圖中設(shè)地址為$0080)存放。這種幾個(gè)不同的變量共同占用同一段的結(jié)構(gòu),稱為“共用體”類型結(jié)構(gòu)。如右圖,為共用體的內(nèi)存使用。圖5-6共用體的內(nèi)存“共用”

0080地址整型變量b字符型變量a長(zhǎng)整型變量c(1)共用體的定義定義一個(gè)聯(lián)合類型的一般形式為:

union共用名

{

成員表

}共用變量名;成員表中含有若干成員,成員的一般形式為:

類型說明符

成員名例如:

unionabc{inta;charb;longc;}u1;(2)共用體的使用在共用體變量u1被分配的內(nèi)存單元數(shù)量等于長(zhǎng)整型變量c的長(zhǎng)度(4字節(jié))中。如果整型變量c的賦值為:

u1.c=0x12345678;則u1.a和u1.b的值也被修改。本例中,它們的值為:

u1.a=0x12;

u1.b=0x1234;

5.1.8位域

有些信息在存儲(chǔ)時(shí),并不需要占用一個(gè)完整的字節(jié),而只需占幾個(gè)或一個(gè)二進(jìn)制位。例如在存放一個(gè)開關(guān)量時(shí),只有0和1兩種狀態(tài),用一位二進(jìn)位即可。為了節(jié)省存儲(chǔ)空間,并使處理簡(jiǎn)便,C語言又提供了一種數(shù)據(jù)結(jié)構(gòu),稱為“位域”或“位段”。所謂“位域”是把一個(gè)字節(jié)中的二進(jìn)制位劃分為幾個(gè)不同的區(qū)域,并說明每個(gè)區(qū)域的位數(shù)。每個(gè)域有一個(gè)域名,允許在程序中按域名進(jìn)行操作。這樣就可以把幾個(gè)不同的對(duì)象用一個(gè)字節(jié)的二進(jìn)制位域來表示。(1)位域的定義位域定義與結(jié)構(gòu)定義相仿,其形式為:

strict位域結(jié)構(gòu)名{

位域列表

};位域列表格式為:類型說明符位域名:位域長(zhǎng)度例如:

structbs{inta:8;intb:2;intc:6;}b1;(2)位域的使用位域的使用和結(jié)構(gòu)成員的使用相同,其一般形式為:位域變量名·位域名位例如在上面定義的位域b1可以這樣調(diào)用:

b1.a=1;//將b1的第0位置1b1.b=7;//將b1的第3~5位置111

通過位域定義位變量,是實(shí)現(xiàn)單個(gè)位操作的重要途徑和方法,采用位域定義位變量,產(chǎn)生的代碼緊湊、高效。

5.1.9編譯預(yù)處理

C語言提供編譯預(yù)處理的功能,“編譯預(yù)處理”是C編譯系統(tǒng)的一個(gè)重要組成部分。C語言允許在程序中使用幾種特殊的命令(它們不是一般的C語句)。在C編譯系統(tǒng)對(duì)程序進(jìn)行通常的編譯(包括語法分析,代碼生成,優(yōu)化等)之前,先對(duì)程序中的這些特殊的命令進(jìn)行“預(yù)處理”,然后將預(yù)處理的結(jié)果和源程序一起再進(jìn)行常規(guī)的編譯處理,以得到目標(biāo)代碼。C提供的預(yù)處理功能主要有宏定義、條件編譯和文件包含。(1)宏定義

#define宏名表達(dá)式表達(dá)式可以是數(shù)字、字符、也可以是若干條語句。在編譯時(shí),所有引用該宏的地方,都將自動(dòng)被替換成宏所代表的表達(dá)式。例如:

#definePI3.1415926#defineS(r)PI*r*r(2)條件編譯

通常源程序的所有行參加編譯。但是有時(shí)希望對(duì)其中一部分內(nèi)容只在滿足一定條件才進(jìn)行編譯,這就是“條件編譯”。條件編譯有三種形式①#if表達(dá)式程序段1#else表達(dá)式程序段2#endif②#ifdef標(biāo)識(shí)符程序段1#else

程序段2#endif③#ifndef標(biāo)識(shí)符程序段1#else

程序段2#endif

形式①在表達(dá)式為真時(shí),則編譯#if下的程序,否則編譯#else下的程序。形式②是當(dāng)所指定的標(biāo)識(shí)符已經(jīng)被#define命令定義過,則編譯程序段1,否則編譯程序段2。形式③是當(dāng)所指定的標(biāo)識(shí)符未被#define命令定義過,則編譯程序段1,否則編譯程序段2。

5.1.10用typedef定義類型

除了可以直接使用C提供的標(biāo)準(zhǔn)類型名(如int、char、float、double、long等)和自己定義的結(jié)構(gòu)體、共用體、指針、枚舉類型外,還可以用typedef定義新的類型名來代替已有的類型名。例如:

typedefunsignedcharINT8U;用法說明:①用typedef可以定義各種類型名,但不能用來定義變量。②用typedef只是對(duì)已經(jīng)存在的類型增加一個(gè)類型名,而沒有創(chuàng)造新的類型。③typedef與#define有相似之處,如:

typedefunsignedintINT16U;#defineINT16Uunsignedint④當(dāng)不同源文件中用到各種類型數(shù)據(jù)(尤其是像數(shù)組、指針、結(jié)構(gòu)體、共用體等較復(fù)雜數(shù)據(jù)類型)時(shí),常用typedef定義一些數(shù)據(jù)類型,并把它們單獨(dú)存放在一個(gè)文件中,而后在需要用到它們的文件中用#include命令把它們包含進(jìn)來。⑤使用typedef有利于程序的通用與移植。

5.208C語言的使用

08C語言適用于Freescale08系列微控制器,它的語法與標(biāo)準(zhǔn)C語法基本相同,但它的函數(shù)庫和標(biāo)準(zhǔn)C的函數(shù)庫有些差異,而且用08C在編程時(shí)涉及的硬件知識(shí)比較多,需要注意一些編程技巧。5.2.1寄存器和I/O口的使用(1)I/O口和寄存器的定位普通變量的定義和訪問同標(biāo)準(zhǔn)C語言,在嵌入式C語言中我們主要要解決寄存器變量和某些特殊變量的定位問題,即把這些變量存放在RAM中指定的位置。

(2)I/O與寄存器的操作使用上面定義的I/O口或寄存器宏,可以方便對(duì)I/O置高低電平或讀取I/O的狀態(tài),讀寫寄存器。例如:

unsignedcharsPortA=PTA;//將A口的狀態(tài)賦給sPortA變量

PTA=0xff;//將0xff賦給A口,A口將全為高電平

T1SC&=0xdf;//對(duì)TT1SC寄存器進(jìn)行位操作

5.2.2位操作方法

在嵌入式系統(tǒng)中,主控MCU的內(nèi)存資源很寶貴,為了減少內(nèi)存的使用量,常常將一些取值為0或1的標(biāo)志位組合到一個(gè)字節(jié)中,而不是使用整個(gè)字節(jié)來保存一個(gè)只能為0或1的值。例如:在開發(fā)一款水表控制的軟件中,定義了一個(gè)狀態(tài)標(biāo)志字節(jié)Flag,其各位的含義如表5-1所示。

表5-1嵌入式軟件中位定義實(shí)例Bit7Bit6Bit5Bit4Bit3Bit2Bit1Bit0備用QLQYZFFMZT3FMZT2FMZT1CGR第0位:磁干擾標(biāo)志(=0,無干擾;=1,有干擾)第1位:閥門狀態(tài)標(biāo)志1(=0,正常;=1,閥門關(guān)不上,卡住)第2位:閥門狀態(tài)標(biāo)志2(=0,閥開著;=1,閥關(guān)著)第3位:閥門狀態(tài)標(biāo)志3(=0,正常;=1,閥門關(guān)不上,打滑)第4位:剩余水量正負(fù)標(biāo)志(=0,正;=1,負(fù))第5位:欠壓標(biāo)志位(=0,電壓正常;=1,欠電壓)第6位:欠量標(biāo)志(=0,未欠量;=1,已欠量)第7位:備用1.用位運(yùn)算符實(shí)現(xiàn)位操作

C語言中提供了6種基本的位運(yùn)算符:按位與(&)、按位或(|)、按位取反(~)、按位異或(^)、左移(<<)及右移(>>)。嵌入式系統(tǒng)編程常常需要寄存器或內(nèi)存內(nèi)單個(gè)二制位。在大多數(shù)情況下,最好的方法是讀出整個(gè)寄存器值,改變二進(jìn)制位,然后把整個(gè)值寫回到設(shè)備寄存器中。2.測(cè)試位按位“與”運(yùn)算最常用于測(cè)試單個(gè)位(或位域)的值,在需要關(guān)注的“位”所在位置由單獨(dú)一個(gè)1組成的特征碼與操作數(shù)作“與”運(yùn)算,當(dāng)關(guān)注的“位”是1時(shí),結(jié)果才是非0值,即邏輯真值。在實(shí)際書寫時(shí),特征碼通常用十六進(jìn)制、二進(jìn)制數(shù)或移位表達(dá)式。例如:要測(cè)試第4位是否為1,有以下幾種寫法:

if((bits&0x10)!=0)if(bits&0x10)if(bits&0b00010000)if(bits&(1<<4))由于任意非0值都解釋為值,所以條件中可以省略對(duì)0的冗余比較。表5-3給出了08C位操作語句編譯后的所對(duì)應(yīng)的指令,從表中可以看出編譯器在編譯時(shí),已經(jīng)做了優(yōu)化,將這些C語句變成了08CPU中的位指令,達(dá)到和匯編相同的執(zhí)行效率。表5-308C中的位操作語句及對(duì)應(yīng)的編譯后的指令C語句編譯后的匯編指令PTA|=(1<<7);//PTA的第7位置1Bset7,0x00PTA&=~(1<<7);//PTA的第7位清0Bclr7,0x00PTA^=0x80;//PTA的第7位取反LDA0x00EOR#-128STA0x00if((PTA&0x81)==0)//檢查第7位和第0位是否為0LDA0x00BIT#-127BNE0x××××08C中除了上述的位操作的方法外,還可以綜合共用體和位域等多種數(shù)據(jù)類型,很直觀地實(shí)現(xiàn)位操作,關(guān)于這部分的講述讀者參考本章的進(jìn)一步討論部分。

5.2.3中斷處理

首先,ISR與C中的正常子函數(shù)是有差別的:第一,正常子函數(shù)被編譯后的返回指令為RTS,而ISR被編譯后的返回指令為RTI。第二,正常子函數(shù)是通過調(diào)用方式進(jìn)入的,而ISR是通過中斷機(jī)制進(jìn)入的。第三,ISR的參數(shù)和返回類型總是void?,F(xiàn)在,就可以帶著ISR的這些獨(dú)有的特性編寫它了:①新建一個(gè)Vectors08.c,并加入工程中。②定義中斷向量表。5.2.408C的常用庫函數(shù)08C提供一系列函數(shù)庫供程序員使用,其中囊括了標(biāo)準(zhǔn)C所具有的大部分庫函數(shù)和一些08C特有的函數(shù),但是08C中的有些函數(shù)和標(biāo)準(zhǔn)C中的函數(shù)的功能不一樣。這些函數(shù)的頭文件位于安裝目錄的include目錄下,庫文件位于安裝目錄的lib目錄下。下面對(duì)08C中一些常用的庫函數(shù)做簡(jiǎn)要說明,更多的函數(shù)庫說明參見附錄D。(1)串口操作類函數(shù)函數(shù)的聲明如下:①intprintf(char*fmt,…)格式化輸出printf函數(shù)是一個(gè)標(biāo)準(zhǔn)庫函數(shù),它的函數(shù)原型在頭文件“stdio.h”中。但作為一個(gè)特例,不要求在使用

printf函數(shù)之前必須包含stdio.h文件。printf函數(shù)調(diào)用的一般形式為:printf(“格式控制字符串”,輸出列表)其中格式控制字符串用于指定輸出格式。格式控制串可由格式字符串和非格式字符串兩種組成。格式字符串是以%開頭的字符串,在%后面跟有各種格式字符,以說明輸出數(shù)據(jù)的類型、形式、長(zhǎng)度、小數(shù)位數(shù)等。如“%d”表示按十進(jìn)制整型輸出,“%ld”表示按十進(jìn)制長(zhǎng)整型輸出,“%c”表示按字符型輸出等。所有的輸出都是發(fā)送至串口,而不是屏幕。表5-308C中的位操作語句及對(duì)應(yīng)的編譯后的指令C語句編譯后的匯編指令PTA|=(1<<7);//PTA的第7位置1Bset7,0x00PTA&=~(1<<7);//PTA的第7位清0Bclr7,0x00PTA^=0x80;//PTA的第7位取反LDA0x00EOR#-128STA0x00if((PTA&0x81)==0)//檢查第7位和第0位是否為0LDA0x00BIT#-127BNE0x××××例如:voidmain(){inta=88,b=89;printf("%d%d\n",a,b);printf("%c,%c\n",a,b);printf("a=%d,b=%d",a,b);}②intputc(charc)putc也是stdio.h中的函數(shù),它只能發(fā)送一個(gè)字符。例如:putc(‘a(chǎn)’);//串口發(fā)送字符a③intputs(char*s)puts比putc功能強(qiáng)一些,可以發(fā)送一個(gè)字符串,但無法像printf那樣做格式化輸出。例如:puts(“hello”);//串口發(fā)送hello④intgetchar(void)通過串行模塊接收一個(gè)字符。例如:receive=getchar();(2)內(nèi)存操作類函數(shù)memcpy聲明:void*memcpy(void*s1,void*s2,size_tn)將以s2為起始地址的n個(gè)字節(jié)復(fù)制到以s1為起始地址的內(nèi)存中。例如://在0x50上存放著‘hello’5個(gè)字符memcpy((void*)0x0120,(void*)0x50,5);//此時(shí),hello已被復(fù)制到0x0120起始的內(nèi)存單元中5.2.508C語言與匯編語言的混合編程在絕大多數(shù)場(chǎng)合采用C語言編程即可完成預(yù)期的目的,但是對(duì)一些特殊情況進(jìn)行編程時(shí)要結(jié)合匯編語言。

(1)在08C中使用匯編

①調(diào)用匯編指令構(gòu)成的子程序定義匯編子程序,定義格式如下:_子程序名

代碼

…rts這種使用方法要注意以下幾點(diǎn):第一,在子程序名前加‘_’;第二,匯編子程序只能放在*.s文件中,然后將該文件加入到工程中;第三,在C代碼中調(diào)用匯編子程序時(shí)可直接調(diào)用:子程序名();第四,在子程序中不能使用映象寄存器的宏定義,只能用它們的直接地址。第五,匯編子程序的編寫時(shí),對(duì)于使用過的寄存器需要進(jìn)行保護(hù)。08C的編譯器把寄存器封裝在下層,不需要用戶管理,如果匯編子程序沒有保存這些寄存器,在返回時(shí)將造成不可預(yù)測(cè)的后果。②嵌入?yún)R編語句

對(duì)于嵌入式匯編,可以在C程序中使用一些關(guān)鍵字嵌入一些匯編程序,這種方法主要用于實(shí)現(xiàn)數(shù)學(xué)運(yùn)算或中斷處理,以便生成精練的代碼,減少運(yùn)行時(shí)間。當(dāng)匯編函數(shù)不大,且內(nèi)部沒有復(fù)雜的跳轉(zhuǎn)時(shí),可以用嵌入式匯編實(shí)現(xiàn)。使用關(guān)鍵字asm可以嵌入一條或多條匯編語句。例如:

asm(“SEI”);//單條指令

asm(“LDA$0000\n”//多條指令

“AND#1\n”

“STA$0000\n”);(2)在匯編中使用C語言在前面已經(jīng)講述了C代碼中嵌入?yún)R編程序的方法,實(shí)際上匯編中也可以調(diào)用C代碼中的變量與子程序。使用C代碼中定義的變量:在變量名前加’_’或’%’,例如:unsignedcharPortA;asm(“LDA%PortA”)或asm(“LDA_PortA”)調(diào)用C代碼中定義的函數(shù):在函數(shù)名前加

‘_’,例如:function1();asm(“JSR_function1”);

5.2.608C與標(biāo)準(zhǔn)C的其他一些不同之處

08C語言與標(biāo)準(zhǔn)C語言的語法基本相同,但是也存在一些不同之處,使用時(shí)應(yīng)該注意。

(1)部分?jǐn)?shù)據(jù)類型不同標(biāo)準(zhǔn)C中,double類型長(zhǎng)度為8字節(jié);而08C中,double類型長(zhǎng)度為4字節(jié)。

(2)地址分配不同標(biāo)準(zhǔn)C語言在分配數(shù)據(jù)存放地址時(shí),高字節(jié)的數(shù)據(jù)存放在高地址處,低字節(jié)的數(shù)據(jù)存放在低地址處,符合“高高低低”的分配原則。08C語言在分配數(shù)據(jù)存放地址時(shí),高字節(jié)的數(shù)據(jù)存放在低地址處,低字節(jié)的數(shù)據(jù)存放在高地址處,符合高低低高”的分配原則。

(3)共用體使用不同由于地址分配的不同,08C語言與標(biāo)準(zhǔn)C語言在共用體的使用上存在著一些差異。

5.308C工程文件組織

第一個(gè)08C工程”PrgFrame.prj”給出了FreescaleMCUC編程框架,同時(shí)它也是一個(gè)很好的編程規(guī)范示例:PrgFrame.prjGP32映像寄存器名定義頭文件(GP32C.h)開放或禁止MCU模塊中斷頭文件(EnDisInt.h)芯片初始化文件(MCUinit.c)中斷處理子程序與中斷向量表文件(Vectors08.c)芯片相關(guān)程序文件主函數(shù)文件(Main.c)小燈驅(qū)動(dòng)頭文件(LED.h)小燈驅(qū)動(dòng)文件(LED.c)硬件對(duì)象控制文件總頭文件(Includes.h)類型別名定義頭文件(Type.h)通用函數(shù)頭文件(GeneralFun.h)通用函數(shù)文件(GeneralFun.c)通用程序文件

5.4進(jìn)一步討論

5.4.1變量的定義

1.全局變量和局部變量全局變量為整個(gè)程序而定義,在整個(gè)程序運(yùn)行期間,它們占用固定的RAM資源。在C語言中,在所有函數(shù)外部聲明的變量都認(rèn)為具有全局作用域,這些聲明通常置于源文件的頂部?!叭帧睂?shí)際上僅僅意味著標(biāo)識(shí)符從聲明點(diǎn)到文件末尾的范圍內(nèi)是可訪問的,當(dāng)程序包含多個(gè)源文件時(shí),則在一個(gè)文件中定義的全局變量在其他文件引用時(shí),需要使用extern關(guān)鍵字聲明。在引用文件內(nèi)部,標(biāo)識(shí)符的作用域是由extern聲明的位置確定的。如果該聲明是全局的,那么該標(biāo)識(shí)符對(duì)于文件是全局的;如果該聲明是放在塊內(nèi)的,則它對(duì)于那個(gè)塊就是局部的。局部變量為某個(gè)函數(shù)或子程序而定義,只在函數(shù)運(yùn)行時(shí),從堆??臻g中分配存儲(chǔ)空間;函數(shù)運(yùn)行結(jié)束,所占用堆??臻g釋放。2.變量修飾符

變量定義有三個(gè)修飾符值得注意,雖然它們與標(biāo)準(zhǔn)C是相同的,但是在嵌入式C語言中又有不同的含義。(1)volatile

大多數(shù)編譯器對(duì)源程序編譯時(shí)做優(yōu)化操作,其中一種優(yōu)化方法是基于這種假設(shè):除非明確地把某值寫到內(nèi)存,否則內(nèi)存中的值不會(huì)改變。所以如果源程序中頻繁使用某個(gè)內(nèi)存,編譯器會(huì)把這個(gè)內(nèi)存放到CPU寄寄存器或高速緩存中,提高代碼運(yùn)行速度。(2)static

在子函數(shù)中static用聲明的變量是局部變量,局部的范圍可能是一個(gè)文件、函數(shù)、過程中,在局部的范圍內(nèi),變量可以調(diào)用,變量值可以共享。(3)const

修飾符const可以用在任何變量之前,用于聲明變量值不會(huì)被改變,即“只讀的”。這提供了一種保護(hù)性編程,編譯器會(huì)將任何想修改這種變量的行為看成是違犯語法的行為。const聲明的變量必須包含一個(gè)初值,不允許在以后的使用中修改它的值。5.4.2變量存儲(chǔ)空間分配嵌入式內(nèi)部數(shù)據(jù)存儲(chǔ)器RAM只有幾百字節(jié),如果通過擴(kuò)展外部存儲(chǔ)器RAM來提高數(shù)據(jù)存儲(chǔ)量必將會(huì)增加了硬件成本,使系統(tǒng)更加的復(fù)雜,訪問外部存儲(chǔ)器比訪問內(nèi)部存儲(chǔ)器所需的代碼也要長(zhǎng)得多。有效地使用片內(nèi)存儲(chǔ)器、提高存儲(chǔ)器空間的利用率對(duì)開發(fā)者來說十分關(guān)鍵。內(nèi)部處理器、內(nèi)部堆棧、壓縮棧、所有程序變量和所有包含進(jìn)來的庫函數(shù)都將使用數(shù)量有限的內(nèi)部數(shù)據(jù)存儲(chǔ)器RAM。因?yàn)镃語言采用了存儲(chǔ)器的覆蓋技術(shù),可以在程序進(jìn)行連接時(shí),它將那些已經(jīng)被其它程序段釋放了的存儲(chǔ)器空間重新定義給另一個(gè)程序段的變量使用,當(dāng)這個(gè)程序運(yùn)行結(jié)束時(shí)再將這些存儲(chǔ)器釋放以供其它程序段使用。全局變量的作用范圍是整個(gè)程序,因此不能被釋放;靜態(tài)變量由于在函數(shù)的調(diào)用中也不能被釋放;只有局部變量中的動(dòng)態(tài)變量可以被釋放。因此在進(jìn)行程序設(shè)計(jì)時(shí)應(yīng)該盡量的使用局部變量,提高內(nèi)部數(shù)據(jù)存儲(chǔ)器的使用率。5.4.3數(shù)據(jù)類型的選用

嵌入式C語言編程不同于一般C語言編程的一個(gè)顯著特點(diǎn),就是要和程序存儲(chǔ)器資源結(jié)合起來,雖然其提供的數(shù)據(jù)類型十分豐富,但是只有bit和char等數(shù)據(jù)類型是機(jī)器語言直接支持的數(shù)據(jù)類型,用此類數(shù)據(jù)類型的語句所生成的代碼較短;而其它的數(shù)據(jù)類型如整型、浮點(diǎn)型等數(shù)據(jù)要有一定的內(nèi)部程序或內(nèi)部函數(shù)的支持,相對(duì)來說用該類數(shù)據(jù)類型的語句生成的代碼要長(zhǎng)。有些C語言程序表面上看起來十分的簡(jiǎn)單,但在實(shí)際編譯時(shí),生成的代碼卻相當(dāng)長(zhǎng)。因此我們要按照實(shí)際需要,盡量選用占用存儲(chǔ)空間少的數(shù)據(jù)類型,可以大大的減少所生成的代碼長(zhǎng)度。例如在08C中用不同的數(shù)據(jù)類型定義i時(shí),語句:for(i=0;i<10;i++);

經(jīng)編譯后生成的代碼長(zhǎng)度如表5-4所示。

通過表5-3我們知道,不同的數(shù)據(jù)類型所生成的機(jī)器代碼長(zhǎng)度相差很大,相同類型的數(shù)據(jù)類型有無符號(hào)對(duì)機(jī)器代碼長(zhǎng)度也有影響。在程序編譯時(shí)生成機(jī)器代碼長(zhǎng)的數(shù)據(jù)類型的優(yōu)先級(jí)越高,不同的數(shù)據(jù)類型在進(jìn)行程序運(yùn)算時(shí)要轉(zhuǎn)化為高優(yōu)先級(jí)的數(shù)據(jù)類型,相應(yīng)的代碼長(zhǎng)度也會(huì)增長(zhǎng)。因此我們應(yīng)盡可能地使用bit,char等機(jī)器語言直接支持的數(shù)據(jù)類型,無符號(hào)數(shù)的變量應(yīng)聲明為無符號(hào)數(shù),盡可能地減少程序中使用的數(shù)據(jù)類型的種類。表5-4不同數(shù)據(jù)類型占用存儲(chǔ)器字節(jié)數(shù)和代碼長(zhǎng)度對(duì)比數(shù)據(jù)類型unsignedcharcharunsingedintintUnsignedlonglongfloat字節(jié)數(shù)1122444代碼長(zhǎng)度12194343134134508

通過表5-3我們知道,不同的數(shù)據(jù)類型所生成的機(jī)器代碼長(zhǎng)度相差很大,相同類型的數(shù)據(jù)類型有無符號(hào)對(duì)機(jī)器代碼長(zhǎng)度也有影響。在程序編譯時(shí)生成機(jī)器代碼長(zhǎng)的數(shù)據(jù)類型的優(yōu)先級(jí)越高,不同的數(shù)據(jù)類型在進(jìn)行程序運(yùn)算時(shí)要轉(zhuǎn)化為高優(yōu)先級(jí)的數(shù)據(jù)類型,相應(yīng)的代碼長(zhǎng)度也會(huì)增長(zhǎng)。因此我們應(yīng)盡可能地使用bit,char等機(jī)器語言直接支持的數(shù)據(jù)類型,無符號(hào)數(shù)的變量應(yīng)聲明為無符號(hào)數(shù),盡可能地減少程序中使用的數(shù)據(jù)類型的種

5.4.4位操作的其他實(shí)現(xiàn)方法

1.用共用體和位域?qū)崿F(xiàn)位操作綜合共用體和位域等多種數(shù)據(jù)類型,可以實(shí)現(xiàn)很直觀的位操作方法。下面以A口的數(shù)據(jù)寄存器位操作方法來說明。(1)定義//A口數(shù)據(jù)寄存器及位定義typedefunion{unsignedcharByte;struct{intPTA_0:1;/*PortA數(shù)據(jù)Bit0*/intPTA_1:1;/*PortA數(shù)據(jù)Bit1*/intPTA_2:1;/*PortA數(shù)據(jù)Bit2*/intPTA_3:1;/*PortA數(shù)據(jù)Bit3*/intPTA_4:1;/*PortA數(shù)據(jù)Bit4*/intPTA_5:1;/*PortA數(shù)據(jù)Bit5*/

#definePTA6_PTA.Bits.PTA_6#definePTA7_PTA.Bits.PTA_7}Bits;}PTASTR;#define_PTA(*(volatilePTASTR*)0x00)#

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論