第4章 单片机的C51语言_第1页
第4章 单片机的C51语言_第2页
第4章 单片机的C51语言_第3页
第4章 单片机的C51语言_第4页
第4章 单片机的C51语言_第5页
已阅读5页,还剩87页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

重点与难点:

C51的数据类型、存储类型和存储模式;C51的指针定义与使用;C51与汇编语言的混合编程;C51的单片机片内、片外资源编程控制方法。教学目标:

※掌握C51语言的数据结构相关内容;

※了解C51与汇编语言的混合编程方法;

※熟悉KeiluVision2的C5l仿真开发环境的使用;

※熟悉KeiluVision2与Proteus联合仿真;

※掌握单片机的工作原理和编程方法。第4章单片机的C51语言4.1C51的程序结构4.1.1C51语言概述如前所述,用汇编语言编写MCS-51单片机程序必须要考虑其存储器结构,尤其是其片内数据存储器与特殊功能寄存器的使用以及按实际地址处理端口数据。而用C51语言编写单片机应用程序,则不用具体组织、分配存储器资源和处理端口数据,但对数据类型与变量的定义,必须要与单片机的存储结构相关联,否则编译器不能正确地映射定位。与标准C语言相比,C51在数据类型、变量存储模式、输入/输出处理、函数等方面有一定差异,它需要根据单片机存储结构及内部资源定义相应的数据类型和变量,而其他语法规则、程序结构及程序设计方法等与标准的C语言程序设计相同。本章着重介绍C51的数据结构、C51与汇编语言的混合编程、C51仿真开发环境以及C51初步编程等内容。学习本章要注意复习标准C语言的基本知识,因为在应用实例中要用到标准C的内容。4.1.2C51的程序结构

C51程序的基本单位是函数。一个C51源程序至少包含一个主函数,也可以是一个主函数和若干个其他函数。主函数是程序的人口;主函数中的所有语句执行完毕,则程序结束。以下通过一个可实现开关控制LED灯亮灭功能的源程序说明C51程序的基本结构#include<REG51.H>//51单片机头文件sbitK1=P3^0;//定义输入开关sbitL1=P1^0;//定义输出指示灯voiddelay();//延时函数声明//---------------------------------voidmain(void)//主函数

