《單片機(jī)原理與應(yīng)用-KFIL C項目教程》課件項目二_第1頁
《單片機(jī)原理與應(yīng)用-KFIL C項目教程》課件項目二_第2頁
《單片機(jī)原理與應(yīng)用-KFIL C項目教程》課件項目二_第3頁
《單片機(jī)原理與應(yīng)用-KFIL C項目教程》課件項目二_第4頁
《單片機(jī)原理與應(yīng)用-KFIL C項目教程》課件項目二_第5頁
已閱讀5頁,還剩168頁未讀, 繼續(xù)免費閱讀

下載本文檔

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

文檔簡介

項目二霓虹燈控制系統(tǒng)

2.1項目說明2.2基礎(chǔ)知識2.3項目實施2.4項目評價2.5拓展與提高

2.1項目說明

項目任務(wù)

霓虹燈為美化城市夜景作出了不可磨滅的貢獻(xiàn)。本項目的任務(wù)是利用51單片機(jī)驅(qū)動8個發(fā)光二極管來模擬霓紅燈控制系統(tǒng)。

知識培養(yǎng)目標(biāo)

(1)掌握C51變量的定義以及運算符的應(yīng)用。

(2)掌握延時的實現(xiàn)及其應(yīng)用。

(3)掌握基本程序的設(shè)計方法。

(4)掌握C51庫函數(shù)的使用。

(5)掌握數(shù)組的定義及其應(yīng)用。

(6)掌握字節(jié)尋址與位尋址的應(yīng)用。

能力培養(yǎng)目標(biāo)

(1)培養(yǎng)單片機(jī)控制系統(tǒng)的硬件分析與設(shè)計能力。

(2)培養(yǎng)元器件的計算與選擇能力。

(3)培養(yǎng)C51的程序設(shè)計能力。

(4)培養(yǎng)分析問題與解決問題的能力。

(5)培養(yǎng)團(tuán)隊協(xié)作能力。

2.2基礎(chǔ)知識

2.2.1C51變量

1.變量的定義

在項目一中,我們對C51中的變量進(jìn)行了簡要的說明,每個變量要有一個變量名,變量的數(shù)據(jù)類型不同,占用的存儲單元數(shù)也不一樣。在使用前必須對變量進(jìn)行定義,完整的變量定義形式要指出變量的數(shù)據(jù)類型和存儲類型,以便編譯系統(tǒng)為它分配相應(yīng)的存儲單元。定義的一般形式為

[存儲種類]數(shù)據(jù)類型說明符[存儲類型]變量名l[=初值],變量名2[=初值]…;

1)數(shù)據(jù)類型說明符

在定義變量時,必須通過數(shù)據(jù)類型說明符指明變量的數(shù)據(jù)類型,也就是規(guī)定了變量在存儲器中占用的字節(jié)數(shù)。數(shù)據(jù)類型說明符可以是基本數(shù)據(jù)類型說明符,也可以是組合數(shù)據(jù)類型說明符,還可以是用typedef或#define定義的類型別名。例如:

typedefunsignedintWORD;

#defineBYTEunsignedchar

BYTEA=0x34;

WORDa2=0x3534;

2)變量名

變量名是C51為了區(qū)分不同變量為變量取的名稱。在C51中規(guī)定變量名由字母、數(shù)字和下畫線三種字符組成,且規(guī)定變量名第一個字符必須是字母或下畫線。變量名有普通變量名和指針變量名兩種,它們的區(qū)別是指針變量名前面要帶“*”號。

3)存儲種類

存儲種類是指變量在程序執(zhí)行過程中的作用域。C51變量的存儲種類有四種,分別是自動(auto)、外部(extern)、靜態(tài)(static)與寄存器(register)。

(1)?auto:使用auto定義的變量稱為自動變量,其作用范圍在定義它的函數(shù)體或復(fù)合語句內(nèi)部。當(dāng)定義它的函數(shù)體或復(fù)合語句執(zhí)行時,C51才為該變量分配內(nèi)存空間,結(jié)束時釋放占用的內(nèi)存空間。自動變量一般分配在內(nèi)存的堆??臻g中。當(dāng)定義變量時,如果省略存儲種類,則該變量默認(rèn)為自動(auto)變量。用自動變量能最有效地使用51單片機(jī)內(nèi)存。由于51單片機(jī)訪問片內(nèi)RAM速度最快,通常將函數(shù)體內(nèi)和復(fù)合語句中使用頻繁的變量存放在片內(nèi)RAM中,且定義為自動變量,可有效地利用片內(nèi)有限的RAM資源。

(2)?extern:使用extern定義的變量稱為外部變量。在一個函數(shù)體內(nèi),要使用一個已在該函數(shù)體外或其他程序中定義過的外部變量時,該變量在該函數(shù)體內(nèi)要用extern說明。外部變量被定義后分配固定的內(nèi)存空間,在程序的整個執(zhí)行時間內(nèi)都有效,直到程序結(jié)束才釋放。通常將多個函數(shù)或模塊共享的變量定義為外部變量。外部變量是全局變量,在程序執(zhí)行期間一直占有固定的內(nèi)存空間。當(dāng)片內(nèi)RAM資源緊張時,不建議將外部變量放在片內(nèi)RAM。

(3)?static:使用static定義的變量稱為靜態(tài)變量。它又分為內(nèi)部靜態(tài)變量和外部靜態(tài)變量。在函數(shù)體內(nèi)部定義的靜態(tài)變量為內(nèi)部靜態(tài)變量,它在對應(yīng)的函數(shù)體內(nèi)有效,一直存在,但在函數(shù)體外不可見。這樣不僅使變量在定義它的函數(shù)體外被保護(hù),還可以實現(xiàn)變量離開函數(shù)時值不被改變。外部靜態(tài)變量是在函數(shù)外部定義的靜態(tài)變量,它在程序中一直存在,但在定義的范圍之外是不可見的。如在多文件或多模塊處理時,外部靜態(tài)變量只在文件內(nèi)部或模塊內(nèi)部有效。

(4)?register:使用register定義的變量稱為寄存器變量。它定義的變量存放在CPU內(nèi)部的寄存器中,處理速度快,但數(shù)目少。C51編譯器編譯時能自動識別程序中使用頻率最高的變量,并自動將其作為寄存器變量,用戶無須專門聲明。

4)存儲類型

存儲類型是用于指明變量存放在單片機(jī)的哪個存儲器中。存儲類型與存儲種類完全不同,存儲類型指明該變量在單片機(jī)內(nèi)所處的存儲空間。如果在變量定義時省略了存儲類型標(biāo)識符,C51編譯器會選擇默認(rèn)的存儲類型。默認(rèn)的存儲類型由SMALL、COMPACT和LARGE存儲模式來決定。C51編譯器能識別的存儲類型如表2-1所示。表2-1C51編譯器能識別的存儲類型

(1)?data區(qū):對data區(qū)的訪問是最快的,所以應(yīng)該把使用頻率高的變量放在data區(qū),由于空間有限,必須有效使用data區(qū),data區(qū)除了包含程序變量外,還包含了堆棧和寄存器組。例如:

unsignedchardatasystem_status=0;

floatdataoutp_value;

unsigedchardatanew_var;在SMALL存儲模式下,當(dāng)未說明存儲類型時,變量默認(rèn)被定位在data區(qū)。標(biāo)準(zhǔn)變量和用戶自定義變量都可以存儲在data區(qū),只要不超過data區(qū)的范圍。因為C51使用默認(rèn)的寄存器組傳遞參數(shù),至少失去了8B。另外要定義足夠大的堆??臻g,當(dāng)內(nèi)部堆棧溢出時,程序會產(chǎn)生莫名其妙的錯誤,實際原因是51系列單片機(jī)沒有硬件報錯機(jī)制,堆棧溢出只能以這種方式表示出來。

(2)?bdata區(qū):在片內(nèi)RAM的位尋址區(qū)(bdata區(qū))定義變量,這個變量就可進(jìn)行位尋址,并且聲明位變量。這對狀態(tài)寄存器來說十分有用,因為這樣可以單獨使用變量的某一位,而不一定要用位變量名引用位變量。下面是一些在bdata區(qū)中聲明變量和使用位變量的例子。

unsignedcharbdatastatus_byte;

unsignedintbdatastatus_word;

sbitstat_flag=status_byte^4;

if(status_word^15)

{

stat_flag=1;

}

編譯器不允許在bdata區(qū)中定義float和double類型的變量。

