第10章 单片机C51程序设计_第1页
第10章 单片机C51程序设计_第2页
第10章 单片机C51程序设计_第3页
第10章 单片机C51程序设计_第4页
第10章 单片机C51程序设计_第5页
已阅读5页,还剩66页未读 继续免费阅读

下载本文档

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

文档简介

1、第10章目录,10.1C51程序设计基础10.1.1C51中的基本数据类型10.1.2C51中扩充的数据类型10.1.3C51中的运算符10.1.4C51中的语句及程序结构10.2C51程序设计10.2.1定时/计数器初始化程序设计举例10.2.2点亮发光二极管实现流水灯程序10.2.3单个中断系统设计举例10.2.4定时器程序设计举例10.2.5串行通信程序设计举例,10.1C51程序设计基础,10.1.1C51中的基本数据类型C语言中的数据有常量和变量之分,当我们在编写单片机程序时,用到的相关变量在单片机的内存中都要占据一定的存储空间,变量大小不同,所占据的空间就不同,所以,“变量”数据的

2、大小是有限制的,我们不能随意给一个变量赋任意大小的值。为了合理利用内存空间,我们在编程时就要选择大小合适的数据,不同的数据类型也就代表了不同大小的数据,所以,我们在使用变量之前,首先要声明这个变量的类型,以便让编译器提前从内存中为其分配空间。表10-1列出了C51的基本数据类型。,10.1C51程序设计基础,10.1.1C51中的基本数据类型,表10-1C51的基本数据类型,10.1C51程序设计基础,10.1.1C51中的基本数据类型说明:(1)在单片机中,所有的数据都是以二进制的形式存储在存储器中的,其中,1个字节=8位二进制(即1Byte=8bit)。(2)在C语言中:shortint即

3、为int,longint即为long。(3)前面若无unsigned则一律认为是signed型。(4)一般情况下,float型数据只能提供7位有效数字,double型数据能够提供1516位有效数字,但是这个精度还和编译器有关系,并不是所有的编译器都遵守这条原则。,10.1C51程序设计基础,10.1.1C51中的基本数据类型例如:floatx;x=32.45678912;由于float类型的有效数字是7位,故x=32.45679,若将x改成double类型的则能将32.45678912全部存放在x中,所以,在声明变量时应根据其使用情况来为其选择类型。例如:inti=9;在内存中占16位(即2个

4、字节的存储单元)。,10.1C51程序设计基础,10.1.2C51中扩充的数据类型在C51中除以上基本数据类型外,为了适应单片机程序开发的需要又扩充了如下的数据类型。位变量声明bit:声明一个位变量。8位寄存器声明sfr:声明一个8位寄存器。16位寄存器声明sfr16:声明一个16位寄存器。特殊功能位声明sbit:声明特殊功能寄存器中的某一位。,10.1C51程序设计基础,10.1.2C51中扩充的数据类型bitflag;说明:声明了一个变量名为flag的位变量。sfrTCON=0 x88H;说明:单片机内部有很多特殊功能寄存器(sfr),每个sfr在单片机内部都分配有唯一的一个地址,当我们在

5、程序中使用sfr时,我们一般通过声明将相应的地址编号赋给一个名称,以后通过该名称来使用相应sfr,其中这些声明绝大多数都包含在相应的头文件(如:reg51.h)中。本例中TCON是单片机的定时器/计数器控制寄存器,它在单片机内存中的地址为0 x88H,经过该语句声明以后,如在后面的程序中使用该寄存器则只需使用其名称TCON即可,当程序进行编译时,编译器也知道实际要操作的是单片机内部0 x88地址处的这个寄存器,而TCON仅仅是这个地址的一个代号或名称而已,当然,我们也可以声明成其他的名称。,10.1C51程序设计基础,10.1.2C51中扩充的数据类型sfr16T1=0 x8BH;说明:声明了