{while(1)//无限循环体

{if(K1==0)//开关是否合上

{delay();//延时

if(K1==0)//开关是否合上,不是抖动

{L1=0;}//合上,灯亮}else//开关是断开的

{L1=1;}//开关断开,灯灭

}}voiddelay(void)//延时函数

{unsignedchari;//字符型变量i定义

for(i=200;i>0;i--);

}//循环延时开关控制LED灯亮灭.DNS图4-1模拟开关灯电路原理图在本例的开始处使用了预处理命令#include,它告诉编译器在编译时将头文件REG51.H读入一起编译。在头文件REG51.H中包括了对51单片机特殊功能寄存器名的集中说明。本例中main()是一个无返回、无参数型函数,虽然参数表为空,但一对圆括号()必须有,不能省略。其中:①sbitL1=P1^0和sbitK1=P3^0是全局变量定义,它将Pl.0端口定义为L1输出变量;它将P3.0端口定义为K输入变量;②unsignedchari是局部变量定义,它说明i是位于片内RAM且长度为8的字符型变量;③while(l)是循环语句,可实现死循环功能;④L1=0和L1=1是两个赋值语句,等号=作为赋值运算符;⑤if(K1==0)是条件判断语句,判断开关是否合上⑥for(i=200,i>0;i--)是没有语句体的循环语句,这里起到软件延时的作用。C51语言程序的基本结构为:包含<头文件>

函数类型说明全局变量定义

main(){局部变量定义

<程序体>}funcl(){局部变量定义

<程序体>}…funcN(){局部变量定义

<程序体>}其中,func1()…funcN()代表用户定义的函数,程序体指C51提供的任何库函数调用语句、控制流程语句或其他函数调用语句。4.2C51的数据结构4.2.1C51的变量在程序执行过程中,数值可以发生改变的量称为变量。变量的基本属性是变量名和变量值。一旦在程序中定义了一个变量,C51编译器就会给这个变量分配相应的存储单元。此后变量名就与存储单元地址相对应,变量值就与存储单元的内容相对应。例如,图4.2所示程序中通过引用变量i实现了对分配内存30H单元的数据操作。图4.2C51的变量概念示意图要在C51程序中使用变量必须先对其进行定义,这样编译系统才能为变量分配相应的存储单元。定义一个变量的格式如下:

[存储种类]数据类型[存储类型]变量名这说明变量具有4大要素,其中数据类型和变量名是不能省略的部分。1.存储种类2.数据类型3.存储类型

4.变量名存储种类是指变量在程序执行过程中的作用范围。变量的存储种类有4种:自动(auto)、外部(extern)、静态(static)和寄存器(register)。使用存储种类说明符auto定义的变量称为自动变量。自动变量的作用范围在定义它的函数体或复合语句内部。在定义它的函数体或复合语句被执行时,C51才为该变量分配内存空间,当函数调用结束返回或复合语句执行结束时,自动变量所占用的内存空间被释放,这些内存空间又可被其他的函数体或复合语句使用。在定义变量时,如果省略存储种类,则变量默认为自动(auto)变量。由于89C51单片机访问片内RAM速度很快,通常将函数体内和复合语句中使用频繁的变量放在片内RAM中,且定义为自动变量,这样可有效地利用片内有限的RAM资源。使用存储种类说明符extern定义的变量称为外部变量。在一个函数体内,要使用一个已在该函数体外或别的程序模块文件中定义过的外部变量时,该变量在本函数体内要用extern说明。外部变量被定义后,即分配了固定的内存空间,在程序的整个执行期间都是有效的。通常将多个函数或模块共享的变量定义为外部变量。外部变量是全局变量,在程序执行期间一直占有固定的内存空间。当片内RAM资源紧张时,不应将外部变量放在片内RAM。使用static定义的变量称为静态变量,它又分为内部静态变量和外部静态变量。在函数体内部定义的静态变量为内部静态变量,它在对应的函数体内有效,但在函数体外不可见,这样不仅使变量在定义它的函数体外被保护,还可以实现当离开函数时值不被改变。外部静态变量是在函数外部定义的静态变量,它在程序中一直可见,但在定义的范围之外是不可见的。如在多文件或多模块处理中,外部静态变量只在文件内部或模块内部有效。

1.存储种类2.数据类型表4-1C51支持的基本数据类型数据类型长度值域字符型(char)signedchar单字节0~255unsignedchar单字节-128~+127整型(int)signedint双字节0~65535unsignedint双字节-32768~+32767长整型signedlong4字节0~4294967295unsignedlong4字节-2147483648~+2147483647浮点型float4字节10-38~1038double8字节10-308~10308指针型普通指针*1~3字节0~65535数据的不同格式叫做数据类型,C51支持的基本数据类型与标准C相同其中,有符号数据类型可以忽略signed标识符,如signedchar等价于char,signedint等价于int等。C51还增加了一些特殊的数据类型,它们分别对应于bit、sfr、sfr16和sbit4个关键字。

(l)bit位型。bit位型是C51编译器的一种扩充数据类型,利用它可定义一个位变量或位函数,但不能定义位指针,也不能定义位数组。它的值是一个二进制位,不是0就是1。

(2)sfr特殊功能寄存器型。51系列单片机内有21个特殊功能寄存器(SFR),分散在片内RAM区的高128字节,地址为80H~FFH。为了能直接访问这些SFR,需要通过关键字sfr对其进行定义,语法如下:

sfrsfr_name=地址常数;

这里sfr_name是一个特殊功能寄存器名,“=”后面必须是常数,其数值范围必须在特殊功能寄存器地址范围内,即位于0x80~0xFF之间。例如:

sfrP1=0x90;//定义P1口地址90HsfrPSW=0xD0;//定义PSW地址D0H

对于16位SFR,可使用关键字sfr16,语法与8位SFR相同,定义的地址必须是16位

SFR的低端地址,例如:

sfr16DPTR=0x82;//定义DPTR,其DPL=82H,DPH=83H

注意:这种定义适用于所有新的SFR,但不能定义定时/计数器0和1,因为他们的地址不是连续的。

(3)sbit可位寻址型。在51系列单片机中,经常要访问特殊功能寄存器中的某些位,用关键字sbit定义可位寻址的特殊功能寄存器的位寻址对象。用关键字sbit定义可位寻址的特殊功能寄存器的位寻址对象。定义方法有如下3种:

1)sbit位变量名=位地址将位的绝对地址赋给位变量名,位地址必须位于0x80~0xFF之间。例如:

sbitCY=0xD7;//将位的绝对地址赋给变量

2)sbit位变量名=SFR名称^位位置当可寻址位位于特殊功能寄存器中时,可采用这种方法。其中SFR名称必须是已定义的SFR的名字,位位置是一个0~7之间的常数。例如:

sfrPSW=0xD0;sbitCY=PSW^7;//定义CY位为PSW.7,位地址为0xD7

3)sbit位变量名=字节地址^位位置这种方法是以一个常数(字节地址)作为基地址,该常数必须在0x80~0xFF之间。位位置是一个0~7之间的常数。例如:

sbitCY=0xD0^7;//将位的相对地址赋给变量注意sbit和bit的区别:sbit定义特殊功能寄存器中的可寻址位,而bit则定义了一个普通的位变量,一个函数中可包含bit类型的参数,函数返回值也可为bit类型。典型REG51.h头文件的部分内容如图4-3所示图4-3reg51.h头文件部分内容示意图

3.存储类型

51系列单片机具有3个存储空间:片内低128BRAM、片外64KBRAM和片内外统一编址的64KBROM,对于8052型单片机还有片内高128BRAM空间。这些存储空间与存储类型的对应关系如图4-4和表4-2所示。data区code区xdata区bdata区pdata区idata区图4-451系列单片机存储空间示意图表4-2C51的存储类型与存储空间对应关系存储类型存储空间位置字节地址说明data片内低128B存储区00H~7FH访问速度快,可作为常用变量或临时性变量存储区bdata片内可位寻址存储区20H~2FH允许位与字节混合访问idata片内高128B存储区80H~FFH只有52系列单片机才有pdata片外页RAM00H~FFH常用于外部设备访问xdata片外64KBRAM0000H~FFFFH常用于存放不常用的变量或等待处理的数据code程序ROM0000H~FFFFH常用于存放数据表格等固定信息一个变量除了与存储单元相对应外,还与它所在的存储空间有关,即还需要指出其存储类型。例如语句chardataa声明了a是位于片内低128BRAM区的字符型变量。如果在定义变量时省略了存储类型说明符,C51编译器会根据当前编译模式自动认定默认的存储类型。编译模式共分为:小编译模式(SMALL)、紧凑编译模式(COMPACT)和大编译模式(LARGE)3种模式。表4-33种编译模式的特点小结编译模式变量存储区域默认存储类型特点SMALL片内低128BRAMdata访问数据的速度最快,但由于存储容量小,难以满足需要定义变量更多的场合COMPACT片外页256BRAMpdata介于两者之间,且受片外RAM的容量限制LARGE片外64KBRAMxdata访问数据的效率不高,但由于存储容量大,可以满足需要定义变量更多的场合由表4-3可知,在SMALL编译模式下,语句chara等价于chardataa,而在LARGE编译模式下,语句chara等价于charxdataa。

4.变量名

C51规定变量名可以由字母、数字和下划线3种字符组成,且第一个字符必须为字母或下划线,变量名长度无统一规定,随编译系统而定。使用时应注意:大写的变量和小写的变量是两个不同的变量,如SUM和sum。习惯上变量用小写表示。另外,变量名除了应避免使用标准C语言的32个关键字外,还要避免使用C51扩展的新关键字。表4-4C51扩展的21个关键字一览表关键字用途说明_at_地址定位为变量进行存储器绝对空间地址定位alien函数特性声明声明与PL/M-51编译器的接口bdata存储类型说明可位寻址的内部数据存储器bit位变量声明声明一个位变量或位函数code存储器类型声明程序存储器compact存储模式声明声明一个紧凑编译存储模式data存储器类型声明直接寻址的内部数据存储器far远变量声明Keil用3BYTE指针来引用它idata存储器类型声明间接寻址的内部数据存储器interrupt中断函数声明定义一个中断服务函数large存储模式声明声明一个大编译存储模式pdata存储器类型声明分页寻址的外部数据存储器_priority_多任务优先声明规定RTX51或RTX51Tiny的任务优先级reentrant再入函数声明用于把函数定义为可重人函数sbit扩充数据类型声明声明一个可位寻址变量sfr扩充数据类型声明声明一个特殊功能寄存器sfr16扩充数据类型声明声明一个16位的特殊功能寄存器small存储模式声明声明一个小编译存储模式_task_任务声明定义实时多任务函数using寄存器组定义定义51单片机工作寄存器组xdata存储器类型声明外部数据存储器所有变量在使用前必须声明,即变量须“先定义,后使用”,凡未被定义的,不作为变量名,这样可保证程序中变量名使用的正确性。

