《单片机的C语言应用程序设计》由会员分享,可在线阅读,更多相关《单片机的C语言应用程序设计(169页珍藏版)》请在金锄头文库上搜索。
1、第7章 单片机的C语言应用程序设计 第7章 单片机的C语言应用程序设计 7.1 C语言与语言与MCS-517.2 C51数据类型及在数据类型及在MCS-51中的存储方式中的存储方式7.3 C51数据的存储类型与数据的存储类型与MCS-51存储结构存储结构7.4 MCS-51特殊功能寄存器特殊功能寄存器(SFR)的的C51定义定义7.5 MCS-51并行接口的并行接口的C51定义定义7.6 位变量的位变量的C51定义定义7.7 C51构造数据类型构造数据类型7.8 模块化程序开发过程模块化程序开发过程7.9 MCS-51内部资源使用的内部资源使用的C语言编程语言编程7.10 MCS-51片外扩展
2、的片外扩展的C语言编程语言编程7.11 频率量测量的频率量测量的C语言编程语言编程7.12 MCS-51机间通信的机间通信的C语言编程语言编程7.13 键盘和数码显示人机交互的键盘和数码显示人机交互的C语言编程语言编程第7章 单片机的C语言应用程序设计 7.1 C语言与语言与MCS51 用汇编程序设计MCS51系列单片机应用程序时,必须要考虑其存储器结构,尤其必须考虑其片内数据存储器与特殊功能寄存器正确、合理的使用以及按实际地址处理端口数据。用C语言编写MCS51单片机的应用程序,虽然不像用汇编语言那样具体地组织、分配存储器资源和处理端口数据,但在C语言编程中,对数据类型与变量的定义,必须要与
3、单片机的存储结构相关联,否则编译器不能正确地映射定位。用C语言编写单片机应用程序与编写标准的C语言程序的不同之处就在于根据单片机存储结构及内部资源定义相应的C语言中的数据类型和变量,其它的语法规定、程序结构及程序设计方法都与标准的C语言程序设计相同。第7章 单片机的C语言应用程序设计 用C语言编写的应用程序必须经单片机的C语言编译器(简称C51),转换生成单片机可执行的代码程序。支持MCS51系列单片机的C语言编译器有很多种。如AmericanAutomation、Auocet、BSO/TASKING、DUNFIELDSHAREWARE、KEIL/Franklin等。其中KEIL/Frankl
4、in以它的代码紧凑和使用方便等特点优于其它编译器。本章是针对这种编译器介绍MCS51单片机C语言程序设计。第7章 单片机的C语言应用程序设计 7.2 C51数据类型及在数据类型及在MCS-51中的存储方式中的存储方式7.2.1 C51的数据类型的数据类型FranklinC51编译器具体支持的数据类型有:位型(bit)、无符号字符(unsignedchar)、有符号字符(singedchar)、无符号整型(unsignedint)、有符号整型(signedint)、无符号长整型(unsignedlong)、有符号长整型(signedlong)、浮点型(float)和指针类型等。第7章 单片机的C
5、语言应用程序设计 表表7.1 Franklin C51的数据类型的数据类型数据类型长度(bit)长度(byte)值域bit110,1unsignedchar810255signedchar81128127unsignedint162065535signedint1623276832767unsignedlong32404294967295signedlong32421474836482147483647float3241.176E383.40E+38(6位数字)double6481.176E383.40E+38(10位数字)一般指针243存储空间065535第7章 单片机的C语言应用程序设计
6、7.2.2 C51数据在数据在MCS-51中的存储方式中的存储方式位变量(bit):与MCS-51硬件特性操作有关的可以定义成位变量。位变量必须定位在MCS-51单片机片内RAM的位寻址空间中。字符变量(char):字符变量的长度为1byte即8位。这很合适MCS-51单片机,因为MCS-51单片机每次可处理8位数据。对于无符号变量(unsignedchar)的值域范围是0255。对于有符号字符变量(signedchar),最具有重要意义的位是最高位上的符号标志位(msb)。此位为1代表负,为0代表正。有符号字符变量和无符号字符变量在表示0127的数值时,其含义是一样的,都是00x7F。负数一
7、般用补码表示,即用11111111表示-1,用11111110表示-2。当进行乘除法运算时,符号问题就变得十分复杂,而C51编译器会自动地将相应的库函数调入程序中来解决这个问题。第7章 单片机的C语言应用程序设计 整型变量(int):整型变量的长度为16位。与8080和8086CPU系列不同,MCS-51系列单片机将int型变量的高位字节数存放在低地址字节中,低位字节数存放在高地址字节中。有符号整型变量(signedint)也使用msb位作符号标志位,并使用二进制补码表示数值。可直接使用几种专用的机器指令来完成多字节的加、减、乘、除运算。整型变量值0x1234以图7.1所示的方式存放在内存中。
8、第7章 单片机的C语言应用程序设计 图7.1整型数的存储结构0x120x34+0+1地址0x120x340x560x78.+0+1+2+3地址图7.2长整型变量的存储结构第7章 单片机的C语言应用程序设计 浮点型变量(float):浮点型变量为32位,占4个字节,许多复杂的数学表达式都采用浮点变量数据类型。应用符号位表示数的符号,用阶码和尾数表示数的大小。用它们进行任何数学运算都需要使用由编译器决定的各种不同效率等级的库函数。FranklinC51的浮点变量数据类型的使用格式与IEEE-754标准有关,具有24位精度,尾数的高位始终为1,因而不保存,位的分布如下:1位符号位。8位指数位。23位
9、尾数。第7章 单片机的C语言应用程序设计 符号位是最高位,尾数为低23位,内存中按字节存储顺序如下:地址地址+0+1+2+3内容内容MMMMMMMMMMMMMMMMEMMMMMMMSEEEEEEE其中,S为符号位,1表示负,0表示正;E为阶码;M为23位尾数,最高位为1。浮点变量值-12.5的十进制为:0xC1480000,它按图7.3所示方式存于内存中。第7章 单片机的C语言应用程序设计 0x000x000x480xC1.+0+1+2+3地址图7.3浮点数的存储结构第7章 单片机的C语言应用程序设计 在编程时,如果只强调运算速度而不进行负数运算时,最好采用无符号(unsigned)格式。无符
10、号字符类型的使用:无论何时,应尽可能使用无符号字符变量,因为它能直接被MCS-51所接受。基于同样的原因,也应尽量使用位变量。有符号字符变量虽然也只占用一个字节,但需要进行额外的操作来进行测试代码的符号位。这无疑会降低代码效率。第7章 单片机的C语言应用程序设计 使用简化形式定义数据类型。其方法是在源程序开头使用#define语句自定义简化的类型标识符。例如:#defineucharunsignedchar#defineuintunsignedint这样,在编程中,就可以用uchar代替unsignedchar,用uint代替unsignedint来定义变量。第7章 单片机的C语言应用程序设计
11、 7.3 C51数据的存储类型与数据的存储类型与MCS-51存储结构存储结构表表 7.2 C51存储类型与存储类型与MCS-51存储空间的对应关系存储空间的对应关系存储类型与存储空间的对应关系data直接寻址片内数据存储区,访问速度快(128字节)bdata可位寻址片内数据存储区,允许位与字节混合访问(16字节)idata间接寻址片内数据存储区,可访问片内全部RAM地址空间(256字节)pdata分页寻址片外数据存储区(256字节)由MOVRi访问(i=0,1)xdata片外数据存储区(64KB)由MOVXDPTR访问code程序存储器64KB空间,由MOVCDPTR访问第7章 单片机的C语言
12、应用程序设计 表表7.3 C51存储类型及其数据长度和值域存储类型及其数据长度和值域存储类型长度(bit)长度(byte)值域范围data810255idata810255pdata810255xdata162065535code162065535第7章 单片机的C语言应用程序设计 带存储类型的变量的定义的一般格式为数据类型存储类型变量名带存储类型的变量定义举例:chardatavar1;bitbdataflags;floatidatax,y,z;unsignedintpdatavar2;unsignedcharvector34;第7章 单片机的C语言应用程序设计 表表 7.4 存储模式说明存
13、储模式说明存储模式说明SMALL默认的存储类型是data,参数及局部变量放入可直接寻址片内RAM的用户区中(最大128字节)。另外所有对象(包括堆栈),都必须嵌入片内RAM。栈长很关键,因为实际栈长依赖于函数嵌套调用层数COMPACT默认的存储类型是pdata,参数及局部变量放入分页的外部数据存储区,通过R0或R1间接访问,栈空间位于片内数据存储区中LARGE默认的存储类型是xdata,参数及局部变量直接放入片外数据存储区,使用数据指针DPTR来进行寻址。用此数据指针进行访问效率较低,尤其对两个或多个字节的变量,这种数据类型的访问机制直接影响代码的长度第7章 单片机的C语言应用程序设计 7.4
14、 MCS-51特殊功能寄存器特殊功能寄存器(SFR)的的C51定义定义MCS-51单片机中,除了程序计数器PC和4组工作寄存器组外,其它所有的寄存器均为特殊功能寄存器(SFR),分散在片内RAM区的高128字节中,地址范围为80H0FFH。SFR中有11个寄存器具有位寻址能力,它们的字节地址都能被8整除,即字节地址是以8或0为尾数的。为了能直接访问这些SFR,FranklinC51提供了一种自主形式的定义方法,这种定义方法与标准C语言不兼容,只适用于对MCS-51系列单片机进行C语言编程。特殊功能寄存器C51定义的一般语法格式如下:sfrsfr-name=intconstant;第7章 单片机
15、的C语言应用程序设计 sfr是定义语句的关键字,其后必须跟一个MSC-51单片机真实存在的特殊功能寄存器名,=后面必须是一个整型常数,不允许带有运算符的表达式,是特殊功能寄存器sfr-name的字节地址,这个常数值的范围必须在SFR地址范围内,位于0x800xFF。例如:sfrSCON=0x98; /*串口控制寄存器地址98H*/sfrTMOD=0x89;/*定时器/计数器方式控制寄存器地址89H*/第7章 单片机的C语言应用程序设计 MCS-51系列单片机的特殊功能寄存器的数量与类型不尽相同,因此建议将所有特殊的sfr定义放入一个头文件中,该文件应包括MCS-51单片机系列机型中的SFR定义
16、。C51编译器的reg51.h头文件就是这样一个文件。在新的MCS-51系列产品中,SFR在功能上经常组合为16位值,当SFR的高字节地址直接位于低字节之后时,对16位SFR的值可以直接进行访问。例如52子系列的定时器/计数器2就是这种情况。为了有效地访问这类SFR,可使用关键字sfr16来定义,其定义语句的语法格式与8位SFR相同,只是=后面的地址必须用16位SFR的低字节地址,即低字节地址作为sfr16的定义地址。第7章 单片机的C语言应用程序设计 例如:sfr16T2=0xCC/*定时器/计数器2:T2低8位地址为0CCH,T2高8位地址为0CDH*/这种定义适用于所有新的16位SFR,
17、但不能用于定时器/计数器0和1。对于位寻址的SFR中的位,C51的扩充功能支持特殊位的定义,像SFR一样不与标准C兼容,使用sbit来定义位寻址单元。第7章 单片机的C语言应用程序设计 第一种格式:sbitbit-name=sfr-nameintconstant;sbit是定义语句的关键字,后跟一个寻址位符号名(该位符号名必须是MCS-51单片机中规定的位名称),=后的sfr-name必须是已定义过的SFR的名字,后的整常数是寻址位在特殊功能寄存器sfr-name中的位号,必须是07范围中的数。例如:sfrPSW=0xD0;/*定义PSW寄存器地址为D0H*/sbitOV=PSW2;/*定义O
18、V位为PSW.2,地址为D2H*/sbitCY=PSW7;/*定义CY位为PSW.7,地址为D7H*/第7章 单片机的C语言应用程序设计 第二种格式:sbitbit-name=intconstantintconstant;=后的intconstant为寻址地址位所在的特殊功能寄存器的字节地址,符号后的intconstant为寻址位在特殊功能寄存器中的位号。例如:sbitOV=0XD02;/*定义OV位地址是D0H字节中的第2位*/sbitCY=0XD07;/*定义CY位地址是D0H字节中的第7位*/第7章 单片机的C语言应用程序设计 第三种格式:sbitbit-name=intconstant
19、;=后的intconstant为寻址位的绝对位地址。例如:sbitOV=0XD2;/*定义OV位地址为D2H*/sbitCY=0XD7;/*定义CY位地址为D7H*/特殊功能位代表了一个独立的定义类,不能与其它位定义和位域互换。第7章 单片机的C语言应用程序设计 7.5 MCS-51并行接口的并行接口的C51定义定义MCS-51系列单片机并行I/O接口除了芯片上的4个I/O口(P0P3)外,还可以在片外扩展I/O口。MCS-51单片机I/O口与数据存储器统一编址,即把一个I/O口当作数据存储器中的一个单元来看待。使用C51进行编程时,MCS-51片内的I/O口与片外扩展的I/O可以统一在一个头
20、文件中定义,也可以在程序中(一般在开始的位置)进行定义,其定义方法如下:对于MCS-51片内I/O口按特殊功能寄存器方法定义。例如:sfrP0=0x80;/*定义P0口,地址为80H*/sfrP1=0x90;/*定义P1口,地址为90H*/第7章 单片机的C语言应用程序设计 对于片外扩展I/O口,则根据硬件译码地址,将其视作为片外数据存储器的一个单元,使用#define语句进行定义。例如#include#definePORTAXBYTE0xFFC0absacc.h是C51中绝对地址访问函数的头文件,将PORTA定义为外部I/O口,地址为FFC0H,长度为8位。一旦在头文件或程序中对这些片外I/
21、O口进行定义后,在程序中就可以自由使用变量名与其实际地址的联系,以便使程序员能用软件模拟MCS-51的硬件操作。第7章 单片机的C语言应用程序设计 (1)位变量C51定义。使用C51编程时,定义了位变量后,就可以用定义了的变量来表示MCS-51的位寻址单元。位变量的C51定义的一般语法格式如下:位类型标识符(bit)位变量名;例如:bitdirection_bit;/*把direction_bit定义为位变量*/bitlook_pointer;/*把look_pointer定义为位变量*/7.6 位变量的位变量的C51定义定义第7章 单片机的C语言应用程序设计 (2)函数可包含类型为bit的参
22、数,也可以将其作为返回值。例如:bitfunc(bitb0,bitb1)/*变量b0,b1作为函数的参数*/return(b1);/*变量b1作为函数的返回值*/注意,使用(#pragmadisable)或包含明确的寄存器组切换(usingn)的函数不能返回位值,否则编辑器将会给出一个错误信息。第7章 单片机的C语言应用程序设计 (3)对位变量定义的限制。位变量不能定义成一个指针,如不能定义:bit*bit_pointer。不存在位数组,如不能定义:bitb_array。在位定义中,允许定义存储类型,位变量都被放入一个位段,此段总位于MCS-51片内的RAM区中。因此,存储类型限制为data和
23、idata,如果将位变量的存储类型定义成其它存储类型都将编译出错。第7章 单片机的C语言应用程序设计 例例1先定义变量的数据类型和存储类型:bdataintibase;/*定义ibase为bdata整型变量*/bdatacharbary4;/*bary4定义为bdata字符型数组*/然后可使用sbit定义可独立寻址访问的对象位:sbitmybit0=ibase0;/*mybit0定义为ibase的第0位*/sbitmybit15=ibase15;/*mybit0定义为ibase的第15位*/sbitAry07=bary07;/*Ary07定义为abry0的第7位*/sbitAry37=bary
24、37;/*Ary37定义为abry3的第7位*/第7章 单片机的C语言应用程序设计 对象ibase和bary也可以字节寻址:ary37=0;/*bary3的第7位赋值为0*/bary3=a;/*字节寻址,bary3赋值为a*/sbit定义要位寻址对象所在字节基址对象的存储类型为bdata,否则只有绝对的特殊位定义(sbit)是合法的。操作符后的最大值依赖于指定的基类型,对于char/uchar而言是07,对于int/uint而言是015,对于long/ulong而言是031。第7章 单片机的C语言应用程序设计 7.7 C51构造数据类型构造数据类型 1基于存储器的指针基于存储器的指针基于存储器
25、的指针以存储器类型为参量,它在编译时才被确定。因此,为指针选择存储器的方法可以省掉,以便这些指针的长度为一个字节(idata*,data*,pdata*)或2个字节(code*,xdata*)。编译时,这类操作一般被行内(inline)编码,而无需进行库调用。基于存储器的指针定义举例:charxdata*px;第7章 单片机的C语言应用程序设计 在xdata存储器中定义了一个指向字符型(char)的指针变量px。指针自身在默认存储区(决定于编译模式),长度为2个字节(值为00xFFFF)。charxdata*datapdx;除了明确定义指针位于MCS-51内部存储区(data)外,其它与上例相
26、同,它与编译模式无关。datacharxdata*pdx;第7章 单片机的C语言应用程序设计 structtimecharhour;charmin;charsec;structtimexdata*pxtime;在结构structtime中,除了其它结构成员外,还包含有一个具有和structtime相同的指针pxtime,time位于外部数据存储器(xdata),指针pxtime具有两个字节长度。第7章 单片机的C语言应用程序设计 structtimeidata*ptime;这个声明定义了一个位于默认存储器中的指针,它指向结构time,time位于idata存储器中,结构成员可以通过MCS-51
27、的R0或R1进行间接访问,指针ptime为1个字节长。ptimepxtimehour=12;使用上面的关于structtime和structidata*ptime的定义,指针pxtime被从结构中间接调用,它指向位于xdata存储器中的time结构。结构成员hour被赋值为12。第7章 单片机的C语言应用程序设计 2一般指针一般指针一般指针包括3个字节:1个字节存储类型和2个字节偏移地址,即地址+0+1+2内容存储器类型偏移地址高位字节偏移地址低位字节其中,第一字节代表了指针的存储器类型,存储器类型编码如下:存储器类型idataxdatapdatadatacode值12345第7章 单片机的C
28、语言应用程序设计 例如,以xdata类型的0x1234地址为指针可以表示如下:地址+0+1+2内容0x020x120x34当用常数作指针时,必须注意正确定义存储器类型和偏移量。例如,将常数值0x41写入地址为0x8000的外部数据存储器。#defineXBYTE(char*)0x20000L)XBYTE0x8000=0x41;其中,XBYTE被定义为(char*)0x20000L,0x20000L为一般指针,其存储类型为2,偏移量为0000H,这样XBYTE成为指向xdata零地址的指针。而XBYTE8000则是外部数据存储器的0x8000绝对地址。第7章 单片机的C语言应用程序设计 7.8
29、模块化程序开发过程模块化程序开发过程图图7.4 程序开发过程程序开发过程 第7章 单片机的C语言应用程序设计 7.8.1 混合编程混合编程1命名规则命名规则表表7.5 函数名的转换函数名的转换说明符号名转换规则voidfunc(void)FUNC无参数传递或不含寄存器参数的函数名不作改变转入目标文件中,名字只是简单地转换为大写形式voidfunc(void)_FUNC带寄存器参数的函数名加入“_”字符前缀,表明这类函数包含寄存器的参数传递voidfunc(void)reentrant_?FUNC对于重入函数加上“_?”字符串前缀,表明这类函数包含栈内的参数传递第7章 单片机的C语言应用程序设计
30、 例例2用汇编语言编写函数toupper,参数传递发生在寄存器R7中。UPPERSEGMENTCODE ;程序段PUBLIC_TOUPPER ;入口地址PSEGUPPER;程序段_TOUPPER:MOVA,R7 ;从R7中取参数 CJNEA,#a,$+3JCUPPERETCJNEA,#z+1,$+3JNCUPPERETCLRACC,5UPPERET:MOVR7,A;返回值放在R7中RET;返回到C第7章 单片机的C语言应用程序设计 2参数传递规则参数传递规则表表7.6 参数传递的寄存器选择参数传递的寄存器选择参数类型charintlong,float一般指针第1个参数R7R6,R7R4R7R1
31、,R2,R3第2个参数R5R4,R5R4R7R1,R2,R3第3个参数R3R2,R3无R1,R2,R3第7章 单片机的C语言应用程序设计 func1(inta)a是第一个参数,在R6,R7中传递。func2(intb,intc,int*d)b是第一个参数,在R6,R7中传递;c是第二个参数,在R4,R5中传递;d是第三个参数,在R1,R2,R3中传递。func3(longe,longf)e是第一个参数,在R4R7中传递;f是第二个参数,不能在寄存器中传递,只能在参数传递段中传递。func4(floatg,charh)g是第一个参数,在R4R7中传递;h是第二个参数,必须在参数传递段中传递。第7
32、章 单片机的C语言应用程序设计 表表7.7 函数返回值的寄存器函数返回值的寄存器返回值寄存器说明bitC进位标位(unsigned)charR7(unsigned)intR6,R7高位字节在R6,低位字节在R7(unsigned)longR4R7高位字节在R4,低位字节在R7floatR4R732位IEEE格式,指数和符号位在R7指针R1,R2,R3R3放存储器类型,高位在R2,低位在R1第7章 单片机的C语言应用程序设计 在汇编子程序中,当前选择的寄存器组及寄存器ACC、B、DPTR和PSW都可能改变。当被C调用时,必须无条件地假设这些寄存器的内容已被破坏。如果已在连接/定位程序时选择了覆盖
33、,那么每个汇编子程序包含一个单独的程序段是必要的,因为在覆盖过程中,函数间参量通过子程序各自的段参量计算。汇编子程序的数据区甚至可包含在覆盖部分中,但应注意下面两点:(1)所有段名必须以C51类似的方法建立。(2)每个有局部变量的汇编程序必须指定自己的数据段,这个数据段只能为其它函数访问作参数传递用。所有参数一个接一个被传递,由其它函数计算的结果保存入栈。第7章 单片机的C语言应用程序设计 7.8.2 覆盖和共享覆盖和共享 1覆盖覆盖单片机片内存储空间有限,连接器/定位器通常重新启用程序不再用的位置。这就是说,若一个程序不再调用,也不由其它程序调用(甚至间接调用),那么在其它程序执行完之前,这
34、个程序不再运行。这个程序的变量可以放在与其它程序完全相同的RAM空间,很像可重用的寄存器。这种技术就是覆盖。在汇编中直接通过手工完成的这些空间分配,C语言中可以由连接器自动管理。若有几个不相关联的程序时,它可以使RAM单元比手工考虑要用的少。第7章 单片机的C语言应用程序设计 2共享共享1)共享变量类型汇编语言C语言动态变量y()intx;静态变量staticintx;公用变量PUBLICXX:ds2Intx;外部变量EXTERNDATA(X)MOVDPTR,#Xexternint;静态子程序/函数Y:staticy();公共子程序/函数PUBLICYY:y();外部子程序/函数EXTERNC
35、ODE(Y)LCALLYy()第7章 单片机的C语言应用程序设计 2)共享函数/子程序C中函数若是全局的(公用的),可以放在调用的函数之后。若函数是模块专用的,它可以定义为静态函数,这样它不能被其它模块调用。C语言的ANSI标准建议所有函数在主函数前要有原型(进行说明),然后实际函数可在主函数之后或其它模块中。这符合自顶向下编程的概念。汇编语言中,子程序使用标号可在给定模块的任何位置。汇编器首先扫描得到所有的符号名,然后值就可填入LCALL或LJMP。一个模块或另一模块共享子程序,一个使用PUBLIC而另一个使用EXTERN。当指定为EXTERN,符号类型(CODE,DATA,XDATA,ID
36、ATA,BIT或NUMBER)必须特别加以指定,以便连接器可以确定放在一起的正确类型。第7章 单片机的C语言应用程序设计 7.8.3 库和连接器库和连接器/定位器定位器 1. 库库表表7.9 Franklin C51的编译库的编译库库说明C51S.LIBSMALL模式,无浮点运算C51FPS.LIB浮点数学运算库(SMALL模式)C51C.LIBCOMPACT模式,无浮点运算C51FPC.LIB浮点运算库(COMPACT模式)C51L.LIBLARGE模式,无浮点运算C51FPL.LIB浮点运算库(LARGE模式)第7章 单片机的C语言应用程序设计 2连接器连接器/定位器定位器1)组合程序模块
37、将几个不同程序模块组合为一个模块,并自动从库中挑选模块嵌入目标文件。输入文件按命令行中出现的顺序处理。通常的程序模块是由C51编译器或A51宏汇编生成的可重入的目标文件。第7章 单片机的C语言应用程序设计 2)组合段将具有相同段名的可重定位段组合成单一的段。在一个程序模块中定义的一个段成为部分段。一个部分段在源文件中以下列形式指定:(1)名字每个重定位段有一个名字,它可与来自其它模块的同名的可重定位段组合。绝对段没有名字。(2)类型类型表明段所属的地址空间CODE,XDATA,DATA或BIT。第7章 单片机的C语言应用程序设计 (3)定位方式可重定位段的定位方式有PAGE,INPAGE,IN
38、BLOCK,BITADDRESSABLE或UNIT。INPAGE表明段必须放入一页(高8位地址相同)中以使用短转移和调用指令。INBLOCK段应使用ACALL,必须放在2048字节块中。因为没有连接器可以灵活地判知调用和转移是否在块内。可重定位的其它限制是:PAGE-不能超过256字节;BITADDRESSABLE-必须放在可位寻址的内部RAM空间;UNIT-允许段从任意字节开始(对位变量是位)。(4)长度一个段的长度。第7章 单片机的C语言应用程序设计 (5)基址段的首址。对于绝对段,地址由汇编器赋予,对于可重定位段,地址由L51决定。在处理程序模块时,L51自动产生段表(MAP),该表包含
39、了每个段的类型、基址、长度、可重定位性和名字。L51自动将所具有相同名字的所有部分段组合到单一可重定位段中。例如,三个程序模块包含字段VAR,在组合时,三个段的长度相加,从而组合段的长度也增加了。对组合段有下列规则:所有具有相同名的部分段必须有相同类型(CODE,DATA,IDATA,XDATA或BIT)。组合段的长度不能超过存储区的物理长度。每个组合的部分段的定位方法也必须相同。绝对段相互不组合,它们被直接拷贝到输出文件。第7章 单片机的C语言应用程序设计 3) 存储器分配存储器分配物理存储区最大长度地址区段类型程序64KB00FFFFHCODE外部数据64KB00FFFFHXDATA直接寻
40、址片内数据128字节07FHDATA间接寻址片内数据256字节00FFHIDATA片内数据的位空间128位07FHBIT表7.10MCS-51系列的物理存储区第7章 单片机的C语言应用程序设计 4)采用覆盖技术使用数据存储器通过采用一定的覆盖技术,MCS-51系列少量的片内数据存储器可由L51有效地使用。由C51编译器或是A51汇编器生成的参数和局部变量(若使用它们的函数不相互调用)可在存储器中覆盖。这样,所用的存储器得到相当程度地减少。为完成数据覆盖,L51分析所有不同函数间的调用,使用该信息可以确定哪个数据和位段可被覆盖。使用控制参数OVERLAY和NOOVERLAY可允许或禁止覆盖。OV
41、ERLAY是默认值,用它可产生非常紧凑的数据区。第7章 单片机的C语言应用程序设计 5)决定外部参考地址具有相同名的外部符号(EXTERN)和公用符号(PUBLIC)被确定后,外部符号指向其它模块中的地址。一个已声明的外部符号用具有相同名字的功用符号确定,外部参考地址由其公共参考地址确定。这还与类型(DATA,IDATA,XDATA,CODE,BIT或NUMBER)有关,如果类型不符或未发现外部符号参考地址的公用符号,则会产生错误。公用符号的绝对地址在段定位后决定。第7章 单片机的C语言应用程序设计 6)绝对地址计算定义绝对地址并计算可重定位段的地址。在段分配和外部公用参考地址处理完后,程序模
42、块中所有可重定位地址和外部地址要进行计算,此时生成的目标文件中的符号信息(DEBUG)被改变以反映新的值。第7章 单片机的C语言应用程序设计 7)产生绝对目标文件可执行程序以绝对目标格式产生。该绝对目标文件可包含附加的符号信息(DEBUG),从而使符号调试成为可能。符号信息可用参数NODEBUGSYMBOLS,NODEBUGPUBLICS和NODEBUGLINES禁止。输出文件是可执行的,并可由仿真器装入调试或被OHS51翻译为IntelHEX格式文件以供EPROM固化。第7章 单片机的C语言应用程序设计 8)产生映像文件产生一个反映每个处理步骤的映像文件,它显示有关连接/定位过程的信息和程序
43、符号,并包含一个公用和外部符号的交叉参考报告。映像文件包含下列信息:文件名和命令行参数。模块的文件名和模块名。一个包含段地址、类型、定位方法和名字的存储器分配表。该表可在命令行中用NOMAP参考禁止。第7章 单片机的C语言应用程序设计 段和符号的所有错误列表。列表文件末尾显示出所有出错的原因。一个包含输入文件中符号信息的符号表。该信息由MODULES,SYMBOLS,PUBLICS和LINES名组成,LINES是C编译器产生的行号。符号信息可用参数NOSYMBOLS,NOPUBLICS和NOLINES完全或部分禁止。一个按字母顺序排列的有关所有PUBLIC和EXTERN符号的交叉参考报告,其中
44、显示出符号类型和模块名。第一个显示的模块名是定义了PUBLIC符号的模块,后面的模块名是定义了EXTERN符号的模块。在命令行输入参数IXREF可产生此报告。在连接器/定位器运行期间检测到的错误同时显示在屏幕和文件尾部。第7章 单片机的C语言应用程序设计 7.8.4 程序优化程序优化以下选择对提高程序效率有很大影响:(1)尽量选择小存储模式以避免使用MOVX指令。(2)使用大模式(COMPACT/LARGE)应仔细考虑要放在内部数据存储器的变量要求是经常用的或是用于中间结果的。访问内部数据存储器要比访问外部数据存储器快得多。内部RAM由寄存器组、位数据区和其它用户用“data”类型定义的变量共
45、享。由于内部RAM容量的限制(128256字节,由使用的单片机决定),必须权衡利弊以解决访问效率和这些对象的数量之间的矛盾。第7章 单片机的C语言应用程序设计 (3)要考虑操作顺序,完成一件事后再做一件事。(4)注意程序编写细则。例如,若使用for(;)循环,DJNZ指令比CJNE指令更有效,可减少重复循环次数。(5)若编译器不能使用左移和右移完成乘除法,应立即修改,例如,左移为乘2。(6)用逻辑AND/&取模比用MOD/%操作更有效。(7)因计算机基于二进制,仔细选择数据存储器和数组大小可节省操作。第7章 单片机的C语言应用程序设计 (8)尽可能使用最小的数据类型,MCS-51系列是8位机,
46、显然对具有char类型的对象的操作比int或long类型的对象的操作要方便得多。(9)尽可能使用unsigned数据类型。MCS-51系列CPU并不直接支持有符号数的运算。因而C51编译器必须产生与之相关的更多的程序代码以解决这个问题。(10)尽可能使用局部函数变量。编译器总是尝试在寄存器里保持局部变量。这样,将循环变量(如for和while循环中的计数变量)说明为局部变量是最好的。使用unsignedchar/int的对象通常能获得最好的结果。第7章 单片机的C语言应用程序设计 7.9 MCS-51内部资源使用的内部资源使用的C语言编程语言编程7.9.1 中断应用的中断应用的C语言编程语言编
47、程C51编译器支持在C源程序中直接开发中断程序。中断服务程序是通过按规定语法格式定义的一个函数。中断服务程序的函数定义的语法格式如下:返回值函数名(参数)interruptmusingn第7章 单片机的C语言应用程序设计 表表7.11 MCS-51中断源编号中断源编号编号中断源入口地址0外部中断00003H1定时器/计数器0000BH2外部中断10013H3定时器/计数器1001BH4串行口中断0023H第7章 单片机的C语言应用程序设计 usingn选项用于实现工作寄存器组的切换,n是中断服务子程序中选用的工作寄存器组号(03)。在许多情况下,响应中断时需保护有关现场信息,以便中断返回后,能
48、使中断前的源程序从断点处继续正确地执行下去。这在MCS-51单片机中,能很方便地利用工作寄存器组的切换来实现。即在进入中断服务程序前的程序中使用一组工作寄存器,进入中断服务程序后,由usingn切换到另一组寄存器,中断返回后又恢复到原寄存器组。这样互相切换的两组寄存器中的内容彼此都没有被破坏。第7章 单片机的C语言应用程序设计 图图 7.5 扩展多个中断源扩展多个中断源 例例3图7.5所示是利用优先权解码芯片,在单片机8031的一个外部中断INT1上扩展多个中断源的原理电路图。图中是以开关闭合来模拟中断请求信号。当有任一中断源产生中断请求,能给8031的INT1引脚送一个有效中断信号,由P1的
49、低3位可得对应中断源的中断号。第7章 单片机的C语言应用程序设计 在中断服务程序中仅设置标志,并保存I/O口输入状态。FranklinC51编译器提供定义特定MCS-51系列成员的寄存器头文件。MCS-51头文件为reg51.h。C51程序如下:#includeunsignedcharstatus;bitflag;voidservice_int1()interrupt2using2/*INT1中断服务程序,使用第2组工作寄存器*/flag=1;/*设置标志*/status=p1;/*存输入口状态*/voidmain(void)IP=0x04;/*置INT1为高优先级中断*/IE=-0x84;/
50、*INT1开中断,CPU开中断*/第7章 单片机的C语言应用程序设计 for(;)if(flag)/*有中断*/switch(status)/*根据中断源分支*/case0:break;/*处理IN0*/case1:break;/*处理IN1*/case2:break;/*处理IN2*/case3:break;/*处理IN3*/default:;flag=0;/*处理完成清标志*/第7章 单片机的C语言应用程序设计 7.9.2 定时器定时器/计数器计数器(T/C)应用的应用的C语言编程语言编程 例例4设单片机的fosc=12MHz晶振,要求在P1.0脚上输出周期为2ms的方波。周期为2ms的方
51、波要求定时时间隔1ms,每次时间到P1.0取反。机器周期=12/fosc=1s需计数次数=1000/(12/fosc)=1000/1=1000由于计数器是加1计数,为得到1000个计数之后的定时器溢出,必须给定时器置初值为-1000(即1000的补数)。第7章 单片机的C语言应用程序设计 (1)用定时器0的方式1编程,采用查询方式,程序如下:#includesbitP1_0=P10;voidmain(void)TMOD=0x01;/*设置定时器1为非门控制方式1*/TR0=1;/*启动T/C0*/for(;)TH0=-(1000/256);/*装载计数器初值*/TL0=-(1000%256);
52、dowhile(!TF0);/*查询等待TF0置位*/P1_0=!P1_0;/*定时时间到P1.0反相*/TF0=0;/*软件清TF0*/第7章 单片机的C语言应用程序设计 (2)用定时器0的方式1编程,采用中断方式。程序如下:#includesbitP1_0=P10;voidtime(void)interrupt1using1/*T/C0中断服务程序入口*/P1_0=!P1_0;/*P1.0取反*/TH0=-(1000/256);/*重新装载计数初值*/voidmain(void)TMOD=0x01;/*T/C0工作在定时器非门控制方式1*/P1_0=0;TH0=-(1000/256);/*
53、预置计数初值*/TL0=-(1000%256);EA=1;/*CPU中断开放*/ET0=1;/*T/C0中断开放*/TR0=1;/*启动T/C0开始定时*/dowhile(1);/*等待中断*/第7章 单片机的C语言应用程序设计 例例5采用10MHz晶振,在P1.0脚上输出周期为2.5s,占空比20%的脉冲信号。10MHz晶振,使用定时器最大定时几十毫秒。取10ms定时,周期2.5s需250次中断,占空比20%,高电平应为50次中断。10ms定时,晶振fosc=10MHz。需定时器计数次数=1010310/12=8333#include#defineucharunsignedcharuchar
54、period=250;ucharhigh=50;第7章 单片机的C语言应用程序设计 timer0()interrupt1using1/*T/C0中断服务程序*/TH0=-8333/256;/*重置计数值*/TL0=-8333%256;if(+time=high)P1=0;/*高电平时间到变低*/elseif(time=period)/*周期时间到变高*/time=0;P1=1;第7章 单片机的C语言应用程序设计 main()TMOD=0x01;/*定时器0方式1*/TH0=-8333/256;/*预置计数初值*/TL0=-8333%256;EA=1;/*开CPU中断*/ET0=1;/*开T/C
55、0中断*/TR0=1;/*启动T/C0*/dowhile(1);第7章 单片机的C语言应用程序设计 图7.6中断服务程序流程图第7章 单片机的C语言应用程序设计 产生一个占空比变化脉冲信号的程序,它产生的脉宽调制信号用于电机变速控制。#include#defineucharunsignedchar#defineuintunsignedintunchartime,status,percent,period;bitone_round;uintoldcount,target=500;voidpulse(void)interrupt1using1/*T/C0中断服务程序*/TH0=-833/256;/
56、*1ms-10MHz*/第7章 单片机的C语言应用程序设计 TL0=-833%256;ET0=1;if(+time=percent)P1=0;elseif(time=100)time=0;P1=1;voidtachmeter(void)interrupt2using2/*外中断1服务程序*/unionunitword;structucharhi;ucharlo;byte;第7章 单片机的C语言应用程序设计 newcount;newcount.byte.hi=TH1;newcount.byte.lo=TL1;period=newcount.word-oldcount;/*测得周期*/oldcou
57、nt=newcount.word;one_round=1;/*每转一圈,引起中断,设置标志*/voidmain(void)IP=0x04;/*置INT1为高位优先级*/TMOD=0x11;/*T0,T116位方式*/TCON=0x54;/*T0,T1运行,IT1边沿触发*/第7章 单片机的C语言应用程序设计 TH1=0;TL1=0;/*设置初始计数值*/IE=0x86;/*允许中断EX1,ET0*/for(;)if(one_round)/*每转一圈,调整*/if(periodtarget)if(percent0)-percent; /*占空比减*/one_round=0;第7章 单片机的C语言
58、应用程序设计 7.9.3 串行口使用的串行口使用的C语言编程语言编程 例例6单片机fosc=11.0592MHz,波特率为9600,各设置32字节的队列缓冲区用于发送接收。设计单片机和终端或另一计算机通信的程序。单片机串行口初始化成9600波特,中断程序双向处理字符,程序双向缓冲字符。背景程序可以放入和提取在缓冲区的字符串,而实际传入和传出SBUF的动作由中断完成。Loadmsg函数加载缓冲数组,标志发送开始。缓冲区分发(t)和收(r)缓冲,缓冲区通过两种指示(进in和出out)和一些标志(满full,空empty,完成done)管理。队列缓冲区32字节接收缓冲(r_buf)区满,不能再有字符
59、插入。当t_in=t_out,发送缓冲区(t_buf)空,发送中断清除,停止UART请求。具体程序如下:第7章 单片机的C语言应用程序设计 #include#defineucharunsignedcharucharxdatar_buf32;/*item1*/ucharxdatat_buf32;ucharr_in,r_out,t_in,t_done;/*队列指针*/bitr_full,t_empty,t_done;/*item2*/codeucharm=thisisatestprogramrn;serial()interrupt4using1/*item3*/if(RI&r_full)r_buf
60、r_in=SBUF;RI=0;第7章 单片机的C语言应用程序设计 r_in=+r_in&ox1f;if(r_in=r_out)r_full=1;elseif(TI&t_empty)SBUF=t_buft_out;TI=0;t_out=+t_out&0x1f;if(t_out=t_in)t_empty=1;elseif(TI)TI=0;t_done=1;第7章 单片机的C语言应用程序设计 voidloadmsg(ucharcode*msg)/*item4*/while(*msg!=0)&(t_in+1)t_out)&0x1f)!=0) /*测试缓冲区满*/t_buft_in=*msg;msg+;
61、t_in=+t_in&0x1f;if(t_done)TI=1;t_empty=t_done=0;/*完成重新开始*/第7章 单片机的C语言应用程序设计 voidprocess(ucharch)return;/*item5*/*用户定义*/voidprocessmsg(void)/*item6*/while(r_out+1)r_in)!=0)/*接收非缓冲区*/process(r_bufr_out);r_out=+r_out&0x1f;第7章 单片机的C语言应用程序设计 main()/*item7*/TMOD=0x20;/*定时器1方式2*/TH1=0xfd;/*9600波特11.0592MHz
62、*/TCON=0x40;/*启动定时器1*/SCON=0x50;/*允许接收*/IE=0x90;/*允许串行口中断*/t_empty=t_done=1;r_full=0;r_out=t_in=0;r_in=1;/*接收缓冲和发送缓冲置空*/for(;)loadmsg(&m);processmsg();第7章 单片机的C语言应用程序设计 item1:背景程序放入和提取字符队列缓冲区。item2:缓冲区状态标志。item3:串行口中断服务程序,从RI,TI判别接收或发送中断,由软件清除。判别缓冲区状态(满full,空empty)和全部发送完成(done)。item4:此函数把字符串放入发送缓冲区,
63、准备发送。item5:接受字符的处理程序,实际应用自定义。item6:此函数逐一处理接收缓冲区的字符。item7:主程序即背景程序,进行串行口的初始化,载入字符串,处理接收的字符串。第7章 单片机的C语言应用程序设计 7.10 MCS-51片外扩展的片外扩展的C语言编程语言编程7.10.1 8255与与8031接口接口C应用程序举例应用程序举例例例78255控制打印机。图7.7是8031扩展8255与打印机接口的电路。8255的片选线为P0.7,打印机与8031采用查询方式交换数据。打印机的状态信号输入给PC7,打印机忙时BUSY=1,微型打印机的数据输入采用选通控制,当-STB上负跳变时数据
64、被输入。8255采用方式0由PC0模拟产生-STB信号。按照接口电路,口A地址为7CH,口C地址为7EH,命令口地址为7FH,PC7PC4输入,PC3PC0输出。方式选择命令字为8EH。第7章 单片机的C语言应用程序设计 图7.78031扩展8255与打印机接口的电路第7章 单片机的C语言应用程序设计 向打印机输出字符串WELCOME的程序如下:#include#include#defineucharunsignedchar#defineCOM8255XBYTE0x007f/*命令口地址*/#definePA8255XBYTE0x007c/*口A地址*/#definePC8255XBYTE0x
65、007e/*口C地址*/voidtoprn(uchar*p) /*打印字符串函数*/while(*p!=0)while(0x80&PC8255)!=0);/*查询等待打印机的BUSY状态*/第7章 单片机的C语言应用程序设计 PA8255=*p;/*输出字符*/COM8255=0x00;/*模拟STB脉冲*/COM8255=0x01;p+;voidmain(void)ucharidataprn=WELCOME;/*设测试用字符串*/COM8255=0x8e;/*输出方式选择命令*/toprn(prn);/*打印字符串*/第7章 单片机的C语言应用程序设计 例例8EPROM编程器。由8031扩展
66、1片EPROM2716、2片SRAM6116及1片8255构成EPROM编程器,编程对象是EPROM2732。扩展编程系统中2716用来存放固化用监控程序,用户的待固化程序放在2片6116中。8255的口A作编程器数据口,口B输出2732的低8位地址,PC3PC0输出2732高4位地址,PC7作2732启动保持控制器与PGM连接。译码地址为:6116(1):0800H;6116(2):1000H17FFH;8255的口A:07FCH;口B:07FDH;口C:07FEH;命令口:07FFH。第7章 单片机的C语言应用程序设计 8255的口A、口B、口C均工作在方式0输出,方式选择命令字为80H;
67、2732的启动编程和停止编程,由PC7的复位/置位控制,当PC7=0时启动编程,PC7=1时,编程无效。EPROM编程如下所示,参数为RAM起始地址、EPROM起始地址和编程字节数。#include#include#defineCOM8255XBYTE0x07ff#definePA8255XBYTE0x07fc#definePB8255XBYTE0x07fd#definePC8255XBYTE0x07fe#defineucharunsignedchar#defineuintunsignedint第7章 单片机的C语言应用程序设计 voidd1_ms(unitx);voidprogram(ram
68、,eprom,con)ucharxdata*ram;/*RAM起始地址*/uinteprom,con;/*EPROM起始固化地址,固化长度*/inti;COM8255=0x08;/*送方式选择命令字*/COM8255=0x0f;/*PC7=1*/for(i=0;icon;i+)PA8255=*ram;/*固化内容口A锁存*/PB8255=eprom%256; /*2732地址低8位*/PC8255=eprom/256;/*2732地址高4位*/第7章 单片机的C语言应用程序设计 eprom+;ram+;COM8255=0x0e;/*PC7=0*/d1_ms(50);COM8255=0x0f;/
69、*PC7=1*/main()program(0x1000,0x0000,0x0100);第7章 单片机的C语言应用程序设计 7.10.2 MCS-51数据采集的数据采集的C语言编程语言编程例例9ADC0809与8031接口的数据采集程序举例。图7.8ADC0809与8031的接口电路第7章 单片机的C语言应用程序设计 程序如下:#include#include#defineucharunsignedchar#defineIN0XBYTE0x7ff8/*设置AD0809的通道0地址*/sbitad_busy=P33;/*即EOC状态*/voidad0809(ucharidata*x)/*采样结果
70、放指针中的A/D采集函数*/uchari;ucharxdata*ad_adr;ad_adr=&IN0;for(i=0;i8;i+)/*处理8通道*/*ad_adr=0;/*启动转换*/第7章 单片机的C语言应用程序设计 i=i;/*延时等待EOC变低*/i=i;while(ad_busy=0);/*查询等待转换结束*/xi=*ad_adr;/*存转换结果*/ad_adr+;/*下一通道*/voidmain(void)staticucharidataad10;ad0809(ad);/*采样AD0809通道的值*/第7章 单片机的C语言应用程序设计 例例10AD574与8031接口的数据采集程序举
71、例。图7.9AD574与8031的接口电路第7章 单片机的C语言应用程序设计 源程序如下:#include#inlucde#defineuintunsignedint#defineADCOMXBYTE0xff7c/*使A0=0,R/C=0,CS=0*/#defineADLOXBYTE0xff7f/*使R/C=1,A0=1,CS=0*/#defineADHIXBYTE0xff7d/*使R/C=1,A0=0,CS=0*/sbitr=P37;sbitw=P36;sbitadbusy=P10;第7章 单片机的C语言应用程序设计 uintad574(void)/*AD574转换器*/r=0;/*产生CE
72、=1*/w=0;ADCOM=0;/*启动转换*/while(adbusy=1);/*等待转换*/return(uint)(ADHI4)+(ADLO&0x0f); /*返回12位采样值*/main()uintidataresult;result=ad574();/*启动AD574进行一次转换,得转换结果*/第7章 单片机的C语言应用程序设计 7.10.3 MCS-51输出控制的输出控制的C语言编程语言编程例例118031与DAC0832双缓冲接口的数据转换程序举例。图7.10DAC0832的双缓冲接口电路第7章 单片机的C语言应用程序设计 将data1和data2数据同时转换为模拟量的C51程序
73、如下:#include#include#defineINPUTR1XBYTE0x8fff#defineINPUTR2XBYTE0xa7ff#defineDACRXBYTE0x2fff#defineucharunsignedcharvoiddac2b(data1,data2)uchardata1,data2;INPUTR1=data1;/*送数据到一片0832*/INPUTR2=data2;/*送数据到另一片0832*/DACR=0;/*启动两路D/A同时转换*/第7章 单片机的C语言应用程序设计 例例128031与DAC0832单缓冲区接口的数据转换举例。图7.11DAC0832与8031的单
74、缓冲接口第7章 单片机的C语言应用程序设计 按片选线确定FFFEH为DAC0832的端口地址。使运行输出端输出一个锯齿波电压信号的C51程序如下:#include#include#defineDA0832XBYTE0xfffe#defineucharunsignedchar#defineuintunsignedintvoidstair(void)uchari;while(1)for(i=0;i=255;i=I+)/*形成锯齿波输出值,最大值为255*/DA0832=i;/*D/A转换输出*/第7章 单片机的C语言应用程序设计 例例138031与AD7521接口的数据转换程序举例。图7.12AD
75、7521与8031的接口第7章 单片机的C语言应用程序设计 使AD7521输出梯形波的C51程序如下:#include#include#defineDA7521LXBYTE0x7fff#defineDA7521HXBYTE0xbfff#defineUP0x010#defineT1000#defineuintunsignedint第7章 单片机的C语言应用程序设计 voiddlms(uinta);voidstair(void)uinti;for(i=0;i=4095;i=i+UP)/*以阶高增量增值,形成梯形波输出值,最大4095*/DA7521L=i%256;/*送低8位数据到第一级缓冲器*/
76、DA7521H=i/256;/*送高4位数据到高4位缓冲器,同时送低8位到第二级/*缓冲转换*/dlms(T);/*延时*/第7章 单片机的C语言应用程序设计 7.11 频率量测量的频率量测量的C语言编程语言编程7.11.1 测量频率法测量频率法测量频率法的最简单的接口电路,可将频率脉冲直接连接到MCS-51的T1端,将8031的T/C0用作定时器,T/C1用作计数器。在T/C0定时时间里,对频率脉冲进行计数。T/C1的计数值便是单位定时时间里的脉冲个数。第7章 单片机的C语言应用程序设计 定时输入脉冲T图7.13测量频率中的脉冲丢失第7章 单片机的C语言应用程序设计 例例14带同步控制的频率
77、测量。图7.14带同步控制的频率测量法接口第7章 单片机的C语言应用程序设计 控制时,首先P1.0发一个清零负脉冲,使U1、U2两个D触发器复位,其输出封锁与门G1和G2。接着由P1.1发一个启动正脉冲,其有效上升沿使U1=1,门G1被开放。之后,被测脉冲上升沿通过G2送T1计数;同时U2输出的高电平使INT0=1,定时器0的门控GATE有效,启动T/C0开始定时。直到定时结束时,从P1.0发一负脉冲,清零U2,封锁G2,停止T/C1计数,完成一次频率采样过程。第7章 单片机的C语言应用程序设计 测量T/C定时时间为500ms,这样长的时间定时,先由T/C0定时100ms,之后软件5次中断后的
78、时间即为5100ms=500ms。中断次数的计数值在msn中。T/C0定时100ms的计数初值:03B0H。计数器1采用16位计数。设T/C0为高优先级,允许计数中断过程定时中断,即定时时间到就中止计数。tf为500ms定时时间到标志。程序如下:第7章 单片机的C语言应用程序设计 #include#defineucharunsignedchar#defineuintunsignedint#defineA5/*500ms的中断次数*/sbitP1_0=P10;sbitP1_1=P11;ucharmsn=A;bitidatatf=0;/*500ms时间到标志*/第7章 单片机的C语言应用程序设计
79、uintcount(void)P1_0=0;P1_0=1;/*产生清零用负脉冲*/TMOD=0x59;TH1=0x00;TL1=0x00;/*T/C1计数器*/TH0=0x3c;TL0=0xb0;/*T/C0定时器100ms*/TR0=1;TR1=1;PT0=1;ET0=1;ET1=1;EA=1;/*启动T/C,开中断*/P1_1=0;P1_1=1;/*产生启动正脉冲*/while(tf!=1);/*等待500ms定时到*/P1_0=0;P1_0=1;/*产生负脉冲,封锁G2*/TR0=0;TR1=0;/*关T/C*/return(TH1*256+TL1);/返回计数值*/第7章 单片机的C语
80、言应用程序设计 voidtimer0(void)interrupt1using1/*100ms定时中断服务*/TH0=0x3c;/*重置初值*/TL0=-0xb0;msn-;if(msn=0)msn=A;tf=1;/*500ms定时时间到设标志*/voidtimer1(void)interrupt3voidmain(void)floatrate;rate=(10/A)*count();/*得每秒的计数率*/第7章 单片机的C语言应用程序设计 7.11.2 频率脉冲的测量周期法频率脉冲的测量周期法图7.15周期测量接口第7章 单片机的C语言应用程序设计 图7.16频率与周期波第7章 单片机的C语
81、言应用程序设计 例例15测量周期的程序举例。设fosc=6MHz,机器周期为2s,测周期的测量值为计数值乘以2。用C语言编写的程序如下:#include#defineuintunsignedintsbitP1_0=P10;uintcount,period;bitrflag=0;/*周期标志*/voidcontrol(void)第7章 单片机的C语言应用程序设计 TMOD=0x09;/*定时器/计数器0为方式1*/IT0=1;TR0=1;TH0=0;TL0=0;P1_0=0;P1_0=1;/*触发器清零*/TR0=1;ET0=1;EA=1;/*启动T/C0开中断*/voidint_0(void)
82、interrupt0using1/*INT0中断服务*/EA=0;TR0=0;count=TL0+TH0*256;/*取计数值*/rflag=1;/*设标志*/EA=1;第7章 单片机的C语言应用程序设计 voidmain(void)contro1();while(rflag=0);/*等待一周期*/period=count*2;/*fosc=6MHz,2s计数增1,周期值单位s*/第7章 单片机的C语言应用程序设计 7.12 MCS-51机间通信的机间通信的C语言编程语言编程7.12.1 点对点的串行异步通信点对点的串行异步通信1通信双方的硬件连接通信双方的硬件连接 图7.178031间RS
83、232C电平信号的传第7章 单片机的C语言应用程序设计 2通信双方的约定通信双方的约定图图 7.18 点点对对点点通通信信的的程程序序框框图图第7章 单片机的C语言应用程序设计 3. 点对点通信编程点对点通信编程点对点通信双方基本等同,只是人为规定一个为发送,一个为接收。要求两机串行口的波特率相同,因而发送和接收方串行口的初始化相同。可编制含有初始化函数、发送函数接收函数的程序,在主函数中根据程序的发送、接收设置TR,采用条件判别决定使用发送函数还是接收函数。这样点对点通信的双方都可运行此程序,只需在程序运行之前人为设置选择TR,一个令TR=0,一个令TR=1,然后分别编译,在两机上分别装入,
84、同时运行。第7章 单片机的C语言应用程序设计 例例16点对点通信。点对点通信的程序如下:#include#defineucharunsignedchar#defineTR1/*发送接收差别值TR=0发送*/ucharidatabuf10;ucharpf;voidinit(void)/*串行口初始化*/TMOD=0x20;/*设T/C1为定时方式2*/TH1=0xe8;/*设定波特率*/第7章 单片机的C语言应用程序设计 TL1=0xe8;PCON=0x00;TR1=1;/*启动T/C1*/SCON=0x50;/*串行口工作在方式1*/voidsend(ucharidata*d)uchari;d
85、oSBUF=0xaa;/*发送联络信号*/while(TI=0);/*等待发送出去*/TI=0;第7章 单片机的C语言应用程序设计 while(RI=0);/*等待B机回答*/RI=0;while(SBUF0xbb)!=0);/*B机未准备好,继续联络*/dopf=0;/*清校验和*/for(i=0;i16;i+)SBUF=di;/*发送一个数据*/pf+=di;/*求校验和*/while(TI=0);TI=0;SBUF=pf;/*发送校验和*/第7章 单片机的C语言应用程序设计 while(TI=0);TI=0;while(RI=0);RI=0;/*等待B机回答*/while(SBUF!=0
86、);/*回答出错,则重发*/voidreceive(ucharidata*d)uchari;dowhile(RI=0);RI=0;while(SBUF0xaa)!=0);/*判A机请求否*/SBUF=0xbb;/*发应答信号*/第7章 单片机的C语言应用程序设计 while(TI=0);TI=0;while(1)pf=0;/*清校验和*/for(i=0;i16;i+)while(RI=0);RI=0;di=SBUF;/*接收一个数据*/pf+=di;/*求校验和*/while(RI=0);RI=0;/*接收A机校验和*/if(SBUFpf)=0)/*比较校验和*/SBUF=0x00;break
87、;/*校验和相同发00*/elseSBUF=0xff;/*出错发FF,重新接收*/第7章 单片机的C语言应用程序设计 while(TI=0);TI=0;voidmain(void)init();if(TR=0)send(buf);elsereceive(buf);第7章 单片机的C语言应用程序设计 7.12.2 多机通信多机通信1通信接口通信接口图7.19总线式主从式多机系统第7章 单片机的C语言应用程序设计 2通信协议通信协议根据MCS-51串行口的多机通信能力,多机通信可以按照以下协议进行:(1)首先使所有从机的SM2位置1处于只接收地址帧的状态。(2)主机先发送一帧地址信息,其中8位地址
88、,第9位为地址/数据信息的标志位,该位置1表示该帧为地址信息。(3)从机接收到地址帧后,各自将接收的地址与本机的地址比较。对于地址相符的那个从机,使SM2位清零,以接收主机随后发来的所有信息;对于地址不符的从机,仍保持SM2=1,对主机随后发来的数据不予理睬,直至发送新的地址帧。第7章 单片机的C语言应用程序设计 (4)当从机发送数据结束后,发送一帧校验和,并置第9位(TB8)为1,作为从机数据传送结束标志。(5)主机接收数据时先判断数据结束标志(RB8),若RB8=1,表示数据传送结束,并比较此帧校验和,若正确,则会送正确信号00H,此信号令该从机复位(即重新等待地址帧);若校验和出错,则发
89、送0FFH,令该从机重发数据。若接收帧的RB8=0,则原数据到缓冲区,并准备接收下帧信息。(6)若主机向从机发送数据,从机在第(3)步中比较地址相符后,从机令SM2=0,同时把本站地址发回主机。作为应答之后才能收到主机发送来的数据。其它从机(SM2=1),无法收到数据。第7章 单片机的C语言应用程序设计 (7)主机收到从机的应答地址后,确认地址是否相符。如果地址不符,发复位信号(数据帧中TB8=1);如果地址相符,则清TB8,开始发送数据。(8)从机接收到复位命令后回到监听地址状态(SM2=1)。否则开始接收数据和命令。第7章 单片机的C语言应用程序设计 3. 通信程序通信程序设主机发送的地址
90、联络信号00H,01H,02H为从机设备地址,地址FFH是命令各从机恢复SM2为1的状态,即复位。主机的命令编码为:01H请求从机接收主机的数据命令;02H请求从机向主机发送数据命令。其它都按从机向主机发送数据命令02H对待。第7章 单片机的C语言应用程序设计 从机的状态字节格式为:ERR00000TRDYRRDYD7D6D5D4D3D2D1D0RRDY=1:从机准备好接收主机的数据。TRDY=1:从机准备好向主机发送数据。ERR=1:从机接收到的命令是非法的。通常从机以中断方式控制和主机的通信。程序可分成主机程序和从机程序,约定一次传送的数据为16个字节,以02H地址的从机为例。第7章 单片
91、机的C语言应用程序设计 1) 主机程序主机程序图图7.20 多多机机通通信信主主机机程程序序流流程程图图 第7章 单片机的C语言应用程序设计 主机程序如下:#include#defineucharunsignedchar#defineSLAVE0x02/*从机地址*/#defineBN16ucharidatarbuf16;ucharidatatbuf16=mastertransmit;voiderr(void)SBUF=0xff;while(TI!=1);TI=0;第7章 单片机的C语言应用程序设计 ucharmaster(charaddr,ucharcommand)ucharaa,i,p;w
92、hile(1)SBUF=SLAVE;/*发呼叫地址*/while(TI!=1);TI=0;while(RI!=1);RI=0;/*等待从机回答*/if(SBUF!=addr)err();/*若地址错,发复位信号*/else/*地址相符*/TB8=0;/*清地址标志*/SBUF=command;/*发命令*/while(TI!=1);TI=0;while(RI!=1);RI=0;第7章 单片机的C语言应用程序设计 aa=SBUF;/*接收状态*/if(aa&0x08)=0x08)TB8=1;err();/*若命令未被接收,发复位信号*/elseif(command=0x01) /*是发送命令*/
93、if(aa&0x01)=0x01)/*从机准备好接收*/dop=0;/*清校验和*/for(i=0;iBN;i+)SBUF=tbufi;/*发送一数据*/p+=tbufi;第7章 单片机的C语言应用程序设计 while(TI!=1);TI=0;SBUF=p;/*发送校验和*/while(TI=0);TI=0;while(RI=0);RI=0;while(SBUF!=0);/*接收不正确,重新发送*/TB8=1;/*置地址标志*/return(0)elseif(aa&0x02)=0x02)/*是接收命令,从机准备好发送*/第7章 单片机的C语言应用程序设计 while(1)p=0;/*清校验和*
94、/for(i=0;iBN;i+)while(RI!=1);RI=0;rbufi=SBUF;/*接收一数据*/P+=rubfi;while(RI=0);RI=0;if(SBUF=p)SBUF=0X00;/*校验和相同发00*/while(TI=0);TI=0;break;第7章 单片机的C语言应用程序设计 elseSBUF=0xff; /*校验和不同发0FF,重新接收*/while(TI=0);TI=0;TB8=1;/*置地址标志*/Retuen(0);第7章 单片机的C语言应用程序设计 voidmain(viod)TMOD=0x20;/*T/C1定义为方式2*/TL1=0xfd;TH1=0xf
95、d;/*置初值*/PCON=0x00;TR1=1;SCON=0xf0;/*串行口为方式3*/master(SLAVE,0x01);master(SLAVE,0x02);第7章 单片机的C语言应用程序设计 2)从机程序图7.21多机通信的从机中断程序流第7章 单片机的C语言应用程序设计 从机程序如下:#include#defineucharunsignedchar#defineSLAVE0x02#defineBN16ucharidatatrbuf16;ucharidatarebuf16;bittready;bitrready;voidmain(void)第7章 单片机的C语言应用程序设计 TMO
96、D=0x20;/*T/C1定义为方式2*/TL1=0xfd;/*置初值*/TH1=0xfd;PCON=0x00;TR1=1;SCON=0xf0;/*串行口为方式3*/ES=1;EA=1;/*开串行口中断*/while(1)tready=1;rready=1;/*假定准备好发送和接收*/第7章 单片机的C语言应用程序设计 voidssio(void)interrupt4using1voidstr(void);voidsre(void);uchara,i;RI=0;ES=0;/*关串行口中断*/if(SBUF!=SLAVE)ES=1;gotoreti;/*非本机地址,继续监听*/SM2=0;/*取
97、消监听状态*/SBUF=SLAVE;/*从本地址发回*/第7章 单片机的C语言应用程序设计 while(TI!=1);TI=0;while(RI!=1);RI=0;if(RB8=1)SM2=1;ES=1;gotoreti;/*是复位信号,恢复监听*/a=SBUF;/*接收命令*/if(a=0x01)/*从主机接收的数据*/if(rready=1)SBUF=0x01;/*接收准备好发状态*/elseSBUF=0x00;while(TI!=1);TI=0;while(RI!=1);RI=0;if(RB8=1)SM2=1;ES=1;gotoreti;sre();/*接收数据*/第7章 单片机的C语言
98、应用程序设计 elseif(a=0x02)/*从机向主机发送数据*/if(tready=1)SBUF=0x02;/*发送准备好发状态*/elseSBUF=0x00;while(TI!=1);TI=0;while(RI!=1);RI=0;if(RB8=1)SM2=1;ES=1;gotoreti;str();/*发送数据*/第7章 单片机的C语言应用程序设计 elseSBUF=0x80;/*命令非法,发状态*/while(TI!=1);TI=0;SM2=1;ES=1;/*恢复监听*/reti:;voidstr(void)/*发数据块*/ucharp,i;tready=0;dop=0;/*清校验和*
99、/第7章 单片机的C语言应用程序设计 for(i=0;iBN;i+)SBUF=trbufi;/*发送一数据*/p+=trbufi;while(TI!=1);TI=0;SUBF=p;/*发送校验和*/while(TI=0);TI=0;while(RI=0);RI=0;while(SBUF!=0);/*主机接收不正确,重新发送*/SM2=1;ES=1;第7章 单片机的C语言应用程序设计 voidsre(void)/*接收数据块*/ucharp,i;rready=0;while(1)p=0;/*清校验和*/for(i=0;iBN;i+)while(RI!=1);RI=0;rebufi=SBUF;/*
100、接收数据*/p+=rebufi;第7章 单片机的C语言应用程序设计 while(RI!=1);RI=0;if(SBUF=p)SBUF=0x00;break; /*校验和相同发00*/elseSBUF=0xff;/*校验和不同发0FF,重新接收*/while(TI=0);TI=0;SM2=1;ES=1;第7章 单片机的C语言应用程序设计 7.13 键盘和数码显示人机交互的键盘和数码显示人机交互的C语言编程语言编程7.13.1 行列式键盘与行列式键盘与8031的接口的接口键盘输入信息的主要过程是:(1)单片机判断是否有键按下。(2)确定按下的是哪一个键。(3)把此步骤代表的信息翻译成计算机所能识别
101、的代码,如ASCII或其它特征码。第7章 单片机的C语言应用程序设计 图7.228031与行列式键盘的接口第7章 单片机的C语言应用程序设计 例例1744键盘的扫描程序。扫描程序查询的内容为:(1)查询是否有键按下。首先单片机向行扫描P1.0P1.3输出全为0扫描码F0H,然后从列检查口P1.4P1.7输入列扫描信号,只要有一列信号不为1,即P1口不为F0H,则表示有键按下。接着要查出按下键所在的行、列位置。第7章 单片机的C语言应用程序设计 (2)查询按下键所在的行列位置。单片机将得到的信号取反,P1.4P1.7中的为1的位便是键所在的列。接下来要确定键所在的行,需要进行逐行扫描。单片机首先
102、使P1.0为0,P1.1P1.7为1,即向P1口发送扫描码FEH,接着输入列检查信号,若全为1,表示不在第一行。接着使P1.1接地,其余为1,再读入列信号这样逐行发0扫描码,直到找到按下键所在的行,将该行扫描码取反保留。当各行都扫描以后仍没有找到,则放弃扫描,认为是键的误动作。第7章 单片机的C语言应用程序设计 (3)对得到的行号和列号译码,得到键值。(4)键的抖动处理。当用手按下一个键时,往往会出现所按键在闭合位置和断开位置之间跳几下才稳定到闭合状态的情况。在释放一个键时,也会出现类似的情况,这就是键抖动,抖动的持续时间不一,通常不会大于10ms,若抖动问题不解决,就会引起对闭合键的多次读入
103、,对于键抖动最方便的解决方法就是当发现有键按下后,不是立即进行逐行扫描,而是延时10ms后再进行。由于键按下的时间持续上百毫秒,延时后再也不迟。第7章 单片机的C语言应用程序设计 扫描函数的返回值为键特征码,若无键按下,返回值为0。程序如下:#include#defineucharunsignedchar#defineuintunsignedintvoiddlms(void)voidkbscan(void);voidmain(void)ucharkey;while(1)key=kbscan();dlms();第7章 单片机的C语言应用程序设计 voiddlms(void)uchari;for(
104、i=200;i0;i-)ucharkbscan(void)/*键扫描函数*/ucharscode,recode;P1=oxf0;if(P1&0xf0)!=0xf0)/*若有键按下*/dlms();/*延时去抖动*/if(P1&0xf0)!=0xf0)scode=0xfe;/*逐行扫描初值*/while(scode&0x10)!=0)P1=scode;/*输出扫描码*/第7章 单片机的C语言应用程序设计 if(P1&0xf0)!=0xf0)/*本行有键按下*/recode=(P1&0xf0)|0x0f;return(scode)+(recode);/*返回特征字节码*/elsescode=(sc
105、ode1)|0x01;/*行扫描左移一位*/return(0);第7章 单片机的C语言应用程序设计 7.13.2 七段数码显示与七段数码显示与8031的接口的接口数码显示器有静态显示和动态显示两种显示方式。数码显示器有发光管的LED和液晶的LCD两种。LED显示器工作在静态方式时,其阴极(或其阳极)点连接在一起接地(或+5V),每一个的端选线(a,b,c,d,e,f,g,dp)分别与一个8位口相连。LCD数码显示只能工作在静态显示,并要求加上专门的驱动芯片4056。LED显示器工作在动态显示方式时,段选码端口I/O1用来输出显示字符的段选码,I/O2输出位选码。I/O1不断送待显示字符的段选码
106、,I/O2不断送出不同的位扫描码,并使每位显示字符停留显示一段时间,一般为15ms,利用眼睛的视觉惯性,从显示器上便可以见到相当稳定的数字显示。第7章 单片机的C语言应用程序设计 例例188155控制的动态LED显示。图图 7.23 经经8155扩展端口的扩展端口的6位位LED动态显示动态显示 第7章 单片机的C语言应用程序设计 确定的8155片内4个端口地址如下:命令/状态口:FFF0H口A:FFF1H口B:FFF2H口C:FFF3H第7章 单片机的C语言应用程序设计 6位待显示字符从左到右依次放在dis_buf数组中,显示次序从右向左顺序进行。程序中的table为段选码表,表中段选码表存放
107、的次序为0F等。以下为循环动态显示6位字符的程序,8155命令字为07H。# include # include #define uchar unsigned char# define COM8155 XBYTE 0xfff0 # define PA8155 XBYTE 0xfff1 # define PB8155 XBYTE 0xfff2 # define PC8155 XBYTE 0xfff3 第7章 单片机的C语言应用程序设计 ucharidatadis_buf6=2,4,6,8,10,12;ucharcodetable18=0x3f,0x06,0x5b,0x4f,0x66,0x6d,0
108、x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40,0x00;voiddl_ms(uchard);voiddisplay(ucharidata*p)ucharsel,i;COM8155=0x07;/*送命令字*/sel=0x01;/*选出右边的LED*/for(i=0;i6;i+)PB8155=table*p;/*送段码*/第7章 单片机的C语言应用程序设计 PA8155=sel;/*送位选码*/dl_ms(1);p-;/*缓冲区下移1位*/sel=sel1/*左移1位*/voidmain(void)display(dis_buf+5);第7章 单片机的C语言应用程序设计 例例19串行口控制的静态LCD显示。图7.24串行口连接的静态LCD显示电路第7章 单片机的C语言应用程序设计 输出两位显示,即一字节的程序如下:#include#defineucharunsignedcharucharbyte=0x59;voiddisplay(ucharx)SBUF=x;/*由串口输出*/while(TI=0);/*等待8位发送结束*/TI=0;voidmain(void)display(byte)