C语言编程在智能化重力加速度测试仪中的应用
摘要在基于单片机的智能化重力加速度测试仪中采用C语言辅程苘化了程序设计任
务,对于汇辅语言难干处理的浮点数运算及扳字打印输出可通过C语言箱译罂的内部库函数调
用实现。舟绍了一种专为8051系列单片机设计的C语言辅译器Franklin C51,它具有代码优化
功能,能产生极高效率的机器码,并且提供了丰富的内部函数库。描述了C51函数库所支持的
IEEE标准浮点数的内存格式 及采用sprintf0函数处理包括汉字在内的各种字符串的方法。给
出了测试仪的输出结果。电导计| 水分测定仪| 浊度计| 色度计| 粘度计| 折射计| 滴定仪| 密度计| 热流计| 浓度计| 折射仪| 采样仪|
单片机在工业测量控制领域内获得了十分广泛的应用-】J。一般在研制单片机应用系统
时太多采用 编语言作为软件工具。但是,当程序中需要采用浮点数运算时,用忙编语言编
写程序十分麻烦;如果程序中需要进行汉字处理,则用汇编语言编程的效率极低。为了提
高程序的编写效率,只有采用高级语言编程。c语言是目前公认的一种高效率计算机开发用
高级程序设计语言口],它既能象汇编语言那样直接操作机器硬件,又能很方便地进行各种
数学运算和字符处理。笔者结合实例介绍采用美国Franklin软件公司推出的c语言编译器
一C51开发8051单片机应用系统中浮点数运算和汉字打印程序的原理和方法。
1 C51编译器的特殊扩充
C51编译器采用符合ANSI标准的c语言进行编程,为了满足8051系列单片机哈福
结构存储器的需要[3],C51编译器扩展了说明存储器类型的关键字data:(可直接寻址的内
部RAM 区O~7FH);bdata:(可位寻址的内部RAM 区20H 2FH);iclata;(可阃接寻址
的内部RAM 区0~0FFH);pdata:(分页寻址的外部RAM 区256页×256字节);xdata:
(外部RAM 区O~0FFFFH);code:(ROM 区0~0FFFFH)。FranklinC51编译器对于变量
的定义符合ANSI C标准,即可以定义多维数组,可以通过指针进行变量访问,可以将若
干个变量组成为结构和联合等。还扩展了可用来简化对8051单片机内部特殊功能寄存器
及可寻址位访问的%bit”和“sfr 数据类型 在每个变量的声明中可采用上述关键字明确
指定变量的存储器类型。如果在定义变量时不指定其存储器类型,则按编译时给出的存储
模式来决定变量的存储区域。表1列出了Franklin C51编译器所支持的存储器模式。这3种存储器模式各有优缺
点,SMALL模式下参数
的传递是在内部RAM 中
完成的,COMPACT 和
LARGE允许参数传递在
外部RAM 中进行。由于
衰1 Franklin C51编译嚣的存储嚣横式
存储模式 说 明
SMALl 参数和局部变量存^可直接寻址的内部RAM(默认值为data)
COMPACT 参数和局部变量存人分页外部RAM(默认值为0aat~)
LARGE 参数和局部变量直接存^外部RAM(默认值为xdata)
8051单片机访问内部RAM 的速度比访问模式下外部RAM要快得多,因此可将需要经常
使用的变量放在内部RAM 中,而将那些较大及很少使用的变量放在外部RAM 中。另外
若将变量放在外部RAM 中时,还会使程序编译后产生的有效代码加长。C51编译器对于局
部变量采用了静态覆盖技术来提高内部RAM 的使用效率,因此在实际编程时只要有可
能,应尽量采用局部变量。
2 C51编译器支持的浮点数格式
C51编译器支持的浮点数采用关键字float来进行声明,它是满足IEEE一754标准的32
位单精度浮点数,占用四个存储器单元,在内存中按从低到高的地址顺序存储.格式如下
地址 +0 +1 +2 +3
内容MMMMMMMM MMMMMMMM E MMMMMMM S EEEEEEE
其中,最高位S为符号位,0表示正数,1表示负数。从次高位开始的8位EEEEEEEE占用
了两个字节,用来表示浮点数的阶码,为了避免出现负阶码值,并不用实际指数作为阶码,
而是将实际指数加上偏移量127之后再作为阶码。指数可正可负,其范围是一127~+128,
加上127之后便使阶码在o~255的范围之内。阶码之后是浮点数尾数的小数部分,共23
位,占用3个字节。尾数的整数部分永远是1,所以不予表示,但它是隐含存在的,因此尾
数实际上应视为24位。例如将+124.75用IEEE一754标准的单精度浮点数表示为(16进
制数)42F98000H,在内存中的存储格式为
地址 +0 +1 +2 +3
内容00000000 10000000 1 1111001 0 1000010
C51编译器提供了大量实用的库函数,C语言源程序经过C51编译器编译后在连接定
位时,根据源程序中是否使用了浮点运算以及所采用的编译模式,连接程序L51会自动选
择正确的浮点函数库加入到目标代码中去 ]。
3 浮点数运算结果及汉字打印输出的实现
在C51的编译器函数库中有一个十分有用的输出函数sprintf(),它可将数值或字符串
以ASIIC码的形式输出到内存中的某个地址单元,利用该函数可十分方便地实现浮点数运
算结果及汉字字符的显示或打印输出。sprintf()函数的一般格式为
sprintf(字符指针,格式说明,输出参数表列)
其中,第一个参数必须是指针,它可以是数组,也可以是变量的地址,用于指定内存单元的
首地址。格式说明是一个用双引号括起来的字符串:“ [flag][width][.precision]type”
其中,flag称为标志符,用于控制输出数据的符号、空格、小数点的位置等;width是
一个十进制的正整数,用来指定
最小输出字符的数目;precision
用来表示输出数据的精度,由小
数点和一个非负的十进制整数组
成;type称为输出格式转换字
符,对于浮点数type可取3种表
示形式,如表2所示。
裹2 type字符豆其意义
type
f
e. E
g.G
数据类型 输出格式
float [一]dddd dddd形式的浮点数
float [一]d.ddddE [sign3 dd形式的浮点数
float e或f形式的浮点数,取其中形式较好者
输出参数表列是需要输出的一些数据项,它们可以是变量的值,也可以是照原样输出
的字符。由于8051系列单片机存储器结构的限制,输出表列中参数的总字节数有一定的限
制,在SMALL和COMPACT模式下,最大可传递1 5个字节的参数(5个指针,或1个指
针和3个long型数据),在LARGE模式下,最多可传递4o个字节的参数。
在C语言源程序中采用浮点数运算时,运算结果是按IEEE一754标准格式存储在内存
单元中的。这种存储格式不便于显示和打印输出。利用sprint{O 函数可以很容易地将运算
结果转换成宜于显示和打印输出的ASIIC码。如果单片机应用系统中需要采用汉字输出,
则可以在汉字操作系统(ga UCDOS)下编写C语言源程序,将要输出的汉字作为字符串数
组直接定义到ROM 区,则该RoM 区中就存储了一系列汉字的代码。从该RoM 区中按顺
序取出各个汉字代码,输出到任何一种具有汉字打印功能的打印机上,即可很方便地实现
汉字的打印输出 。
4 实 例
所研制的智能化重力加速度测试仪的监控程序采用C51编程,实现了复杂的浮点数运
算处理及汉字打印输出功能 测试仪利用光电转换器的输出脉冲触发8051单片机的外中断
0,读取两次外部中断之问定时器0的计数值,通过计算获得加速度的值.为了提高计算精
度采用了浮点数运算。测试仪通过并行口与EPSON—LQ300K打印机相连,可将测量结果
以汉字方式打印输出。打印机的并行接口为Centronics标准.用8051单片机的P1口作为数
据线,P3.3和P3.5分别作为联络信号STROB和BUSY很容易实现与打印机的接口。智能
化重力加速度测试仪已经研制成功并通过江汉石油学院设备处组织的鉴定。该仪器操作简
单,一次测量可获得多个测量数据, 自动计算出最终结果和相对误差值,并可通过打印机
接表3格式输出测量结果。
裹3 重力加速度测试仪的输出涮■结果
落球法测量结果 单摆法涮量结果
预置高度:H1—100 00 ram-H2—800 00 ram
下落时问:T1—65 076 m .T2=297.27 ms
重力加速度:g 9.9042 m/s0
相对误差:Er一0.0186
单撄撄长:I,=l309 ram
撄动周期;TI,一2288.4 Ills
重力加速度:g一9.8752 m/s
相对误差;Er 0.0096
下面给出了仪器监控程序中关于浮点数处理和汉字打印输出部分的源程序。其中prn
()是浮点数打印输出函数。对于小于0的浮点数先将其转换为正数,再调用sprint{()库
函数将其转换成ASIIC码,按浮点数的科学表示法存于数组array中。如果需要按科学表示
法打印输出,可直接从数组array中逐个取出ASIIC码数据进行打印。本例要求按工程表示
法打印输出,因此先要根据浮点数的阶码确定小数点的位置。prnint O 是单个字符打印输
出函数。它用来向打印机输出各种打印控制命令以及汉字的打印输出。在ROM 区中定义了
若干个汉字字符串数组,打印汉字时只要从各个数组中取出需要输出的汉字字符串代码作
为实际参数来调用prnint()函数即可。
C语言源程序如下:
#include< reg51.h>
#include< stdio.h>
#indude< math.h>
#include< stdlib.h>
float g,dscountl·El,E2,s1,s2; /*定义浮点型数据变量*/
sbit busy=P3 3 sbit stb=P3 5; /*定义打印机联络信号*/
uns~ned char bdata flag, /*定义其它类型数据变量*/
sbit flagl—Ihg OI
unsigned im clockO,count;
unsigned char dsp[8],array E]o3,
char code head1口一“欢迎使用重力加速度测量仪”} /*定义汉字字符串*/
char code head2[]一“落球法测量结果”;
char code head3口一“单摆测量结果”;
char code hl[]= “硬置高度H1一H2= ;
char codeI1[]= 单摆按长L一”;
char codetl[]= 下落时间TI T2= ”}
char codetI[]= “摆动周期TL= ;
char code gg[]; “重力加速度g一”}
char code er[]= “相对误差Er= ;
exten~char xdata[MtaPortt StaPort; ,*定义8279数据、命令口*,
exter~unsigned char keyval(unsigned char va1)} /} 汇编语言键值处理子程序*/
extern unsigned char tedseg(unsigned char x); ‘ /*汇编语言显示段码处理子程序*/
xint()interrupt 0 using 1 《
/*外部中断INT0处理函数*/)
timer0 O interrupt 1 using 1{
/*定时器TO处理函数*/)
prn (float x){ /*浮点数打印输出函数*/
unsigned cha r i}
if(x<0)x=l x; /*将浮点数作为正数处理*/
sprintf(array, 4e”+x)} ^ 转换为ASIIC码存于数组array中*/
arTay E1o]一array[93=array[93~0x0f; /*取阶码}/
j(array[73一=0x2b){ /*正阶码处理*/
for(1—1}i<一8r阳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()《
/*键盘显示接口韧始化函数*/)
unsigned char readkey ()f
/*打印输出*/
rettlrn{ }
1]} /* 负阶码处理*/
/* 打印输出*/
/* 字符打印输出函数*/
/*键值处理函数*/)
/* 其他处理函数*/
void kg(){ /* 重力加速度及相对误差计算函数*/
一2.O* ( ( ( (goat)s2) /E2) 一( ( (float)s1)/E1)) / ( (E2一E1) /1000.O)}
dse~mtl—g; /*计算重力加速度*,
if(d~otmt1<9.781){d~eountl一(9.781-dscount1)/9.781}rettlrn;)
dsc~ntl一(dscountl-9.781)/9.781} /*计算相对误差*/ )
void kel O { /*测量时间参数El*/
unsigned char data i;
TL0=0‘TH0=0;TMOD=Ox51;"ICON—o)=O; /* 定时器初始化*/
IE=Ox83~while(!flag1){ /*开中断.等待*/
El一(clock0*65536.O+cotmt)/1000.O} /*计算E1的值*/ }
d ke2(){ /*测量时间参数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);
/*打印输出函数*/
/*打印机初始化命令*/
/* 打印汉字*/
/*打印浮点数并换行*/
/*打印汉字*/
/*打印浮点数并换行*/
/*打印汉字*/
/*打印浮点数并换行*/
/*打印汉字*/
/*打印浮点数并换行*/
/*打印汉字*/
/*打印浮点数并换行*/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()])(); )
打印汉字*/
打印浮点数并换行*/ }
键值处理*/
主函数*/