(3)?idata區(qū):idata區(qū)可以存放使用比較頻繁的變量,使用寄存器作為指針進(jìn)行尋址。在寄存器中設(shè)置8位地址進(jìn)行間接尋址,與外部存儲器尋址比較,它的指令執(zhí)行周期和代碼長度都比較短。例如:

unsignedcharidatasystem_status=0;

floatidataoutp_value;

(4)?pdata和xdata區(qū):在這兩個區(qū)聲明變量和在其他區(qū)的語法是一樣的,pdata區(qū)只有256B,而xdata區(qū)可達(dá)64KB。例如:

unsignedcharxdatasystem_status=0;

floatpdataoutp_value;

對pdata和xdata的操作是相似的,對pdata區(qū)尋址比對xdata區(qū)尋址要快,因為對pdata區(qū)尋址只需要裝入8位地址,而對xdata區(qū)尋址需裝入16位地址。所以盡量把外部數(shù)據(jù)存儲在pdata區(qū)中,匯編語言中對pdata和xdata尋址要使用MOVX指令,需要2個機(jī)器周期。

(5)?code區(qū):code區(qū)即51單片機(jī)的程序存儲器,所以存入的數(shù)據(jù)是不可以改變的,即不可重寫。程序存儲器除了存放用戶編寫的程序代碼外,還可存放數(shù)據(jù)表、跳轉(zhuǎn)向量和狀態(tài)表,對code區(qū)的訪問和對xdata區(qū)的訪問的時間是一樣的,代碼區(qū)中的變量必須在編譯時初始化,否則就得不到想要的值,下面是代碼區(qū)的聲明例子。

unsignedintcodeunit_id[2]={0x1234,0x89ab};

unsignedcharcodeuchar_data[]

={0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07};

2.變量的存儲模式

變量的存儲模式確定了變量在內(nèi)存中的地址空間,C51編譯器允許采用小編譯模式SMALL、緊湊編譯模式COMPACT、大編譯模式LARGE三種存儲模式。SMALL模式下,變量存放在51單片機(jī)的內(nèi)部RAM中;COMPACT和LARGE模式下,變量存放在51單片機(jī)的外部RAM中。同樣一個函數(shù)的存儲模式確定了函數(shù)的參數(shù)和局部變量在內(nèi)存中的地址空間,SMALL模式下,函數(shù)的參數(shù)和局部變量存放在51單片機(jī)的內(nèi)部RAM中;COMPACT和LARGE模式下,函數(shù)的參數(shù)和局部變量存放在51單片機(jī)的外部RAM中。例如:

#pragmasmall //存儲模式為SMALL

unsignedchardatai,j,k;

intxdatam,n;

unsignedchara=0x99,b=0x88;

unsignedcharxdataram[128];

unsignedintfunc1(inti,intj)large

{

return(i+j);

}

unsignedintfunc2(inti,intj)

{

return(i-j);

}由于是SMALL模式,故a、b、i、j、k都存儲在片內(nèi)數(shù)據(jù)存儲器中。不同的存儲類型訪問速度是不一樣的,如:

unsignedchardatavar1;

unsignedcharpdatavar1;

unsignedcharxdatavar1;

在SMALL模式下,var1被定位在DATA區(qū),經(jīng)C51編譯器編譯后,采用內(nèi)部RAM直接尋址方式訪問速度最快;在COMPACT模式下,var1被定位在pdata區(qū),經(jīng)C51編譯器編譯后,采用外部RAM間接尋址方式訪問速度較快;在LARGE模式下,var1被定位在xdata區(qū),經(jīng)C51編譯器編譯后,采用外部RAM間接尋址方式訪問速度最慢。為了提高系統(tǒng)運行速度,建議在編寫源程序時,把存儲模式設(shè)定為SMALL,再在程序中對xdata、pdata和idata等類型變量進(jìn)行專門聲明。

定義變量時也可以省略存儲類型,省略時C51編譯器將按存儲模式選擇存儲類型。單擊圖2-1中所圈圖標(biāo)或選擇“Project\Optionsfor、Target'Target1'”,出現(xiàn)如圖2-1所示窗口,單擊第二個選項“Target”,“MemoryModel”用于選擇數(shù)據(jù)存儲模式,“CodeRomSize”用于選擇程序存儲模式,選擇好后,單擊“確定”按鈕。圖2-1C51編譯器存儲模式

3.特殊功能寄存器變量

51系列單片機(jī)片內(nèi)有許多特殊功能寄存器,通過這些特殊功能寄存器可以管理與控制51系列單片機(jī)的定時器、計數(shù)器、串口、I/O及其他功能部件,每一個特殊功能寄存器都占據(jù)片內(nèi)RAM中的一個或兩個字節(jié)。在C51中,允許用戶對這些特殊功能寄存器進(jìn)行訪問,訪問時需通過sfr或sfr16類型說明符進(jìn)行定義,定義時需指明它們在片內(nèi)RAM中對應(yīng)單元的地址。一般形式為

sfr/sfr16特殊功能寄存器名稱=字節(jié)地址;

sfr用于對51系列單片機(jī)中單字節(jié)(8位)的特殊功能寄存器進(jìn)行定義,sfr16用于對雙字節(jié)(16位)特殊功能寄存器(DPTR)進(jìn)行定義。特殊功能寄存器名一般用大寫字母表示,地址一般采用直接地址形式,如:

sfrPSW=0xD0; //定義程序狀態(tài)字PSW的地址為D0H

sfrTMOD=0x89; //定義定時/計數(shù)器方式寄存器TMOD的地址為89H

sfrP1=0x90; //定義P1端口的地址為90H

4.位變量

在C51中,允許用戶通過位類型符定義位變量。關(guān)鍵字有bit和sbit兩個。

bit用于定義一般的可進(jìn)行位處理的位變量,位地址由編譯器在編譯時分配,位地址位于片內(nèi)RAM中的20H~27H單元。它的一般形式為

bit位變量名;

在格式中可以加上各種修飾,但嚴(yán)格來說只能是bdata,如:

bitbdataa1;

而bitpdataa3是錯誤的。

sbit用于定義可位尋址特殊功能寄存器中的某一位,定義時需指明其位地址,可以是直接位地址、特殊功能寄存器字節(jié)地址值帶位號或特殊功能寄存器名帶位號。一般形式為

sbit位地址名=位地址;

如位地址為直接位地址,其取值范圍為0x7F~0xFF中可位尋址的位地址;如采用特殊功能寄存器名稱帶位號時,需在定義位地址之前用sfr/sfr16對特殊功能寄存器進(jìn)行定義,且字節(jié)地址與位號之間、特殊功能寄存器名稱與位號之間一般用“^”作間隔。如:

sbitled1=0x80; //直接位地址

sbitled1=0x80^0; //特殊功能寄存器字節(jié)地址值帶位號 sbitled1=P0^0; //特殊功能寄存器名稱帶位號

這三條指令都可以將P0口最低位的位地址定義為led1。

5.局部變量與全局變量

局部變量是指在在函數(shù)內(nèi)部定義的變量;全局變量是指在函數(shù)外部定義的變量,也稱外部變量。全局變量和局部變量體現(xiàn)了變量能被有效引用的范圍,即變量的作用域。

它們的區(qū)別:局部變量只在當(dāng)前函數(shù)中有效,即當(dāng)該函數(shù)被調(diào)用時,為函數(shù)內(nèi)定義的變量分配存儲單元,在該函數(shù)執(zhí)行完后,在它內(nèi)部定義的所有變量將自動銷毀,分配的存儲單元將自動釋放,當(dāng)下次再被調(diào)用時,編譯器重新為其分配新的存儲單元;而編譯器為全局變量分配存儲單元后,它將永遠(yuǎn)占據(jù)這些存儲單元。

1)局部變量的作用域注意事項

(1)主函數(shù)中定義的變量也只能在主函數(shù)中使用,不能在其他函數(shù)中使用;同時,主函數(shù)中也不能使用其他函數(shù)中定義的變量,因為主函數(shù)也是一個函數(shù),它與其他函數(shù)是平行關(guān)系。這一點與其他語言不同,應(yīng)予以注意。

(2)形參變量是屬于被調(diào)函數(shù)的局部變量,實參變量是屬于主調(diào)函數(shù)的局部變量。

(3)允許在不同的函數(shù)中使用相同的變量名,它們代表不同的對象,分配不同的單元,相互間不干擾,也不會發(fā)生混淆。

(4)在復(fù)合語句中也可定義變量,其作用域只在復(fù)合語句范圍內(nèi)。

2)全局變量的作用域注意事項

(1)全局變量可以被本文件中所有函數(shù)共用,它的作用范圍是從定義變量的位置開始到本源文件結(jié)束。