6、一个16位SFR,起始地址为0 x8BH,道理与上例相同不再重复解释。sbitTR0=TCON4;说明:首先应该有sfrTCON=0 x88H;这句的声明后,我们才知道TCON是定时器/计数器控制寄存器的名称,它是一个8位的寄存器,其中,TCON0为最低位,TCON7为最高位,通过本例的声明,以后当我们使用TCON4时,直接操作TR0即可,当然,我们也可以将TCON4声明成其他的名称。,10.1C51程序设计基础,10.1.3C51中的运算符绝大多数的程序都需要对数据进行加工处理,即进行运算,要进行运算就得规定可以使用的运算符,C51中的运算符与C语言基本相同,在单片机程序开发过程中最常用的主

7、要有算术运算符、关系运算符、逻辑运算符和位运算符等。,10.1C51程序设计基础,10.1.3C51中的运算符说明:(1)“”表示测试两数是否相等,如a=b,测试a和b中的值是否相等。(2)“!”表示测试两数是否不相等,如a!=b,测试a和b中的值是否不相等。,10.1C51程序设计基础,10.1.3C51中的运算符说明:(1)右移,操作符为“”,每执行一次右移指令,被操作数的各位依次向右移动一位,其中,最高位补0,最低位移入PSW寄存器CY位,CY位中原来的数被覆盖。如图10-1所示。(2)左移,操作符为“”,每执行一次左移指令,被操作数的各位依次向左移动一位,其中,最高位移入PSW寄存器C

8、Y位,CY位中原来的数被覆盖,最低位补0,如图10-2所示。,10.1C51程序设计基础,10.1.3C51中的运算符其他运算符赋值运算符:条件运算符:?:逗号运算符:,指针运算符:*和&求字节运算符:sizeof强制类型转换运算符:(类型)成员运算符:.下标运算符:其他,10.1C51程序设计基础,10.1.4C51中的语句及程序结构1、语句C51程序是由语句组成的,其中,语句可分为以下几类:(1)表达式语句:由一个表达式(赋值表达式、算术运算表达式、关系表达式、逻辑表达式等)加上一个分号构成,最典型的就是赋值表达式构成的赋值语句。例如:x=3.6;(2)函数调用语句:由一个函数调用加上一个

9、分号构成。例如:printf(“Helloeveryone!”);说明:在屏幕上输出显示Helloeveryone!,10.1C51程序设计基础,10.1.4C51中的语句及程序结构(3)控制语句:条件语句:if、switch。循环语句:for、while、dowhile。控制转移语句:break、continue、return、goto。(4)复合语句:用把一些语句和声明括起来就构成了复合语句。floatx=3,y=9,z;z=x+y;printf(“z=%f”,z);(5)空语句:只有一个分号;什么也不做,可以作为程序流程的转向点,也可用作循环语句的循环体(循环体是空语句)。,10.1C5

10、1程序设计基础,10.1.4C51中的语句及程序结构2、程序结构由以上语句可以构成C程序的三大结构,即顺序结构、选择结构(分支结构)、循环结构。(1)顺序结构:仅仅是简单的一个语句一个语句的执行。(2)选择结构:主要由条件语句构成。(3)循环结构:主要由循环语句构成,与标准C语言一样,循环结构中可以使用break、continue、return、goto。,10.1C51程序设计基础,10.1.5C51中的头文件C51中已定义的常用的头文件主要有如下类别:absacc.h-包含允许直接访问8051不同存储区的宏定义。assert.h-文件定义assert宏,可以用来建立程序的测试条件。ctyp

11、e.h-字符转换和分类程序。math.h-数学函数程序。reg51.h-51的特殊寄存器声明。reg52.h-52的特殊寄存器声明。setjmp.h-定义jmp_buf类型和setjmp和longjmp程序的原型。stdarg.h-可变长度参数列表程序。stdlib.h-存储器分配程序。stdio.h-流输入和输出程序。string.h-字符转操作程序,缓冲区操作程序。,10.1C51程序设计基础,10.1.5C51中的头文件在单片机程序中,经常使用的头文件主要有math.h、reg51.h或reg52.h、stdio.h等。1、头文件math.h头文件math.h是数学函数库,各种数学计算函

