第5章 MCS-51 C語(yǔ)言程序設(shè)計(jì)
不知道一本學(xué)習(xí)匯編語(yǔ)言的書(shū)怎么會(huì)花一章的時(shí)間帶上C語(yǔ)言,,不過(guò)也但是給自己的C語(yǔ)言鞏固下
本章要點(diǎn)
MCS-51 C語(yǔ)言的特殊數(shù)據(jù)類型
MCS-51 C語(yǔ)言程序的存儲(chǔ)模式
MCS-51 C語(yǔ)言程序的結(jié)構(gòu)
C語(yǔ)言與匯編語(yǔ)言的混合編程
5.1 C語(yǔ)言與MCS-51單片機(jī)
5.1.1 C語(yǔ)言的開(kāi)發(fā)過(guò)程
一般的C語(yǔ)言開(kāi)發(fā)套件中,,包括編譯器,、連接器和符號(hào)轉(zhuǎn)換程序,,編譯器將源程序翻譯為可重定位的目標(biāo)代碼文件(也可產(chǎn)生等價(jià)的匯編語(yǔ)言程序),;連接器將目標(biāo)代碼文件連接為絕對(duì)目標(biāo)文件,;符號(hào)轉(zhuǎn)換程序可將絕對(duì)目標(biāo)文件轉(zhuǎn)換為Intel HEX格式文件,,編程到程序存儲(chǔ)器中運(yùn)行,,若使用提供了集成開(kāi)發(fā)環(huán)境(IDE)的套件,,則編輯、編譯,、連接,、符號(hào)轉(zhuǎn)換,甚至調(diào)試可在一個(gè)窗口中完成,。
5.1.2 C語(yǔ)言的特點(diǎn)
單片機(jī)的C語(yǔ)言符合ANSI C標(biāo)準(zhǔn),,可以產(chǎn)生緊湊的目標(biāo)碼,效率可以與匯編媲美,,與匯編語(yǔ)言相比,,C語(yǔ)言不有以下優(yōu)點(diǎn)
1,、不必詳細(xì)了解單片機(jī)的指令系統(tǒng)
2、僅要求對(duì)MCS-51存儲(chǔ)器結(jié)構(gòu)有初步了解
3,、寄存器分配,、不同存儲(chǔ)器區(qū)域的尋址及數(shù)據(jù)類型等細(xì)節(jié)由編譯程序管理
4、程序具有規(guī)范的結(jié)構(gòu)和固有的模塊化思想
5,、運(yùn)算符和關(guān)鍵字用接近于自然語(yǔ)言的方式表示
6,、提供包含大量標(biāo)準(zhǔn)子程序的函數(shù)庫(kù),具有較強(qiáng)的數(shù)據(jù)處理能力
7,、在對(duì)執(zhí)行效率要求較高的場(chǎng)合,,可以嵌入?yún)R編,也可以與匯編語(yǔ)言協(xié)同開(kāi)發(fā)
5.1.3單片機(jī)C語(yǔ)言的移植
移植的難點(diǎn)就是單片機(jī)C語(yǔ)言要解決的問(wèn)題
1,、MCS-51存儲(chǔ)器的非馮·諾依曼結(jié)構(gòu),,加上內(nèi)部有位尋址空間,對(duì)存儲(chǔ)器變量的使用提出了挑戰(zhàn)
2,、內(nèi)部的數(shù)據(jù)存儲(chǔ)器存儲(chǔ)空間太小,,而外部還可擴(kuò)展存儲(chǔ)容量,編譯程序如何根據(jù)實(shí)際情況合理使用這些空間
3,、內(nèi)部各功能單元采用特殊功能寄存器集中管理,,在C語(yǔ)言中如何實(shí)現(xiàn)寄存器訪問(wèn)
4、MCS-51單片機(jī)派生各類繁多,,硬件配置不統(tǒng)一,,但要求必須能夠使用C語(yǔ)言操控所有硬件資源
5、MXD-51內(nèi)部只有一個(gè)堆棧,,且存儲(chǔ)空間有限,,傳統(tǒng)的利用堆棧傳遞參數(shù)的方法難以奏效
隨著技術(shù)的發(fā)展和各軟件廠商的努力,以上問(wèn)題都得到了解決,,C語(yǔ)言日趨成熟,,成為專業(yè)化的實(shí)用高級(jí)語(yǔ)言
5.2單片機(jī)C語(yǔ)言的擴(kuò)充
5.2.1數(shù)據(jù)類型
無(wú)論是出現(xiàn)在表達(dá)式中的常量,還是程序自己定義的變量,,都有數(shù)據(jù)類型,,特別是變量,數(shù)據(jù)類型是編譯程序進(jìn)行存儲(chǔ)器分配的依據(jù)之一
表中大部分類型是C語(yǔ)言中的標(biāo)準(zhǔn)類型,,像字符型,、枚舉型、各種整形和單精度浮點(diǎn)型等,,枚舉型根據(jù)實(shí)際枚舉常量的多少同編譯程序確定其長(zhǎng)度,,而bit、sbit、sfr和sfr16是為訪問(wèn)MCS-51硬件中的內(nèi)部RAM中的位,、SRF中的位以及8位SRF和16位SFR(如DPTR)所特有的類型,,它們不是ANSI C的一部分,不能用指針對(duì)它們進(jìn)行訪問(wèn),,也不能定義包含這些類型元素的數(shù)組,、結(jié)構(gòu)體、聯(lián)合體等
例:MCS-51系統(tǒng)中需要處理以下幾個(gè)數(shù)據(jù):一個(gè)是從擴(kuò)展的I/O端口輸入的8位開(kāi)關(guān)量狀態(tài)數(shù)據(jù)in_data,,一個(gè)是記錄系統(tǒng)運(yùn)行時(shí)間的log_time(以秒為單位),還有一個(gè)是保存設(shè)備是否正常運(yùn)行的標(biāo)志ok_flag,,它們以變量形式存儲(chǔ),,使用哪種類型最合適?
in_data為8位無(wú)符號(hào)數(shù)據(jù),,使用unsingned char最合適,;log_time需要記錄較長(zhǎng)時(shí)間若用16位的整形變量,只能累計(jì)幾個(gè)小時(shí),,用long型最好,,也應(yīng)該是無(wú)符號(hào)的;log_time有一位即可,,可用bit型,,具體變量定義時(shí),可使用以下語(yǔ)句
unsigned char in_data;
unsigned long log_time;
bit ok_flag;
實(shí)際上,,只要不是必需,,在MCS-51的C語(yǔ)言程序中應(yīng)該盡量使用較短的、無(wú)符號(hào)的類型,,unsigned char是第一選擇,,因?yàn)榫幾g成機(jī)器碼后最適合單片機(jī)處理的是字節(jié)數(shù)據(jù)。
5.2.2存儲(chǔ)器類型
C語(yǔ)言中的變量的存儲(chǔ)位置通常同編譯程序根據(jù)一定的約定進(jìn)行分配,,如果編程時(shí)比較清楚某些變量的屬性,,程序員也可在變量定義進(jìn)指定其存儲(chǔ)區(qū)域
例:指出以下變量的存儲(chǔ)位置
char data varl;
char code text[]="ENTER PARAMETER";
unsigned long xdata array[100];
float idata x,y,z;
unsigned int pdata dimension;
unsigned char xdata vector[10][4][4];
char bdata flags;
varl保存于內(nèi)部RAM中,;"ENTER PARAMETER"存儲(chǔ)于程序存儲(chǔ)器中;其首地址以text表示,,程序運(yùn)行期間該符號(hào)串不能修改,;100個(gè)長(zhǎng)整形元素的數(shù)組array只能存于外部RAM中,占400個(gè)字節(jié),;單精度浮點(diǎn)數(shù)變量XYZ保存于內(nèi)部RAM內(nèi),;無(wú)符號(hào)數(shù)變量dimension則存儲(chǔ)于外部RAM的某一頁(yè)內(nèi);有160個(gè)字節(jié)數(shù)據(jù)的三維數(shù)組vector也只能存儲(chǔ)于外部RAM中;由多個(gè)標(biāo)志位組成的標(biāo)志字節(jié)flags存儲(chǔ)于位尋址區(qū),。
若定義變量時(shí)指定了存儲(chǔ)器的類型,,編譯程序按要求為其分配存儲(chǔ)空間;若未指定,,編譯程序按照存儲(chǔ)器模式自動(dòng)為變量選擇默認(rèn)存儲(chǔ)器類型,。
5.2.3存儲(chǔ)模式
常用的存儲(chǔ)模式有以下幾種,
1,、SMALL模式
SMALL模式下,,所有的變量默認(rèn)存放于內(nèi)部RAM中,相當(dāng)于定義時(shí)使用了data類型,,這時(shí)的變量訪問(wèn)速度最快,、效率最高,但是所有對(duì)像(包括堆棧)必須能夠存入內(nèi)部RAM的128字節(jié)
2,、COMPACT模式
COMPACT模式下,,所有變量默認(rèn)存放于外部RAM的一頁(yè)中,相當(dāng)于定義時(shí)使用了pdata類型,,這種存儲(chǔ)模式可以滿足最多256字節(jié)的變量,,由于對(duì)變量的訪問(wèn)必須使用間接尋址方式,所以速度也比訪問(wèn)內(nèi)部RAM慢一些,,COMPACT模式產(chǎn)生的機(jī)器碼不如SMALL模式的快,,但是比LARGE模式要好
3、LARGE模式
LARGE械下,,所有變量默認(rèn)存放于外部RAM中,,最多可以有64KB,相當(dāng)于定義時(shí)使用了xdata類型,,數(shù)據(jù)指針DPTR用來(lái)尋址變量,。這種訪問(wèn)方式效率不高,特別是當(dāng)變量長(zhǎng)度超過(guò)一個(gè)字節(jié)時(shí),。尋址方式直接影響代碼長(zhǎng)度,,產(chǎn)生的機(jī)器碼比SMALL和COMPACT模式產(chǎn)生的都要多
如果沒(méi)有說(shuō)明,編譯程序默認(rèn)使用SMALL模式,,由于各種存儲(chǔ)模式在訪問(wèn)效率,、代碼長(zhǎng)度、變量總長(zhǎng)度等方面各有優(yōu)缺點(diǎn),,現(xiàn)在常用的C編譯程序通常允許使用混合模式,,即不管存儲(chǔ)模式如何,把經(jīng)常使用的變量強(qiáng)制存放于內(nèi)部RAM,,大塊數(shù)據(jù)則存放于外部RAM而將其指針存放于內(nèi)部RAM中,,可以使用存儲(chǔ)器類型說(shuō)明符指定。
5.2.4硬件資料訪問(wèn)
1、特殊功能寄存器
MCS-51 C語(yǔ)言使用sfr,、sfr16和sbit數(shù)據(jù)類型訪問(wèn)特殊功能寄存器
sfr P0=0x80; /*P0口,,地址為80H*/
sfr P1=0x90; /*P1口,地址為90H*/
sfr P2=0xA0; /*P2口,,地址為A0H*/
sfr P3=0xB0; /*P3口,,地址為B0H*/
其中,P0,、P1,、P2、P3是定義的特殊功能寄存器名字,。實(shí)際上任何合法的標(biāo)識(shí)符號(hào)都可以做為sfr定義中的特殊功能寄存器的變量名,,等號(hào)后的地址必須是數(shù)值常數(shù),而且一定要在特殊功能寄存器區(qū)域內(nèi)(0x80~0xFF).
大多數(shù)C環(huán)境附帶了一些C頭文件,,比較典型的是在reg51.h中對(duì)所有51子系統(tǒng)中的特殊功能寄存器進(jìn)行了sfr定義。
2,、特殊功能寄存器中的位
任何合法的標(biāo)識(shí)符都可以作為sbit名字,,等號(hào)右邊的表達(dá)式為該標(biāo)識(shí)符賦予了一個(gè)位地址,指定地址有三種方式
A,、sfr名字^整形常,,該方式使用先前已經(jīng)定義的sfr名字作為sbit的基地址,要求該sfr地址必須為8位的倍數(shù)(即該sfr確實(shí)是可以按位訪問(wèn)的),。在^符號(hào)后的整形常數(shù)指定該sbit在sfr中的位置,,范圍是0~7,其中0為最低有效位,。
例:下面定義的三個(gè)符號(hào)名稱的含義各是什么,?
srf PSW =0xD0;
sbit OV =PWS^2;
sbit P =PWS^0;
它們分別代表程序狀態(tài)字PSW(地址為D0H)、溢出標(biāo)志OV和奇偶標(biāo)志P
B,、整形常數(shù)^整形常數(shù)
sbit OV =0xD0^2;
sbit P =0xD0^0;
C,、整形常數(shù),這種方式直接指定位變量的位地址
sbit OV =0xD2;
sbit P =0xD0;
3,、內(nèi)部RAM中的位尋址資源
將一個(gè)變量定義為bit型后,,C編譯程序就會(huì)在位尋址區(qū)為其分配一位的空間,定義一個(gè)其他類型變量時(shí)若指定了bdata存儲(chǔ)器類型,,C編譯程序也會(huì)在內(nèi)部RAM的位尋址地區(qū)為其分配存儲(chǔ)空間,,這個(gè)變量中的位也可以單獨(dú)訪問(wèn),但必須先行定義,。
4,、指定絕對(duì)地址的變量
在某些與硬件密切相關(guān)的應(yīng)用中,可能需要指定變量在系統(tǒng)中的絕對(duì)地址,而不是讓編譯程序自行分配,。在MCS-51的C語(yǔ)言程序中,,可使用_at_滿足這一要求,其格式如下:
[存儲(chǔ)器類型] 變量類型 變量名 _at_ 地址常數(shù),;
例:在某MCS-51系統(tǒng)中,,擴(kuò)展的外部數(shù)據(jù)存儲(chǔ)器地址2000H~20FFH共256個(gè)字節(jié)單元作為通信中的接收緩沖區(qū),請(qǐng)對(duì)該區(qū)域進(jìn)行定義,。
若以r_buf命名該區(qū)域,,可以如下定義
xdata nusigned char r_buf[256] _at_ ox20000;
對(duì)于外部擴(kuò)展的I/O口,所占外部RAM空間的地址已由硬件設(shè)計(jì)決定,,必須指定絕對(duì)地址
5,、存儲(chǔ)器絕對(duì)地址的訪問(wèn)
單片機(jī)C語(yǔ)言頭文件absacc.h中包含了一些宏定義,使用這些宏可以顯示使用存儲(chǔ)器絕對(duì)地址,,把每個(gè)存儲(chǔ)區(qū)定義成一個(gè)字節(jié)或字?jǐn)?shù)組,,對(duì)指定地址的訪問(wèn)使用數(shù)組元素引用的形式
例:使用存儲(chǔ)器絕對(duì)地址訪問(wèn)的方式,怎么實(shí)現(xiàn)以上兩個(gè)例子的功能》
#include <absaacc.h>
#define r_buf (XBYTE+0x20000)
#define data_reg XBYTE[0xFF80]
#define con_reg XBYTE[0xFF81]
定義后,,對(duì)數(shù)組r_buf和寄存器data_reg,、con_reg的訪問(wèn)方式?jīng)]有變化
5.2.5指針
C語(yǔ)言程序中可以使用指針變量或指針常量,其值為所指類型變量的地址,,也可以是該類型數(shù)組的起始地址
1,、基于存儲(chǔ)器的指針
基于存儲(chǔ)器的指針類型與源程序中存儲(chǔ)器類型有關(guān),編譯進(jìn)即可確定其長(zhǎng)度,。這種指針的長(zhǎng)度可以為1個(gè)字節(jié)(dtat*,、idtat*、pdata*)或2個(gè)字節(jié)(code*,、xdata*)
例:指出下面指針定義的作用
char data *str; /*指向data字符的指針*/
int xdata *numtab; /*指向xdata整形數(shù)據(jù)的指針*/
long code *powtab; /*指向data長(zhǎng)整形的指針*/
所定義的str numtab powtab三個(gè)指針變量長(zhǎng)度分別為1個(gè)字節(jié),、2字節(jié)、2字節(jié),,它們自身所占用的存儲(chǔ)位置由存儲(chǔ)模式確定,。
同普通變量一樣,在定義指針變量時(shí)可以指定其存儲(chǔ)類型
例:指出下面指針定義的含義
char data *xdata str; /*指針變量位于xdata*/
int xdata *data numtab;/*指針變量位于data*/
long code *idata powtab;/*指針變量位于idata*/
2,、通用指針
定義和標(biāo)準(zhǔn)C指針定義相同,,凡是指針定義中未對(duì)指向的對(duì)象存儲(chǔ)器類型進(jìn)行修飾說(shuō)明的,編譯程序都將其作為通用指針,,使用3個(gè)字節(jié)存儲(chǔ)指針內(nèi)部,,第一個(gè)字節(jié)存放存儲(chǔ)器類型,第二和第三個(gè)字節(jié)分別存放該指針?biāo)笇?duì)象地址的高字節(jié)和低字節(jié)
例:指出以下代碼段中各變量的含義以及變化情況
xdata int x;
int *data px,*data py;
px=&x;
py=0x021234;
*px=1000;
*py=-1;
整形變量X位于外部RAM中,;兩個(gè)通用指針px和py,,通過(guò)賦值語(yǔ)句使px指向x,,而py指向外部RAM的1234H單元,最后兩條賦值語(yǔ)句使x值成為1000,,1234單元內(nèi)容成為FFFFH,。
這兩節(jié)的內(nèi)部概念性的東西太多了,一時(shí)很難記的住,,我都看幾邊了也沒(méi)有記住不知道是不是方法有問(wèn)題,。
繼續(xù)這章內(nèi)容
5.3 C語(yǔ)言程序結(jié)構(gòu) 程序入口為main函數(shù),每個(gè)函數(shù)內(nèi)部可以使用結(jié)構(gòu)化程序設(shè)計(jì)技術(shù)的三種結(jié)構(gòu) 5.3.1函數(shù) 1,、函數(shù)定義 C語(yǔ)言一般采用模塊化設(shè)計(jì),,最基本的模塊就是同函數(shù)表示,MCS-51的C語(yǔ)言程序中,,在定義函數(shù)時(shí)還可以指定是否為中斷算是函數(shù),、是否為可重入函數(shù),可以選擇工作寄存器組以及確定其存儲(chǔ)模式,,函數(shù)定義的基本格式如下 [返回值類型] 函數(shù)名稱(表達(dá)式) [{small ︱ compact ︱ large}] [reentrant] [interrupt n] [using n] 若省略返回值類型部分,,則默認(rèn)為整形( int),可以指定該函數(shù)的存儲(chǔ)模式,,以取代默認(rèn)值,;若使用using,編譯程序?qū)a(chǎn)生切換工作寄存器組的代碼,;對(duì)于有返回值的函數(shù),不能使用using,,因?yàn)榉祷刂凳峭ㄟ^(guò)寄存器傳遞的,。 2、參數(shù)傳遞 參數(shù)用于幾函數(shù)傳遞數(shù)據(jù),,作為函數(shù)的輸入,,MSC-51參數(shù)傳遞是通過(guò)存儲(chǔ)器和寄存器傳遞的,通過(guò)寄存器傳遞速度快是默認(rèn)的傳遞方式,,傳遞時(shí)所使用的寄存器分配如下表,,這時(shí)最多能3個(gè)參數(shù),若函數(shù)參數(shù)較多,,寄存器不足以傳遞所有參數(shù)則使用固定地址的存儲(chǔ)器單元作為函數(shù)的存放位置,,當(dāng)?shù)谝粋€(gè)參數(shù)是bit型時(shí),無(wú)法用寄存器傳遞參數(shù),。若參數(shù)個(gè)數(shù)不超過(guò)3個(gè),,可以將bit型參數(shù)放在參數(shù)表最后 表5-4傳遞參數(shù)的寄存器分配
3、返回值 與傳遞參數(shù)不同,,函數(shù)的返回值總是通過(guò)寄存器送回的如下表 表5-5函數(shù)返回值所用寄存器分配
4,、內(nèi)部函數(shù)和外部函數(shù) 如果一個(gè)函數(shù)只能在其定義的文件中被調(diào)用,則稱為內(nèi)部函數(shù),,也稱為靜太函數(shù),,定義內(nèi)部函數(shù)時(shí)需要用static存儲(chǔ)類型說(shuō)明 Static unsigned int fun(unsigned char the_byte,bit the_flag) 函數(shù)fun在包含其定義的文件外不可訪問(wèn)。 允許在其它文件中調(diào)用的函數(shù)為外部函數(shù),,可以使用extern存儲(chǔ)類型說(shuō)明符指明,。函數(shù)定義時(shí)若無(wú)存儲(chǔ)類型說(shuō)明,默認(rèn)為外部函數(shù),。 5,、可重入函數(shù) 單片機(jī)的C編譯程序通常的局部變量分配在存儲(chǔ)器的固定位置,如果正在執(zhí)行該函數(shù)時(shí)發(fā)生了中斷,,而中斷服務(wù)程序中也調(diào)用該函數(shù),,先前的局部變量值便會(huì)被破壞,類似的情況在實(shí)現(xiàn)函數(shù)遞歸調(diào)用時(shí)也會(huì)發(fā)生,,對(duì)于一個(gè)函數(shù),,如果確實(shí)需要遞歸調(diào)用,或者確實(shí)非中斷服務(wù)程序代碼與中斷服務(wù)程序都要調(diào)用,,應(yīng)當(dāng)將它定義為可重入函數(shù),,使編譯程序產(chǎn)生能夠保護(hù)局部變量的代碼,可以重入函數(shù)是使用reentrant來(lái)說(shuō)明,。 例:有一個(gè)延時(shí)函數(shù),,在程序中多次被調(diào)用,包括中斷服務(wù)程序,,請(qǐng)將其定義為可重入函數(shù),。 void delay(void)reentrant { int i; for(i=0;i<1000;i++) ; } 其實(shí)若非遞歸調(diào)用,也可以不編寫可重入函數(shù),,而是將同一函數(shù)改寫為非中斷服務(wù)程序調(diào)用和中斷服務(wù)程序調(diào)用的兩個(gè)函數(shù),,變量所需存儲(chǔ)空間沒(méi)有顯著減少,代碼腳加長(zhǎng)了 6,、中斷處理函數(shù) 中斷處理函數(shù)也稱作中斷服務(wù)程序,,是CPU響應(yīng)中斷后要執(zhí)行的一段程序,在C語(yǔ)言中組織成一個(gè)函數(shù)的形式,,編寫中斷處理函數(shù)時(shí),,程序員只需要中斷類型號(hào)和寄存器組的選擇,編譯程序會(huì)自動(dòng)產(chǎn)生中斷向量和返回地址的入棧及出棧代碼,。在函數(shù)定義時(shí)可以使用interrupt將其指定為一個(gè)中斷處理函數(shù),,還可以用using分配中斷處理函數(shù)所使用的寄存器組,。 例:說(shuō)明下面函數(shù)定義的作用 unsigned int interruptcnt; unsigned char second; void timer0 (void) interrupt 1 using 2 { if(++interruptcnt= =1000) { second++; interruptcnt=0; } } 函數(shù)timer0是一個(gè)中斷處理函數(shù),所對(duì)應(yīng)的中斷類型號(hào)為1,,使用第二組工作寄存器 7,、intrinsic函數(shù) 在MSC-51 C語(yǔ)言中,intrinsic函數(shù)是一類用匯編語(yǔ)言代碼實(shí)現(xiàn)的短小函數(shù),,若C語(yǔ)言程序中有對(duì)intrinsic函數(shù)的調(diào)用,,編譯程序?qū)?huì)直接用被調(diào)用函數(shù)代碼替換函數(shù)調(diào)用語(yǔ)句。 常見(jiàn)intrinsic函數(shù)的原型如下,,它們一般在intris.h文件中 extern void _nop_ (void); extern bit _testbit_ (bit); extern unsigned char _cror_ (unsigned char,unsigned char); extern unsigned int _iror_ (unsigned int,unsigned int); extern unsigned long _lror_ (unsigned long,unsigned long); extern unsigned char _crol_ (unsigned char,unsigned char); extern unsigned int _irol_ (unsigned int,unsigned int); extern unsigned long _lrol_ (unsigned long,unsigned long); 這些函數(shù)名稱前后都有下劃線,,這是與其它庫(kù)函數(shù)的最明顯區(qū)別,以上函數(shù)實(shí)現(xiàn)的功能分別是空操作,、位測(cè)試以及字符型,、整形和長(zhǎng)整形數(shù)據(jù)的左、右移位,。 例:編寫代碼,,若位變量flag值為1,則8位位數(shù)據(jù)data8右移兩位并將flag清零,,否則左移3位,。 if(_teatbit_(flag)) data8=_cror_(data8,2); else data8=_crol_(data8,3); 5.3.2流程控制 1、分支,,C語(yǔ)言有兩種分支方式 A,、if語(yǔ)句 If(表達(dá)式) 語(yǔ)句1 這種形式實(shí)現(xiàn)了單分?jǐn)偨Y(jié)構(gòu),若表達(dá)式值非0,,則執(zhí)行后面的語(yǔ)句1,,然后繼續(xù)往下執(zhí)行,若表達(dá)式的值為0,,則跳過(guò)語(yǔ)句1直接往下執(zhí)行 兩個(gè)分支的if語(yǔ)句形式為 if(表達(dá)式) 語(yǔ)句1 else 語(yǔ)句2 若表達(dá)式值是非0,,則執(zhí)行后的語(yǔ)句1,,然后執(zhí)行表達(dá)式語(yǔ)句2后面的語(yǔ)句,,若表過(guò)式的值為0則跳過(guò)語(yǔ)句1執(zhí)行語(yǔ)句2,然后繼續(xù)執(zhí)行下面語(yǔ)句 多分支if語(yǔ)句形式為 if(表達(dá)式) 語(yǔ)句1 else if(表達(dá)式2) 語(yǔ)句2 else if(表達(dá)式3) 語(yǔ)句3 ………….. else 語(yǔ)句n 多選結(jié)構(gòu)n個(gè)語(yǔ)句中只能執(zhí)行一個(gè),,即第一個(gè)值非0表達(dá)式后面的語(yǔ)句,。 以上三種形式中,所有語(yǔ)句都可以是復(fù)合語(yǔ)句,,即用花括號(hào)引起來(lái)的語(yǔ)句組,。 B、switch-case結(jié)構(gòu) 當(dāng)選擇較多時(shí)使用if語(yǔ)句和程序結(jié)構(gòu)會(huì)變得臃腫,,switch-case結(jié)構(gòu)是比較簡(jiǎn)潔的寫法形式為 switch(表達(dá)式) { csae 常量表達(dá)式1:語(yǔ)句組1,;break; csae 常量表達(dá)式2:語(yǔ)句組2,;break; ……… csae 常量表達(dá)式n:語(yǔ)句組n;break; default; 語(yǔ)句組n+1,;break; } Switch后的表達(dá)式可以是整形或字符型,、枚舉型數(shù)據(jù),case后的各常量大達(dá)式須與其類型相同或可以相互轉(zhuǎn)換,,當(dāng)前者的值與某一case后表達(dá)式的值相等時(shí),,執(zhí)行其后的語(yǔ)句組,然后執(zhí)行break退出switch語(yǔ)句,,若所有case后表達(dá)式與之皆不相等,,則執(zhí)行default后語(yǔ)句組,case后表達(dá)式須各不相乖,。 2,、循環(huán) C語(yǔ)言中實(shí)現(xiàn)循環(huán)結(jié)構(gòu)的語(yǔ)句也有多種 A、 goto語(yǔ)句:用來(lái)實(shí)現(xiàn)轉(zhuǎn)移,,結(jié)合if語(yǔ)句,,可以實(shí)現(xiàn)簡(jiǎn)單的循環(huán),類似于指令系統(tǒng)中的條件轉(zhuǎn)移指令的作用,,但是goto語(yǔ)句可以轉(zhuǎn)向程序中任何位置,,所以受到結(jié)構(gòu)化程序設(shè)計(jì)支持者的強(qiáng)烈抵制 B、 while結(jié)構(gòu),,形式為 while(表達(dá)式) 語(yǔ)句 其中表達(dá)式為循環(huán)條件,,語(yǔ)句構(gòu)成循環(huán)體。若循環(huán)條件值非0,,則執(zhí)行循環(huán)體,,一種常見(jiàn)的形式為 while(1) { ………. } 這種形式可以稱為無(wú)限循環(huán),一般單片機(jī)軟件就是這種形式,,如下代碼 while(!(P1&0x01)) ; 實(shí)現(xiàn)的是等待P1.0為1,,循環(huán)體部分為空語(yǔ)句,循環(huán)條件是輸入的P1值最低有效位為0. C,、 do….while結(jié)構(gòu),,形式為 do 語(yǔ)句 While(表達(dá)式); 不像while結(jié)構(gòu)先判斷條件,,do….while結(jié)構(gòu)是先執(zhí)行一次語(yǔ)句(循環(huán)體),然后再判斷條件,,若條件表達(dá)式值非0,則繼續(xù)下次循環(huán),。 D,、for結(jié)構(gòu),for結(jié)構(gòu)是使用最靈活的循環(huán)控制語(yǔ)句,,形式為 for(表達(dá)式1,;表達(dá)式2,;表達(dá)式3) 語(yǔ)句 for結(jié)構(gòu)的執(zhí)行過(guò)程為:先對(duì)表達(dá)式1求值;再對(duì)表達(dá)式2求值,,若表達(dá)式2的值是非0,,則執(zhí)行一次語(yǔ)句,然后對(duì)表達(dá)式3求值,,再一次對(duì)表達(dá)式2求值,,若非0,則在此形成循環(huán),,直到表達(dá)式2的值為0,,則循環(huán)結(jié)束。如下代碼 for(;P1&0x01;) ; 實(shí)現(xiàn)的也是等待P1.0輸入為1,,而 for(i=0;(i<10000)&&(P1&0x01);i++) ; 實(shí)現(xiàn)的是有時(shí)間限制的等待P1.0為1,,具體時(shí)間可以通過(guò)檢查編譯產(chǎn)生的代碼計(jì)算得到,或者在仿真器上設(shè)置斷點(diǎn)觀察得知,。 E,、 break和continue語(yǔ)句 break語(yǔ)句不公能夠跳出switch結(jié)構(gòu),還可以從循環(huán)體中跳出,,提前結(jié)束循環(huán)而執(zhí)行循環(huán)后面的語(yǔ)句,。Break只能用在循環(huán)語(yǔ)句(包括while、do….whilet和for結(jié)構(gòu))和switch語(yǔ)句中,。 Continue語(yǔ)句則是提前結(jié)束本次循環(huán),,跳過(guò)循環(huán)體中continue后面未執(zhí)行的語(yǔ)句,接著進(jìn)行一次循環(huán)條件的判定,。 break和continue語(yǔ)句其實(shí)是結(jié)構(gòu)化程序設(shè)計(jì)方法中實(shí)現(xiàn)非結(jié)構(gòu)化的一種手段,,在退出循環(huán)或提前結(jié)束循環(huán)的條件不易表達(dá)時(shí),這類語(yǔ)句可以使程序更容易理解,。 5.3.3輸入與輸出 一些C開(kāi)發(fā)環(huán)境提供了流式輸入/輸出函數(shù),,可以實(shí)現(xiàn)通過(guò)串口或用戶自定義I/O接口的輸入/輸出操作,例如getchar,、gets,、scanf、putchar,、puts,、printf等,,輸入/輸出功能需要調(diào)用_getkey和putchar兩個(gè)函數(shù),,這兩個(gè)函數(shù)的默認(rèn)實(shí)現(xiàn)是通過(guò)串行口實(shí)現(xiàn)的,所以如果使用輸入/輸出函數(shù),,還需要在程序中加入一些代碼,,以便調(diào)用時(shí)已經(jīng)對(duì)串行口進(jìn)行了適當(dāng)?shù)某跏脊ぷ鳌?/span> 例:說(shuō)明以下代碼的運(yùn)行結(jié)果 #include <reg51.h> /*初始化時(shí)要用到SFR*/ #include <stdio.h> /*引入輸入/輸出函數(shù)原型*/ Void main(void) { int x,y /*變量*/ SCON =0x50; /*開(kāi)始對(duì)串行口的初始化代碼*/ PCON &=0x7F; TMOD &=0xCF; TMOD &=0x20; TH1 =0xFD; TR1 =1; TI =1; /*初始化結(jié)束*/ While(1) { scanf(“%d%d,&x,&y); /*輸入*/ printf(“x=%04x,y=%04x\n”,x,y); /*輸出*/ } } 該程序接收用戶輸入的十進(jìn)制數(shù)值,,然后從串行口以十六進(jìn)制格式輸出 程序員可以根據(jù)系統(tǒng)輸入/輸出接口的配置情況重寫_getkey和putchar兩個(gè)函數(shù),其他函數(shù)功能保持不變,。 5.3.4程序的入口 C語(yǔ)言程序的入口是main函數(shù),,而單片機(jī)復(fù)位后是從0000H開(kāi)始執(zhí)行代碼程序,main函數(shù)位于系統(tǒng)程序存儲(chǔ)器的何處呢,?下面觀察一下例子程序的可執(zhí)行代碼
最左邊一列是程序存儲(chǔ)器地址,,第二列是指令機(jī)器碼,最右邊一列為助記符表示,,可以看到單片機(jī)復(fù)位后,,先轉(zhuǎn)移到0012H,將內(nèi)部RAM單元00H~7FH清零,,置SP為08H后,,轉(zhuǎn)移到main函數(shù)(001EH)處執(zhí)行,即在main函數(shù)執(zhí)行之前,,已經(jīng)做了一些初始化處理,。 這是默認(rèn)的初始化操作,至于堆棧,,取決于編譯程序在內(nèi)部RAM中為局部變量分配空間的大小,,若有在main函數(shù)執(zhí)行之前就應(yīng)當(dāng)初始化的資源,或者需要將存儲(chǔ)區(qū)初始化謎團(tuán)特定的值,,程序員可以在匯編語(yǔ)言程序STARTUP.A51中修改或添加代碼,,在使用C語(yǔ)言開(kāi)發(fā)的單片機(jī)軟件中,單片機(jī)程序的入口其實(shí)還是0000H,,在STARTUP.51中初始化代碼的最后一條指令才轉(zhuǎn)向main函數(shù)執(zhí)行,。 5.4 C語(yǔ)言與匯編語(yǔ)言的混合編程 C語(yǔ)言和匯編語(yǔ)言各有優(yōu)缺點(diǎn),C語(yǔ)言中數(shù)據(jù)類型豐富,,程序結(jié)構(gòu)清晰,,但是在執(zhí)行速度、精確定時(shí),、控制硬件等方面不如匯編語(yǔ)言方便,,如果要在各方面都獲得滿意的結(jié)果,可以使用C語(yǔ)言與匯編語(yǔ)言的混合編程,。 用C語(yǔ)言調(diào)用匯編語(yǔ)言程序時(shí),,被調(diào)用函數(shù)(匯編語(yǔ)言函數(shù))要在調(diào)用函數(shù)(C語(yǔ)言函數(shù))所在文件中說(shuō)明,對(duì)于匯編語(yǔ)言程序有以下要求 1,、 要使用SEGMENT偽指令定義可重定的CODE段,。 2、 要根據(jù)不同的情況對(duì)函數(shù)名進(jìn)行轉(zhuǎn)換,見(jiàn)下表5-6. 3,、 須使用PUBLIC偽指令將被調(diào)用函數(shù)說(shuō)明為外部可用函數(shù) 4,、 若有參數(shù)傳遞,按照表5-4所列的規(guī)則使用參數(shù),。 5,、 若有返回值,按照表5-5所列的規(guī)則存儲(chǔ)寄存器,。 表5-6函數(shù)名轉(zhuǎn)換規(guī)則
例:編寫匯編語(yǔ)言函數(shù)max,,參數(shù)為兩個(gè)8位無(wú)符號(hào)數(shù),功能是求出其中的大數(shù)返回,。 在C語(yǔ)言中可按如下方式聲明和調(diào)用 extern unsigned char max(unsigned, unsigned); /*聲明*/ void main(void) { unsigned char x,y; x=130;y=131; x=max(x,y); /*調(diào)用*/ while(1); } 兩個(gè)參數(shù)分別在R7和R5中傳遞到子程序,,返回值應(yīng)保存在R7中,匯編語(yǔ)言程序文件可如下編寫, PUBLIC _MAX ;聲明 MIXED SEGMENT CODE ,;定義一個(gè)可重定位的段MIXED ESEG MIXED ,;選擇MIXED為當(dāng)前段 _MAX: MOV A,R7 ;第一個(gè)參數(shù) CLR C SUBB A,R5 ,;減去第二個(gè)參數(shù) JNC _MAX_RET ,;無(wú)借位,第一個(gè)參數(shù)值大 MOV A,R5 ,;有借位,,第二個(gè)參數(shù)值大 MOV R7,A ;返回值在R7 _MAX_RET: RET EDN 例:編寫匯編語(yǔ)言函數(shù)delayms,,參數(shù)為一個(gè)8位無(wú)符號(hào)數(shù),,功能是按照參數(shù)指定的毫秒數(shù)實(shí)現(xiàn)延時(shí)。這個(gè)函數(shù)有參數(shù)傳遞,,但是無(wú)返回值,。 匯編語(yǔ)言文件可如下編寫 PUBLIC _DELAYMS ;聲明 HAHA SEGMENT CODE ;定義一個(gè)可重定位的段HAHA RSEG HAHA ,;選擇HAHA為當(dāng)前段 _DELAYMS: MOV R6,#2 ,;以下實(shí)現(xiàn)約1ms的延時(shí) _DELAY_NEXT: MOV R5,#248 DJNZ R5,$ DJNZ R6,_DELAY_NEXT DJNZ R7,_DELAYMS RET END ;返回 例:編寫匯編語(yǔ)言函數(shù)delay10ms,沒(méi)有參數(shù),,功能是延時(shí)10ms,,這個(gè)問(wèn)題與上例的區(qū)別在于無(wú)參數(shù)傳遞,所以函數(shù)名無(wú)需加“_”,。 匯編語(yǔ)言文件可寫成如下形式 PUBILIC EDLAY10MS MIXED SEGMENT CODE RSEG MIXED DELAY10MS: MOV R6,#20 DELAY_NEXT: MOV R5,#248 DJNZ R5,$ DJNZ R6,DELAY_NEXT RET END 以上例子中,,只是各編寫了一個(gè)供C語(yǔ)言程序調(diào)用的匯編語(yǔ)言函數(shù),若需要多個(gè),,也是可以寫在同一個(gè)匯編語(yǔ)言程序文件中,,匯編語(yǔ)言程序中也可以調(diào)用C語(yǔ)言函數(shù),,但不常用。 |
|
來(lái)自: 昵稱7419213 > 《我的圖書(shū)館》