(2)全局變量定義在使用它的函數(shù)之后,當(dāng)在該函數(shù)中使用全局變量時,應(yīng)作全局變量說明,只有在函數(shù)內(nèi)經(jīng)過說明的全局變量才能使用,全局變量的說明符為extern。但在一個函數(shù)之前定義的全局變量,在該函數(shù)內(nèi)使用時可不再加以說明。

(3)在同一源文件中,允許全局變量和局部變量同名。在局部變量的作用域內(nèi),全局變量不起作用。

(4)外部變量可加強(qiáng)函數(shù)模塊之間的數(shù)據(jù)聯(lián)系,但是又使函數(shù)要依賴這些變量,因而使得函數(shù)的獨立性降低,從模塊化程序設(shè)計的觀點來看這是不利的,因此在不必要時盡量不要使用全局變量。我們知道單片機(jī)內(nèi)部的數(shù)據(jù)存儲器容量有限,51系列單片機(jī)供用戶使用的只有128B,如果定義unsignedint類型變量,一個變量是兩個字節(jié),最多只能定義64個;定義unsignedchar類型變量,一個變量為一個字節(jié),最多也只能定義128個。當(dāng)源程序較長,控制功能較復(fù)雜時,會遇到內(nèi)存不夠用的情況,因此用C51編程,雖然降低了對單片機(jī)硬件知識的要求,但也要在剛開始學(xué)習(xí)時養(yǎng)成良好的思考習(xí)慣,牢記單片機(jī)內(nèi)部的可用資源,從節(jié)省片內(nèi)RAM的角度出發(fā),能用局部變量的就不用全局變量,能用unsignedchar類型的就不用unsignedint類型。2.2.2C51的運算符與表達(dá)式

C語言中運算符和表達(dá)式數(shù)量之多,應(yīng)用之靈活,是其他高級語言所沒有的。C51中常用運算符主要有算術(shù)運算符、關(guān)系運算符、邏輯運算符、位運算符、賦值運算符。這些運算符按其所在表達(dá)式中參與運算的操作數(shù)的個數(shù)可分為單目運算符、雙目運算符和三目運算符。

(1)算術(shù)運算符:用于各類數(shù)值運算,包括加(+)、減(-)、乘(*)、除(/運算)、求余(%)、自增(++)、自減(--),共7種運算。

(2)關(guān)系運算符:用于各類比較運算,包括大于(>)、小于(<)、等于(==)、大于等于(>=)、小于等于(<=)和不等于(!=),共6種運算。

(3)邏輯運算符:用于各類邏輯運算,包括與(&&)、或(

||)、非(!),共3種運算。

(4)位運算符:參與運算的操作數(shù),按二進(jìn)制位進(jìn)行運算。包括按位與(&)、按位或(|)、按位非(~)、按位異或(^)、左移(<<)、右移(>>),共6種運算。

(5)賦值運算符:用于賦值運算,分為簡單賦值(=)、復(fù)合算術(shù)賦值(+=,-=,*=,/=,%=)和復(fù)合位運算賦值(&=,|=,^=,>>=,<<=)三類,共11種運算。

C51運算符的優(yōu)先級是不相同的。一個表達(dá)式中包含若干運算符時,先進(jìn)行高優(yōu)先級、后進(jìn)行低優(yōu)先級的運算,如y=a+b*c,則先計算b*c的值,然后再與a的值相加。而當(dāng)一個運算量兩側(cè)的運算符優(yōu)先級相同時,則按運算符的結(jié)合性所規(guī)定的結(jié)合方向處理。運算符的結(jié)合性一般有兩種,即左結(jié)合性(自左至右)和右結(jié)合性(自右至左)。例如算術(shù)運算符的結(jié)合性是自左至右,即先左后右。如有表達(dá)式x-y+z則y應(yīng)先與“-”號結(jié)合,執(zhí)行x-y運算,然后再執(zhí)行+z的運算。這種自左至右的結(jié)合方向就稱為左結(jié)合性,而自右至左的結(jié)合方向稱為右結(jié)合性。最典型的右結(jié)合性運算符是賦值運算符,如x=y=z,由于“=”的右結(jié)合性,應(yīng)先執(zhí)行y=z,再執(zhí)行x=(y=z)運算。用各運算符和括號將運算對象或操作數(shù)(常量、變量和函數(shù)等)連接起來的、符合C語法規(guī)則的式子就稱為表達(dá)式。每個表達(dá)式都有一個值和類型,求表達(dá)式的值時,按運算符的優(yōu)先級和結(jié)合性所規(guī)定的順序進(jìn)行,當(dāng)一個操作數(shù)兩側(cè)運算符的優(yōu)先級相同時,再按C語言規(guī)定的結(jié)合方向(結(jié)合性)進(jìn)行。

1.算術(shù)運算符和算術(shù)表達(dá)式

加法運算符+雙目運算符,應(yīng)有兩個量參與加法運算,具有左結(jié)合性。

減法運算符-雙目運算符,具有左結(jié)合性。乘法運算符*雙目運算符,具有左結(jié)合性。

除法運算符/雙目運算符,具有左結(jié)合性。當(dāng)參與運算的操作數(shù)為整型量時,結(jié)果也為整型量,舍去小數(shù);操作數(shù)中有一個是實型,則結(jié)果為實型。例如:10/4=2;10/4.0=2.5。

求余運算符%雙目運算符,具有左結(jié)合性,要求參與運算的量均為整型,求余運算的結(jié)果等于兩數(shù)相除后的余數(shù)。如10%3=1。

自增運算符++單目運算符,使變量的值自增1。

自減運算符--??單目運算符,使變量的值自減1。對某變量x進(jìn)行自增、自減運算時,有++x、--x、x--、x++四種情況,其中++x、--x是先使x加1或減1,然后使用修改后x的值,即先修改后使用;而x--、x++是先使用x的值,再對x加1或減1,即先使用后修改。

若x的初值為3,則執(zhí)行y=++x后,x為4、y為4;若執(zhí)行y=--x后,x為2、y為2;如執(zhí)行y=x++后,x為4、y為3;執(zhí)行y=x--后,x為2、y為3。

自增、自減是單目運算符,且運算對象只能是變量,不能是常量或表達(dá)式,如++6,(x+a)++都是錯誤的。

2.賦值運算符與賦值表達(dá)式

“=”是賦值運算符,作用是將一個表達(dá)式的值賦給一個變量。由“=”連接的式子稱為賦值表達(dá)式。一般形式為:

變量=表達(dá)式

例如:

x=m+n

y=sin(a)+sin(b)

賦值表達(dá)式的功能是計算表達(dá)式的值并將值賦予左邊的變量。賦值運算符具有右結(jié)合性。如x=y=z=22,可理解為x=(y=(z=22))。在C51中,把“=”定義為運算符,從而構(gòu)成賦值表達(dá)式。凡是表達(dá)式可以出現(xiàn)的地方,均可出現(xiàn)賦值表達(dá)式。例如,式子y=(m=4)+(n=5)是合法的。它的意義是把4賦予m,5賦予n,再把m、n相加,和賦予y,故y應(yīng)等于9。

在C51中也可以組成賦值語句,任何表達(dá)式在其末尾加上分號就構(gòu)成為語句。如“x=8;”、“a=b=c=5;”都是賦值語句。在項目一中我們已使用過了。

如果賦值運算符兩邊的數(shù)據(jù)類型不相同,系統(tǒng)將自動進(jìn)行類型轉(zhuǎn)換,即把賦值號右邊的類型換成左邊的類型。具體規(guī)定如下:

(1)實型量賦予整型變量時,舍去小數(shù)部分。如x為unsignedchar型,在執(zhí)行x=4.56時,只將整數(shù)部分賦給x,即x=4。

(2)整型量賦予實型變量時,數(shù)值不變,但將以浮點形式存放,即增加小數(shù)部分(但小數(shù)部分值為0)。

(3)字符型量賦予整型變量,由于字符型量為一個字節(jié),而整型量為兩個字節(jié),故將字符型的值存放到整型量的低八位中,高八位為0。

(4)整型量賦予字符型變量,只把低八位賦予字符型變量。

3.關(guān)系運算符和關(guān)系表達(dá)式

關(guān)系運算是雙目運算,用于比較兩個操作數(shù)的大小。C51提供了6種關(guān)系運算符,即小于(<)、小于等于(<=)、大于(>)、大于等于(>=)、等于(==)、不等于(!=)。這6個關(guān)系運算符分為兩個優(yōu)先級,前四種高于后兩種。