12、数的具体实现就放在文件math.h里,比如,平方根函数sqrt、绝对值函数fabs、指数函数exp、正弦函数sin、余弦函数cos等,当我们需要使用时可以直接调用这些函数。2、头文件reg51.h和reg52.h头文件reg51.h或reg52.h是声明51单片机或52单片机特殊功能寄存器和位寄存器的,这两个头文件中大部分内容是一样的,51单片机比52单片机少一个定时/计数器T2,因此,reg51.h中也就比reg52.h中少了几行对寄存器T2声明的内容。,10.1C51程序设计基础,10.1.5C51中的头文件在单片机程序中,经常使用的头文件主要有math.h、reg51.h或reg52.h

13、、stdio.h等。、头文件stdio.h包含了与标准输入输出库有关的变量定义和宏定义以及对函数的声明,标准输入输出函数主要有printf、scanf、putchar、getchar等。除以上已定义的头文件外,用户也可以根据自己的需要定义扩展名为.h的头文件,存放到指定的目录下,在编程中可以直接使用。,10.2C51程序设计,10.2.1定时/计数器初始化程序设计举例【例10-1】在8051单片机中,利用定时器T0产生50ms的定时时间。通过本例我们将对头文件、主函数main()、注释等三个方面的知识点做讲解。程序代码如下:#include/包含头文件voidmain()TMOD=0 x01;

14、/设置定时器0为方式1、定时模式TH0=(65536-45872)/256/装初值TL0=(65536-45872)%256;TR0=1;/启动定时器0while(1)/程序停止,10.2C51程序设计,10.2.1定时/计数器初始化程序设计举例说明:(1)按照定时/计数器的初始化流程,首先设置TMOD=0 x01,置T0工作于方式1、定时模式,给TH0和TL0装入初值,之后启动定时器开始计时,获得50ms的时间。(2)下面说一下初值的计算。晶振频率为fosc=11.0592Mhz机器周期T=12*1/fosc=12*1/11.0592Mhz=1.09s计数值=5010-3/(1.0910-6

15、)=45872因单片机的定时器是加法计数器,如果想获得50ms的定时时间,我们就要给TH0和TL0装入计数初值X,在这个初值的基础上计45872个数至65536(方式1的溢出值216),定时器溢出,此时刚好50ms。因此,计数初值X=65536-45872=19664。在编写程序时要将计数值转换成16进制的,我们采用下面的方法转换,把19664对256求模,即19664/256装入THO中,把19664对256求余,即19664%256装入TLO中。,10.2C51程序设计,10.2.1定时/计数器初始化程序设计举例1、头文件(1)头文件包含的书写方法:两种当使用#include时(即本例中所

16、使用的方法),编译器先进入到软件安装文件夹(KeilC51INC)处开始搜索这个头文件,如果这个文件夹下没有引用的头文件,编译器将会报错。当使用#include“reg51.h”时,编译器先进入到当前工程所在文件夹处开始搜索该头文件,如果当前工程所在文件夹下没有该头文件,编译器将继续回到软件安装文件夹处搜索这个头文件,若仍然找不到该头文件,编译器将报错。,10.2C51程序设计,10.2.1定时/计数器初始化程序设计举例1、头文件(2)头文件中的内容:在安装好Keiluvision3编译软件后,我们可以到KeilC51INC这个文件夹下找到头文件reg51.h或者在Keil中编辑程序时鼠标移动