unsignedchardatasystem_status=0;//定义system_status为无符号字符型自动变

//量,该变量位于data区中且初值为0unsignedcharbdatastatus_byte;//定义status_byte为无符号字符型自动变量,

//该变量位于bdata区中

unsignedintcodeunit_id[2]={Ox1234,Ox89ab};//定义unit_id[2]为无符号整型自

//动变量,该变量位于code区中,且

//为长度为2的数组,初值为0x1234//和0x89abstaticcharm,n;//定义m和n为2个位于data区中的有符号字符型静态变量

externfloatxdatavar4;//在片外RAM64KB空间定义外部实型变量var4#pragmaCOMPACT//设置编译模式,指定默认存储类型

chark2;//定义k2为有符号字符型自动变量,且默认为pdata型

4.2.2C51的指针标准C语言指针的一般定义形式为:

数据类型*指针变量名其中“*指针变量名”表示这是一个指针变量,它指向一个由“数据类型”说明的变量。被指向变量和指针变量都位于C编译器默认的存储区中。例如:inta='A';int*p1=&a;

这表示p1是一个指向int型变量的指针变量,此时p1的值为int型变量a的地址,而a和p1两个变量都位于C编译器默认的内存区域中。对于C51来讲,除上述信息外,指针定义还应包括以下信息:(1)指针变量自身位于哪个存储区中(2)被指向变量位于哪个存储区中故C51指针的一般定义形式为:

数据类型[存储类型1]*[存储类型2]指针变量名;其中“数据类型”是被指向变量的数据类型,如int型或char型等;“存储类型1”是被指向变量所在的存储区类型,如data,code,xdata等,缺省时根据该变量的定义语句确定;“存储类型2”是指针变量所在的存储区类型,如data,code,xdata等,缺省时根据C51编译模式的默认值确定;指针变量名可按C51变量名的规则选取。

例4-1charxdataa='A’;char*ptr=&a;

解:在这个例子里,ptr是一个指向char型变量的指针变量,它本身位于SMALL编译模式默认的data存储区里,它的值是位于xdata存储区里的char型变量a的地址。

例4-2charxdataa='A';char*ptr=&a;charidatab='B';*ptr=&b;

解:在这个例子里,前两句与例4-1相同。而后两句里,由于变量b位于idata存储区中,所以当执行完*ptr=&b之后,ptr的值是位于idata存储区里的char型变量b的地址。从此可看出,以char*ptr形式定义的指针变量,其数值既可以是位于xdata存储区的char型变量的地址,也可以是位于idata存储区的char型变量的地址,具体结果由赋值操作关系决定。例4-3charxdataa='A';charxdata*ptr=&a;

解:这里变量a是位于xdata存储区里的char型变量,而ptr是位于data存储区且固定指向xdata存储区的char型变量的指针变量,此时ptr的值为变量a的地址(不能像例4-2那样再将idata存储区的char型变量b的地址赋予ptr)。

例4-4charxdataa='A';charxdata*idataptr=&a;

解:这里表示,ptr是固定指向xdata存储区的char型变量的指针变量,它自身存放在idata存储区中,此时ptr的值为位于xdata存储区中的char型变量a的地址。4.3C51与汇编语言的混合编程C51语言提供了丰富的库函数,具有很强的数据处理能力,可生成高效简洁的目标代码,在绝大多数场合采用C51语言编程即可完成预期的任务。尽管如此,有时仍需要采用一定的汇编语言程序,如对于某些特殊的I/O接口地址的处理、中断向量地址的安排、提高程序代码的执行速度等。为此,C51编译器提供了与汇编语言程序的接口规则,按此规则可以方便地实现C51语言程序与汇编语言程序的相互调用。

4.3.1在C51中调用汇编程序要实现在C51函数中调用汇编函数,需要了解C51编译器的编译规则。下面我们从一个实例人手,介绍有关内容,即在两个给定数据中选出较大的那个数据,其程序源代码如下://以下代码在main.c文件中实现voidmax(chara,charb);//由汇编语言实现main(){chara=30,b=40,C;C=max(a,b);}在上面的主函数中,voidmax(chara,charb)函数是在下面的汇编文件中实现的:

;以下代码在汇编文件max.asm中实现