將兩個表達(dá)式(可以是算術(shù)表達(dá)式、賦值表達(dá)式、關(guān)系表達(dá)式等)用關(guān)系運算符連接起來就構(gòu)成了關(guān)系表達(dá)式。關(guān)系表達(dá)式的值是邏輯值“真”或“假”。但是C51中沒有邏輯型變量和常量,也沒有專門的邏輯值,故以“非0”代表“真”,以“0”代表“假”。當(dāng)關(guān)系表達(dá)式成立時,表達(dá)式的值為真,否則表達(dá)式的值為假。如5>3,則該表達(dá)式為真,即該關(guān)系表達(dá)式的值為1;8==12,該表達(dá)式的值為0,即為假。算術(shù)、關(guān)系、賦值這三類運算符的優(yōu)先級是由高到低,例如a=b<c,該表達(dá)式等效于a=(b<c)。下面所示關(guān)系表達(dá)式也是合法的:

a+b>c+d

(a=3)>(b=5)

(a>b)>(b>c)

如a=3、b=4、c=5、d=6,則這三個關(guān)系表達(dá)式的值分別為0、1、0。

注意:不要誤將關(guān)系運算符“==”寫作賦值運算符“=”。

4.邏輯運算符和邏輯表達(dá)式

邏輯運算用于判斷運算對象的邏輯關(guān)系,運算對象為關(guān)系表達(dá)式或邏輯量。C51提供了邏輯非(!)、邏輯與(&&)、邏輯或(||)三種邏輯運算。

通過邏輯運算符將邏輯量或關(guān)系表達(dá)式連接起來,可以構(gòu)成邏輯表達(dá)式。邏輯表達(dá)式在程序中一般用于控制語句(if、for、while、do-while),對某些條件作出判斷,根據(jù)條件的成立(真)與不成立(假)決定程序的流程。參加邏輯運算的對象將“非0”視為“真”,“0”視為“假”;邏輯運算的結(jié)果為邏輯意義上的“真”或“假”,以數(shù)字1表示“真”,以數(shù)字0表示“假”。

(1)邏輯非。邏輯非的一般形式:

!表達(dá)式

功能:單目運算符,其結(jié)果為表達(dá)式邏輯值的“反”。若表達(dá)式值為0,則“!表達(dá)式”值為1;若表達(dá)式值為非0,則“!表達(dá)式”值為0。

例如:x=3,由于3是非0,因此x為真,所以!x=0,即為假。

(2)邏輯與。邏輯與的一般形式:

表達(dá)式1&&表達(dá)式2功能:若參加運算的兩個表達(dá)式的值均為非0,則結(jié)果為1;若有任一表達(dá)式的值為0,則結(jié)果為0。

例如:若x=3、y=3、z=0,則x&&y=1,x&&z=0。

(3)邏輯或。邏輯或的一般形式:

表達(dá)式1

||

表達(dá)式2

功能:若參加運算的兩個表達(dá)式的值均為0,則結(jié)果為0;若有任一表達(dá)式的值為1,則結(jié)果為1。

例如:若x=3、y=3、z=0,則x||y=1,x||z=1。三個邏輯運算符中,邏輯非(!)是單目運算符,優(yōu)先級最高,邏輯與(&&)的優(yōu)先級比邏輯或(||)高。邏輯運算常用于分支或循環(huán)結(jié)構(gòu)的條件中。如:

x&&y=1

x||z=1

!y=0

邏輯與的運算規(guī)則:全為真時,相與的結(jié)果為真;邏輯或的運算規(guī)則:有任一為真時,相或的結(jié)果為真;邏輯非的運算規(guī)則:真的非為假,假的非為真。它們與數(shù)字電路中的邏輯運算完全一致。

5.位運算符和位表達(dá)式

在計算機(jī)中,1位0或1是能夠操作的最小數(shù)據(jù)單位,針對1位0和1的操作就稱為位運算。一般的位運算是用來控制硬件的,或做數(shù)據(jù)變換使用,但是靈活的位運算能夠有效地提高程式運行的效率。C語言提供了位運算的功能,這使得C語言也能像匯編語言一樣用來控制硬件。

C51中的位運算主要有按位與(&)、按位或(|)、按位異或(^)、按位非(~)、左移(<<)、右移(>>)6種。

1)按位與

按位與運算符“&”是雙目運算符,其功能是將參與運算的兩個操作數(shù)各對應(yīng)的二進(jìn)位相與。只有對應(yīng)的兩個二進(jìn)位均為1時,結(jié)果位才為1;否則為0。如:

11001001&00001111=00001001

按位與的應(yīng)用:

(1)清零某些位。例如,a為unsignedchar型,將a的最高位保留,其余位清0,可通過表達(dá)式a&0x80來實現(xiàn)。

(2)取某操作數(shù)中指定位。例如,x為unsignedchar型,要取出其低四位,即使其高半字節(jié)為零,低半字節(jié)保持,可用表達(dá)式x&0x0f來實現(xiàn)。

2)按位或

按位或運算符“|”是雙目運算符,其功能是將參與運算的兩個操作數(shù)各對應(yīng)的二進(jìn)位相或。只要對應(yīng)的兩個二進(jìn)位有一個為1時,結(jié)果位就為1。如:

10011101|11000111=11011111

按位或的應(yīng)用:按位或運算常用來將操作數(shù)的某些位置為1。例如:x為unsignedchar型,如果是使x的最高位為1,其余位不變,可通過表過式x|0x80來實現(xiàn)。

3)按位異或

按位異或運算符“^”是雙目運算符,其功能是將參與運算的兩個操作數(shù)各對應(yīng)的二進(jìn)位相異或。當(dāng)兩個對應(yīng)的二進(jìn)位不相同時,結(jié)果為1;相同時,結(jié)果為0。如:

10110101^00111011=10001110

按位異或的應(yīng)用:

(1)交換兩個值,不用臨時變量。例如:a=3,二進(jìn)制數(shù)為011B;b=4,二進(jìn)制數(shù)為100B。若欲將a和b互換,可用以下賦值語句來實現(xiàn):

a=a^b;

b=b^a;

a=a^b;

(2)取反。某數(shù)與0xFF按位異或時,相當(dāng)于對該數(shù)取反。例如,0x88^0xFF=0x77。

4)按位非

按位非運算符“~”為單目運算符,具有右結(jié)合性。其功能是對參與運算的操作數(shù)的各二進(jìn)位按位求反。如:

~11000011=00111100

按位非的應(yīng)用:按位非主要用于求取某一二進(jìn)制數(shù)的反碼,或和其他運算結(jié)合使用。

5)左移

左移運算符“<<”是雙目運算符,其功能是把“<<”左邊的操作數(shù)的各二進(jìn)位全部左移若干位,由“<<”右邊的操作數(shù)指定移位的位數(shù),移位時將溢出的高位丟棄,低位補0。如:a=11000011,執(zhí)行a<<2=00001100。

左移的應(yīng)用:左移1位相當(dāng)于該數(shù)乘以2,左移2位相當(dāng)于該數(shù)乘以4,左移n位相當(dāng)于該數(shù)乘以2n。但此結(jié)論只適用于該數(shù)左移時被溢出舍棄的高位中不包含1的情況。例如:a<<4指把a的每個二進(jìn)位向左移動4位。如a=00000011(十進(jìn)制3),左移4位后為00110000(十進(jìn)制48)。

6)右移

右移運算符“>>”是雙目運算符,其功能是把“>>”左邊的操作數(shù)的各二進(jìn)位全部右移若干位,“>>”右邊的操作數(shù)指定移動的位數(shù)。移到右端的低位被舍棄,對于無符號數(shù),高位補0;對于有符號數(shù),算術(shù)移位是在左邊用符號位填補,邏輯移位則是在左邊補0。例如:a=00110011,執(zhí)行a>>2=00001100。

右移的應(yīng)用:右移1位相當(dāng)于該數(shù)除2,右移2位相當(dāng)于該數(shù)除4,右移n位相當(dāng)于該數(shù)除2n。但此結(jié)論只適用于該數(shù)右移時被舍棄的低位中不包含1的情況。

設(shè)a為unsigneduchar類型,且a=12,表示把000001100右移2位為00000011(十進(jìn)制3),即a>>2=3。對于左邊移出的空位補入0。

6.復(fù)合賦值運算符

在賦值運算符“=”的前面加上其他運算符,組成復(fù)合賦值運算符。C51中支持的復(fù)合賦值運算符有:+=加法賦值;-=減法賦值;*=乘法賦值;/=除法賦值;%=取余賦值;&=按位與賦值;|=按位或賦值;^=按位異或賦值;~=邏輯非賦值;>>=右移賦值;<<=左移賦值。