17、到reg51.h上,单击右键选择Opendocument也可以打开,打开之后其具体内容参考教材。,10.2C51程序设计,10.2.1定时/计数器初始化程序设计举例1、头文件说明:在该头文件中声明了51系列单片机内部的21个特殊功能寄存器的名称和可以进行位操作的特殊功能寄存器中的各个位的名称。在这里主要用到了前面讲到的sfr和sbit这两个关键字。例如:sfrTMOD=0 x89;把单片机内部地址0 x89处的这个寄存器重新命名为TMOD,以后在程序中使用该寄存器时直接使用TMOD即可,如上例所示。其实,对单片机而言它只知道它的内部地址0 x89是什么,而并不知道TMOD是什么,现在通过sfr

18、TMOD=0 x89;这条语句,单片机就清楚了程序中的TMOD具体指什么了。因此,以后凡是编写5l内核单片机程序时,我们在源代码的前面都应写上#include或#include“reg51.h”。,10.2C51程序设计,10.2.1定时/计数器初始化程序设计举例1、头文件例如:sbitEA=IE7;或sbitEA=0 xAF;(有一些头文件也这样书写,0 xAF是IE寄存器中的最高位的地址,与IE7的写法作用相同)把IE这个寄存器中的最高位重新命名为EA,以后单独操作EA即可。本例中的TMOD、TH0、TL0、TR0四个名称之所以能在程序中代表相关寄存器来使用,就是因为它们已经在头文件中进行

19、了声明。注意:在应用时,如果将大写的TMOD或EA写成小写的tmod或ea,编译程序时将会出错,找不到tmod或ea,因为在头文件中定义的是“sfrTMOD=0 x89;”“sbitEA=IE7;”都是大写的,这也是大多数初学者开始编写程序时容易犯的错误。,10.2C51程序设计,10.2.1定时/计数器初始化程序设计举例2、主函数main()一个没有返回值,没有参数的函数。格式:voidmain()或voidmain(void)说明:(1)任何一个C程序中可以包含一个或多个函数,其中必须包含main函数(且只能有一个),本例中只有一个函数main(),它是整个程序开始执行的入口,所有的单片机

20、在运行程序时,总是从主函数开始运行的。(2)无返回值的函数:main()是一个无返回值的函数,其前面的void表示无返回值的意思(void可省略不写),该函数执行完后不返回任何值。(3)无参数的函数:main()是一个无参数的函数,表示该函数不带任何参数,即main后面的括号中没有任何参数,括号中是空的或void。(4)在main()之后有两个大括号,程序中所有的代码都写在两个大括号内,代码中的每条语句后都要加上分号,语句与语句之间可以用空格或回车隔开。,10.2C51程序设计,10.2.1定时/计数器初始化程序设计举例3、注释注释有两种写法:(1)只能注释一行,当换行时,需要在新行上重新写两

21、个斜扛进行注释。(2)/*/可以注释任意行,即斜扛星号与星号斜扛之间的所有文字都作为注释。所有注释都不参与程序编译,编译器在编译过程中会自动删除注释,一般在编写较大的程序时都应加上注释,这样,以后当我们再次读程序时,因为有了注释,其代码的意义便一目了然了。若无注释,我们不得不将程序重新阅读一遍方可知道代码含义。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序实现流水灯,可以用移位操作来实现,也可以用C51自带的函数来实现。1、循环移位操作在10.1节中我们介绍了移位运算符,这里我们来学习循环移位操作。(1)循环右移被操作数的各位依次向右移动一位,其中,最低位移入最高位。C语言

22、中没有专门的循环右移位运算符,我们可以利用右移位运算符编写程序实现循环右移位,或直接利用C51中自带的库函数_cror_来实现。循环右移位操作,如图10-3所示。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序【例10-2】编写程序实现如下功能,使变量t中的值循环右移位。#include/包含头文件unsignedchart;/定义无符号字符类型变量tvoidmain()/主函数t=0 x66;/给t赋初始值while(1)/循环程序,每次循环使t中的当前值左移1位t=t1;,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序(2)循环左移被操作数的各位依次向左

23、移动一位,其中,最高位移入最低位。C语言中没有专门的循环左移位运算符,我们可以利用左移位运算符编写程序实现循环左移,或直接利用C51库中自带的函数_crol_来实现。循环左移位操作,如图10-4所示。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序【例10-3】编写程序实现如下功能,使变量t中的值循环左移位。#include/包含头文件unsignedchart;/定义无符号字符类型变量tvoidmain()/主函数t=0 x55;/给t赋初始值while(1)/循环程序,每次循环使t中的当前值左移1位t=t1;,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程

24、序2、移位函数_crol_介绍大家打开Keil软件安装文件夹,定位到KeilC51HLP文件夹,打开此文件夹下的C51lib文件,这是C51自带库函数帮助文件。在索引栏我们找到_crol_函数,双击打开它的介绍,内容如下:#includeunsignedchar_crol_(unsignedcharc,/*charactertorotateleft*/unsignedcharb);/*bitpositionstorotate*/,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序2、移位函数_crol_介绍大家打开Keil软件安装文件夹,定位到KeilC51HLP文件夹,打开此文