PUBLICMAXDESEGMENTCODERSEGDE_MAX:MOVA,R7;取第一个参数

MOV30H,R5;取第二个参数

CJNEA,30H,TAG SJMPEXITTAG:JNCEXITMOVA,R5;

MOVR7,A;返回第二个参数

EXIT:RET;返回第一个参数END要想使以汇编语言实现的函数能够在C程序中被调用,需要解决下面3个问题:①程序的寻址。在main()中调用的max()函数,如何与汇编文件中的相应代码对应起来;②参数传递,从main()中传递给max()函数的参数a和b,存放在何处可使汇编程序能够获取它们的值;③返回值传递,汇编语言计算得到的结果,存放在何处可使C语言程序能够获取。程序的寻址是通过在汇编文件中定义同名的“函数”来实现的,例如上面汇编代码中的:

PUBLICMAXDESEGMENTCODERSEGDE_MAX:…

在上面的例子中,

“_MAX”与C程序中的max相对应。在C程序和汇编语言之间,函数名的转换规则见表4-5。表4-5函数名的转换规则C程序的函数声明汇编语言的符号名解释voidfunc(void)FUNC无参数传递或不含寄存器参数的函数名不做改变地传人目标文件中,名字只是简单地转换为大写形式voidfunc(char)_FUNC带寄存器参数的函数名转为大写,并加上“_”前缀voidfunc(void)reentrant_?FUNC重人函数须使用前缀“_?”传递参数的简单办法是使用寄存器,这种做法能够产生精炼高效的代码,具体规则见表4-6。

表4-6参数传递规则参数类型charintlong,float一般指针第1个参数R7R6,R7R4~R7R1,R2,R3第2个参数R5R4,R5R4~R7R1.R2,R3第3个参数R3R2,R3无R1.R2,R3在前面的例子语句voidmax(chara,charb);中,第一个char型参数a放在寄存器R7中,第二个char型参数b放在寄存器R5中。因此在后面的汇编代码中,就是分别从R7和R5中取这两个参数:

…MAX:MOVA,R7;取第一个参数

MOV30H,R5;取第二个参数

…汇编语言通过寄存器或存储器传递参数给C语言程序。汇编语言通过寄存器传递参数给C语言的返回值见表4-7。

表4-7汇编语言返回值返回值寄存器说明bitC进位标志(unsigned)charR7(unsigned)intR6.R7高位在R6,低位在R7(unsigned)longR4~R7高位在R4,低位在R7floatR4~R732位IEEE格式,指数和符号位在R7指针R1,R2,R3R3存放寄存器类型,高位在R2,低位在R1在前面的例子中,汇编程序就是通过把两个数中较大的一个保存在寄存器R7中返回给C函数的。

4.3.2在C51中嵌入汇编代码在C51函数内嵌入汇编代码,可以有3种不同的方法。

方法1

直接在函数体内的每个汇编语句前加“asm”预编译指令。例如:

voidreset__data(void){asmMOVR1,#0AHasmLOOP:INCA

asmDJNZR0,LOOPreturn;

}

方法2把asm作为关键字,后续的汇编语句用大括号括起来即可。例如:

voidreset__data(void){asm{MOVR1,#0AHLOOP:INCADJNZR0,LOOP}return;}

方法3在C模块内通过语句“#pragma”嵌入汇编代码。例如:

voidreset(void){#pragmaasmMOVR1,#0AHLOOP:INCADJNZR0,LOOP#pragmaendasmreturn;

}4.4C51仿真开发环境4.4.1Keil的编译环境µVision2Keil是德国KeilSoftware公司出品的单片机集成开发软件,该软件支持51单片机的所有变种(目前共有400多种型号)。Keil提供了包括C编译器、宏汇编、连接器、库管理及一个功能强大的仿真调试器在内的完整开发方案,并通过一个集成开发环境(µVision2)将这些部分组合在一起。Keil单片机集成开发软件可以运行在Windows98/NT/2000及XP等操作系统下。

µVision2的软件界面包括4大组成部分,即菜单工具栏、工程管理窗口、文件窗口和输出窗口(如图4.5所示)。以下仅针对组成结构做个简单介绍,具体使用方法将在本书附录A中结合实验需要进行介绍。图4-5µvision2的软件界面①菜单工具栏:菜单为标准的Windows风格,µVision2中共有11个下拉菜单。②工程管理窗口:工程管理窗口用于管理工程文件目录,它由5个子窗口组成,可以通过子窗口下方的标签进行切换,它们分别是文件窗口、寄存器窗口、帮助窗口、函数窗口及模板窗口。③文件窗口:文件窗口用于显示打开的程序文件,多个文件可以通过窗口下方的文件标签进行切换。④输出窗口:输出窗口用于输出编译过程中的信息,由三个子窗口组成,可以通过子窗口下方的标签进行切换,它们分别是编译窗口、命令窗口和搜寻窗口。为了掌握程序运行信息,Keil软件在调试程序时还提供了许多信息窗口,包括输出窗口、观察窗口、存储器窗口、反汇编窗口以及串行窗口等。为了能够比较直观地了解单片机中定时器、中断、并行端口、串行端口等常用外设的使用情况,Keil还提供了一些外围接口对话框。然而,Keil的这些调试手段都是通过数值变化来监测程序运行的,很难直接看出程序的实际运行效果,特别是对于包含测量、控制、人机交互等外部设备的单片机应用系统来讲缺乏直观性。具有强大仿真功能的Proteus软件虽然较好地解决了外围电路与单片机混合仿真的问题,它却没有C51仿真功能。Proteus与KeilC的联合使用则可使这两个仿真软件优势互补,组建单片机应用系统在C51条件下的整机虚拟实验环境。该虚拟实验环境包括一个硬件执行环境和一个软件执行环境,其中Proteus提供硬件仿真与运行环境,Keil提供软件执行环境。4.4.2基于KeilC和Proteus的程序开发过程在PC机上安装KeiluVision2软件,完成后先要建立一个项目,如图4-6所示,启动KeiluVision2,单击“Project菜单/New…”选项:图4-6建立新项目图4-7保存新项目

从弹出的窗口中,选择要保存项目的路径,并输入项目文件名“ADC0809.uv2”,然后点击保存按钮,如图4-7所示:这时会弹出一个选择CPU型号的对话框,可以根据所使用的单片机来选择,如图4-8所示选择SST89x516RD2,选定CPU型号之后从窗口右边一栏可以看到对这个单片机的基本说明,点击确定按钮后会弹出如图4-9所示窗口,询问是否要将启动代码“StartupCode”加入到项目中,对于采用高级语言C51编写的程序,点击“是”按钮,对于采用汇编语言编写的程序可以不用启动代码“StartupCode”,因此点击否按钮。图4-8选择CPU图4-9添加启动代码图4-10创建程序文件接下来要创建程序文件,如图4-10所示,单击“File菜单/New…”选项:在弹出的编辑窗口中输入如下C51源程序://********************************************************************************//********************************************************************************//**<程序名>:数字电压表 **//**<功能>:使用LCD显示被检测电压,精度为0.05V,范围是0~5V。**//**<版本说明>:这是第1版,使用16X2LCD显示。 **//**<作者>:皮大能 **//**<完成时间>:2010年2月8日 **//**<联系方式>:pidaneng@163.com;QQ:399676777 **//********************************************************************************//*******************************头文件及宏定义*******************************//********************************************************************************#include<REG52.h>#include"delay.h"//插入延时头文件(见4.5)#include"LCD1602.h"//插入LCD1602显示头文件#defineTIME0H0x3C//定时初值高八位#defineTIME0L0xB0//定时初值高低位#defineucharunsignedchar#defineuintunsignedint//*************************************************************************//***********************端口设置宏定义************************************//*************************************************************************sbitSTART=P2^4;//ATART,ALE接口。0->1->0:启动AD转换。sbitOE=P2^3;//读ADC0809数据,低电平有效sbitEOC=P3^3;//转换完毕由0变1.#defineOUTPORTP0//ADC0809数据接口//**************************************************************************//********************************全局变量**********************************//**************************************************************************ucharuc_Clock=0; //定时器0中断计数bitb_ADTransform=0;//启动A/D转换时间到标志,为1时启动A/D转换//*************************************************************************//******************************函数声明***********************************voidDelay(); //延时函数。voidWR_CMD(ucharucCommand);//把1个命令写入LCD函数。voidInitialize(); //LCD初始化函数voidWR_Data(ucharucData); //把1个数据写入LCD。voidShow_1_Char(ucharucChar);//把1个字符写入LCD.voidShow_Char(ucharucaChar[]);//把组字符写入LCD.voidvShowVoltage(unintuiNumber);//uintuiADTransform();//AD转换函数,返回转换结果.voidTime0();//定时器0中断函数.//**************************************************************************//AD转换函数//AD转换函数,返回转换结果。//转换结果是3位数,小数点在百位与十位之间。//**************************************************************************uintuiADTransform(){uintuiResult;OUTPORT=0x00;//选ADC0809的0通道START=1; //启动AD转换。0->1->0START=0;while(EOC==0);//等待转换结束。

OUTPORT=0xff;//数据口设为输入状态

OE=0;//打开ADC0809的数据口三态门uiResult=OUTPORT;//出入转换结果。

OE=1;//关闭ADC0809的数据口uiResult=(100*uiResult)/51;//处理运算结果。returnuiResult;//带处理好的AD转换结果返回}//**************************************************************************//电压数据写入LCD函数//**************************************************************************voidvShowVoltage(unintuiNumber){ucharucaNumber[3],ucCount;if(uiNumber>999) {uiNumber=999;} ucaNumber[0]=uiNumber/100;//把计算数字的每个位存入数组。取整数部分

ucaNumber[1]=(uiNumber/10)%10;//取小数点后第1位数

ucaNumber[2]=uiNumber%10;//取小数点后第2位数

for(ucCount=0;ucCount<3;ucCount++) { Show_1_Char(ucaNumber[ucCount]+48); //从首位到末位逐一输出。

if(ucCount==0) {Show_1_Char('.');} }}//**************************************************************************//*********************************主函数***********************************//**************************************************************************voidmain(){TMOD=0x01; //定时器0,模式1。TH0=TIME0H;//定时器初值的高八位送入TH0TL0=TIME0L;//定时器初值的低八位送入TL0uc_Clock=5;//设定时计数初值TR0=1;//启动定时器。ET0=1;//开定时器中断。EA=1;//开总中断LCD_Initial1();//LCD初始化WR_CMD(0x84);//写入显示"Voltage:"的起始地址Show_Char("Voltage:");//写入"Voltage:"到LCD显示WR_CMD(0xC9);//写入显示"(V)"的起始地址Show_Char("(V)");//写入"(V)"到LCD显示while(1){if(b_ADTransform==1)//如果A/D采样时间到就启动A/D转换

{ b_ADTransform=0;//清采样时间到标志

WR_CMD(0xC4);//写入显示电压数据的起始地址

vShowVoltage(uiADTransform()); } }}//**************************************************************************//***************************定时器0中断函数********************************//**************************************************************************voidTime0()interrupt1{TH0=TIME0H;//恢复定时器0的初值。TL0=TIME0L;uc_Clock--;//定时计数器减1if(uc_Clock==0){ uc_Clock=5;//定时计数器置初值