復(fù)合賦值運算的一般形式為

變量復(fù)合運算賦值符表達(dá)式

相當(dāng)于

變量=變量運算符表達(dá)式它的處理過程:先把變量與其后的表達(dá)式進(jìn)行某種運算,然后將運算的結(jié)果賦給前面的變量。其實這是C51中簡化程序的一種方法,大多數(shù)二目運算都可以用復(fù)合賦值運算符簡化來表示。例如:a+=6相當(dāng)于a=a+6;a%=5相當(dāng)于a=a%5;b&0x55相當(dāng)于b=b&0x55;x>>=2相當(dāng)于x=x>>2。2.2.3C51語句

1.?C51語句概述

C51的函數(shù)主要是由語句組成,程序的功能也是通過執(zhí)行語句來實現(xiàn)的。C51中的語句可分為表達(dá)式語句、函數(shù)調(diào)用語句、復(fù)合語句、控制語句、空語句五類。

1)表達(dá)式語句

表達(dá)式語句由表達(dá)式加上分號“;”組成。其一般形式為

表達(dá)式;

執(zhí)行表達(dá)式語句就是計算表達(dá)式的值。例如:x=y+z;是利用賦值語句將y+z保存到x變量中;又如y+z;是合法的加法表達(dá)式,但計算結(jié)果不能保留,無實際意義;i++;是自增語句,i值增1。

a=3與a=3;是不一樣的。不加分號,為賦值表達(dá)式,不是語句;加分號后才是賦值語句。對賦值語句的使用要注意以下幾點:

(1)由于在賦值符“=”右邊的表達(dá)式也可以又是一個賦值表達(dá)式,因此形式為

變量=(變量=表達(dá)式);

是成立的,形成嵌套的情形。其展開之后的一般形式為

變量=變量=…=表達(dá)式;

例如:a=b=c=d=e=5;按照賦值運算符的右結(jié)合性,實際上等效于:e=5;d=e;c=d;b=c;a=b;

(2)在變量定義中給變量賦初值是變量定義的一部分,賦初值后的變量與其后的其他同類變量之間仍必須用逗號間隔,而賦值語句則必須用分號結(jié)尾。

(3)在變量定義中,不允許連續(xù)給多個變量賦初值。如inta=b=c=5是錯誤的,必須寫為inta=5,b=5,c=5;而賦值語句允許連續(xù)賦值。

(4)注意賦值表達(dá)式和賦值語句的區(qū)別。賦值表達(dá)式是一種表達(dá)式,它可以出現(xiàn)在任何允許表達(dá)式出現(xiàn)的地方,而賦值語句則不能。如:

if((x=y+5)>0)

z=x;

此語句是合法的,語句的功能是,若表達(dá)式x=y+5大于0,則z=x。

而下述語句是非法的:

if((x=y+5;)>0)

z=x;

因為=y+5;是語句,不能出現(xiàn)在表達(dá)式中。

2)函數(shù)調(diào)用語句

將函數(shù)調(diào)用作為一個語句。函數(shù)調(diào)用的一般形式為

函數(shù)名(實際參數(shù)表);

執(zhí)行函數(shù)調(diào)用語句就是調(diào)用函數(shù)體并把實際參數(shù)賦予函數(shù)定義中的形式參數(shù),然后執(zhí)行被調(diào)函數(shù)體中的語句,求取函數(shù)值;調(diào)用無參函數(shù)時,無需實際參數(shù)表。

3)復(fù)合語句

把多個語句用括號“{}”括起來組成一條復(fù)合語句。在程序中應(yīng)把復(fù)合語句看成是單條語句,而不是多條語句,例如:

{

x=y+z;

a=b+c;

z=x/c;

}

復(fù)合語句內(nèi)的各條語句都必須以分號“;”結(jié)尾,在括號“}”后不能加分號。

4)控制語句

控制語句用于控制程序的流程,實現(xiàn)程序的各種結(jié)構(gòu)方式??刂普Z句共有九種,它們是:

if()~else 條件語句

switch 多分支選擇語句

for()~ 循環(huán)語句

while()~ 循環(huán)語句

do~while() 循環(huán)語句

continue 結(jié)束本次循環(huán)語句

break 中止執(zhí)行swith或循環(huán)語句

goto 轉(zhuǎn)向語句

return 函數(shù)的返回語句括號“()”中表示條件,“~”表示內(nèi)部語句。如:

if(a>45)

{

b=x*y;

c=x/y;

}

else

b=c=0;

5)空語句

只有分號“;”組成的語句才稱為空語句??照Z句是什么也不執(zhí)行的語句,在程序中空語句可用來作循環(huán)體或內(nèi)部語句。如“while(1);”就是用空語句作循環(huán)體,其作用是無數(shù)遍執(zhí)行空語句。

2.?for語句

for語句是C語言所提供的功能強(qiáng)大且使用廣泛的一種循環(huán)語句。其一般形式為

for(表達(dá)式1;表達(dá)式2;表達(dá)式3)

{

循環(huán)體語句;

}

表達(dá)式1用于給循環(huán)變量賦初值,一般為賦值表達(dá)式,也允許在for語句外給循環(huán)變量賦初值;表達(dá)式2是循環(huán)結(jié)束條件,可以是關(guān)系表達(dá)式或邏輯表達(dá)式;表達(dá)式3用于修改循環(huán)變量的值,一般是賦值語句;循環(huán)體語句可以為空語句。表達(dá)式1~3都可以是逗號表達(dá)式,即每個表達(dá)式都可由多個表達(dá)式組成;三個表達(dá)式都是任選項,都可以省略,但分號不能省略。for語句的執(zhí)行過程是:

第一步:計算表達(dá)式1。

第二步:計算表達(dá)式2,若值為真(非0),則執(zhí)行循環(huán)體語句,然后執(zhí)行第三步;否則結(jié)束for語句,不再執(zhí)行循環(huán)體和第三步,接著執(zhí)行for的下一條語句。圖2-2for語句執(zhí)行流程圖

第三步:計算表達(dá)式3。

第四步:轉(zhuǎn)至第二步重復(fù)執(zhí)行。

在整個for語句的執(zhí)行過程中,表達(dá)式1只計算一次,表達(dá)式2和表達(dá)式3則可能計算多次;循環(huán)體可能執(zhí)行多次,也可能一次都不執(zhí)行;for語句的執(zhí)行過程如圖2-2所示。圖2-2for語句執(zhí)行流程圖

3.?do-while語句

do-while語句也是C51中常用的循環(huán)語句,它的一般形式為

do

{

語句;

}

while(表達(dá)式);

說明:“表達(dá)式”為循環(huán)條件,“語句”為循環(huán)體。

do-while語句的執(zhí)行過程是先執(zhí)行循環(huán)體語句一次,再判別表達(dá)式的值,若為真(非0),則繼續(xù)執(zhí)行循環(huán)體語句;若為假,則終止循環(huán)。流程圖如圖2-3所示。

do-while語句和while語句的區(qū)別在于do-while是先執(zhí)行后判斷,因此do-while至少要執(zhí)行一次循環(huán)體;而while是先判斷后執(zhí)行,如果條件不滿足,則一次循環(huán)體語句也不執(zhí)行。while語句和do-while語句可以相互替換。圖2-3do-while語句的執(zhí)行流程圖例1用do-while語句與while語句分別執(zhí)行2000次空語句。

解:源程序1

voiddelay()

{

unsignedinti=0;

do

i++;

while(i<2000);

}

源程序2

voiddelay()

{

unsignedinti=0;

while(i<2000)

i++;

}2.2.4C51函數(shù)

1.C51函數(shù)的分類

C51源程序是由一個或多個函數(shù)組成的,函數(shù)是C51源程序的基本模塊,通過對函數(shù)的調(diào)用實現(xiàn)特定的功能。C51語言不僅提供了許多庫函數(shù),還允許用戶自己定義函數(shù),用戶可把自己的算法編成一個個相對獨立的函數(shù)模塊,然后用調(diào)用的方法來使用這些函數(shù)。由于采用了函數(shù)結(jié)構(gòu),C51易于實現(xiàn)結(jié)構(gòu)化程序設(shè)計,使程序的層次結(jié)構(gòu)更為清晰,便于程序的編寫、閱讀和調(diào)試。在C51中可以從不同的角度對函數(shù)進(jìn)行分類。

1)從函數(shù)定義的角度分類

(1)庫函數(shù):由C51系統(tǒng)提供,用戶無須定義,也不必在程序中作類型說明,只需在程序前包含該函數(shù)原型的頭文件就可以在程序中直接調(diào)用。