25、件夹下的C51lib文件,这是C51自带库函数帮助文件。在索引栏我们找到_crol_函数,双击打开它的介绍,内容如下:#includeunsignedchar_crol_(unsignedcharc,/*charactertorotateleft*/unsignedcharb);/*bitpositionstorotate*/函数_crol_是C51自带的内部库函数,在使用这个函数之前,需要在程序开头包含它所在的头文件(即#include)。_crol_是一个有返回值、带参数的函数,其功能是将字符c循环左移b位同时将左移之后的值返回。,10.2C51程序设计,10.2.2点亮发光二极管实现流水

26、灯程序【例10-4】编程实现如下功能,将变量a中的二进制值向左移3位。#include/包含头文件voidmain()chara;/定义字符类型的变量acharb;/定义字符类型的变量ba=0 xA5;/给a赋初始值b=_crol_(a,3);/*移位后的值返回给b故b=0 x2D*/,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序3、流水灯程序的实现【例10-5】参考第二章的图2-12,利用C51自带的库函数_crol_,每隔300ms依次点亮发光二级管,实现流水灯的程序。通过本例我们将对#define宏定义、循环语句for和while、自定义子函数等三个方面知识进行讲解。

27、,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序3、流水灯程序的实现程序代码如下所示:#include/包含头文件#include#defineucharunsignedchar/宏定义#defineuintunsignedintvoiddelay(uint);/声明子函数uchart;,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序3、流水灯程序的实现程序代码如下所示:voidmain()/主函数t=0 xfe;/给t赋初值11111110while(1)/循环点亮发光二级管P1=t;delay(300);/延时300毫秒t=_crol_(t,1);/将t

28、循环左移1位后再赋给tvoiddelay(uintx)/延时1ms子函数uinti,j;for(i=x;i0;i-)for(j=110;j0;j-);,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序3、流水灯程序的实现对于程序中“t=_crol_(t,1);”语句,因为_crol_是一个带返回值的函数,本句在执行时,先执行等号右边的表达式,即将t的值循环左移一位,然后将结果再重新赋给t,如t初值为0 xfe,二进制为11111110,执行此函数后值为11111101,即0 xfd,然后再将0 xfd重新赋给t变量,点亮第2个发光二级管,依次循环点亮后面的发光二极管。除这种方法

29、实现流水灯外,利用左移、右移指令与逻辑运算指令也可以实现循环移位,大家若感兴趣可以自己编写这方面的程序。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序4、#define宏定义(1)格式:#define新名称原名称注意后面没有分号,#define命令可以解释为给“原名称”重新起一个比较简单的“新名祢”,方便以后在程序中直接使用简短的新名称,而不必每次都写烦琐的原名称。(2)本例中我们使用宏定义的目的就是将unsignedint用uint代替,在上面的程序中可以看到,当我们需要定义unsignedint型变量时,并没有写“unsignedinti,j;”,取而代之的是“uint