b_ADTransform=1;//置采样周期到标志}}//**************************************************************************//*LCD1602驱动程序 *// 文件名:LCD1602.h//**************************************************************************#ifndef__LCD1602_H__#define__LCD1602_H__//*****************************************************#include<REG52.h>//插入52特殊功能寄存器头文件#include<intrins.h>#include"delay.h"//插入延时头文件(见4.5)//*************************************************************************#defineuintunsignedint#defineucharunsignedchar//***********************端口设置宏定义************************************//*************************************************************************sbitLCDRS=P2^0;//寄存器选择信号sbitLCDRW=P2^1;//读写信号sbitLCDE=P2^2;//片选信号,当输入下降沿信号时,执行指令或传送数据。#defineLCDDBPP0//LCD数据接口。/***********1602液晶显示部分子程序****************///内部等待函数*********************************************unsignedcharLCD_Wait(void){chardatamykey;LCDE=0;//操作脉冲信号置低电平_nop_();LCDRS=0;//LCD设为命令模式_nop_();LCDRW=1;//LCD设为读状态

_nop_();LCDE=1;//产生操作脉冲上升沿

_nop_();mykey=LCDDBP&0x80;//读LCD的状态信息LCDE=0;returnmykey;//带LCD状态信息返回

}//**************************************************************************//把1个命令写入LCD函数//**************************************************************************voidWR_CMD(unsignedcharCommand) {LCDE=0;//操作脉冲信号置低电平_nop_();LCDRS=0;//LCD设为命令模式_nop_();LCDRW=0;//LCD设为写状态LCDE=1;//产生操作脉冲上升沿_nop_(); LCDDBP=Command;//输出命令。_nop_();LCDE=0;//最后执行命令。while((LCD_Wait())==0x80);//测试操作完否}//**************************************************************************//把1个数据写入LCD函数//**************************************************************************voidWR_Data(unsignedcharData) {LCDE=0;//操作脉冲信号置低电平_nop_();LCDRS=1;//LCD设为数据模式_nop_();LCDRW=0;//LCD设为写状态_nop_();LCDE=1;//然后把LCD改为写入数据状态。_nop_(); LCDDBP=Data;//再输出数据。LCDE=0;//最后显示数据。while((LCD_Wait())==0x80);//测试操作完否}//**************************************************************************//设置显示模式************************************************************#defineLCD_SHOW 0x04//显示开#defineLCD_HIDE 0x00//显示关#defineLCD_CURSOR 0x02//显示光标#defineLCD_NO_CURSOR 0x00//无光标#defineLCD_CLEAR_SCREEN 0x01//清屏#defineLCD_HOMING 0x02//光标返回原点#defineLCD_FLASH 0x01//光标闪动#defineLCD_NO_FLASH 0x00//光标不闪动voidLCD_SetDisplay(unsignedcharDisplayMode){WR_CMD(0x08|DisplayMode);}//设置输入模式************************************************************#defineLCD_AC_UP 0x02#defineLCD_AC_DOWN 0x00//default#defineLCD_MOVE 0x01//画面可平移#defineLCD_NO_MOVE 0x00//defaultvoidLCD_SetInput(unsignedcharInputMode){WR_CMD(0x04|InputMode);}//LCD初始化1************************************************************voidLCD_Initial1(){LCDE=0;//操作脉冲信号置低电平WR_CMD(0x38);//8位数据端口,2行显示,5*7点阵LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR);//开启显示,无光标WR_CMD(LCD_CLEAR_SCREEN);//清屏LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE);//AC递增,画面不动}//LCD初始化2************************************************************voidLCD_Initial2(){LCDE=0;//操作脉冲信号置低电平WR_CMD(0x38);//8位数据端口,2行显示,5*7点阵LCD_SetDisplay(LCD_SHOW|LCD_CURSOR|LCD_FLASH);//开启显示,有光标,光标闪动WR_CMD(LCD_CLEAR_SCREEN);//清屏LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE);//AC递增,画面不动}//************************************************************************** //把1个字符写入LCD函数//**************************************************************************voidShow_1_Char(unsignedcharucChar){WR_Data(ucChar);}//调用写数据函数//**************************************************************************//把一组字符写入LCD函数//**************************************************************************voidShow_Char(unsignedcharucaChar[]){unsignedchari;for(i=0;;i++){Show_1_Char(ucaChar[i]);//调用一个字符写入LCD函数if(ucaChar[i+1]=='\0')//如果下一个字符是'\0'就退出