(2)用戶定義函數(shù):由用戶按需要寫的函數(shù)。對于用戶自定義函數(shù),不僅要在程序中定義函數(shù)本身,而且在主調(diào)函數(shù)模塊中還必須對該被調(diào)函數(shù)進(jìn)行類型說明,然后才能使用。

2)從有無返回值的角度分類

(1)有返回值函數(shù):此類函數(shù)被調(diào)用后將向調(diào)用者返回一個執(zhí)行結(jié)果,稱為函數(shù)返回值,如數(shù)學(xué)函數(shù)即屬于此類函數(shù)。由用戶定義的有返回值函數(shù)必須在函數(shù)定義和函數(shù)說明中明確返回值的類型。

(2)無返回值函數(shù):此類函數(shù)用于完成某項特定的處理任務(wù),執(zhí)行完成后不向調(diào)用者返回函數(shù)值。由于函數(shù)無須返回值,因此用戶在定義此類函數(shù)時可指定其為空類型,空類型的說明符為void。

3)從主調(diào)函數(shù)和被調(diào)函數(shù)之間數(shù)據(jù)傳送的角度分類

(1)無參函數(shù):函數(shù)定義、函數(shù)說明及函數(shù)調(diào)用中均不帶參數(shù)。主調(diào)函數(shù)和被調(diào)函數(shù)之間不進(jìn)行參數(shù)傳送。此類函數(shù)通常用來完成一組指定的功能,可以返回或不返回函數(shù)值。

(2)有參函數(shù):也稱為帶參函數(shù)。在函數(shù)定義及函數(shù)說明時都有參數(shù),稱為形式參數(shù)(簡稱為形參);在函數(shù)調(diào)用時也必須給出參數(shù),稱為實際參數(shù)(簡稱為實參)。在調(diào)用有參函數(shù)時,主調(diào)函數(shù)將把實參的值傳送給形參,供被調(diào)函數(shù)使用。在C51中,所有的函數(shù)定義,包括主函數(shù)main在內(nèi),都是平行的,也就是說,在一個函數(shù)的函數(shù)體內(nèi),不能再定義另一個函數(shù),即不能嵌套定義。但是函數(shù)之間允許相互調(diào)用,也允許嵌套調(diào)用,習(xí)慣上把調(diào)用者稱為主調(diào)函數(shù),函數(shù)還可以自己調(diào)用自己,稱為遞歸調(diào)用。main函數(shù)是主函數(shù),它可以調(diào)用其他函數(shù)(不包括中斷函數(shù)),而不允許被其他函數(shù)調(diào)用。因此,C51程序的執(zhí)行總是從main函數(shù)開始的,完成對其他函數(shù)的調(diào)用后再返回到main函數(shù),最后由main函數(shù)結(jié)束整個程序執(zhí)行。一個C51源程序必須有,也只能有一個主函數(shù)main。

2.函數(shù)的定義與調(diào)用

1)無參函數(shù)的定義與調(diào)用

(1)無參函數(shù)的定義。無參函數(shù)定義的一般形式為

類型說明符函數(shù)名()

{

類型說明語句;

語句;

}類型說明符和函數(shù)名稱為函數(shù)頭,類型說明符指明了該函數(shù)的類型,函數(shù)的類型實際上是函數(shù)返回值的類型,而無參函數(shù)多數(shù)沒有返回值,可定義為void類型;函數(shù)名是由用戶定義的標(biāo)識符,函數(shù)名后有一個空括號,其中無參數(shù),但括號不可少。{}中的內(nèi)容稱為函數(shù)體,在函數(shù)體中也有類型說明,這是對函數(shù)體內(nèi)部所用到的變量的類型說明。

在C51中,如果有一些語句多次用到,而語句的內(nèi)容都相同時,就可以將這些語句寫成一個函數(shù),在主函數(shù)中通過調(diào)用該函數(shù)來使用這些語句。

(2)無參函數(shù)的調(diào)用。在C51中,無參函數(shù)調(diào)用的一般形式為

函數(shù)名();

2)有參函數(shù)的定義與調(diào)用

(1)有參函數(shù)的定義。有參函數(shù)定義的一般形式為

類型說明符函數(shù)名(形參類型1參數(shù)1,……,形參類型n參數(shù)n)

{

類型說明語句;

語句;

}函數(shù)名后“()”中定義的參數(shù)稱為形式參數(shù),它們可以是各種類型的變量,各參數(shù)之間用逗號間隔;在函數(shù)調(diào)用時,主調(diào)函數(shù)將賦予這些形式參數(shù)以實際數(shù)值,這些實際的數(shù)值稱為實參。形參出現(xiàn)在函數(shù)定義中,在整個函數(shù)體內(nèi)都可以使用,離開該函數(shù)則不能使用;實參出現(xiàn)在主調(diào)函數(shù)中,進(jìn)入被調(diào)函數(shù)后,實參變量也不能使用;形參和實參的功能是作數(shù)據(jù)傳送,當(dāng)發(fā)生函數(shù)調(diào)用時,主調(diào)函數(shù)把實參的值傳送給被調(diào)函數(shù)的形參,從而實現(xiàn)主調(diào)函數(shù)向被調(diào)函數(shù)的數(shù)據(jù)傳送。函數(shù)的形參和實參還具有以下特點:①形參變量只有在被調(diào)用時才分配內(nèi)存單元,在調(diào)用結(jié)束時,即刻釋放所分配的內(nèi)存單元。因此形參只有在函數(shù)內(nèi)部有效,函數(shù)調(diào)用結(jié)束返回主調(diào)函數(shù)后則不能再使用該形參變量。

②實參可以是常量、變量、表達(dá)式、函數(shù)等,無論實參是何種類型的量,在進(jìn)行函數(shù)調(diào)用時,它們都必須具有確定的值,以便把這些值傳送給形參。因此應(yīng)預(yù)先用賦值、輸入等辦法使實參獲得確定值。

③實參和形參在數(shù)量、類型和順序上應(yīng)嚴(yán)格一致,否則會發(fā)生“類型不匹配”的錯誤。④函數(shù)調(diào)用中發(fā)生的數(shù)據(jù)傳送是單向的,即只能把實參的值傳送給形參,而不能把形參的值反向地傳送給實參。因此在函數(shù)調(diào)用過程中,形參的值會發(fā)生改變,而實參中的值不會產(chǎn)生變化。

(2)有參函數(shù)的調(diào)用。在C51中,有參函數(shù)調(diào)用的一般形式為

函數(shù)名(實際參數(shù)表);

實際參數(shù)表中的參數(shù)可以是常數(shù)、變量或其他構(gòu)造類型數(shù)據(jù)及表達(dá)式。各實參之間用逗號分隔。函數(shù)的調(diào)用方式靈活,常用的還有函數(shù)表達(dá)式和函數(shù)實參兩種方式。①函數(shù)表達(dá)式。函數(shù)作為表達(dá)式中的一部分出現(xiàn)在表達(dá)式中,函數(shù)返回值參與表達(dá)式的運算。當(dāng)采用這種調(diào)用方式時,被調(diào)用的函數(shù)必須有返回值。例如max(x,y)為求x,y之間的最大值,z=max(x,y)是一個賦值表達(dá)式,把調(diào)用max函數(shù)的返回值賦予變量z。

②函數(shù)實參。函數(shù)作為另一個函數(shù)調(diào)用的實際參數(shù)出現(xiàn),這種情況是把該函數(shù)的返回值作為實參進(jìn)行傳送,因此被調(diào)函數(shù)也必須要有返回值。

3.函數(shù)的聲明

在C51中,除主函數(shù)外的其他函數(shù)在定義時可以寫在主函數(shù)的前面,也可以寫在主函數(shù)的后面,但是不可以寫在主函數(shù)的內(nèi)部。當(dāng)函數(shù)寫在主函數(shù)的后面時,必須要在主函數(shù)之前對函數(shù)進(jìn)行聲明,聲明的作用是為了編譯器在編譯主函數(shù)過程中,當(dāng)遇到函數(shù)調(diào)用時,知道有這樣一個函數(shù)存在,才能夠根據(jù)它的類型和參數(shù)等信息為它分配必要的存儲空間。函數(shù)聲明語句的一般形式為

無參函數(shù):類型說明符函數(shù)名(); 如:voiddelay500ms();

有參函數(shù):類型說明符函數(shù)名(類型1,……,類型n); 如:voiddelay10ms(uchar);可以省略函數(shù)聲明的幾種情況:

(1)當(dāng)被調(diào)函數(shù)的函數(shù)定義出現(xiàn)在主調(diào)函數(shù)之前時,在主調(diào)函數(shù)中可以不對被調(diào)函數(shù)再作說明而直接調(diào)用。

(2)所有函數(shù)定義之前,在函數(shù)外預(yù)先說明各個函數(shù)的類型,這樣則在以后的各主調(diào)函數(shù)中,可不再對被調(diào)函數(shù)作說明。例如:

charstr(inta); //聲明有參函數(shù)str

voidf(); //聲明無參函數(shù)f

main()

{

}

charstr(inta)

{

}

voidf()

{

}

其中第1、2行對str函數(shù)和f函數(shù)預(yù)先作了聲明,因此在以后各函數(shù)中無須對str和f函數(shù)再作聲明就可以直接調(diào)用了。

(3)對庫函數(shù)的調(diào)用不需要再作聲明,但在源程序的開始處須用“#include”命令包含所需的頭文件。

4.函數(shù)的值

有返回值的函數(shù)被調(diào)用后,會向主調(diào)函數(shù)返回一個數(shù)值,稱為函數(shù)的值或稱為函數(shù)返回值。

關(guān)于函數(shù)的值有以下一些說明:

(1)函數(shù)的值只能通過return語句返回主調(diào)函數(shù)。return語句的一般形式為

return表達(dá)式;

return(表達(dá)式);

該語句的功能是計算表達(dá)式的值,并返回給主調(diào)函數(shù)。在函數(shù)中允許有多個return語句,但每次調(diào)用只能有一個return語句被執(zhí)行,因此只能返回一個函數(shù)值。

(2)函數(shù)值的類型和函數(shù)定義中函數(shù)的類型應(yīng)保持一致,如果兩者不一致,則以函數(shù)類型為準(zhǔn),自動進(jìn)行類型轉(zhuǎn)換。2.2.5一個發(fā)光二極管的閃爍

1.閃爍原理

控制一個發(fā)光二極管點亮→延時→熄滅→延時→點亮,就會形成閃爍的效果,如圖2-4所示流程圖。閃爍的效果與發(fā)光二極管點亮、熄滅的時間有關(guān),如果時間太短,人的眼睛無法分辨;若時間太長,發(fā)光二極管閃爍的速度會太慢,而影響效果。因此發(fā)光二極管點亮或熄滅的時間一般控制在100ms~1s之間。

點亮與熄滅的時間不需要特別準(zhǔn)確,可以用延時語句或延時函數(shù)來實現(xiàn),稱為軟件延時。軟件延時的原理就是讓單片機(jī)重復(fù)執(zhí)行沒有意義的空語句來實現(xiàn)時間的推移,一般由循環(huán)語句構(gòu)成。在延時時間要求特別精確時可用定時/計數(shù)器來實現(xiàn)。圖2-4發(fā)光二極管閃爍流程圖

2.for語句實現(xiàn)延時

1)由for構(gòu)成的延時語句

C51中的循環(huán)語句均可實現(xiàn)延時,但for語句用得最多。由for構(gòu)成的延時語句為

unsignedinti;

for(i=0;i<1827;i++);

先定義了一個unsignedchar類型的變量i,用于控制for的循環(huán)次數(shù),表達(dá)式1給i賦初值0,表達(dá)式2是循環(huán)結(jié)束語句,判斷i是否小于1827,表達(dá)式3通過i自增1修改i,循環(huán)體為空語句,什么也不需要做。當(dāng)變量i從0遞增至1826時,條件i<1827為真,執(zhí)行空語句,共1827次;當(dāng)i遞增至1827時,條件i<1827為假,退出for循環(huán)。由C51編寫的延時語句不能精確地計算出延時時間,通過KEILC仿真后可知當(dāng)晶振為12MHz,for循環(huán)1827次時,約延時10ms。需要修改延時時間時,以1827為基準(zhǔn)進(jìn)行調(diào)整,或者由for語句的嵌套來實現(xiàn)更長時間的延時,不必每次都進(jìn)行仿真。

如果要延時1s,可以以10ms為基準(zhǔn),采用雙重循環(huán)來實現(xiàn)。由于1s?=?100?×?10ms,內(nèi)層循環(huán)實現(xiàn)10ms延時,外層再循環(huán)100次,就可以達(dá)到延時1s的要求。1s的延時語句為

unsignedinti,j;

for(i=0;i<100;i++)

for(j=0;j<1827;j++);定義i、j兩個變量用于控制雙層循環(huán)的次數(shù)。第一個for語句的后面沒有分號,它的循環(huán)體就是第二個for語句;第二個for語句的循環(huán)體是空語句,因此第二個for語句為內(nèi)層循環(huán),由變量j控制循環(huán)1827次,延時10ms;第一個for語句為外層循環(huán),由變量i控制循環(huán)100次,延時1s。

執(zhí)行時,第一個for語句的i每加一次,第二個for語句的j就需要加1827次,因此兩重循環(huán)共執(zhí)行了100×1827次,約延時1s。若需要更長的時間時可以改變外層for循環(huán)的循環(huán)次數(shù)或增加嵌套的次數(shù)。

2)應(yīng)用舉例

例2編程使圖1-18中發(fā)光二極管LED0閃爍,點亮和熄滅的時間均為1s。

解:只要單片機(jī)上電,發(fā)光二極管LED0就能不停地閃爍,即無休止地點亮1s、熄滅1s。要求控制一個LED閃爍時,字節(jié)尋址與位尋址均可實現(xiàn),本例中采用位尋址。

源程序

#include<reg51.h> //包含51系列單片機(jī)的頭文件

#defineucharunsignedchar

#defineuintunsignedint

sbitLED0=P2^0; //定義P2.0為LED0

main() //主函數(shù){

uinti,j; //定義局部變量i、j,用于延時

while(1) //while死循環(huán),無數(shù)遍點亮、熄滅LED0

{

LED0=0; //位尋址,點亮LED0

for(i=0;i<100;i++) //延時1s

for(j=0;j<1827;j++); //分號必須有

LED0=1; //位尋址,熄滅LED0

for(i=0;i<100;i++) //延時1s

for(j=0;j<1827;j++); //分號必須有

}

}主函數(shù)中先定義了i、j兩個變量,在函數(shù)內(nèi)部定義的變量為局部變量,然后執(zhí)行while循環(huán),由于while的表達(dá)式是1,永遠(yuǎn)為真,形成死循環(huán),因此CPU將一直執(zhí)行while的循環(huán)體語句。在while的循環(huán)體中,先點亮LED0,延時1s,后熄滅,延時1s,編譯后下載至單片機(jī)就可以觀察到LED0閃爍的效果。例3編程使圖1-18中發(fā)光二極管LED0閃爍,點亮和熄滅的時間均為500ms。

解:源程序

#include<reg51.h> //包含51系列單片機(jī)的頭文件

#defineucharunsignedchar

#defineuintunsignedint

sbitLED0=P2^0; //定義P2.0為LED0

/*延時函數(shù)*/

voiddelay500ms() //延時500ms

{

uinti,j; //定義局部變量i、j,用于延時

for(i=0;i<50;i++) //外層for語句循環(huán)50次

for(j=0;j<1827;j++); //內(nèi)層for語句約延時10ms,分號必須有

}

/*主函數(shù)*/

main()

{

while(1) //while死循環(huán),無數(shù)遍點亮、熄滅LED0

{

LED0=0; //位尋址,點亮LED0

delay500ms(); //調(diào)用延時函數(shù),延時500ms

LED0=1; //位尋址,熄滅LED0

delay500ms(); //調(diào)用延時函數(shù),延時500ms

}

}

“/**/”也是C51的注釋符,可以注釋多行,而“//”只能注釋一行。由于關(guān)鍵字unsignedchar、unsignedint較長,使用時會很麻煩,英文不好時也容易寫錯,這時可用#define命令給它們重新起一個比較簡單的新名字,如:uchar、uint,當(dāng)然也可以是其他名字,在后續(xù)程序中可直接用這個新名字定義變量,如:uinti,j;。

源程序中,在主函數(shù)之前定義了無參延時函數(shù)voiddelay500ms(),“void”表示該函數(shù)執(zhí)行完后不需要返回任何數(shù)值,是一個無返回值的函數(shù);“delay500ms”是延時函數(shù)的名字,表示它是一個延時500ms的函數(shù),函數(shù)名可以任意起,但是要注意兩點:一是不能和C51中的關(guān)鍵字相同,二是要做到見名知義;函數(shù)名后的括號中沒有寫任何參數(shù),表示它是一個無參函數(shù)。延時函數(shù)delay500ms()的函數(shù)體內(nèi)定義了i、j兩個變量用于控制循環(huán)次數(shù),它們均為局部變量。