30、i,j;”,在一个程序代码中,只要宏定义过一次,那么在整个代码中都可以直接使用它的“新名称”。注意,对同一个内容,宏定义只能定义一次,若定义两次,将会出现重复定义的错误提示。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序5、循环语句for和while(1)for语句格式:for(表达式1;表达式2;表达式3)语句说明:对于语句大括号中的语句如果没有,可以为空或不写大括号。三个表达式之间必须用分号分隔。for语句执行步骤:第1步:求解一次表达式1,设置初始条件,可以为0个、1个或多个变量赋初值。第2步,求解循环条件表达式2,若其值为真(非0即为真),则执行for中语句,然后执

31、行第3步;否则结束for语句,直接跳出,不再执行第3步。第3步,求解表达式3。第4步,跳到第2步重复执行。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序5、循环语句for和while延时时间:for循环在本例题中的应用是利用for语句(或while语句)经过若干次循环以后,可以获得所需的延时时间,下面就是用for语句来写的一个简单的延时程序段。unsignedintj;for(j=1;j=200;j+);看上面这两句,首先定义一个无符号整型变量j,之后,进入for循环,单片机在执行for语句的时候是需要时间的,上面的延时程序段for语句重复执行了200次,从而可以获得一个延

32、时时间,因此,我们就可以利用单片机执行for循环(或其他循环语句)来获得延时时间。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序5、循环语句for和while在语句for(j=1;j=200;j+);中如果我们将j=200改为j=65536想获得更大的延时时间,那么程序运行时将会出现问题,为什么呢?因为j是一个无符号整数其最大的值为65535,所以,程序运行后就会出现问题,将达不到我们所需要的时间。这是很多初学者最容易犯的错误。因此我们尤其要注意,在使用变量时首先要考虑变量类型,然后根据变量类型赋一个合理的值。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程

33、序5、循环语句for和while那么,怎样才能通过for语句写出更长的延时时间呢?在下例中所使用的两层嵌套for语句即可获得。uinti,j;for(i=x;i0;i-)for(j=100;j0;j-);第一个for后面没有分号,那么编译器默认第二个for语句就是第一个for语句的内部语句,而第二个for语句内部语句为空,程序在执行时,第一个for语句中的i每减一次,第二个for语句便执行100次,因此上面这个例子便相当于共执行了110 x次for语句。通过这种嵌套我们便可以获得较长的时间延时,我们还可以进行更多层的嵌套来增加延时时间,或是改变变量类型等来增加执行时间。,10.2C51程序设计

34、,10.2.2点亮发光二极管实现流水灯程序5、循环语句for和while在C语言中这种延时语句不好算出它的精确时间,如果需要非常精确的延时时间,我们在前面讲到利用单片机内部的定时器来延时,它的精度非常高,可以精确到微秒级。而一般的简单延时语句实际上我们并不需要太精确。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序5、循环语句for和while(2)while语句介绍格式:while(表达式)语句特点:先判断表达式,后执行内部语句。原则:若表达式为真,那么执行内部语句。否则跳出while语句,执行后面的语句。需要注意如下的三点:在C语言中我们一般把“0”认为是“假”,“非0”

35、认为是“真”,也就是说,只要不是0就是真,所以,非0的数都是真。语句中的语句部分可以是语句块,也可以为空,就是说“while(1);”或“while(1);”也正确,但分号“;”一定不能少,否则while()会把跟在它后面第一个分号前的语句认为是它的内部语句;,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序例如:while(1)P0=11;P1=22;上面这个例子中,while()会把“P0=11;”当做它的语句,即使这条语句并没有加大括号。因此,以后在写程序时,如果while()内部只有一条语句,我们就可以省去大括号,而直接将这条语句跟在它的后面。例如:while(1)P0

