C語(yǔ)言編程在智能化重力加速度測(cè)試儀中的應(yīng)用
摘要在基于單片機(jī)的智能化重力加速度測(cè)試儀中采用C語(yǔ)言輔程苘化了程序設(shè)計(jì)任
務(wù),對(duì)于匯輔語(yǔ)言難干處理的浮點(diǎn)數(shù)運(yùn)算及扳字打印輸出可通過(guò)C語(yǔ)言箱譯罌的內(nèi)部庫(kù)函數(shù)調(diào)
用實(shí)現(xiàn)。舟紹了一種專為8051系列單片機(jī)設(shè)計(jì)的C語(yǔ)言輔譯器Franklin C51,它具有代碼優(yōu)化
功能,能產(chǎn)生極高效率的機(jī)器碼,并且提供了豐富的內(nèi)部函數(shù)庫(kù)。描述了C51函數(shù)庫(kù)所支持的
IEEE標(biāo)準(zhǔn)浮點(diǎn)數(shù)的內(nèi)存格式 及采用sprintf0函數(shù)處理包括漢字在內(nèi)的各種字符串的方法。給
出了測(cè)試儀的輸出結(jié)果。電導(dǎo)計(jì)| 水分測(cè)定儀| 濁度計(jì)| 色度計(jì)| 粘度計(jì)| 折射計(jì)| 滴定儀| 密度計(jì)| 熱流計(jì)| 濃度計(jì)| 折射儀| 采樣儀|
單片機(jī)在工業(yè)測(cè)量控制領(lǐng)域內(nèi)獲得了十分廣泛的應(yīng)用-】J。一般在研制單片機(jī)應(yīng)用系統(tǒng)
時(shí)太多采用 編語(yǔ)言作為軟件工具。但是,當(dāng)程序中需要采用浮點(diǎn)數(shù)運(yùn)算時(shí),用忙編語(yǔ)言編
寫程序十分麻煩;如果程序中需要進(jìn)行漢字處理,則用匯編語(yǔ)言編程的效率極低。為了提
高程序的編寫效率,只有采用高級(jí)語(yǔ)言編程。c語(yǔ)言是目前公認(rèn)的一種高效率計(jì)算機(jī)開發(fā)用
高級(jí)程序設(shè)計(jì)語(yǔ)言口],它既能象匯編語(yǔ)言那樣直接操作機(jī)器硬件,又能很方便地進(jìn)行各種
數(shù)學(xué)運(yùn)算和字符處理。筆者結(jié)合實(shí)例介紹采用美國(guó)Franklin軟件公司推出的c語(yǔ)言編譯器
一C51開發(fā)8051單片機(jī)應(yīng)用系統(tǒng)中浮點(diǎn)數(shù)運(yùn)算和漢字打印程序的原理和方法。
1 C51編譯器的特殊擴(kuò)充
C51編譯器采用符合ANSI標(biāo)準(zhǔn)的c語(yǔ)言進(jìn)行編程,為了滿足8051系列單片機(jī)哈福
結(jié)構(gòu)存儲(chǔ)器的需要[3],C51編譯器擴(kuò)展了說(shuō)明存儲(chǔ)器類型的關(guān)鍵字data:(可直接尋址的內(nèi)
部RAM 區(qū)O~7FH);bdata:(可位尋址的內(nèi)部RAM 區(qū)20H 2FH);iclata;(可閫接尋址
的內(nèi)部RAM 區(qū)0~0FFH);pdata:(分頁(yè)尋址的外部RAM 區(qū)256頁(yè)×256字節(jié));xdata:
(外部RAM 區(qū)O~0FFFFH);code:(ROM 區(qū)0~0FFFFH)。FranklinC51編譯器對(duì)于變量
的定義符合ANSI C標(biāo)準(zhǔn),即可以定義多維數(shù)組,可以通過(guò)指針進(jìn)行變量訪問(wèn),可以將若
干個(gè)變量組成為結(jié)構(gòu)和聯(lián)合等。還擴(kuò)展了可用來(lái)簡(jiǎn)化對(duì)8051單片機(jī)內(nèi)部特殊功能寄存器
及可尋址位訪問(wèn)的%bit”和“sfr 數(shù)據(jù)類型 在每個(gè)變量的聲明中可采用上述關(guān)鍵字明確
指定變量的存儲(chǔ)器類型。如果在定義變量時(shí)不指定其存儲(chǔ)器類型,則按編譯時(shí)給出的存儲(chǔ)
模式來(lái)決定變量的存儲(chǔ)區(qū)域。表1列出了Franklin C51編譯器所支持的存儲(chǔ)器模式。這3種存儲(chǔ)器模式各有優(yōu)缺
點(diǎn),SMALL模式下參數(shù)
的傳遞是在內(nèi)部RAM 中
完成的,COMPACT 和
LARGE允許參數(shù)傳遞在
外部RAM 中進(jìn)行。由于
衰1 Franklin C51編譯囂的存儲(chǔ)囂橫式
存儲(chǔ)模式 說(shuō) 明
SMALl 參數(shù)和局部變量存^可直接尋址的內(nèi)部RAM(默認(rèn)值為data)
COMPACT 參數(shù)和局部變量存人分頁(yè)外部RAM(默認(rèn)值為0aat~)
LARGE 參數(shù)和局部變量直接存^外部RAM(默認(rèn)值為xdata)
8051單片機(jī)訪問(wèn)內(nèi)部RAM 的速度比訪問(wèn)模式下外部RAM要快得多,因此可將需要經(jīng)常
使用的變量放在內(nèi)部RAM 中,而將那些較大及很少使用的變量放在外部RAM 中。另外
若將變量放在外部RAM 中時(shí),還會(huì)使程序編譯后產(chǎn)生的有效代碼加長(zhǎng)。C51編譯器對(duì)于局
部變量采用了靜態(tài)覆蓋技術(shù)來(lái)提高內(nèi)部RAM 的使用效率,因此在實(shí)際編程時(shí)只要有可
能,應(yīng)盡量采用局部變量。
2 C51編譯器支持的浮點(diǎn)數(shù)格式
C51編譯器支持的浮點(diǎn)數(shù)采用關(guān)鍵字float來(lái)進(jìn)行聲明,它是滿足IEEE一754標(biāo)準(zhǔn)的32
位單精度浮點(diǎn)數(shù),占用四個(gè)存儲(chǔ)器單元,在內(nèi)存中按從低到高的地址順序存儲(chǔ).格式如下
地址 +0 +1 +2 +3
內(nèi)容MMMMMMMM MMMMMMMM E MMMMMMM S EEEEEEE
其中,最高位S為符號(hào)位,0表示正數(shù),1表示負(fù)數(shù)。從次高位開始的8位EEEEEEEE占用
了兩個(gè)字節(jié),用來(lái)表示浮點(diǎn)數(shù)的階碼,為了避免出現(xiàn)負(fù)階碼值,并不用實(shí)際指數(shù)作為階碼,
而是將實(shí)際指數(shù)加上偏移量127之后再作為階碼。指數(shù)可正可負(fù),其范圍是一127~+128,
加上127之后便使階碼在o~255的范圍之內(nèi)。階碼之后是浮點(diǎn)數(shù)尾數(shù)的小數(shù)部分,共23
位,占用3個(gè)字節(jié)。尾數(shù)的整數(shù)部分永遠(yuǎn)是1,所以不予表示,但它是隱含存在的,因此尾
數(shù)實(shí)際上應(yīng)視為24位。例如將+124.75用IEEE一754標(biāo)準(zhǔn)的單精度浮點(diǎn)數(shù)表示為(16進(jìn)
制數(shù))42F98000H,在內(nèi)存中的存儲(chǔ)格式為
地址 +0 +1 +2 +3
內(nèi)容00000000 10000000 1 1111001 0 1000010
C51編譯器提供了大量實(shí)用的庫(kù)函數(shù),C語(yǔ)言源程序經(jīng)過(guò)C51編譯器編譯后在連接定
位時(shí),根據(jù)源程序中是否使用了浮點(diǎn)運(yùn)算以及所采用的編譯模式,連接程序L51會(huì)自動(dòng)選
擇正確的浮點(diǎn)函數(shù)庫(kù)加入到目標(biāo)代碼中去 ]。
3 浮點(diǎn)數(shù)運(yùn)算結(jié)果及漢字打印輸出的實(shí)現(xiàn)
在C51的編譯器函數(shù)庫(kù)中有一個(gè)十分有用的輸出函數(shù)sprintf(),它可將數(shù)值或字符串
以ASIIC碼的形式輸出到內(nèi)存中的某個(gè)地址單元,利用該函數(shù)可十分方便地實(shí)現(xiàn)浮點(diǎn)數(shù)運(yùn)
算結(jié)果及漢字字符的顯示或打印輸出。sprintf()函數(shù)的一般格式為
sprintf(字符指針,格式說(shuō)明,輸出參數(shù)表列)
其中,第一個(gè)參數(shù)必須是指針,它可以是數(shù)組,也可以是變量的地址,用于指定內(nèi)存單元的
首地址。格式說(shuō)明是一個(gè)用雙引號(hào)括起來(lái)的字符串:“ [flag][width][.precision]type”
其中,flag稱為標(biāo)志符,用于控制輸出數(shù)據(jù)的符號(hào)、空格、小數(shù)點(diǎn)的位置等;width是
一個(gè)十進(jìn)制的正整數(shù),用來(lái)指定
最小輸出字符的數(shù)目;precision
用來(lái)表示輸出數(shù)據(jù)的精度,由小
數(shù)點(diǎn)和一個(gè)非負(fù)的十進(jìn)制整數(shù)組
成;type稱為輸出格式轉(zhuǎn)換字
符,對(duì)于浮點(diǎn)數(shù)type可取3種表
示形式,如表2所示。
裹2 type字符豆其意義
type
f
e. E
g.G
數(shù)據(jù)類型 輸出格式
float [一]dddd dddd形式的浮點(diǎn)數(shù)
float [一]d.ddddE [sign3 dd形式的浮點(diǎn)數(shù)
float e或f形式的浮點(diǎn)數(shù),取其中形式較好者
輸出參數(shù)表列是需要輸出的一些數(shù)據(jù)項(xiàng),它們可以是變量的值,也可以是照原樣輸出
的字符。由于8051系列單片機(jī)存儲(chǔ)器結(jié)構(gòu)的限制,輸出表列中參數(shù)的總字節(jié)數(shù)有一定的限
制,在SMALL和COMPACT模式下,最大可傳遞1 5個(gè)字節(jié)的參數(shù)(5個(gè)指針,或1個(gè)指
針和3個(gè)long型數(shù)據(jù)),在LARGE模式下,最多可傳遞4o個(gè)字節(jié)的參數(shù)。
在C語(yǔ)言源程序中采用浮點(diǎn)數(shù)運(yùn)算時(shí),運(yùn)算結(jié)果是按IEEE一754標(biāo)準(zhǔn)格式存儲(chǔ)在內(nèi)存
單元中的。這種存儲(chǔ)格式不便于顯示和打印輸出。利用sprint{O 函數(shù)可以很容易地將運(yùn)算
結(jié)果轉(zhuǎn)換成宜于顯示和打印輸出的ASIIC碼。如果單片機(jī)應(yīng)用系統(tǒng)中需要采用漢字輸出,
則可以在漢字操作系統(tǒng)(ga UCDOS)下編寫C語(yǔ)言源程序,將要輸出的漢字作為字符串?dāng)?shù)
組直接定義到ROM 區(qū),則該RoM 區(qū)中就存儲(chǔ)了一系列漢字的代碼。從該RoM 區(qū)中按順
序取出各個(gè)漢字代碼,輸出到任何一種具有漢字打印功能的打印機(jī)上,即可很方便地實(shí)現(xiàn)
漢字的打印輸出 。
4 實(shí) 例
所研制的智能化重力加速度測(cè)試儀的監(jiān)控程序采用C51編程,實(shí)現(xiàn)了復(fù)雜的浮點(diǎn)數(shù)運(yùn)
算處理及漢字打印輸出功能 測(cè)試儀利用光電轉(zhuǎn)換器的輸出脈沖觸發(fā)8051單片機(jī)的外中斷
0,讀取兩次外部中斷之問(wèn)定時(shí)器0的計(jì)數(shù)值,通過(guò)計(jì)算獲得加速度的值.為了提高計(jì)算精
度采用了浮點(diǎn)數(shù)運(yùn)算。測(cè)試儀通過(guò)并行口與EPSON—LQ300K打印機(jī)相連,可將測(cè)量結(jié)果
以漢字方式打印輸出。打印機(jī)的并行接口為Centronics標(biāo)準(zhǔn).用8051單片機(jī)的P1口作為數(shù)
據(jù)線,P3.3和P3.5分別作為聯(lián)絡(luò)信號(hào)STROB和BUSY很容易實(shí)現(xiàn)與打印機(jī)的接口。智能
化重力加速度測(cè)試儀已經(jīng)研制成功并通過(guò)江漢石油學(xué)院設(shè)備處組織的鑒定。該儀器操作簡(jiǎn)
單,一次測(cè)量可獲得多個(gè)測(cè)量數(shù)據(jù), 自動(dòng)計(jì)算出最終結(jié)果和相對(duì)誤差值,并可通過(guò)打印機(jī)
接表3格式輸出測(cè)量結(jié)果。
裹3 重力加速度測(cè)試儀的輸出涮■結(jié)果
落球法測(cè)量結(jié)果 單擺法涮量結(jié)果
預(yù)置高度:H1—100 00 ram-H2—800 00 ram
下落時(shí)問(wèn):T1—65 076 m .T2=297.27 ms
重力加速度:g 9.9042 m/s0
相對(duì)誤差:Er一0.0186
單攖攖長(zhǎng):I,=l309 ram
攖動(dòng)周期;TI,一2288.4 Ills
重力加速度:g一9.8752 m/s
相對(duì)誤差;Er 0.0096
下面給出了儀器監(jiān)控程序中關(guān)于浮點(diǎn)數(shù)處理和漢字打印輸出部分的源程序。其中prn
()是浮點(diǎn)數(shù)打印輸出函數(shù)。對(duì)于小于0的浮點(diǎn)數(shù)先將其轉(zhuǎn)換為正數(shù),再調(diào)用sprint{()庫(kù)
函數(shù)將其轉(zhuǎn)換成ASIIC碼,按浮點(diǎn)數(shù)的科學(xué)表示法存于數(shù)組array中。如果需要按科學(xué)表示
法打印輸出,可直接從數(shù)組array中逐個(gè)取出ASIIC碼數(shù)據(jù)進(jìn)行打印。本例要求按工程表示
法打印輸出,因此先要根據(jù)浮點(diǎn)數(shù)的階碼確定小數(shù)點(diǎn)的位置。prnint O 是單個(gè)字符打印輸
出函數(shù)。它用來(lái)向打印機(jī)輸出各種打印控制命令以及漢字的打印輸出。在ROM 區(qū)中定義了
若干個(gè)漢字字符串?dāng)?shù)組,打印漢字時(shí)只要從各個(gè)數(shù)組中取出需要輸出的漢字字符串代碼作
為實(shí)際參數(shù)來(lái)調(diào)用prnint()函數(shù)即可。
C語(yǔ)言源程序如下:
#include< reg51.h>
#include< stdio.h>
#indude< math.h>
#include< stdlib.h>
float g,dscountl·El,E2,s1,s2; /*定義浮點(diǎn)型數(shù)據(jù)變量*/
sbit busy=P3 3 sbit stb=P3 5; /*定義打印機(jī)聯(lián)絡(luò)信號(hào)*/
uns~ned char bdata flag, /*定義其它類型數(shù)據(jù)變量*/
sbit flagl—Ihg OI
unsigned im clockO,count;
unsigned char dsp[8],array E]o3,
char code head1口一“歡迎使用重力加速度測(cè)量?jī)x”} /*定義漢字字符串*/
char code head2[]一“落球法測(cè)量結(jié)果”;
char code head3口一“單擺測(cè)量結(jié)果”;
char code hl[]= “硬置高度H1一H2= ;
char codeI1[]= 單擺按長(zhǎng)L一”;
char codetl[]= 下落時(shí)間TI T2= ”}
char codetI[]= “擺動(dòng)周期TL= ;
char code gg[]; “重力加速度g一”}
char code er[]= “相對(duì)誤差Er= ;
exten~char xdata[MtaPortt StaPort; ,*定義8279數(shù)據(jù)、命令口*,
exter~unsigned char keyval(unsigned char va1)} /} 匯編語(yǔ)言鍵值處理子程序*/
extern unsigned char tedseg(unsigned char x); ‘ /*匯編語(yǔ)言顯示段碼處理子程序*/
xint()interrupt 0 using 1 《
/*外部中斷INT0處理函數(shù)*/)
timer0 O interrupt 1 using 1{
/*定時(shí)器TO處理函數(shù)*/)
prn (float x){ /*浮點(diǎn)數(shù)打印輸出函數(shù)*/
unsigned cha r i}
if(x<0)x=l x; /*將浮點(diǎn)數(shù)作為正數(shù)處理*/
sprintf(array, 4e”+x)} ^ 轉(zhuǎn)換為ASIIC碼存于數(shù)組array中*/
arTay E1o]一array[93=array[93~0x0f; /*取階碼}/
j(array[73一=0x2b){ /*正階碼處理*/
for(1—1}i<一8r陽(yáng)y[93;I++)array[1]=array[i+1]} array[i]一Ox2e,
for(i一0‘i< 6}i+ + )《
P1一array即‘sfb=0‘stb=11 while(busy)})
next; for(1—6‘i<一3‘i一一)array[|]一arrav[j
array[9]一array[9]一1;
if(array[9]>o)goto next i
for(i一2{i—array[10];i++)array[j]一O}
for(i—O{i< 6l i+ + ) {
P1一array[i]‘stb=O}stb=1}while(busy)‘
prnint (unsigned char x){
P1一x}stb—O}stb= 1}while (busy); }
void kbinit()《
/*鍵盤顯示接口韌始化函數(shù)*/)
unsigned char readkey ()f
/*打印輸出*/
rettlrn{ }
1]} /* 負(fù)階碼處理*/
/* 打印輸出*/
/* 字符打印輸出函數(shù)*/
/*鍵值處理函數(shù)*/)
/* 其他處理函數(shù)*/
void kg(){ /* 重力加速度及相對(duì)誤差計(jì)算函數(shù)*/
一2.O* ( ( ( (goat)s2) /E2) 一( ( (float)s1)/E1)) / ( (E2一E1) /1000.O)}
dse~mtl—g; /*計(jì)算重力加速度*,
if(d~otmt1<9.781){d~eountl一(9.781-dscount1)/9.781}rettlrn;)
dsc~ntl一(dscountl-9.781)/9.781} /*計(jì)算相對(duì)誤差*/ )
void kel O { /*測(cè)量時(shí)間參數(shù)El*/
unsigned char data i;
TL0=0‘TH0=0;TMOD=Ox51;"ICON—o)=O; /* 定時(shí)器初始化*/
IE=Ox83~while(!flag1){ /*開中斷.等待*/
El一(clock0*65536.O+cotmt)/1000.O} /*計(jì)算E1的值*/ }
d ke2(){ /*測(cè)量時(shí)間參數(shù)E2*/
unsigned char data l}
TL0— 0;TH0— 0;TMOD一0x51;TCON=Ox0
E2一(crock0*65536.O+count) /1000.0; }
void k0rn O {
IE= 0x83;whi~e (1山g1)
unsigned ch丑r i:
prnint (Oxlb);prnint (0x74)}prnint (0x00);
i一0;while(head[i]j一0){prnint(head D]);i++
l一0;while(hl[j]j一0)tprnint(hi[‘])}i++;)
prn (s1);prn (Ox0a)‘
i一0f while(h2[i]l一0){prnint(h2[i])}i++‘)
prn (s2)4 prn (Ox0a)f
o‘while(tl[1]!一o){ornint(tl[i]);j++})
prn (E1)‘prn (0x0a);
0‘while(t2[1]!一o){ornint(t2[j]);i++;)
prn (E2)}orn (OxOa)4
— o;while(gg[i]!一0){prnint(gg[i]) i++f)
orn (g)}orn (OxOa);
/*打印輸出函數(shù)*/
/*打印機(jī)初始化命令*/
/* 打印漢字*/
/*打印浮點(diǎn)數(shù)并換行*/
/*打印漢字*/
/*打印浮點(diǎn)數(shù)并換行*/
/*打印漢字*/
/*打印浮點(diǎn)數(shù)并換行*/
/*打印漢字*/
/*打印浮點(diǎn)數(shù)并換行*/
/*打印漢字*/
/*打印浮點(diǎn)數(shù)并換行*/i—o;while(er Ill!
prn (dscount1);prn
code void (code *keytab
void main (void){
kbinit ():IE— O:
while (1)(*keytab
一0){prnint(er[j]);i++
(0x0a)·
[])()一{kg,/*⋯ */}
[readkey()])(); )
打印漢字*/
打印浮點(diǎn)數(shù)并換行*/ }
鍵值處理*/
主函數(shù)*/