在主函數(shù)中需要延時500ms時,可以通過語句“delay500ms();”調(diào)用延時函數(shù),CPU轉(zhuǎn)去執(zhí)行延時函數(shù),從而實現(xiàn)延時功能,這樣會使主函數(shù)看起來條理清晰,增強(qiáng)了程序的可讀性。例4編程使圖1-18中的發(fā)光二極管LED0閃爍,點亮500ms、熄滅300ms。

解:該題目要求發(fā)光二極管點亮與熄滅的時間不相同,如果編寫無參延時函數(shù),需將無參延時函數(shù)的時間設(shè)置為100ms,調(diào)用3次實現(xiàn)300ms的延時,調(diào)用5次實現(xiàn)500ms的延時,方可實現(xiàn)題目要求。最好的方法是編寫有參延時函數(shù),在調(diào)用時給以不同的實參,這樣就可以實現(xiàn)不同時間的延時了。

main()

{

while(1) //while死循環(huán),無數(shù)遍點亮、熄滅LED0

{

LED0=0; //位尋址,點亮LED0

delay10ms(50); //實參為50,延時500ms

LED0=1; //位尋址,熄滅LED0

delay10ms(30); //實參為30,延時300ms

}

}由“voiddelay10ms(uchara)”定義一個有參延時函數(shù),括號中的“uchara”表示a是該函數(shù)的一個形式參數(shù),用a來控制函數(shù)體內(nèi)第一個for語句的循環(huán)次數(shù),兩個for語句共執(zhí)行a×1827次,約延時a×10ms;在主函數(shù)中調(diào)用該函數(shù)時,需要對應(yīng)給出一個具體的數(shù)據(jù),即實參,當(dāng)執(zhí)行被調(diào)函數(shù)時,將函數(shù)體中的所有形參用實參代替。

主函數(shù)中通過調(diào)用語句“delay10ms(50);”及“delay10ms(30);”調(diào)用delay10ms函數(shù),并將實參50或30傳送給delay10ms中的形參a,執(zhí)行延時函數(shù)后可分別獲得500ms及

300ms的延時,改變實參可以方便地改變延時時間。延時函數(shù)是單片機(jī)編程中常用的一個函數(shù),除了由for語句構(gòu)成外,也可以由while語句構(gòu)成;循環(huán)變量的修改不僅可以用自增,也可以用自減。

3)思考

編寫實現(xiàn)下述要求的源程序。

(1)分別用前述方法實現(xiàn)300ms延時。

(2)用位尋址使P2.2端口所接發(fā)光二極管閃爍,點亮、熄滅時間自定。

(3)用字節(jié)尋址使P2.3、P2.4端口所接的兩個發(fā)光二極管同時閃爍,點亮、熄滅時間自定。2.2.6流水燈

流水燈是霓虹燈中最簡單的一種閃爍效果,要求每次只點亮一個發(fā)光二極管,輪流點亮所有的發(fā)光二極管,點亮?xí)r間為500ms;輪流點亮所有發(fā)光二極管時,既可從高位至低位,也可從低位至高位,還可在高位與低位之間往復(fù)。硬件電路如圖1-18所示。

從程序流程的角度來看,程序可以分為順序結(jié)構(gòu)、分支結(jié)構(gòu)、循環(huán)結(jié)構(gòu)三種形式,如圖2-5所示。分支結(jié)構(gòu)與循環(huán)結(jié)構(gòu)形式多樣,圖2-5(b)、(c)所示的分支結(jié)構(gòu)與循環(huán)結(jié)構(gòu)只是其中常見的一種。這三種基本結(jié)構(gòu)可以組成各種復(fù)雜程序,它們不是絕對獨立的,常常是相互融合的,從前述源程序中就可以清楚地看到這一點。圖2-5三種程序結(jié)構(gòu)

1.順序結(jié)構(gòu)

順序結(jié)構(gòu)是最簡單的程序結(jié)構(gòu),執(zhí)行時單片機(jī)按照程序中指令的順序逐條執(zhí)行。在實現(xiàn)復(fù)雜的功能時,順序結(jié)構(gòu)常常用于實現(xiàn)一些基本功能或用作循環(huán)結(jié)構(gòu)的循環(huán)體。

例5采用順序結(jié)構(gòu)使P2口所接的8個發(fā)光二極管形成流水燈。

解:用順序結(jié)構(gòu)實現(xiàn)流水燈時,只需要按照點亮的次序?qū)⒋a發(fā)送至I/O口即可。從LED0~LED7依次點亮,當(dāng)最高位的LED7點亮后,又從LED0重新開始,流程圖如圖2-6所示。圖2-6流水燈流程圖(順序結(jié)構(gòu))流水燈在點亮一個LED的同時,要使其他7個LED熄滅,用字節(jié)尋址較為方便。從低位至高位輪流點亮LED時,P2口所需代碼如表2-2所示。表2-2流水燈代碼

從表2-2可知,由于每次只能點亮一個LED,因此每個代碼中都只有一個低電平。

源程序

#include<reg51.h> //包含51系列單片機(jī)的頭文件

#defineucharunsignedchar

#defineuintunsignedint

/*延時函數(shù)*/

voiddelay10ms(uchara)

//定義有參延時函數(shù),延時時間為a×10ms

{

uinti,j; //定義局部變量i、j,用于延時 for(i=0;i<a;i++)

for(j=0;j<1827;j++); //分號必須有

}

/*主函數(shù)*/

main()

{

while(1) //while死循環(huán),無數(shù)遍點亮LED0~LED7

{

P2=0xfe; //字節(jié)尋址,點亮LED0

delay10ms(50); //延時500ms

P2=0xfd; //字節(jié)尋址,點亮LED1

delay10ms(50); //延時500ms

P2=0xfb; //字節(jié)尋址,點亮LED2

delay10ms(50); //延時500ms

P2=0xf7; //字節(jié)尋址,點亮LED3

delay10ms(50); //延時500ms

P2=0xef; //字節(jié)尋址,點亮LED4 delay10ms(50); //延時500ms

P2=0xdf; //字節(jié)尋址,點亮LED5

delay10ms(50); //延時500ms

P2=0xbf; //字節(jié)尋址,點亮LED6

delay10ms(50); //延時500ms

P2=0x7f; //字節(jié)尋址,點亮LED7

delay10ms(50); //延時500ms

}

}主函數(shù)由while語句構(gòu)成死循環(huán),在while的循環(huán)體中順序向P2口發(fā)送代碼,使LED0~LED7輪流點亮,由于while(1)的表達(dá)式永遠(yuǎn)為真,因此當(dāng)LED7點亮后又重新從LED0開始點亮。上述源程序之所以稱為順序結(jié)構(gòu),就是指在while的循環(huán)體中,發(fā)送代碼、延時這個操作按點亮發(fā)光二極管的次序連續(xù)寫了8次。

順序結(jié)構(gòu)的優(yōu)點是簡單、直觀;缺點是書寫量較大,源程序較長。

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

C51中提供了while語句、do-while語句、for語句三種循環(huán)結(jié)構(gòu)語句,循環(huán)結(jié)構(gòu)使程序具有了重復(fù)處理能力。在編程時,先找出需要反復(fù)執(zhí)行的某種操作,再將其編寫為一個可重復(fù)執(zhí)行的程序段,每次執(zhí)行該程序段時會得到新的結(jié)果,該程序段就是循環(huán)結(jié)構(gòu)的循環(huán)體。

如例5用順序結(jié)構(gòu)實現(xiàn)流水燈時,while語句的循環(huán)體中雖然有16條語句,但實際上是在重復(fù)著同一個操作,就是將代碼發(fā)送至P2口后再延時500ms,如果用一個變量a表示每一個代碼,那么這16條語句就可以用2條語句表示,即:“P2=a;delay10ms(50);”,只是每次執(zhí)行時,變量a要有新的數(shù)值,如何修改變量a就是循環(huán)結(jié)構(gòu)編程時的重點與難點了。

1)利用移位指令實現(xiàn)流水燈

仔細(xì)觀察表2-2,可發(fā)現(xiàn)點亮LED0~LED7的8個代碼之間的特殊性在于將前一個代碼左移一位之后就是所需的新代碼,而這一規(guī)律可用C51中的左移運算“<<”實現(xiàn)。但是左移運算“<<”是將最高位移入程序狀態(tài)字寄存器PSW中的進(jìn)位CY,其他位數(shù)

溫馨提示

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

評論

0/150

提交評論