{break;}}}//***********************************************************//液晶字符输入的位置*****************************************voidGotoXY(unsignedcharx,unsignedchary){if(y==0)//是0行吗?

{WR_CMD(0x80|x);}//是,写入0行的写入数据的首地址

if(y==1)//是1行吗?

{WR_CMD(0x80|(x-0x40));}////是,写入1行的写入数据的首地址}//------------------------------------------------------------#endif程序输入完成后,单击“File菜单/Saveas…”选项,如图4-11示:图4-11存程序文件从弹出的窗口中,选择要保存程序文件的路径,并输入程序文件名“ADC0809.c”,然后点击保存按钮,如图4.12示:图4.12保存程序文件图4-13添加程序文件下面需要将刚才创建的程序文件添加到项目中去。先用鼠标左建点击uVision2左边“项目窗口”中“Target1”前面的“+”号,展开里面的内容“SourceGroup1”,然后将鼠标指向“SoureceGroup1”并单击右键,弹出一个右键菜单,单击右键菜单中的“AddFilestoGuoup'SourceGroup1'”选项,如图4-13所示:图4-14选择添加程序文件从弹出的窗口中选择刚才保存的文件“ADC0809.c”添加到项目中去,如图4-14所示。图4-15设置目标选项程序文件添加完毕后,还要设置当前项目的目标选项,将鼠标指向“Target1”并单击右键,再从弹出的右键菜单中单击“OptionsforTarget”选项,如图4-15所示:从弹出的“Options”窗口中选择“Target”标签栏,并如图4-16所示设置其中各项:图4-16设置Target选项图

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论