學(xué)習(xí)C51,想把數(shù)據(jù)類型及函數(shù)這些基礎(chǔ)內(nèi)容掌握了!
掃描二維碼
隨時(shí)隨地手機(jī)看文章
1 編譯器支持的數(shù)據(jù)類型
1.1 C-51 編譯器支持下列數(shù)據(jù)類型:
數(shù) 據(jù) 類 型
長(zhǎng) 度
值 域
bit
1 字節(jié)
0 或 1
signed char
1 字節(jié)
-128~+127
unsigned char
1 字節(jié)
0~255
signed int
2 字節(jié)
-32768~+32867
unsigned int
2 字節(jié)
0~65535
signed long
4 字節(jié)
-2147483648~+2147483647
unsigned long
4 字節(jié)
0~4294967295
float
4 字節(jié)
±1.176E-38~±3.40E+38
指針
1~3 字節(jié)
對(duì)象地址
sbit
1 位
0 或 1
sfr
1 字節(jié)
0~255
sfr16
2 字節(jié)
0~65535
編譯的數(shù)據(jù)類型(如結(jié)構(gòu))包含上表所列的數(shù)據(jù)類型。由于8051系列是8位機(jī),因而不存在字節(jié)校準(zhǔn)問(wèn)題。這意味著數(shù)據(jù)結(jié)構(gòu)成員是順序放置的。
數(shù)據(jù)類型的轉(zhuǎn)換:當(dāng)計(jì)算結(jié)果隱含著另外一種數(shù)據(jù)類型時(shí),數(shù)據(jù)類型可以自動(dòng)進(jìn)行轉(zhuǎn)換,例如,將一個(gè)位變量賦給一個(gè)整型變量時(shí),位型值自動(dòng)轉(zhuǎn)換為整型值,有符號(hào)變量的符號(hào)也能自動(dòng)進(jìn)行處理。這些轉(zhuǎn)換也可以用C語(yǔ)言的標(biāo)準(zhǔn)指令進(jìn)行人工轉(zhuǎn)換。
1.2 數(shù)據(jù)類型的物理結(jié)構(gòu)
1.2.1 bit
“bit”類型只有1位,不允許有位指針和位數(shù)組。位對(duì)象始終位于8051 CPU的可尋址RAM空間。如果程序控制流允許,L51將位對(duì)象交迭。
1.2.2 signed/unsigned char;data/idata/pdata 指針
“char”類型標(biāo)量和基于存貯器的“data/idata/pdata”指針具有1個(gè)字節(jié)長(zhǎng)度(8 bits)。
1.2.3 signed/unsigned int/short;xdata/code 指針
“int”和“short”類型標(biāo)量及指向xdata/code區(qū)域的指針具有2字節(jié)長(zhǎng)度(16
bits)。
整型值(或偏移)0x1234以下面方式保存在內(nèi)存中:
地址: +0 +1
內(nèi)容: 0x12 0x34
1.2.4 signed/unsigned long
“long”類型標(biāo)量長(zhǎng)為4個(gè)字節(jié)(32 bits),值0x12345678以下面方式放置:
地址: +0 +1 +2 +3
內(nèi)容: 0x12 0x34 0x56 0x78
1.2.5 “一般”指針
“一般”指針包括3個(gè)字節(jié):2字節(jié)偏移和1字節(jié)存貯器類型:
地址: +0 +1 +2
內(nèi)容: 存貯器類型 偏移高位 偏移低位
第一個(gè)字節(jié)代表了指針的存貯器類型,存貯器類型編碼如下:
存貯器類型 IDATA XDATA PDATA DATA CODE
值 1 2 3 4 5
使用其它類型值可能導(dǎo)致不可預(yù)測(cè)的程序動(dòng)作。
XDATA類型的0x1234地址作為指針表示如下:
地址: +0 +1 +2
內(nèi)容: 0x02 0x12 0x34
當(dāng)用常數(shù)作指針時(shí),必須注意正確定義存貯器類型和偏移。下例將值0x41寫入絕對(duì)地址為0x8000的外部數(shù)據(jù)存貯器:
#define XBYTE ((char *)0x20000L)
XBYTE[0x8000]=0x41;
上例中用其它常數(shù)索引或索引變量也起作用。這樣,各種存貯器類型的絕對(duì)地址可以一種非常有效的方式訪問(wèn)。但有一個(gè)例外,即SFR。
注意:絕對(duì)地址定義為“long”型常量,低16位包含偏移,高8位表明了xdata類型。為了表示這種指針,必須用長(zhǎng)整數(shù)來(lái)定義存貯器類型。
C51編譯器不檢查指針常數(shù),用戶必須選擇有實(shí)際意義的值。
1.2.6 float
“float”類型為4個(gè)字節(jié)(32位),使用的格式與IEEE-754標(biāo)準(zhǔn)(32位)具有24位精度,尾數(shù)的高位始終為“1”,因而不保存,位的分布如下:
l 1位符號(hào)
l 8位指數(shù)位
l 23位尾數(shù)
符號(hào)位是最高位,尾數(shù)為最低的位,內(nèi)存中按字節(jié)存貯如下:
地址: +0 +1 +2 +3
內(nèi)容: MMMM MMMM MMMM MMMM E MMM MMMM S EEE EEEE
其中: S:符號(hào)位,1=負(fù),0=正
E:指數(shù)(在兩個(gè)字節(jié)中),偏移為127
M:23位尾數(shù),最高位“1”
浮點(diǎn)值——12.5的十六進(jìn)制為0xC1480000,它按下面方式存貯:
地址: +0 +1 +2 +3
內(nèi)容: 0x00 0x00 0x48 0xc1
8051不包括捕獲浮點(diǎn)錯(cuò)誤(例外)的中斷向量。用戶軟件因此必須對(duì)錯(cuò)誤條件作出適當(dāng)反應(yīng)。下面推薦一種方法(也可以用其它可靠辦法):“union”用來(lái)保存浮點(diǎn)值,這個(gè)“union”必須包括一個(gè)“float”和一個(gè)“unsigned long”,以根據(jù)IEEE對(duì)錯(cuò)誤作出響應(yīng)。除了通常浮點(diǎn)值外,IEEE標(biāo)準(zhǔn)可能出錯(cuò)的條件以下面二進(jìn)制值表示,為檢查可能出現(xiàn)的計(jì)算錯(cuò)誤,可在計(jì)算后進(jìn)行檢查。因?yàn)楫?dāng)執(zhí)行一個(gè)運(yùn)算時(shí)考慮了每個(gè)運(yùn)算符的錯(cuò)誤狀態(tài)并且該狀態(tài)被送到結(jié)果中。
NaN 0xFFFFFFF 不是一個(gè)數(shù)
+INF 0x7F80000 正無(wú)窮(正溢出)
-INF 0XFF80000 負(fù)無(wú)窮(負(fù)溢出)
1.3 C-51 的擴(kuò)充定義
1.3.1 特殊功能寄存器的聲明
MSC-51 系列包括多種寄存器,其中一些具有特殊功能,如定時(shí)器,端口的控制寄存器等,為了能夠直接訪問(wèn)這些寄存器,C51編譯器提供了一種定義的自主形式,這是必要的,因?yàn)檫@些定義與標(biāo)準(zhǔn)C語(yǔ)言是不兼容的。
為了支持這些特殊功能寄存器(SFR)的聲明,引入了關(guān)鍵詞“sfr”,語(yǔ)法如下:
sfr-dcl:sfr sfr_name=int_constant
例:
sfr p0=0x80;
sfr p1=0x90;
必須注意的是“sfr”后不是一個(gè)地址而是一個(gè)名字。因此上例中名字P0和P1(port0和port1)定義為特殊功能寄存器并被賦予相應(yīng)的絕對(duì)地址,名字可按意愿自由選取,源文件中不應(yīng)有先定義的sfr名字。
“=”號(hào)后的地址必須是常數(shù),不允許帶有運(yùn)算符的表達(dá)式,這個(gè)常數(shù)表達(dá)式必須在特殊功能寄存器的地址范圍內(nèi),位于0X80到0XFF之間。
8051系列寄存器數(shù)量和類型是極其不同的,因此建議將所有特別的“sfr”聲明放入一個(gè)頭文件,頭文件包括8051一些系列成員中的SFR定義。進(jìn)一步的定義可由用戶用一文件編輯器產(chǎn)生。
1.3.2 對(duì)SFR的16位數(shù)據(jù)訪問(wèn)
在新的8051系列產(chǎn)品中,SFR在功能上經(jīng)常組合為16位的,為了有效的訪問(wèn)這類SFR,使用定義“sfr16”,當(dāng)“SFR”的高端直接位于低端后時(shí),對(duì)SFR16位的訪問(wèn)是可能的。例如8052的定時(shí)器2就是這種情況,16位聲明的語(yǔ)法與“sfr”相同,SFR低地址部分必須作為sfr16的地址:
例:sfr16 T2=0xCC /*Timer2:T2L=0CCH,T2H=0CDH */
sfr16 RCAP2=0xCA /*RCAP2L=0CAH,PCAP2H=0CBH */
本例中,T2(由T2L和T2H組成)和RCAP2(由RCAP2L和RCAP2H組成)被定義為16位SFR,即使在這種情況下,聲明中的名字后仍不是賦值語(yǔ)句,而是一個(gè)SFR地址,高字節(jié)必須直接位于低字節(jié)之后,這種聲明適用于所有新的SFR,但不能用于Timer0和Timer1。
1.3.3 SBIT:特殊功能位聲明
在典型的8051應(yīng)用問(wèn)題中,經(jīng)常需要單獨(dú)訪問(wèn)SFR中的位,C51擴(kuò)充功能使之成為可能,特殊位,象SFR一樣,不與標(biāo)準(zhǔn)C語(yǔ)言兼容,使用保留字“sbit”可訪問(wèn)位尋址對(duì)象。與SFR聲明一樣,用保留字“sbit”聲明某些特殊位接受符號(hào)名,“=”后語(yǔ)句將絕對(duì)值地址賦給變量名,這種地址分配有三種方法:
方法1:sfr_name^int_constant
當(dāng)字節(jié)是特殊功能寄存器的地址可用這個(gè)方法。sfr_name必須是已定義的SFR的名字,“^”后的語(yǔ)句定義了基地址上的特殊位的位置,該位置必須是一個(gè)0~7的數(shù)。
例: sfr PSW="0xD0";
sfr LE="0xA8";
sbit OV="PSW"^2;
sbit CY="PSW"^7;
方法2:int_constant^int_constant
這種方法以一整常數(shù)作基地址,該值必須在0x80~0xFF之間,并能被8整除,確定位的位置方法同上。
例: sbit OV="0xD0"^2;
sbit CV="0xD0"^7;
sbit EA="0xA8"^7;
方法3: int_constant
這種方法是將位的絕對(duì)地址賦給變量,地址必須位于0x80~0xFF之間。
例: sbit OV="0xD2";
sbit CY="0xD7";
sbit EA="0xAF";
特殊功能位代表了一個(gè)獨(dú)立的聲明類,它不能與其它聲明和位域互換。
1.3.4 BIT:位標(biāo)量聲明
除了通常的C數(shù)據(jù)類型外,C51編譯器支持“bit”數(shù)據(jù)類型,對(duì)此有下列擴(kuò)充與限制:
(1) 函數(shù)可包含類型為“bit”的參數(shù),也可將其作為返回值。
bit bfunc(bit b0,bit b1){
/*……*/
return(b1);
}
注:使用禁止中斷(#pragma disable)或包含明確的寄存器組切換(using n)的函數(shù)不能返回位值,在這種情況下,編譯器會(huì)識(shí)別出來(lái)并產(chǎn)生一個(gè)錯(cuò)誤信息。
(2) 位標(biāo)量聲明的語(yǔ)法及C聲明的語(yǔ)義
static bit dirction_bit;
extern bit lock_printer_port;
bit display_invers;
(3) 對(duì)于位聲明的限制
l 位不能聲明為一個(gè)指針(bit *bit_poiter)
l 不存在位數(shù)組(bit b_array[5])
位聲明中允許定義存貯器類型,位都被放入一個(gè)位段,它總是在8051內(nèi)部RAM中,因此存貯器類型限制為DATA或IDATA,聲明為其它存貯器類型都將導(dǎo)致編譯出錯(cuò)。
1.3.5 可位尋址對(duì)象
可位尋址對(duì)象指可以字節(jié)或位尋址的對(duì)象,當(dāng)對(duì)象位于MSC-51可尋址RAM中時(shí)會(huì)有這種情況,C51允許帶“bdata”類型的對(duì)象放入可位尋址存貯器中。
bdata int ibase; /*位尋址指針 int*/
bdata char bary[4]; /*位尋址數(shù)組 arrray*/
使用“sbit”聲明可獨(dú)立訪問(wèn)可位尋址對(duì)象的位:
sbit mybit0=ibase^0;
sbit mybit15=ibase^15;
sbit ary07=bary[0]^7;
sbit ary37=bary[3]^7;
對(duì)象“ibase”和“bary”也可位尋址:
ary37=0; /*尋址“bary[3]”中的位7*/
ibase=-1; /*尋址字節(jié)地址*/
mybit15=0; /*尋址“ibase”的位15*/
sbit聲明要求基址對(duì)象的存貯器類型為“bdata”,否則只有絕對(duì)的位聲明方法是合法的。位位置(‘^’操作符號(hào)后)的最大值依賴于指定的基類型,這個(gè)值于char/uchar而言是0~7,對(duì)于int/uint/short/ushort而言是0~15,對(duì)于long/ulong而言是0~31。
在編譯器內(nèi)存貯器類型bdata與data一樣操作,并且只作與可再定位的sbit的運(yùn)算。注:可位尋址的的段長(zhǎng)最大不能超過(guò)16字節(jié),可再定位的sbit聲明自動(dòng)轉(zhuǎn)為公共的(PBULIC)以使它們能被其它C模塊使用。
模塊1: sbit ary37=bary[3]^7;
模塊2: extern bit ary37;
sbit聲明也可為結(jié)構(gòu)和函數(shù)所用:
union lft {float mf;long ml;} ;
bdata struct bad { char ml; union lft u; }tcp;
sbit tcpf31=tcp.u.ml^31; /*浮點(diǎn)限制*/
sbit tcpml0=tcp.ml^0;
sbit tcmpl7=tcp.ml.^7;
注:位位置的指定不能直接被float類型所用,如果需要這樣做,浮點(diǎn)標(biāo)量必須與一個(gè)長(zhǎng)整型標(biāo)量一起放入一個(gè)聯(lián)合中并且位位置必須由長(zhǎng)整型標(biāo)題指定(見(jiàn)上例)。
1.4 存貯器類型
C51編譯器完全支持8051微處理器及其系列的結(jié)構(gòu),可完全訪問(wèn)MCS-51硬件系統(tǒng)所有部分。每個(gè)變量可準(zhǔn)確地賦予不同的存貯器類型(data,idata,pdata,xdata,code)。訪問(wèn)內(nèi)部數(shù)據(jù)存貯器(idata)要比訪問(wèn)外部數(shù)據(jù)存貯器(xdata)相對(duì)要快一些,因此,可將經(jīng)常使用的變量置于內(nèi)部數(shù)據(jù)存貯器中,而將較大及很少使用的數(shù)據(jù)單元置于外部數(shù)據(jù)存貯器中。
存貯器類型
描 述
data
直接尋址內(nèi)部數(shù)據(jù)存貯器,訪問(wèn)變量速度最快(128bytes)
bdata
可位尋址內(nèi)部數(shù)據(jù)存貯器,允許位與字節(jié)混合訪問(wèn)(16 bytes)
iIdata
間接尋址內(nèi)部數(shù)據(jù)存貯器,可訪問(wèn)全部地址空間(256bytes)
pPdata
分頁(yè)(256bytes)外部數(shù)據(jù)存貯器,由操作碼MOVX @Ri訪問(wèn)
xdata
外部數(shù)據(jù)存貯器(64K),由MOVX @DPTR訪問(wèn)
code
代碼數(shù)據(jù)存貯器(64K),由MOVC @A+DPTR訪問(wèn)
變量說(shuō)明舉例:
data char charvar;
char code msg[]=”ENTER PARAMETER:”;
unsigned long xdata array[100];
float idata x,y,z;
unsigned char xdata vector[10][4][4];
sfr p0=0x80;
sbit RI="0x98";
char bdata flags;
sbit flago="flags"^0;
如果在變量說(shuō)明時(shí)略去存貯器類型標(biāo)志符,編譯器會(huì)自動(dòng)選擇默認(rèn)的存貯器類型。默認(rèn)的存貯器類型進(jìn)一步由控制指令SMALL、COMPACT和LARGE限制。例如:如果聲明char charvar,則默認(rèn)的存貯器模式為SMALL,charvar放在data存貯器;如果使用COMPACT模式,則charvar放入idata存貯區(qū);在使用LARGE模式的情況下,charvar被放入外部存貯區(qū)或xdata存貯區(qū)。
1.5 存貯器模式
存貯器模式?jīng)Q定了自動(dòng)變量和默認(rèn)存貯器類型,參數(shù)傳遞區(qū)和無(wú)明確存貯區(qū)類型的說(shuō)明。在固定的存貯器地址變量參數(shù)傳遞是C51的一個(gè)標(biāo)準(zhǔn)特征,在SMALL模式下參數(shù)傳遞是在內(nèi)部數(shù)據(jù)存貯區(qū)中完成的。LARGRE和COMPACT模式允許參數(shù)在外部存貯器中傳遞。C51同時(shí)也支持混合模式,例如在LARGE模式下生成的程序可將一些函數(shù)分頁(yè)放入SMALL模式中從而加快執(zhí)行速度。
存貯器模式
描 述
SMALL
參數(shù)及局部變量放入可直接尋址的內(nèi)部寄存器(最大128bytes,默認(rèn)存貯器類型是DATA)
COMAPCT
參數(shù)及局部變量放入分頁(yè)外內(nèi)部存貯區(qū)(最大256bytes,默認(rèn)存貯器類型是PDATA)
LARGE
參數(shù)及局部變量直接放入外部數(shù)據(jù)存貯器(最大64K,默認(rèn)存貯器類型是XDATA)
1.6 指針
Franklin C-51支持“基于存貯器的”和“一般指針”。
1.6.1 基于存貯器的指針
基于存貯器的指針由C源代碼中存貯器類型決定并在編譯時(shí)確定,用這種指針可高效訪問(wèn)對(duì)象且只需一個(gè)字節(jié)(idata*,data*,pdata*)或2個(gè)字節(jié)(code*,xdata*)。操作較短指針的代碼被縮短,一般被“內(nèi)行”編碼;庫(kù)調(diào)用不再必要。
聲明舉例:
char xdata *pt
在xdata存貯器中聲明一個(gè)指向?qū)ο箢愋蜑?ldquo;char”的指針。指針默認(rèn)自身在默認(rèn)存貯區(qū)(決定于編譯模式),長(zhǎng)度為2字節(jié)。(值為0~0xFFFF)
char xdata *data pdx;
除了指針明確位于內(nèi)部數(shù)據(jù)存貯器(data)中外,與上例相同。它與編譯模式無(wú)關(guān)。
data char xdata *pdx;
本例與上例完全相同。存貯器類型定義既可放在聲明的開(kāi)頭也可直接放在聲明的對(duì)象之前。這種形式是為了與早期C-51編譯器版本兼容。
上面例子闡明了指針的一般聲明及使用。它們與所有的數(shù)據(jù)類型和存貯器類型相關(guān)。所有用于一般指針的操作同樣可用于基于存貯器的指針。
1.6.2 一般指針
“一般”指針需3個(gè)字節(jié):1個(gè)字節(jié)為存貯類型,2個(gè)字節(jié)為偏移量。存貯器類型決定了對(duì)象所用的8051存貯器空間,偏移量指向?qū)嶋H地址。一個(gè)“一般”指針可訪問(wèn)任何變量而不管它在8051存貯器空間中的位置。這樣就允許一般性函數(shù),如memcpy將數(shù)據(jù)從任意一個(gè)地址拷貝到另一個(gè)地址空間。
1.6.3 基于存貯器的指針與一般指針的轉(zhuǎn)換
一個(gè)已定位指針可轉(zhuǎn)換為一個(gè)一般指針(3字節(jié))及副本。這在某些時(shí)候是很有用的。例如庫(kù)函數(shù)包含一個(gè)一般指針變量,象printf(),sprintf(),gets()等包含一個(gè)一般指針變量,如同以前的編譯器和庫(kù)版本一樣。這些函數(shù)因而廣泛適用。
例:extern int printf(void *format,…);
在printf調(diào)用中,2字節(jié)指針自動(dòng)轉(zhuǎn)換為一個(gè)3字節(jié)指針,而printf的原型正需要一個(gè)一般指針(3字節(jié))作為其第一參量。
注:如果沒(méi)有函數(shù)原型,函數(shù)調(diào)用的參量中指針總是轉(zhuǎn)換為一般指針。如果函數(shù)確實(shí)需要一個(gè)短指針作參量,這會(huì)產(chǎn)生錯(cuò)誤,為了避免在程序中發(fā)生這類錯(cuò)誤,需要使用頭文件,或某些函數(shù)聲明函數(shù)原型。這將保證讓編譯器轉(zhuǎn)換為所需類型,否則會(huì)產(chǎn)生類型不匹配錯(cuò)誤。
例:指針定義說(shuō)明
指 針 說(shuō) 明
長(zhǎng) 度
指 向
float *p;
3 字節(jié)
所有8051存貯器空間中的“float”(一般指針)
char data *dp;
1字節(jié)
“data”存貯器中的“char”
int idata *ip;
1字節(jié)
“idata”中的“int”
long pdata *pp;
1字節(jié)
“pdata”中的“long”
char xdata *xp;
2字節(jié)
“xdata”中的“char”
int code *cp;
2字節(jié)
“code”中的“int”
1.6.4 抽象指針類型
抽象指針類型用來(lái)在每個(gè)存貯區(qū)訪問(wèn)任意絕對(duì)地址,或來(lái)產(chǎn)生絕對(duì)CALLs。在這個(gè)過(guò)程中,常數(shù)類型或字符型、整型都用抽象類型作了原則性修改(類型整理)以允許進(jìn)行絕對(duì)訪問(wèn)或調(diào)用。
例:
char xdata *px;
char idata *pi;
char code *pc;
char c;
int i;
pc = (void*)main;
i = ((int(code*)(void))0xFF00(); /*LCALL 0FF00H */
c = *((char code*)0x8000); /*char [code[0x8000]]*/
i = *((int code*)0x1200); /*int from code[0x1200]*/
px = *((char xdata *xdata*)0x4000); /*x ptr from xdata[0x4000]*/
px = ((char xdata *xdata*)0x4000)[0]; /*同上*/
px = ((char xdata *xdata *)0x4000)[1] /*x ptr from xdata[0x4002]*/
1.7 寄存器組定義
8051系列的器件包含4個(gè)相同的寄存器組,每個(gè)寄存器組包括8個(gè)寄存器(R0~R7),C51編譯器可使在一函數(shù)中決定用哪一寄存器組成為可能。絕對(duì)寄存器的訪問(wèn)可用AREGS/NOAREGS和REGISTERBANK來(lái)控制。
定義一個(gè)帶擴(kuò)展性的函數(shù)語(yǔ)法如下:
返回類型 函數(shù)名([參數(shù)])[模式][再入][中斷 n]using n
再入和中斷將在后兩節(jié)討論。
例:void rb_function(void) using 3;
“using”不允許用于外部函數(shù),它對(duì)函數(shù)的目標(biāo)代碼影響如下:
l 函數(shù)入口處將當(dāng)前寄存器保存入棧;
l 指它的寄存器還會(huì)改變;
l 函數(shù)退出前寄存器組被恢復(fù)。
“using”定義對(duì)于返回一個(gè)寄存器內(nèi)的值的函數(shù)是無(wú)用的。編程者必須十分小心以保證任何寄存器切換都只在仔細(xì)控制的區(qū)域發(fā)生。如果不做到這一點(diǎn)將會(huì)產(chǎn)生不正確的函數(shù)結(jié)果。即使當(dāng)編程者使用同一寄存器組時(shí),帶“using”屬性的函數(shù)原則上也不能返回一個(gè)位值。
實(shí)際產(chǎn)生的代碼決定于編譯器及不同開(kāi)關(guān)條件,有時(shí)用命令產(chǎn)生絕對(duì)的寄存器地址,當(dāng)需要進(jìn)行這樣的地址計(jì)算時(shí),使用REGISTERBANK指令的影響只是計(jì)算Arn寄存器使用的地址,而必進(jìn)行實(shí)際切換。
1.8 中斷服務(wù)程序
C51編譯器及其對(duì)C語(yǔ)言的擴(kuò)充允許編程者對(duì)中斷的所有方面進(jìn)行控制。這種支持能使系統(tǒng)編程者創(chuàng)建高效的中斷服務(wù)程序,用戶只需在普通和高級(jí)方式下關(guān)心中斷及必要的寄存器組切換操作,C51編譯器將產(chǎn)生最合適的代碼。
1.8.1 中斷服務(wù)程序的定義
使用中斷服務(wù)函數(shù)的完整語(yǔ)法如下:
返回值 函數(shù)名([參數(shù)])[模式][再入] interrupt n[using n]
“interrupt”后接一個(gè)0~31的常數(shù),不允許使用表達(dá)式。
中斷不允許用于外部函數(shù),它對(duì)函數(shù)目標(biāo)代碼的影響如下:
l 當(dāng)使用函數(shù)時(shí),SFR中的ACC、B、DPH、DPL和PSW(當(dāng)需要時(shí))入棧;
l 如不使用寄存器組切換,甚至中斷函數(shù)所需的所有工作寄存器(Rn)都入棧;
l 函數(shù)退出前,所有的寄存器內(nèi)容出棧;
l 函數(shù)由8051控制命令“RETI”終止。
1.8.2 開(kāi)發(fā)中斷過(guò)程時(shí)的規(guī)則
l 不能進(jìn)行參數(shù)傳遞,如果中斷過(guò)程包括任何參數(shù)聲明,編譯器將產(chǎn)生一個(gè)錯(cuò)誤信息;
l 無(wú)返回值,如果想定義一個(gè)返回值將產(chǎn)生錯(cuò)誤,然而,如果返回整型值編譯器將不產(chǎn)生錯(cuò)誤信息,因?yàn)檎椭凳悄J(rèn)值,因而編譯器不能清楚識(shí)別。
l 編譯器會(huì)識(shí)別對(duì)中斷過(guò)程的直接調(diào)用并拒絕它們,在任何情況下不能直接調(diào)用中斷過(guò)程,因?yàn)橥顺鲈撨^(guò)程是由操作碼RETI完成的。RETI影響8051芯片的硬件中斷系統(tǒng),由于硬件上沒(méi)有中斷請(qǐng)求存在,因而這個(gè)操作碼的結(jié)果是不定的并且通常是致命的。由于疏忽,可能用指針來(lái)間接調(diào)用它,這是值得注意的。
l 編譯器從絕對(duì)地址8n+3處產(chǎn)生一個(gè)中斷向量,其中n為中斷號(hào),該向量包括一個(gè)到中斷過(guò)程的跳轉(zhuǎn),向量的產(chǎn)生可由指令NOINTVECTOR壓縮。因而用戶有能力從獨(dú)立的匯編模塊中提供中斷向量。
l C51編譯器允許0~31個(gè)中斷,究竟允許哪些中斷依賴于使用的8051系列芯片,編譯器不能檢查。
l 如果中斷程序中有浮點(diǎn)運(yùn)算,必須保持浮點(diǎn)寄存器狀態(tài),當(dāng)沒(méi)有其它程序執(zhí)行浮點(diǎn)運(yùn)算時(shí),可能不保存,函數(shù)“fsave”和“fprestore”用來(lái)保存浮點(diǎn)狀態(tài)。
l 中斷過(guò)程調(diào)用的函數(shù)所使用的寄存器必須與中斷過(guò)程相同,當(dāng)沒(méi)有使用“using”指令時(shí),編譯器會(huì)選擇一個(gè)寄存器組作絕對(duì)寄存器訪問(wèn),當(dāng)子程序使用另一個(gè)寄存器組時(shí)會(huì)發(fā)生錯(cuò)誤,用戶必須保證按要求使用相應(yīng)寄存器組,C編譯器不會(huì)對(duì)此檢查。
例:
unsigned int interruptent;
unsigned char second;
time() interrupt 1 using 2 /*定時(shí)器0中斷服務(wù)程序,工作寄存器使用2區(qū)*/
{
if(++interruptcnt==4000) {
second++; /*秒計(jì)數(shù)加一*/
interruptcnt=0; /*清中斷計(jì)數(shù)*/
}
}
1.9 再入函數(shù)
再入函數(shù)可被遞歸調(diào)用,調(diào)用可發(fā)生在任何時(shí)候,即使是在中斷過(guò)程中。在實(shí)時(shí)處理的應(yīng)用問(wèn)題中常常需要再入函數(shù)。
使用關(guān)鍵字“reentrant”可有選擇地定義函數(shù)有再入能力。在存貯器模式的基礎(chǔ)上為再入函數(shù)在內(nèi)部或外部存貯器中模擬了一個(gè)棧區(qū)域。由于MCS-51缺乏合適的尋址方法,使用棧結(jié)構(gòu)是相當(dāng)必要的。因而應(yīng)盡量少用再入函數(shù)。
定義一再入函數(shù)的語(yǔ)法如下:
返回值 函數(shù)名([參數(shù)])[模式]reetrant[interrupt n][using n]
例:
int calc(char i,int b) reentrant {
int x;
x=table[i];
return(x*b);
}
使用再入函數(shù)有如下規(guī)定:
l 不能傳遞類型為“bit”的參數(shù)。也不能聲明一個(gè)局部標(biāo)量,再入功能不能包括位操作及MCS-51可位尋址區(qū)域。
l 不能在“alien”函數(shù)調(diào)用再入函數(shù)。
l 再入函數(shù)可同時(shí)有其它屬性,如“using”函數(shù)模式和“interrupt”。
l 再入函數(shù)不能同時(shí)有“alien”屬性,從而遵守PL/M規(guī)則。
l 返回地址及可能的PUSH/POP操作存入MCS-51的棧中或被執(zhí)行(不在再入棧中)。
l 在同一模塊中,任意模塊的再入函數(shù)(small reentrant,lage reentrant,compact reentrant)不能與具有不同模式的再入函數(shù)混合。
再入函數(shù)舉例:
/*這個(gè)再入函數(shù)可以從“main”及中斷程序中調(diào)用*/
int calc(char i,int b)reentrant {
int x;
x=table[i];
return(x*b);
}
1.10 參數(shù)傳遞
通過(guò)CPU的寄存器可傳遞至多三個(gè)參數(shù)。這樣產(chǎn)生與匯編子程序相當(dāng)?shù)挠行?shù)機(jī)制。如果寄存器被占用,或說(shuō)明了“#pragma NOREGPARMS”,參數(shù)變量將使用固定的存貯器位置,存貯器模式?jīng)Q定了8051存貯器為參數(shù)提供的位置。
表:候選的參數(shù)寄存器
參數(shù)類型
char,1字節(jié)指針
int,2字節(jié)指針
long,float
一般指針
一個(gè)參數(shù)
R7
R6,R7
R4~R7
R1,R2,R3
二個(gè)參數(shù)
R5
R4,R5
R4~R7
R1,R2,R3
三個(gè)參數(shù)
R3
R2,R3
…
R1,R2,R3
函數(shù)的返回值放在CPU固定的寄存器中,列表如下。這樣,與匯編子程序的接口變得非常容易。
表:函數(shù)返回值的寄存器用法
返 回 值
寄 存 器
意 義
bit
進(jìn)位標(biāo)志 C
(unsigned) char
R7
(unsigned) int
R6,R7
高位在R6,低位在R7
(unsigned) long
R4~R7
高位在R4,低位在R7
float
R4~R7
32位IEEE格式
指 針
R1,R2,R3
類型選擇在R3,高位在R2,低位在R1
1.11 PL/M51接口
Franklin C51利用關(guān)鍵字“alien”提供了一個(gè)與Intel PL/M-51直接和簡(jiǎn)單和接口,關(guān)鍵字“alien”在所有存貯器模式下可用于“extern”和“public”函數(shù)。現(xiàn)有的PL/M-51程序利用C語(yǔ)言的強(qiáng)大功能可與Franklin C-51連接起來(lái)。
使用關(guān)鍵字“alien”,C51可用PL/M-51規(guī)定的參數(shù)傳遞方式工作。“alien”可用于外部或公共函數(shù),并可用于任一模式,這樣,已有的PL/M-51程序可加入到C-51中。Alien函數(shù)始終包含一個(gè)標(biāo)準(zhǔn)的參數(shù)數(shù)量,因此,C中定義的三點(diǎn)(…)記號(hào)不被接受,且會(huì)產(chǎn)生一個(gè)錯(cuò)誤信息。
例:
extern alien char plm_function(unsigned char,unsigned int);
extern char c_function(unsigned char x,unsigned char y) {
return(x*y);
}
PL/M-51兼容函數(shù)必須定義以關(guān)鍵字“alien”。這樣,PL/M函數(shù)的參數(shù)傳遞及參數(shù)返回規(guī)定在C編譯器中才被考慮。
1.12 匯編接口
參數(shù)是通過(guò)固定的CPU寄存器傳給匯編程序的,當(dāng)使用“#pragma NOREGPARMS”時(shí),則通過(guò)固定的存貯器位置傳遞參數(shù)。這樣就給匯編與Franklin C-51之間提供了一個(gè)非常簡(jiǎn)潔的接口。返回值在CPU寄存器中。
下例為在匯編中用來(lái)編碼的“toupper”函數(shù),參數(shù)傳遞發(fā)生在寄存器中。
UPPER SEGMENT CODE ;程序代碼段
PUBLIC _toupper ;入口地址
RESG UPPER ;選擇程序代碼段
toupper: MOV A,R7 ;char 參數(shù)在寄存器R7中
CJNE A,#’a’,UPP1
UPP1: JC UPPERT
CJNE A,#’z’+1,UPP2
UPP2: JNE UPPRET
CLR ACC.5
UPPRET: MOV R7,A ;char 返回值在寄存器R7中
RET ;返回C
1.13 內(nèi)部函數(shù)
Franklin C-51支持下列內(nèi)部函數(shù)。內(nèi)部函數(shù)既是再入的又是有效的。
表:C51的內(nèi)部函數(shù)
函 數(shù)
描 述
memcpy,memsset,memchr,memmove,memcmp
ANSI的內(nèi)存操作功能
strcmp,strcpy
ANSI字符串處理功能
_crol_,_irol_,lrol_
左移字符、整數(shù)、長(zhǎng)整數(shù)
_crolr_,_irolr_,lrolr_
右移字符、整數(shù)、長(zhǎng)整數(shù)
_nop_
空操作
_testbit_
測(cè)試并清位(JBC指令)
1.14 代碼優(yōu)化
Franklin C51可將即使有經(jīng)驗(yàn)的程序員編制的代碼進(jìn)行優(yōu)化。用戶可選6個(gè)優(yōu)化級(jí),另外,用OPTIMIZE(SIZE),NOREGPARMS和NOAREGS時(shí)會(huì)影響生成代碼的類型。C51的所有優(yōu)化如下:
(1) 一般優(yōu)化:
l 常數(shù)折迭:發(fā)生在一個(gè)表達(dá)式或地址計(jì)算中的幾個(gè)常數(shù)值組合為一個(gè)常數(shù)。
l 跳轉(zhuǎn)優(yōu)化:跳轉(zhuǎn)轉(zhuǎn)到最終的目標(biāo)地址,以提高程序效率。
l 死碼消除:不可執(zhí)行代碼(死碼)可從程序中去掉。
l 寄存器變量:只要有可能,自動(dòng)變量和參量放入寄存器中,為這些變量保留的數(shù)據(jù)存貯器將去除。
l 通過(guò)寄存器傳遞參數(shù):寄存器中可傳遞最多三個(gè)參數(shù)。
l 全局公共子式消除:相同的子表達(dá)式或地址計(jì)算(多次發(fā)生在同一函數(shù)中)將被識(shí)別出來(lái),并且只要有可能,將只計(jì)算一次。
(2) 基于8051的優(yōu)化:
l 窺孔(PEEPHOLE)優(yōu)化:只要能節(jié)省存貯空間或執(zhí)行時(shí)間,復(fù)雜的運(yùn)算都將化簡(jiǎn)。
l 訪問(wèn)優(yōu)化:常數(shù)和變量直接包含在操作中。
l 數(shù)據(jù)覆蓋:函數(shù)的數(shù)據(jù)和位移被標(biāo)記為OVERLAYABLE,被L51用其它數(shù)據(jù)和位覆蓋。
l CASE/SWITCH優(yōu)化:SWITCH/CASE語(yǔ)句優(yōu)化為一個(gè)跳轉(zhuǎn)或一串跳轉(zhuǎn)。
(3) 代碼生成選項(xiàng):
l OPTMIZE(SIZE):共同的“C”操作被子程序代替:程序碼長(zhǎng)被壓縮。
l NOAREGS:不使用絕對(duì)寄存器訪問(wèn),程序代碼在這種方式下獨(dú)立于寄存器組。
l NOREGPARMS:參數(shù)傳遞總是在本數(shù)據(jù)段完成,程序代碼與早期C-51版本兼容。
1.15 C庫(kù)
C-51編譯器包含6個(gè)不同的編譯庫(kù),可根據(jù)不同函數(shù)的需要進(jìn)行優(yōu)化,這些庫(kù)幾乎支持所有的ANSI函數(shù)調(diào)用。因此,用此標(biāo)準(zhǔn)的C程序可在編譯和連接后立即運(yùn)行。
庫(kù)
描 述
C51S.LIB
SMALL模式,無(wú)浮點(diǎn)運(yùn)算
C51FPS.LIB
浮點(diǎn)數(shù)學(xué)運(yùn)算庫(kù)(SMALL模式)
C51C.LIB
COMPACT模式,無(wú)浮點(diǎn)運(yùn)算
C51FPC.LIB
浮點(diǎn)運(yùn)算庫(kù)(COMPACT模式)
C51L.LIB
LARGE模式,無(wú)浮點(diǎn)運(yùn)算
C51FPL.LIB
浮點(diǎn)運(yùn)算庫(kù)(LARGE模式)
C51編譯器包含的庫(kù)模塊,都有源代碼,對(duì)它們可作與硬件相關(guān)的修改。用戶改變對(duì)于現(xiàn)有硬件輸入和輸出結(jié)構(gòu)的兩個(gè)模塊,就可修改所有庫(kù)函數(shù),同樣也可以重新很快地構(gòu)造如“printf”和“puts”函數(shù)用LCD顯示。
L51連接器的檢查從而保證所有模塊都用一種模式編譯并自動(dòng)選擇編譯庫(kù),從而使用戶完全可以不用不同庫(kù)的細(xì)節(jié)。
1.16 配置文件
C51編譯器可根據(jù)不同的硬件環(huán)境由4個(gè)文件作出修改。下列配置文件包括在C-51軟件包中:
STARTUP51.51:
C51編譯器的啟動(dòng)程序,所有的棧指針和存貯器,只要需要,將被初始化。
INIT.A51:
在文件中已明確初始化了變量作初始化。如果系統(tǒng)裝入“看門狗”,該文件可包含附加的“看門狗”刷新。
PUBCHAR.C:
函數(shù)“printf”、“puts”等的字符輸出核心程序,該程序可根據(jù)用戶硬件加以修改(如LCD顯示)。
GETKEY.C:
函數(shù)“getchar”、“scanf”等的字符輸入核心程序,該程序可根據(jù)硬件加以修改(如矩陣鍵盤)。
所有的文件都包含在C運(yùn)行庫(kù)中,因此,不能在連接時(shí)指定調(diào)用。如果用戶改變一個(gè)文件,可將其編譯后與其它目標(biāo)文件一起連接,因而不必改動(dòng)運(yùn)行庫(kù)。庫(kù)中原文件自動(dòng)忽略。
例:
L51 DEMO1.OBJ,DEMO2.OBJ,STARTUP.OBJ,PUTCHAR.OBJ
本例將用戶建立的STARTUP.OBJ和PUTCHAR.OBJ連接起來(lái)
STARTUP.A51
文件STARTUP.51開(kāi)頭包含一些C編譯結(jié)構(gòu)使用的EQU語(yǔ)句。每個(gè)EQU語(yǔ)句的功能描述如下:
IDATALEN 聲明系統(tǒng)開(kāi)始時(shí)有多少內(nèi)存需要用0初始化。默認(rèn)值為80H,因?yàn)閹缀趺總€(gè)
8051指令至少包含128字節(jié)內(nèi)部RAM。對(duì)于256字節(jié)內(nèi)部RAM的8052可使用100H。當(dāng)用戶程序在開(kāi)始時(shí)需要使用0初始化的內(nèi)存時(shí)才有必要作改動(dòng)。如果內(nèi)存初始化必須保持對(duì)掉電模式系統(tǒng)的完全抑制,IDATALEN應(yīng)設(shè)為0。這種情況下至少得保持所有位于段?C_LIB_DATA和?C_LIB_DBIT中的變量都置為0。否則有些庫(kù)函數(shù)不能完全發(fā)揮作用,?C_LIB_DATA段的長(zhǎng)度因不同應(yīng)用問(wèn)題而不同,其當(dāng)前長(zhǎng)度可在MAP文件中找到。
XDATASTART
XDATALEN 表明了需要以0初始化的PDATA區(qū)首址和長(zhǎng)度,XDATASTART指明了XDATA區(qū)首址,XDATALEN表明了需初始化的字節(jié)數(shù)。
PDATASTART
PDATALEN 表明了需以0初始化的PDATA區(qū)首址及長(zhǎng)度,PDATASTART指明了首址,XDATALEN指定了長(zhǎng)度。
LBPSTACK
LBPSTACKTOP 定義了SMALL模式下創(chuàng)建的再入函數(shù)使用的棧區(qū)。LBPSTACK表明是否對(duì)棧指針(變量?C_LBP)初始化,LBPSTACKTOP指明了棧頂首址。對(duì)于具有256字節(jié)內(nèi)部RAM的8051系統(tǒng),當(dāng)存貯區(qū)作首址為0XFF的棧時(shí),可不初始化。C51不作棧區(qū)是否滿足特定應(yīng)用的檢查,用戶必須自己進(jìn)行測(cè)試。
XBPSTACK
XBPSTACKTOP 為在LARGE模式下創(chuàng)建的再入函數(shù)定義了棧區(qū),XBPSTACK表明指針(變量?C_XBP)是否初始化,XBPSTACKTOP指定了棧頂?shù)刂贰.?dāng)存貯區(qū)作為首址為0Xffff(在XDATA區(qū))的棧時(shí),可不作初始化。同上一樣,C51不作棧檢查,需要用戶自己測(cè)試。
PBPSTACK
PBPSTACKTOP 為在COMPACT模式下創(chuàng)建的再入函數(shù)定義了棧區(qū),PBPSTACK表明棧指針(變量?C_PBP)是否初始化。PBPSTACKTOP指定了棧頂?shù)刂?。?dāng)存貯區(qū)作為首址為0Xff(在PDATA區(qū))的棧時(shí),可不作初始化。同上一樣,C51不作棧檢查,需要用戶自己測(cè)試。
PPAGEENABLE
PPAGE 當(dāng)在COMPACT模式中用16位尋址XDATA存貯區(qū)時(shí)需要這些指令。對(duì)于使用LARGE模式的程序,可用它提高運(yùn)行速度或減小代碼長(zhǎng)度。PPAGEENABLE允許8051端口2的初始化,對(duì)端口2的尋址允許在任意XDATA頁(yè)256字節(jié)變量空間的映射。這兩個(gè)指令必須和L51的控制指令PDATA一起使用。PDATA指定了XDATA存貯器中PDATA區(qū)的首址。例:在STARTUP.A51中,PPAGEENABLE置為1,PPAGE置為10H。這種情況下PDATA區(qū)首址為1000H(10H頁(yè)),而L51必須包含一個(gè)值在1000和10FFH之間的控制語(yǔ)句:L51〈輸入模塊〉PDATA(1050H)。注:L51和C51都不對(duì)PPAGE/PDATA指令正確性進(jìn)行檢查,用戶必須保證PPAGE和PDATA包含一個(gè)合適的值。
INIT.A51:
文件INIT.A51包含一個(gè)定義了“看門狗”刷新的宏。當(dāng)系統(tǒng)包括“看門狗”以及用戶變量初始化時(shí)間比“看門狗”刷新時(shí)間要長(zhǎng)時(shí),必須改變這個(gè)宏。這種情況下,宏WATCHCOG必須包含“看門狗”刷新的代碼。
例: ;Watchdog refresh for 80515 system
WATCHDOG MACRO
SETB WDT
SETB SWDT
ENDM
PUTCHAR.C
文件PUTCHAR.C包含字符輸出的核心程序,該文件通過(guò)串行口輸出。這種情況下考慮了XON/XOFF協(xié)議,字符LF()被轉(zhuǎn)為字符串CR,LF,這在很多終端中是需要的。用戶可按自己的要求改變putchar()函數(shù)。
GETKEY.C
文件GETKEY.C包含字符輸入的核心程序,該文件從串行接口讀入一個(gè)字符,不作數(shù)據(jù)轉(zhuǎn)換,用戶可根據(jù)需要修改getkey()函數(shù)。
本節(jié)包含幾個(gè)怎樣提高8051程序效率的注解。
定位變量
經(jīng)常訪問(wèn)的數(shù)據(jù)對(duì)象應(yīng)放入在片內(nèi)數(shù)據(jù)RAM中,這可在任一模式(COMPACT/LARGE)下用輸入存貯器類型的方法實(shí)現(xiàn)。訪問(wèn)片內(nèi)數(shù)據(jù)RAM要比訪問(wèn)外部數(shù)據(jù)存貯器快得多。片內(nèi)RAM由寄存器組,位數(shù)據(jù)區(qū)棧和其它由用戶用“data”類型定義的變量共享。由于片內(nèi)RAM容量的限制(128~256字節(jié),由使用的處理器決定),必須權(quán)衡利弊以解決訪問(wèn)效率和這些對(duì)象的數(shù)量之間的矛盾。
總是使用可能的最小數(shù)據(jù)類型
8051系列CPU都是8位機(jī),因此,顯然對(duì)具有“char”類型的對(duì)象的操作比“int”或“long”類型的對(duì)象方便得多。建議編程者只要滿足要求,應(yīng)盡量使用最小數(shù)據(jù)類型。
C51編譯器直接支持所有的字節(jié)操作,因而如果不是運(yùn)算符要求,就不作“int”類型的轉(zhuǎn)換,這可用一個(gè)乘積運(yùn)算來(lái)清楚說(shuō)明,兩個(gè)“char類型”對(duì)象的乘積與8051操作碼“MUL AB”剛好相符。如果用整型量完成同樣的運(yùn)算,則需要調(diào)用庫(kù)函數(shù)。
只要有可能,使用“unsigned”數(shù)據(jù)類型
8051系列CPU并不直接支持有符號(hào)數(shù)的運(yùn)算。因而C51編譯器必須產(chǎn)生與之相關(guān)的更多的代碼以解決這個(gè)問(wèn)題。如果使用無(wú)符號(hào)類型,產(chǎn)生的代碼要少得多。
只要有可能,使用局部函數(shù)變量
編譯器總是嘗試在寄存器里保持局中變量。這樣,將索引變量(如FOR和WHILE循環(huán)中計(jì)數(shù)變量)聲明為局部變量是最好的,這個(gè)優(yōu)化步驟只為局部變量執(zhí)行。使用“unsigned char/int”的對(duì)象通常能獲得最好的結(jié)果。
2 C51編譯器控制指令
編譯選項(xiàng)可能被控制指令激活、禁止或改變。這些指令可在命令行輸入或在源文件上加入#pragma給預(yù)處理器??刂浦噶罘譃閮山M,即首要的和一般的,并可分為三類:源控制、目標(biāo)控制、列表控制。見(jiàn)表2.1編譯控制指令。“P/G”列表明指令是首要的或是一般的,首要指令僅出現(xiàn)一次,一般指令按需要可出現(xiàn)多次。
表2.1 編譯控制指令
分 類
P/G
指 令
縮 寫
默 認(rèn) 值
源
P
G
G
G
P
DEFINE
SAVE
RESTORE
DISABLE
[NO]EXTEND
DF
—
—
—
——
—
—
—
—
EXTEND
目 標(biāo)
P
P
P
P
P
P
G
G
G
P
P
P
[NO]DEBUG
[NO]OBJECT
OPTIMIZE(n)
SMALL
COMPACT
LARGE
[NO]REGPARMS
REGISTERBANK(n)
[NO]AREGS
[NO]INTVECTER
OBJECTEXTEND
ROM()
[NO]DB
[NO]OJ
OT
SM
CP
LA
[NO]RP
RB
[NO]AR
[NO]IV
OE
—
NODEBUG
OBJECT(名字.OBJ)
OPTIMIZE(2)
SMALL
—
—
REGPARMS
REGISTERBANK(0)
AREGS
INTVECTOR
—
ROM(LARGE)
列 表
P
P
P
P
P
P
P
G
P
[NO]LISTINCLUDE
[NO]SYMBOLS
[NO]PREPRINT
[NO]CODE
[NO]PRINT
[NO]CODE
PAGELENGTH(n)
EJECT
PAGEWIDTH(n)
[NO]LC
[NO]SB
[NO]PP
[NO]CD
[NO]PR
[NO]CO
PL
EJ
PW
NOLISTINCLUDE
NOSYMBOLS
NOPREPRINT
NOCODE
PRINT(名字.OBJ)
CODE
PAGELENGTH(69)
—
PAGEWIDTH(132)
2.1 源控制
2.1.1 DEFINE
名 字: DEFINE
縮 寫: DF
變 量: 一個(gè)或幾個(gè)由逗號(hào)分開(kāi)的名字,與C語(yǔ)言的命令規(guī)格一致。
默認(rèn)值: 無(wú)
DEFINE 指令定義了在命令行使用的名字,可為“if”,“ifdef”及“ifndef”等預(yù)處理命令作條件編譯,定義的名字被精確拷貝,因而對(duì)大小寫敏感。
例: C51 DEMO.C DEFINE(check,NoExtRam)
2.1.2 SAVE/RESTORE
名 字: SAVE/RESTORE
縮 寫: 無(wú)
變 量: 無(wú)
默認(rèn)值: 無(wú)
SAVE指令存儲(chǔ)當(dāng)前的ARGES,REGPARMS,OPTIMIZE因子和優(yōu)化選項(xiàng)的SPEEDSIZE設(shè)置。這樣,上面所述的設(shè)置被保留下來(lái),例如在#INCLUDE語(yǔ)句之前用SAVE保護(hù),然后用RESTORE指令恢復(fù)。
SAVE/RESTORE只能在源文件中以#pragma語(yǔ)句的參數(shù)形式出現(xiàn),而不能用于命令行。
例:
#pragma save
#pragma noregparms
extern void test1(char c,int I);
extern char test2(long l,float f);
#pragma restore
兩個(gè)外部函數(shù)的寄存器內(nèi)參數(shù)傳遞被禁止,然后SAVE指令時(shí)的設(shè)置被恢復(fù)。
2.1.3 DISABLE
名 字: DISABLE
縮 寫: 無(wú)
變 量: 無(wú)
默認(rèn)值:無(wú)
DISABLE指令在一個(gè)函數(shù)執(zhí)行期間禁止中斷發(fā)生。DISABLE必須在函數(shù)前以#pragma指定并且只能用在一個(gè)函數(shù)中,因此它是由編譯器內(nèi)部設(shè)置的,每個(gè)要禁止的中斷在函數(shù)前必須包括一個(gè)獨(dú)立的#pragma指定,而不能用于命令行。
例: typedef using char uchar;
#pragma disable /*Disable interrupts */
uchar dfunc(uchar p1,uchar p2) {
return(p1*P2+P2*p1);
}
2.1.4 EXTEND/NOEXTEND
名 字: EXTEND/NOEXTEND
縮 寫: 無(wú)
變 量: 無(wú)
默認(rèn)值: EXTEND
EXTEND指令支持ANSI-C對(duì)C51的特殊擴(kuò)展。NOEXTEND指令使編譯器只處理ANSI-C。不同的保留字如bit,reentrant,using等對(duì)編譯器來(lái)說(shuō)是未知的。
例: C51 DEMO.C NOEXTEND
#pragma NOEXTEND
2.2 目標(biāo)控制
2.2.1 DEBUG/NODEBUG
名 字: DEBUG/NODEBUG
縮 寫: DB/NODB
變 量: 無(wú)
默認(rèn)值:NODEBUG
DEBUG指令指示編譯器將調(diào)試信息加入到目標(biāo)文件中,該信息對(duì)于符號(hào)調(diào)試而言是必需的,它包括全局和局部變量的定義及其地址、以及函數(shù)名及其行號(hào)。目標(biāo)模塊中的調(diào)試信息在用L51連接過(guò)程中一直保持有效,它也可被與dscope-51或intel兼容的仿真器使用。NODEBUG指令指示在目標(biāo)文件中不加入調(diào)試信息。QTH建議用戶要加入DB信息。
例: C51 DEMO.C DEBUG
#pragma db
2.2.2 OBJECT/NOOBJECT
名 字: OBJECT/NOOBJECT
縮 寫: OJ/NOOJ
變 量: 括號(hào)內(nèi)文件名
默認(rèn)值: OBJECT(bname.OBJ)
OBJECT(filename)指令改變生成的目標(biāo)文件名,默認(rèn)值是路徑名加上基本擴(kuò)展名“.OBJ”,NOOBJECT指令不產(chǎn)生目標(biāo)文件。
例: C51 DEMO.C NOOBJEECT
#pragma oj(demo.obj)
2.2.3 OBJECTEXTEND
名 字: OBJECTEXTEND
縮 寫: OE
變 量: 無(wú)
默認(rèn)值: 無(wú)
OBJECTEXTEND使編譯器產(chǎn)生的目標(biāo)文件包含附加的變量類型定義信息。這個(gè)信息可在目標(biāo)模塊具有相同名字的時(shí)候?qū)⑺鼈儏^(qū)別開(kāi)來(lái)以供不同的仿真器使用。
注:QTH系列仿真器不需要該控制項(xiàng)。
2.2.4 OPTIMIZE
名 字: OPTIMIZE
縮 寫: OT
變 量: 括號(hào)內(nèi)一個(gè)0~5的十進(jìn)制數(shù),另外可選OPTIMIZE(SIZE)和OPTIMIZE(SPEED)
以決定優(yōu)化重點(diǎn)是放在代碼長(zhǎng)度上還是執(zhí)行速度上。
默認(rèn)值:OPTIMIZE(2,SPEED)。
OPTIMIZE指令設(shè)置優(yōu)化級(jí),在這種設(shè)置中,高一級(jí)的優(yōu)化級(jí)包含前一級(jí)較低的優(yōu)化級(jí)的設(shè)置。
(1) OPTIMIZE(0)
l 常數(shù)折疊:編譯時(shí)只要有可能,編譯器就執(zhí)行包含常數(shù)的計(jì)算,包括執(zhí)行地址計(jì)算。
l 簡(jiǎn)單訪問(wèn)化:對(duì)8051系統(tǒng)內(nèi)部數(shù)據(jù)和地址進(jìn)行訪問(wèn)優(yōu)化。
l 跳轉(zhuǎn)優(yōu)化:編譯器總是將跳轉(zhuǎn)延遲至最終目標(biāo)上,因此跳轉(zhuǎn)到跳轉(zhuǎn)的指令被消除。
(2) OPTIMIZE(1)
l 死碼消除:無(wú)用的代碼將被消除。
l 跳轉(zhuǎn)否決:根據(jù)一個(gè)測(cè)試反饋,條件轉(zhuǎn)移被仔細(xì)檢查,以決定是否能夠進(jìn)行簡(jiǎn)化或消除。
(3) OPTIMIZE(2)
l 數(shù)據(jù)覆蓋:適用于靜態(tài)覆蓋的數(shù)據(jù)和位段被鑒別并標(biāo)記出來(lái)。L51有這樣一個(gè)功能,通過(guò)對(duì)全局?jǐn)?shù)據(jù)流的分析,選擇可靜態(tài)覆蓋的段。
(4) OPTIMIZE(3)
l “窺孔”優(yōu)化:冗余的MOV指令被刪去,這也包括不必要的、從存貯器裝入對(duì)象及裝入常數(shù)的操作。另外,當(dāng)它能節(jié)省存貯器空間或執(zhí)行時(shí)間時(shí),復(fù)雜操作由簡(jiǎn)單操作所取代。
(5) OPTIMIZE(4)
l 寄存器變量:自動(dòng)和參數(shù)變量位于寄存器中,只要有可能,將不為這些就是變量保留數(shù)據(jù)存貯器空間。
l 擴(kuò)展訪問(wèn)優(yōu)化:由IDATA,XDATA和CODE區(qū)域來(lái)的變量直接包含在操作中,因此在大多數(shù)時(shí)候裝入中間寄存器是不必要的。
l 局部公共子式的消除:如果表達(dá)式中有一個(gè)重復(fù)執(zhí)行的計(jì)算,只要有可能,第一次計(jì)算的結(jié)果將被用于后續(xù)的計(jì)算,因此可以從代碼中消除繁雜的計(jì)算。
l CASE/SWITCH優(yōu)化:CASE/SWITCH語(yǔ)句作為跳轉(zhuǎn)表或跳轉(zhuǎn)串被優(yōu)化。
(6) OPTIMIZE(5)
l 全局公共子式消除:只要有可能,函數(shù)內(nèi)相同的子表達(dá)式只計(jì)算一次。中間結(jié)果存入一個(gè)寄存器以代替新的計(jì)算。
l 簡(jiǎn)單循環(huán)優(yōu)化:以常量占據(jù)一段內(nèi)存的循環(huán)被轉(zhuǎn)化并在運(yùn)行時(shí)被優(yōu)化。OPTIMIZE(5)包括了從0級(jí)到4的所有優(yōu)化。
例: C51 SAMPLE.C OPTIMIZE(4)
#pragma ot(5,SIZE)
#pragma ot(size)
注: 全局優(yōu)化從優(yōu)化級(jí)4開(kāi)始。同時(shí),一個(gè)完整的函數(shù)被優(yōu)化時(shí),如果分配給生成優(yōu)
化代碼所必要的數(shù)據(jù)結(jié)構(gòu)的內(nèi)存不夠,全局優(yōu)化只執(zhí)行一部分,或根本不執(zhí)行。
2.2.5 SMALL/COMPACT/LARGE
名 字: SMALL/COMPACT/LARGE
縮 寫: 無(wú)
變 量: 無(wú)
默認(rèn)值: SMALL
SMALL,COMPACT,LARGE這些指令控制存貯器模式選擇。存貯器模式對(duì)不同的變量定義有所影響。
SMALL:
所有函數(shù)和過(guò)程變量及局部數(shù)據(jù)段被定義在8051系統(tǒng)內(nèi)部數(shù)據(jù)存貯器,因此以這種模式訪問(wèn)數(shù)據(jù)對(duì)象是非常有效的。這種模式的缺點(diǎn)是地址空間有限。
COMPACT:
所有函數(shù)和過(guò)程變量及局部數(shù)據(jù)段被定義在8051系統(tǒng)外部數(shù)據(jù)存貯器中,這個(gè)存貯區(qū)可達(dá)256字節(jié)(1頁(yè))。這種模式使用訪問(wèn)外部數(shù)據(jù)存貯器的簡(jiǎn)潔形式(@R0/R1)。
LARGE:
所有變量和局部變量數(shù)據(jù)段定義在8051系統(tǒng)的外部數(shù)據(jù)存貯器中,可訪問(wèn)達(dá)64K字節(jié)的地址空間。因此,它需要通過(guò)數(shù)據(jù)指針(DPTR),這是一種效率不高的數(shù)據(jù)訪問(wèn)形式。
注意:調(diào)用子程序的棧始終放在內(nèi)部存貯器中。
例: C51 SAMPLE.C LARGE
#pragma compact
2.2.6 REGPARMS/NOREGPARMS
名 字: REGPARMS/NOREGPARMS
縮 寫: RP/NORP
變 量: 無(wú)
默認(rèn)值: REGPARMS
使用REGPARMS指令時(shí),編譯器在寄存器內(nèi)至多傳遞三個(gè)參數(shù)。該指令產(chǎn)生的參數(shù)傳遞代碼與早期的C51版本兼容。
REGPARMS和NOREGPARMS指令可以在源程序中定義多次。這樣就允許程序的某一節(jié)使用寄存器進(jìn)行參數(shù)傳遞,而其它函數(shù)仍然用原來(lái)的傳遞方式,匯編語(yǔ)言函數(shù)及庫(kù)文件(已經(jīng)存在的)則不必更改。
例: C51 DEMO.C NOREGPARMS
#pragma RP
2.2.7 REGISTERBANK
名 字: REGISTERBANK
縮 寫: RB
變 量: 括號(hào)內(nèi)一個(gè)0~3的數(shù)
默認(rèn)值: REGISTERBANK(0)
REGISTERBANK指令選擇當(dāng)前調(diào)用時(shí)使用的寄存器組,除非使用“using”標(biāo)志。當(dāng)能夠計(jì)算寄存器的絕對(duì)數(shù)量時(shí),生成代碼可能使用寄存器訪問(wèn)的絕對(duì)形式,用助記符“Arn”來(lái)表示。
與“using n”相比,REGISTERBANK不轉(zhuǎn)換寄存器組!
有返回值的函數(shù)必須始終使用同一個(gè)寄存器組。否則,返回值可能放在錯(cuò)誤的寄存器組中。
例: C51 DEMO.C REGISTERBANK
#pragma rp(3)
2.2.8 AREGS/NOAREGS
名 字: AREGS/NOAREGS
縮 寫: AR/NOAR
變 量: 無(wú)
默認(rèn)值: AREGS
AREGS使編譯器使用絕對(duì)寄存器尋址方式,絕對(duì)寄存器尋址方式提高了效率。例如:PUSH和POP指令只能直接(絕對(duì))尋址。用REGSITERBANK指令可建立指定使用的寄存器組。NOAREG指令關(guān)閉絕對(duì)尋址。未使用NOAREGS指令編譯的函數(shù)不依賴于寄存器組,即它們可使用8051所有的寄存器。AREGS/NOAREGS指令可在程序中定義多次,然而,這只能在函數(shù)外使用。
例: C51 DEMO.C NOAREGS
#pragma AREGS
2.2.9 INTVECTOR/NOINTVECTOR
名 字: INTVECTOR/NOINTVECTOR
縮 寫: IV/NOIV
變 量: 無(wú)
默認(rèn)值: INTVECTOR
INTVECTOR指令使編譯器產(chǎn)生3字節(jié)跳轉(zhuǎn)(LJMP)目標(biāo)值。這些向量放在絕對(duì)地址8n+3處,n為中斷號(hào)。NOINTVECTOR指令可以防止產(chǎn)生這些中斷向量,這種靈活性使用戶可以利用其它編程工具提供的中斷服務(wù)子程序。
例: C51 SAMPLE.C NOINTVECTOR
#pragma noiv
2.2.10 ROM
ROM指令用來(lái)決定程序內(nèi)存的大小,它影響跳轉(zhuǎn)指令的編碼。
ROM(SMALL):
以CALL和JMP指令作為ACALL和AJMP指令的編碼,最大程序空間可達(dá)2K字節(jié),整個(gè)用戶程序必須分布在這2K字節(jié)空間內(nèi)。
ROM(COMPACT):
CALL指令以LCALL編碼,函數(shù)內(nèi)JMP指令以AJMP編碼,因此函數(shù)長(zhǎng)度不得超過(guò)2K字節(jié),而整個(gè)程序長(zhǎng)度不得超過(guò)64K字節(jié),這種用法必須根據(jù)不同的目的而決定,看其是否比標(biāo)準(zhǔn)設(shè)置ROM(LARGE)效果更佳。
ROM(LARGE)
將CALL和JMP指令以LCALL和LJMP編碼。這樣就允許不加限制地使用整個(gè)地址空間,用戶程序最大可達(dá)64K字節(jié)。
例: C51 DEMO.C ROM(SMALL)
#pragma ROM(SMALL)
2.3 列表控制
2.3.1 LISTINCLUDE
名 字: LISTINCLUDE/NOLISTINCLUDE
縮 寫: LC/NOLC
變 量: 無(wú)
默認(rèn)值: NOLISTINCLUDE
LISTINCLUDE指令導(dǎo)致在列表文件中出現(xiàn)頭文件的內(nèi)容。QTH仿真器建議用戶使用默認(rèn)值。
2.3.2 SYMBOLS/NOSYMBOLS
名 字: SYMBOLS/NOSYMBOLS
縮 寫: SB/NOSB
變 量: 無(wú)
默認(rèn)值: NOSYMBOL
SYMBOLS,NOSYMBOLS指令在編譯時(shí)產(chǎn)生一個(gè)被程序模塊使用過(guò)的符號(hào)表。對(duì)于每一個(gè)符號(hào)對(duì)象,包含了存貯器登錄,存貯類型,偏移和對(duì)象大小。
例: C51 DEMO.C SB
#pragma sb
2.3.3 PREPRINT/NOPREPRINT
名 字: PREPRINT/NOPREPRINT
縮 寫: PP/NOPP
變 量: 括號(hào)內(nèi)可選的文件名
默認(rèn)值: NOPREPRINT
PREPRINT指令產(chǎn)生一個(gè)預(yù)處理器列表,宏調(diào)用被擴(kuò)展并且注釋被刪除。如果PREPRINT指令不帶變量,則源文件名加上擴(kuò)展名“·I”作為列表文件名,如果不用此名,則須指定一個(gè)文件名。
例: C51 DEMO.C PP(DEMO.LST)
2.3.4 CODE/NOCODE
名 字: CODE/NOCODE
縮 寫: CD/NOCD
變 量: 無(wú)
默認(rèn)值: NOCODE
CODE指令在列表文件后附加上一個(gè)匯編記憶表,源程序中的每個(gè)函數(shù)被表示為匯編代碼。
2.3.5 PRINT/NOPRINT
名 字: PRINT/NOPRINT
縮 寫: PR/NOPR
變 量: 括號(hào)內(nèi)文件名
默認(rèn)值: PRINT(bname.LST)
PRINT指令使編譯器用路徑,源文件名及擴(kuò)展名“.LST”為每個(gè)被編譯的程序產(chǎn)生一個(gè)列表文件,該列表文件名可以用PRINT選項(xiàng)重新定義,當(dāng)使用NOPRINT時(shí),將不產(chǎn)生列表文件。QTH仿真器建議用戶使用默認(rèn)值。
2.3.6 COND/NOCOND
名 字: COND/NOCOND
縮 寫: CO/NOCO
變 量: 無(wú)
默認(rèn)值: COND
指令COND和NOCOND決定源文件中條件編譯部分是否出現(xiàn)在列表文件中,COND選項(xiàng)禁止向列表文件輸出跳過(guò)的行。每當(dāng)預(yù)處理器檢測(cè)到該指令時(shí),該指令就會(huì)對(duì)該行產(chǎn)生影響。QTH仿真器建議用戶使用默認(rèn)值。
2.3.7 PAGELENGTH
名 字: PAGELENGTH
縮 寫: PL
變 量: 括號(hào)內(nèi)一個(gè)最大為65535十進(jìn)制數(shù)
默認(rèn)值: PAGELENGTH(69)
PAGELENGTH指令指定了列表文件中每頁(yè)的行數(shù)。默認(rèn)值為69頁(yè)。包括抬頭和空行。
例: C51 DEMO.C PAGELENGTH(60)
#pragma pl(60)
2.3.8 PAGEWIDTH
名 字: PAGEWIDTH
縮 寫: PW
變 量: 括號(hào)內(nèi)一個(gè)78~132的十進(jìn)制數(shù)
默認(rèn)值: PATHWIDTH(132)
PAGEWIDTH指定了每行的字符數(shù)。若超過(guò)此數(shù),該行將變?yōu)閮尚谢蚨嘈小TH仿真器建議用戶使用默認(rèn)值。
2.3.9 EJECT
名 字: EJECT
縮 寫: EJ
變 量: 無(wú)
默認(rèn)值: 無(wú)
EJECT指令使列表文件換頁(yè)。該指令只能用在源文件上,且必須是#pragma的一部分。
3 C庫(kù)函數(shù)
C-51軟件包的庫(kù)包含標(biāo)準(zhǔn)的應(yīng)用程序,每個(gè)函數(shù)都在相應(yīng)的頭文件(.h)中有原型聲明。如果使用庫(kù)函數(shù),必須在源程序中用預(yù)編譯指令定義與該函數(shù)相關(guān)的頭文件(包含了該函數(shù)的原型聲明)。例如:
#include
#include
如果省掉頭文件,編譯器則期望標(biāo)準(zhǔn)的C參數(shù)類型,從而不能保證函數(shù)的正確執(zhí)行。
3.1 CTYPE.H:字符函數(shù)
在CTYPE.H頭文件中包含下列一些庫(kù)函數(shù):
函數(shù)名: isalpha
原 型: extern bit isalpha(char)
功 能: isalpha檢查傳入的字符是否在‘A’-‘Z’和‘a’-‘z’之間,如果為真返回
值為1,否則為0。
函數(shù)名: isalnum
原 型: extern bit isalnum(char)
功 能: isalnum檢查字符是否位于‘A’-‘Z’,‘a’-‘z’或‘0’-‘9’之間,為真返
回值是1,否則為0。
函數(shù)名: iscntrl
原 型: extern bit iscntrl(char)
功 能: iscntrl檢查字符是否位于0x00~0x1F之間或0x7F,為真返回值是1,否則為0。
函數(shù)名: isdigit
原 型: extern bit isdigit(char)
功 能: isdigit檢查字符是否在‘0’-‘9’之間,為真返回值是1,否則為0。
函數(shù)名: isgraph
原 型: extern bit isgraph(char)
功 能: isgraph檢查變量是否為可打印字符,可打印字符的值域?yàn)?x21~0x7E。若為可
打印,返回值為1,否則為0。
函數(shù)名: isprint
原 型: extern bit isprint(char)
功 能: 除與isgraph相同外,還接受空格字符(0X20)。
函數(shù)名: ispunct
原 型: extern bit ispunct(char)
功 能: ispunct檢查字符是否位為標(biāo)點(diǎn)或空格。如果該字符是個(gè)空格或32個(gè)標(biāo)點(diǎn)和格式
字符之一(假定使用ASCII字符集中128個(gè)標(biāo)準(zhǔn)字符),則返回1,否則返回0。Ispunct對(duì)下列字符返回1:
(空格)!“$%^&()+,-./:<=>?_[‘~{
}
函數(shù)名: islower
原 型: extern bit islower(char)
功 能: islower檢查字符變量是否位于‘a’-‘z’之間,為真返回值是1,否則為0。
函數(shù)名: isupper
原 型: extern bit isupper(char)
功 能: isupper檢查字符變量是否位于‘A’-‘Z’之間,為真返回值是1,否則為0。
函數(shù)名: isspace
原 型: extern bit isspace(char)
功 能: isspace檢查字符變量是否為下列之一:空格、制表符、回車、換行、垂直制表
符和送紙。為真返回值是1,否則為0。
函數(shù)名: isxdigit
原 型: extern bit isxdigit(char)
功 能: isxdigit檢查字符變量是否位于‘0’-‘9’,‘A’-‘F’或‘a’-‘f’之間,
為真返回值是1,否則為0。
函數(shù)名: toascii
原 型: toascii(c)((c)&0x7F);
功 能: 該宏將任何整型值縮小到有效的ASCII范圍內(nèi),它將變量和0x7F相與從而去掉低
7位以上所有數(shù)位。
函數(shù)名: toint
原 型: extern char toint(char)
功 能: toint將ASCII字符轉(zhuǎn)換為16進(jìn)制,返回值0到9由ASCII字符‘0’到‘9’得
到,10到15由ASCII字符‘a’-‘f’(與大小寫無(wú)關(guān))得到。
函數(shù)名: tolower
原 型: extern char tolower(char)
功 能: tolower將字符轉(zhuǎn)換為小寫形式,如果字符變量不在‘A’-‘Z’之間,則不作轉(zhuǎn)
換,返回該字符。
函數(shù)名: _tolower
原 型: tolower(c);(c-‘A’+‘a’)
功 能: 該宏將0x20參量值逐位相或。
函數(shù)名: toupper
原 型: extern char toupper(char)
功 能: toupper將字符轉(zhuǎn)換為大寫形式,如果字符變量不在‘a’-‘z’之間,則不作轉(zhuǎn)
換,返回該字符。
函數(shù)名: _toupper
原 型: _toupper(c);((c)-‘a’+’A’)
功 能: _toupper宏將c與0xDF逐位相與。
3.2 STDIO.H:一般I/O函數(shù)
C51編譯器包含字符I/O函數(shù),它們通過(guò)處理器的串行接口操作,為支持其它I/O機(jī)制,只需修改getkey()和putchar()函數(shù),其它所有I/O支持函數(shù)依賴這兩個(gè)模塊,不需要改動(dòng)。在使用8051串行口之前,必須將它們初始化,下例以2400波特率,12MHz初始化串口:
SCON=0x52 /*SCON*/
TMOD=0x20 /*TMOD*/
TR1=1 /*Timer 1 run flag*/
TH1=0Xf3 /*TH1*/
其它工作模式和波特率等細(xì)節(jié)問(wèn)題可以從8051用戶手冊(cè)中得到。
函數(shù)名: _getkey
原 型: extern char _getkey();
功 能: _getkey()從8051串口讀入一個(gè)字符,然后等待字符輸入,這個(gè)函數(shù)是改變整個(gè)
輸入端口機(jī)制應(yīng)作修改的唯一一個(gè)函數(shù)。
函數(shù)名: getchar
原 型: extern char _getchar();
功 能: getchar()使用_getkey從串口讀入字符,除了讀入的字符馬上傳給putchar函數(shù)
以作響應(yīng)外,與_getkey相同。
函數(shù)名: gets
原 型: extern char *gets(char *s,int n);
功 能: 該函數(shù)通過(guò)getchar從控制臺(tái)設(shè)備讀入一個(gè)字符送入由‘s’指向的數(shù)據(jù)組???/p>
慮到ANSI標(biāo)準(zhǔn)的建議,限制每次調(diào)用時(shí)能讀入的最大字符數(shù),函數(shù)提供了一個(gè)字符計(jì)數(shù)器‘n’,在所有情況下,當(dāng)檢測(cè)到換行符時(shí),放棄字符輸入。
函數(shù)名: ungetchar
原 型: extern char ungetchar(char);
功 能: ungetchar將輸入字符推回輸入緩沖區(qū),因此下次gets或getchar可用該字符。
ungetchar成功時(shí)返回‘char’,失敗時(shí)返回EOF,不可能用ungetchar處理多個(gè)字符。
函數(shù)名: _ungetchar
原 型: extern char _ungetchar(char);
功 能: _ungetchar將傳入字符送回輸入緩沖區(qū)并將其值返回給調(diào)用者,下次使用getkey
時(shí)可獲得該字符,寫回多個(gè)字符是不可能的。
函數(shù)名: putchar
原 型: extern putchar(char);
功 能: putchar通過(guò)8051串口輸出‘char’,和函數(shù)getkey一樣,putchar是改變整個(gè)
輸出機(jī)制所需修改的唯一一個(gè)函數(shù)。
函數(shù)名: printf
原 型: extern int printf(const char*,…);
功 能: printf以一定格式通過(guò)8051串口輸出數(shù)值和串,返回值為實(shí)際輸出的字符數(shù),
參量可以是指針、字符或數(shù)值,第一個(gè)參量是格式串指針。
注:允許作為printf參量的總字節(jié)數(shù)由C51庫(kù)限制,因?yàn)?051結(jié)構(gòu)上存貯空間有限,在SMALL和COMPACT模式下,最大可傳遞15個(gè)字節(jié)的參數(shù)(即5個(gè)指針,或1個(gè)指針和3個(gè)長(zhǎng)字節(jié)),在LARGE模式下,至多可傳遞40個(gè)字節(jié)的參數(shù)。格式控制串包含下列域(方括號(hào)內(nèi)的域是可能的):
%[flags][width][.precision]type
“width”域定義了參量欲顯示的字符數(shù),它必須是一個(gè)十進(jìn)制數(shù),如果實(shí)際顯示的字符數(shù)小于“width”,輸出左端補(bǔ)以空格,如果“width”域以0開(kāi)始,則左端補(bǔ)0。
“flag”域用來(lái)定義下面選項(xiàng):
Falg
意 義
-
輸出左齊
+
輸出值如果是有符號(hào)數(shù)值,則加上+/-號(hào)
‘ ‘(空格)
輸出值如果為正則左邊補(bǔ)以空格顯示
#
如果它與0,x或X聯(lián)用,則在輸出前加上字符0、0x,0X。當(dāng)與值類型g、G、f、e、E聯(lián)用時(shí),‘#’使輸出數(shù)產(chǎn)生一個(gè)十進(jìn)制小數(shù)點(diǎn)。
b,B
它們與格式類型d、i、o、u、x、X聯(lián)用,這樣參量類型被接受為‘[unsigned]char’,如:%bu,%bd或%bx。
L,L
它們與格式類型d、i、o、u、x、X聯(lián)用,這樣參量類型被接受為‘[unsigned]long’,如:%lu,%ld或%lx。
*
下一個(gè)參量不作輸出。
“type“域定義參量如下類型:
字 符
類 型
輸 出 格 式
d
int
有符號(hào)十進(jìn)制數(shù)(16位)
U
int
無(wú)符號(hào)十進(jìn)制數(shù)
o
int
無(wú)符號(hào)八進(jìn)制數(shù)
X,x
int
無(wú)符號(hào)十六進(jìn)制數(shù)
f
float
[-]dddd.dddd形式的浮點(diǎn)數(shù)
e,E
float
[-]d.ddddE[sign]dd形式的浮點(diǎn)數(shù)
g,G
float
e或f形式浮點(diǎn)數(shù),看哪一種輸出形式好。
c
char
字符
s
pointer
指向一個(gè)帶結(jié)束符號(hào)的串
p
pointer
帶存貯器指示符和偏移的指針。M:aaaa。
M:=C(ode),D(ata),I(data),P(data) aaaa:指針偏移值。
例子:
printf(“Int-Val%d,Char-Val%bd,Long-Val%d”,I,c,l);
printf(“String%s,Character%c”,array,character);
printf(“Pointer%p”,&array[10]);
函數(shù)名: sprintf
原 型: extern int sprintf(char *s,const char*,…);
功 能: sprintf與printf相似,但輸出不顯示在控制臺(tái)上,而是通過(guò)一個(gè)指針S,送入
可尋址的緩沖區(qū)。
注:sprintf允許輸出的參量總字節(jié)數(shù)與printf完全相同。
函數(shù)名: puts
原 型: extern int puts(const char*,…);
功 能: puts將串‘s’和換行符寫入控制臺(tái)設(shè)備,錯(cuò)誤時(shí)返回EOF,否則返回一非負(fù)數(shù)。
函數(shù)名: scanf
原 型: extern int scanf(const char*,…);
功 能: scanf在格式串控制下,利用getcha函數(shù)由控制臺(tái)讀入數(shù)據(jù),每遇到一個(gè)值(符
號(hào)格式串規(guī)定),就將它按順序賦給每個(gè)參量,注意每個(gè)參量必須都是指針。scanf返回它所發(fā)現(xiàn)并轉(zhuǎn)換的輸入項(xiàng)數(shù)。若遇到錯(cuò)誤返回EOF。格式串包括:
l 空格、制表符等,這些空白字符被忽略。
l 字符,除需匹配的“%”(格式控制字符)外。
l 轉(zhuǎn)換指定字符“%”,后隨幾個(gè)可選字符;賦值抑制符“*”,一個(gè)指定最大域?qū)挼臄?shù)。
注:scanf參量允許的總字節(jié)數(shù)與printf相同,格式控制串可包括下列域(方括號(hào)內(nèi)是可選的):
%[flags][width]type
格式串總是以百分號(hào)開(kāi)始,每個(gè)域包含一個(gè)或多個(gè)字符或數(shù)。
“width”域定義了參量可接受的字符數(shù),“width”必須是一個(gè)正十進(jìn)制數(shù)。如果實(shí)際輸入字符數(shù)量小于“width”,則不會(huì)進(jìn)行填充。
‘flag’域用來(lái)定義下面選項(xiàng):
Flag
意 義
*
輸入被忽略
b,h
它們用作格式類型d,i,o,u和x的前綴,用這些變量可定義參量是字符指針還是無(wú)符號(hào)字符指針。如%bu,%bd,%bx。
L
它們被作格式類型d,i,o,u和x的前綴,使用這個(gè)前綴可定義參量是長(zhǎng)指針還是無(wú)符號(hào)字長(zhǎng)指針。如%lu,%ld,%lx。
“type”域定義參量為如下類型:
描 述 符
類 型
輸 入 格 式
d
ptr to int
有符號(hào)十進(jìn)制數(shù)(16位)
i
ptr to int
如C中記號(hào)一樣,整型值
u
ptr to int
無(wú)符號(hào)十進(jìn)制數(shù)
o
ptr to int
無(wú)符號(hào)八進(jìn)制數(shù)
x
ptr to int
無(wú)符號(hào)十六進(jìn)制數(shù)
f,e,g
ptr to float
浮點(diǎn)數(shù)
c
ptr to char
一個(gè)字符
s
ptr to string
一個(gè)字串
例子:
scanf(“%d%bd%ld”,&i,&c,&l);
scanf(“%f”,&f);
scanf(“%3s,%c”,&string[0],&character);
函數(shù)名: sscanf
原 型: extern int sscanf(const *s,const char*,…);
功 能: sscanf與scanf方式相似,但串輸入不是通過(guò)控制臺(tái),而是通過(guò)另一個(gè)以空結(jié)束
的指針。
注:sscanf參量允許的總字節(jié)數(shù)由C-51庫(kù)限制,這是因?yàn)?051處理器結(jié)構(gòu)內(nèi)存的限制,在SMALL和COMPACT模式,最大允許15字節(jié)參數(shù)(即至多5個(gè)指針,或2個(gè)指針,2個(gè)長(zhǎng)整型或1個(gè)字符型)的傳遞。在LARGE模式下,最大允許傳送40個(gè)字節(jié)的參數(shù)。
3.3 STRING.H:串函數(shù)
串函數(shù)通常將指針串作輸入值。一個(gè)串就包括2個(gè)或多個(gè)字符。串結(jié)以空字符表示。在函數(shù)memcmp,memcpy,memchr,memccpy,memmove和memset中,串長(zhǎng)度由調(diào)用者明確規(guī)定,使這些函數(shù)可工作在任何模式下。
函數(shù)名: memchr
原 型: extern void *memchr(void *sl, char val,int len);
功 能: memchr順序搜索s1中的len個(gè)字符找出字符val,成功時(shí)返回s1中指向val的
指針,失敗時(shí)返回NULL。
函數(shù)名: memcmp
原 型: extern char memcmp(void *sl, void *s2,int len);
功 能: memcmp逐個(gè)字符比較串s1和s2的前l(fā)en個(gè)字符。相等時(shí)返回0,如果串s1大
于或小于s2,則相應(yīng)返回一個(gè)正數(shù)或負(fù)數(shù)。
函數(shù)名: memcpy
原 型: extern void *memcpy(void *dest, void *src,int len);
功 能: memcpy由src所指內(nèi)存中拷貝len個(gè)字符到dest中,返回批向dest中的最后一
個(gè)字符的指針。如果src和dest發(fā)生交迭,則結(jié)果是不可預(yù)測(cè)的。
函數(shù)名: memccpy
原 型: extern void *memccpy(void *dest, void *src,char val,int len);
功 能: memccpy拷貝src中l(wèi)en個(gè)字符到dest中,如果實(shí)際拷貝了len個(gè)字符返回NULL。
拷貝過(guò)程在拷貝完字符val后停止,此時(shí)返回指向dest中下一個(gè)元素的指針。
函數(shù)名: memmove
原 型: extern void *memmove(void *dest, void *src,int len);
功 能: memmove工作方式與memcpy相同,但拷貝區(qū)可以交迭。
函數(shù)名: memset
原 型: extern void *memset(void *s, char val,int len);
功 能: memset 將val值填充指針s中l(wèi)en個(gè)單元。
函數(shù)名: strcat
原 型: extern char *strcat(char *s1, char *s2);
功 能: strcat將串s2拷貝到串s1結(jié)尾。它假定s1定義的地址區(qū)足以接受兩個(gè)串。返
回指針指向s1串的第一字符。
函數(shù)名: strncat
原 型: extern char *strncat(char *s1, char *s2,int n);
功 能: strncat拷貝串s2中n個(gè)字符到串s1結(jié)尾。如果s2比n短,則只拷貝s2。
函數(shù)名: strcmp
原 型: extern char strcmp(char *s1, char *s2);
功 能: strcmp比較串s1和s2,如果相等返回0,如果s1
一個(gè)正數(shù)。
函數(shù)名: strncmp
原 型: extern char strncmp(char *s1, char *s2,int n);
功 能: strncmp比較串s1和s2中前n個(gè)字符,返回值與strncmp相同。
函數(shù)名: strcpy
原 型: extern char *strcpy(char *s1, char *s2);
功 能: strcpy將串s2包括結(jié)束符拷貝到s1,返回指向s1的第一個(gè)字符的指針。
函數(shù)名: strncpy
原 型: extern char *strncpy(char *s1, char *s2,int n);
功 能: strncpy與strcpy相似,但只拷貝n個(gè)字符。如果s2長(zhǎng)度小于n,則s1串以‘0’
補(bǔ)齊到長(zhǎng)度n。
函數(shù)名: strlen
原 型: extern int strlen(char *s1);
功 能: strlen返回串s1字符個(gè)數(shù)(包括結(jié)束字符)。
函數(shù)名: strchr,strpos
原 型: extern char *strchr(char *s1, char c);
extern int strpos(char *s1,char c);
功 能: strchr搜索s1串中第一個(gè)出現(xiàn)的‘c’字符,如果成功,返回指向該字符的別指
針,搜索也包括結(jié)束符。搜索一個(gè)空字符返回指向空字符的指針而不是空指針。
strpos與strchr相似,但它返回字符在串中的位置或-1,s1串的第一個(gè)字符位置是0。
函數(shù)名: strrchr,strrpos
原 型: extern char *strrchr(char *s1, char c);
extern int strrpos(char *s1,char c);
功 能: strrchr搜索s1串中最后一個(gè)出現(xiàn)的‘c’字符,如果成功,返回指向該字符的
指針,否則返回NULL。對(duì)s1搜索也返回指向字符的指針而不是空指針。
strrpos與strrchr相似,但它返回字符在串中的位置或-1。
函數(shù)名: strspn,strcspn,strpbrk,strrpbrk
原 型: extern int strspn(char *s1, char *set);
extern int strcspn(char *s1,char *set);
extern char *strpbrk(char *s1,char *set);
extern char *strpbrk(char *s1,char *set);
功 能: strspn搜索s1串中第一個(gè)不包含在set中的字符,返回值是s1中包含在set里
字符的個(gè)數(shù)。如果s1中所有字符都包含在set里,則返回s1的長(zhǎng)度(包括結(jié)束符)。如果s1是空串,則返回0。
strcspn與strspn類似,但它搜索的是s1串中的第一個(gè)包含在set里的字符。strpbrk與strspn很相似,但它返回指向搜索到字符的指針,而不是個(gè)數(shù),如果未找到,則返回NULL。
strrpbrk與strpbrk相似,但它返回s1中指向找到的set字集中最后一個(gè)字符的指針。
3.4 STDLIB.H:標(biāo)準(zhǔn)函數(shù)
函數(shù)名: atof
原 型: extern double atof(char *s1);
功 能: atof將s1串轉(zhuǎn)換為浮點(diǎn)值并返回它。輸入串必須包含與浮點(diǎn)值規(guī)定相符的數(shù)。
C51編譯器對(duì)數(shù)據(jù)類型float和double相同對(duì)待。
函數(shù)名: atol
原 型: extern long atol(char *s1);
功 能: atol將s1串轉(zhuǎn)換成一個(gè)長(zhǎng)整型值并返回它。輸入串必須包含與長(zhǎng)整型值規(guī)定相
符的數(shù)。
函數(shù)名: atoi
原 型: extern int atoi(char *s1);
功 能: atoi將s1串轉(zhuǎn)換為整型數(shù)并返回它。輸入串必須包含與整型數(shù)規(guī)定相符的數(shù)。
3.5 MATH.H:數(shù)學(xué)函數(shù)
函數(shù)名: abs,cabs,fabs,labs
原 型: extern int abs(int va1);
extern char cabs(char val);
extern float fabs(float val);
extern long labs(long val);
功 能: abs決定了變量val的絕對(duì)值,如果val為正,則不作改變返回;如果為負(fù),則
返回相反數(shù)。這四個(gè)函數(shù)除了變量和返回值的數(shù)據(jù)不一樣外,它們功能相同。
函數(shù)名: exp,log,log10
原 型: extern float exp(float x);
extern float log(float x);
extern float log10(float x);
功 能:exp返回以e為底x的冪,log返回x的自然數(shù)(e=2.718282),log10返回x以10
為底的數(shù)。
函數(shù)名: sqrt
原 型: extern float sqrt(float x);
功 能: sqrt返回x的平方根。
函數(shù)名:rand,srand
原 型: extern int rand(void);
extern void srand(int n);
功 能: rand返回一個(gè)0到32767之間的偽隨機(jī)數(shù)。srand用來(lái)將隨機(jī)數(shù)發(fā)生器初始化成
一個(gè)已知(或期望)值,對(duì)rand的相繼調(diào)用將產(chǎn)生相同序列的隨機(jī)數(shù)。
函數(shù)名: cos,sin,tan
原 型: extern float cos(flaot x);
extern float sin(flaot x);
extern flaot tan(flaot x);
功 能: cos返回x的余弦值。Sin返回x的正弦值。tan返回x的正切值,所有函數(shù)變量
范圍為-π/2~+π/2,變量必須在±65535之間,否則會(huì)產(chǎn)生一個(gè)NaN錯(cuò)誤。
函數(shù)名: acos,asin,atan,atan2
原 型: extern float acos(float x);
extern float asin(float x);
extern float atan(float x);
extern float atan(float y,float x);
功 能: acos返回x的反余弦值,asin返回x的正弦值,atan返回x的反正切值,它們
的值域?yàn)?π/2~+π/2。atan2返回x/y的反正切,其值域?yàn)?π~+π。
函數(shù)名: cosh,sinh,tanh
原 型: extern float cosh(float x);
extern float sinh(float x);
extern float tanh(float x);
功 能: cosh返回x的雙曲余弦值;sinh返回x的雙曲正弦值;tanh返回x的雙曲正切
值。
函數(shù)名: fpsave,fprestore
原 型: extern void fpsave(struct FPBUF *p);
extern void fprestore (struct FPBUF *p);
功 能: fpsave保存浮點(diǎn)子程序的狀態(tài)。fprestore將浮點(diǎn)子程序的狀態(tài)恢復(fù)為其原始狀
態(tài),當(dāng)用中斷程序執(zhí)行浮點(diǎn)運(yùn)算時(shí)這兩個(gè)函數(shù)是有用的。
3.6 ABSACC.H:絕對(duì)地址訪問(wèn)
函數(shù)名: CBYTE,DBYTE,PBYTE,XBYTE
原 型: #define CBYTE((unsigned char *)0x50000L)
#define DBYTE((unsigned char *)0x40000L)
#define PBYTE((unsigned char *)0x30000L)
#define XBYTE((unsigned char *)0x20000L)
功 能: 上述宏定義用來(lái)對(duì)8051地址空間作絕對(duì)地址訪問(wèn),因此,可以字節(jié)尋址。CBYTE
尋址CODE區(qū),DBYTE尋址DATA區(qū),PBYTE尋址XDATA區(qū)(通過(guò)MOVX @R0命令),XBYTE尋址XDATA區(qū)(通過(guò)MOVX @DPTR命令)。
例:下列指令在外存區(qū)訪問(wèn)地址0x1000
xval=XBYTE[0x1000];
XBYTE[0X1000]=20;
通過(guò)使用#define指令,用符號(hào)可定義絕對(duì)地址,如符號(hào)X10可與XBYTE[0x1000]地址相等:#define X10 XBYTE[0x1000]。
函數(shù)名: CWORD,DWORD,PWORD,XWORD
原 型: #define CWORD((unsigned int *)0x50000L)
#define DWORD((unsigned int *)0x40000L)
#define PWORD((unsigned int *)0x30000L)
#define XWORD((unsigned int *)0x20000L)
功 能:這些宏與上面相似,只是它們指定的類型為unsigned int。通過(guò)靈活的數(shù)據(jù)類型,
所有地址空間都可以訪問(wèn)。
3.7 INTRINS.H:內(nèi)部函數(shù)
函數(shù)名: _crol_,_irol_,_lrol_
原 型: unsigned char _crol_(unsigned char val,unsigned char n);
unsigned int _irol_(unsigned int val,unsigned char n);
unsigned int _lrol_(unsigned int val,unsigned char n);
功 能:_crol_,_irol_,_lrol_以位形式將val左移n位,該函數(shù)與8051“RLA”指令
相關(guān),上面幾個(gè)函數(shù)不同于參數(shù)類型。
例:
#include
main()
{
unsigned int y;
y=0x00ff;
y=_irol_(y,4); /*y=0x0ff0*/
}
函數(shù)名: _cror_,_iror_,_lror_
原 型: unsigned char _cror_(unsigned char val,unsigned char n);
unsigned int _iror_(unsigned int val,unsigned char n);
unsigned int _lror_(unsigned int val,unsigned char n);
功 能:_cror_,_iror_,_lror_以位形式將val右移n位,該函數(shù)與8051“RRA”指令
相關(guān),上面幾個(gè)函數(shù)不同于參數(shù)類型。
例:
#include
main()
{
unsigned int y;
y=0x0ff00;
y=_iror_(y,4); /*y=0x0ff0*/
}
函數(shù)名: _nop_
原 型: void _nop_(void);
功 能:_nop_產(chǎn)生一個(gè)NOP指令,該函數(shù)可用作C程序的時(shí)間比較。C51編譯器在_nop_函
數(shù)工作期間不產(chǎn)生函數(shù)調(diào)用,即在程序中直接執(zhí)行了NOP指令。
例:
P()=1;
_nop_();
P()=0;
函數(shù)名: _testbit_
原 型:bit _testbit_(bit x);
功 能:_testbit_產(chǎn)生一個(gè)JBC指令,該函數(shù)測(cè)試一個(gè)位,當(dāng)置位時(shí)返回1,否則返回0。
如果該位置為1,則將該位復(fù)位為0。8051的JBC指令即用作此目的。 _testbit_只能用于可直接尋址的位;在表達(dá)式中使用是不允許的。
STDARG.H:變量參數(shù)表
C51編譯器允許再入函數(shù)的變量參數(shù)(記號(hào)為“…”)。頭文件STDARG.H允許處理函數(shù)的參數(shù)表,在編譯時(shí)它們的長(zhǎng)度和數(shù)據(jù)類型是未知的。為此,定義了下列宏。
宏 名: va_list
功 能: 指向參數(shù)的指針。
宏 名: va_stat(va_list pointer,last_argument)
功 能: 初始化指向參數(shù)的指針。
宏 名: type va_arg(va_list pointer,type)
功 能:返回類型為type的參數(shù)。
宏 名: va_end(va_list pointer)
功 能: 識(shí)別表尾的啞宏。
3.8 SETJMP.H:全程跳轉(zhuǎn)
Setjmp.h中的函數(shù)用作正常的系列數(shù)調(diào)用和函數(shù)結(jié)束,它允許從深層函數(shù)調(diào)用中直接返回。
函數(shù)名: setjmp
原 型: int setjmp(jmp_buf env);
功 能: setjmp將狀態(tài)信息存入env供函數(shù)longjmp使用。當(dāng)直接調(diào)用setjmp 時(shí)返回值
是0,當(dāng)由longjmp調(diào)用時(shí)返回非零值,setjmp只能在語(yǔ)句IF或SWITCH中調(diào)用一次。
函數(shù)名: long jmp
原 型: long jmp(jmp_buf env,int val);
功 能:longjmp恢復(fù)調(diào)用setjmp時(shí)存在env中的狀態(tài)。程序繼續(xù)執(zhí)行,似乎函數(shù)setjmp
已被執(zhí)行過(guò)。由setjmp返回的值是在函數(shù)longjmp中傳送的值val,由setjmp調(diào)用的函數(shù)中的所有自動(dòng)變量和未用易失性定義的變量的值都要改變。
3.9 REGxxx.H:訪問(wèn)SFR和SFR-BIT地址
文件REG51.H,REG52.H和REG552.H允許訪問(wèn)8051系列的SFR和SFR-bit的地址,這些文件都包含#include指令,并定義了所需的所有SFR名以尋址8051系列的外圍電路地址,對(duì)于8051系列中其它一些器件,用戶可用文件編輯器容易地產(chǎn)生一個(gè)“.h”文件。
下例表明了對(duì)8051 PORT0和PORT1的訪問(wèn):
#include
main() {
if(p0==0x10) p1=0x50;
}