36、=11;,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序表达式可以是一个常数、一个运算或一个带返回值的函数。在本例中使用while语句我们将不停地循环点亮发光二级管,因为在语句while(1)中的条件是1,非0即为真,条件永远为真,因此只要程序运行,发光二级管将不停地被依次循环点亮。初学者可能会这样想,我让单片机把发光二极管点亮后,就让它停止工作,不再执行别的指令,这样不是更好吗?请大家注意,单片机是不能停止工作的,只要它有电,有晶振在起振,它就不会停止工作,每过一个机器周期,它内部的程序指针就要加1,程序指针就指向下一条要执行的指令。想让它停止工作的办法就是把电断掉不过这样

37、发光二极管也就不会亮了。不过我们可以将单片机设置为休眠状态或掉电模式,这样可以最大限度地降低它的功耗。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序6、自定义子函数在C语言中,除了系统自带的库函数比如求平方根函数sqrt、求绝对值函数fabs以及上例中用到的移位函数_crol_等外,用户也可以根据编程需要自定义一些子函数,比如有一些程序段不止一次要被用到,为了不重复编写相同代码,我们就可以把这样的程序段写成一个子函数,当在主函数或其他函数中需要用到这个程序段时,直接调用这个子函数就可以了。C语言中的自定义子函数(从带参数与否的角度)主要分为有两种:不带参数的自定义子函数带参

38、数的自定义子函数。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序6、自定义子函数(1)不带参数子函数的定义、声明及调用不带参数子函数的定义:我们以下面这个程序为例:voiddelay()for(i=100;i0;i-)for(j=200;j0;j-);,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序其中,void表示这个函数执行完后不返回任何数据,即它是一个无返回值的函数。delay是函数名,这个名字只要符合C语言标识符的命名规则即可,但是注意不要和C语言中的关键字相同。一般我们写成方便记忆或读懂的名字,也就是一看到函数名就知道此函数实现的内容是什么。在这

39、里写成delay这个函数是一个延时子函数。紧跟函数名后面的是一个括号,这个括号里没有任何数据或符号(即C语言当中的“参数”),因此,这个函数是一个无参数的函数。接下来两个大括号中包含着其他要实现的语句。以上讲解的是一个无返回值、不带参数的函数的定义。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序不带参数子函数的声明:声明子函数的目的是为了编译器在编译主程序的时候,当它遇到一个子函数时知道有这样一个子函数存在,并且知道它的类型和带参情况等信息,以方便为这个子函数分配必要的存储空间。子函数可以写在调用函数(比如main()函数)的前面或后面。当写在main()函数后面时,必须要

40、在调用函数之前声明子函数,声明方法如下:将返回值特性、函数名及后面的小括号完全复制,无参函数,则小括号内为空。最后在小括号的后面必须加上分号“;”。当写在main()函数前面时,不需要声明,因为写函数体的同时就已经相当于声明了函数本身。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序不带参数子函数的调用:函数名();,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序【例10-6】编程实现在主函数中调用延时子程序,从而获得一定的延时时间。#include/包含头文件#defineuintunsignedint/宏定义voiddelay();/子函数声明voidm

41、ain()delay();/子函数调用voiddelay()/延时子函数定义uinti,j;for(i=500;i0;i-)for(j=110;j0;j-);,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序本例中,我们注意到“uinti,j;”语句,i和j两个变量的定义放到了子函数里,而没有写在所有函数的最外面。在函数外面定义的变量叫做全局变量;在某个函数内部定义的变量叫做局部变量,这里i和j就是局部变量。注意:局部变量只在当前函数中有效,程序一旦执行完当前函数,在它内部定义的所有变量都将自动销毁,当下次再调用该函数时,编译器重新为其分配内存空间。我们要知道在一个程序中,每个

42、全局变量都占据着单片机内固定的RAM单元,局部变量是用时随时分配,不用时立即销毁。一个单片机的RAM是有限的,如8051只有128B的RAM,如果要定义unsignedchar型变量的话,我们最多只能定义128个。很多时候,当写一个比较大的程序时,经常会遇到内存不够用的情况,因此我们从一开始写程序时就要坚持能用局部变量就不用全局变量的原则,以节省内存空间。,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序(2)带参数子函数的定义、声明及调用带参数子函数的定义:【例10-5】中voiddelay(uintx)是延时1ms的带参数子函数,其定义如程序中所示。带参数子函数的声明:因d

43、elay写在了被调用函数main()的后面,所以,需在前面进行函数声明。声明时必须将参数类型带上,如果有多个参数,多个参数类型都要写上,类型后面可以不跟变量名也可以写上变量名。带参数子函数的调用:,10.2C51程序设计,10.2.2点亮发光二极管实现流水灯程序(2)带参数子函数的定义、声明及调用本例中delay后面的括号中多了一句“uintx”,x就是这个子函数所带的参数,它是一个unsignedint型变量,又叫做这个函数的形参,当进行函数调用时我们用一个具体真实的数据代替这个形参,这个真实数据被称为实参,形参被实参代替之后,在子函数内部所有和形参名相同的变量将都被实参代替。有了这种带参函

44、数,我们要调用一个延时300ms的函数就可以写成“delay(300);”,要延时200ms可以写成“delay(200);”,十分方便。,10.2C51程序设计,10.2.3单个中断系统设计举例【例10-7】用C51编程实现【例5-3】的功能程序代码如下所示:#include/包含头文件sbitCINT0=P30;/声明清除中断的引脚voiddelay(unsignedinti);/delay函数声明unsignedchararr100;/声明数组用于存储外设数据,容量为100,可变unsignedinta;/定义无符号整型变量a,10.2C51程序设计,10.2.3单个中断系统设计举例vo

45、idmain(void)CINT0=0;/清除INT0时提供负脉冲IT0=0;/低电平触发外部中断0EX0=1;/允许外部中断0EA=1;/CPU开中断a=0;while(1),10.2C51程序设计,10.2.3单个中断系统设计举例/INT0中断服务程序voidcounter0(void)interrupt0CINT0=1;/不清除INT0时提供负脉冲delay(100);/调用延时子程序CINT0=0;/清除INT0时提供负脉冲arra=P1;/将数据存入数组delay(100);/调用延时子程序a+;,10.2C51程序设计,10.2.3单个中断系统设计举例voiddelay(unsig

46、nedinti)/定义延时子程序charj;for(i;i0;i-)for(j=200;j0;j-);,10.2C51程序设计,10.2.3单个中断系统设计举例说明:C51的中断函数格式如下:void函数名()interrupt中断号using工作组中断服务程序代码其中:函数名前有void,中断函数不返回任何值。函数名按C语言标识符来起,但不要与C语言中的关键字相同。函数名后面的括号是空的,中断函数不带任何的参数。,10.2C51程序设计,10.2.3单个中断系统设计举例中断号是指单片机的几个中断源的序号,如下表所示10-5,本例中所使用的是外部中断0所以中断号为0。using工作组,可选,确

47、定中断函数使用单片机内存中4组工作寄存器中的哪一组,C51编译器在编译程序时会自动分配工作组,因此,可以省略不写。,10.2C51程序设计,10.2.4定时器程序设计举例【例10-8】用C51编程实现【例6-4】的功能。程序代码如下:#include/包含头文件sbitWORK=P10;/WORK为1时包装机工作/*系统初始化子函数*/voidsystem_Ini()TMOD=0 x06;/置T0工作于计数模式2TH0=0 xE8;/装入计数初值TL0=0 xE8;ET0=1;/T0开中断EA=1;/CPU开中断TR0=1;/启动T0,10.2C51程序设计,10.2.4定时器程序设计举例/*延时子函数*/voiddelay(unsignedinti)charj;for(i;i0;i-)for(j=200;j0;j-);/*主函数*/voidmain()system_Ini();/系统初始化while(1).,10.2C51程序设计,10.2.4定时器程序设计举例/*定时计数器0的中断函数

温馨提示

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

评论

0/150

提交评论