《单片机ppt课件-第14章--C语言程序设计.ppt》由会员分享,可在线阅读,更多相关《单片机ppt课件-第14章--C语言程序设计.ppt(142页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、1 第第14章章 目目录14.1 编程程语言言Keil C51简介介14.1.1KeilC51简介14.1.2KeilC51的开发环境14.1.3C51与标准C的主要区别14.2 C51语言程序言程序设计基基础14.2.1C51语言中的数据14.2.2C51的位变量定义14.2.3一个简单的C51程序14.2.4C51的运算符14.2.5C51的分支与循环程序结构14.2.6AT89S51不同存储区的C51定义14.2.7C51中断服务函数的定义14.3 C51的程序的程序设计举例例14.3.1中断程序的编写14.3.2定时器程序的编写14.3.3串行口方式0应用程序的编写14.3.4独立式键
2、盘查询方式14.3.5行列式键盘查询方式14.3.6DAC0832应用程序的编写214.3.7ADC0809应用程序的编写14.4 C51的集成开的集成开发环境境Keil Vision3介介绍14.4.1集成开发环境KeilVision3简介14.4.2KeilVision3软件的安装、启动和运行14.4.3C51程序的开发流程14.5 C51与与汇编语言的混合言的混合编程程14.5.1C51与MCS-51汇编语言的比较14.5.2C51与汇编语言混合编程的方法3内容概要内容概要本章在假定读者本章在假定读者已掌握已掌握标准标准C语言前提下语言前提下,初步介绍如何使用如何使用C51来编写来编写A
3、T89C51单片机的应用程序单片机的应用程序。C51是在是在标准标准C的基础上的基础上,根据单片机存储器硬件结,根据单片机存储器硬件结构及内部资源,构及内部资源,扩展了相应的数据类型和变量扩展了相应的数据类型和变量,而C51在在语法规定、程序结构语法规定、程序结构与与设计方法设计方法上,都与标准上,都与标准C相同。相同。本章重点介绍本章重点介绍C51对标准对标准C所扩展的部分所扩展的部分,并通过一些例程例程来介绍C51的程序设计思想。最后还对C51的集成开发环境KeilVision3以及C51与汇编语言的与汇编语言的混合编程混合编程作以介绍。414.1 编程语言编程语言Keil C51简介简介
4、目前51系列单片机编程的C语言都采用KeilC51(简称C51),KeilC51是在标准是在标准C语言基础上发展起来的语言基础上发展起来的。14.1.1 Keil C51简介简介C语言是美国国家标准协会(ANSI)制定的编程语言标准,1987年ANSI公布87 ANSI C,即标准C语言。Keil C51语言语言是在是在ANSI C的基础上针对的基础上针对51单片机的硬件单片机的硬件特点进行的扩展,特点进行的扩展,并向51单片机上移植,经过多年努力,C51语言已经成为公认的高效、简洁而又贴近51单片机硬件的实用高级编程语言。5目前大多数的51单片机用户都在使用C51语言来进行程序设计。用C51
5、进行单片机软件开发,有如下优点:(1)可读性好。)可读性好。C51语言程序比汇编语言程序的可读性好,因而编程效率高,程序便于修改。(2)模块化开发与资源共享)模块化开发与资源共享。用C51开发出来的程序模块可以不经修改,直接被其他项目所用,这使得开发者能够很好地利用已有的大量的标准C程序资源与丰富的库函数,减少重复劳动。6(3)可移植性好。)可移植性好。为某种型号单片机开发的C语言程序,只需将与硬件相关之处和编译连接的参数进行适当修改,就可以方便地移植到其他型号的单片机上。例如,为51单片机编写的程序通过改写头文件以及少量的程序行,就可以方便地移植到PIC单片机上。(4)代码效率高)代码效率高
6、。当前较好的C51语言编译系统编译出来的代码效率只比直接使用汇编语言低低20%左右,如果使用优化编译选项优化编译选项,效果会更好。714.1.2 Keil C51的开发环境的开发环境Keil C51是德国德国Keil software公司开发的公司开发的用于用于51系列系列单片机的单片机的C51语言开发软件语言开发软件。KeilC51在兼容ANSIC的基础上,又增加很多与51单片机硬件相关的编译特性,使得开发51系列单片机程序更为方便和快捷,程序代码运行速度快,所需存储器空间小,完全可以和汇编语言相媲美。它支持众多的MCS-51架构的芯片,同时集编辑、编译、仿真等功能于一体,具有强大的软件调试
7、功能,是众多的单片机应用开发软件中最优秀的软件之一。8Keil公司目前已推出V7.0以上版本的以上版本的C51编译器编译器,为51单片机软件开发提供了全新的C语言环境,同时保留了汇编代码高效、快速的特点。现在,KeilC51已被完全集成完全集成到一个功能强大的全新集成开发环境(IDE)Vision3中,该环境下集成了集成了文件编辑处理、编译链接、项目(Project)管理、窗口、工具引用和仿真软件模拟器以及Monitor51硬件目标调试器等多种功能,这些功能均可在KeilVision3环境中极为简便地进行操作。9 本章经常用到Keil C51和Keil Vision3两个术语两个术语。Keil
8、C51一般简写为简写为C51,指的是51单片机编程所用的C语言;而KeilVision3,可简写为简写为Vision3,指的是用于51单片机的C51程序编写、调试的集成开发环境集成开发环境。Vision3内部集成了源程序编辑器,源程序编辑器,并允许用户在编辑源文件时就可设置程序调试断点,便于在程序调试过程中快速检查和修改程序。此外,Vision3还支持软件模拟支持软件模拟仿真(仿真(Simulator)和用户目标板调试用户目标板调试(Monitor51)两种工两种工作方式作方式。在软件模拟仿真方式下软件模拟仿真方式下不需任何51单片机及其外围硬件即可完成用户程序仿真调试。10在用户目标板调试方
9、式用户目标板调试方式下,利用硬件目标板中的监控程序可以直接调试目标硬件系统,使用户节省购买硬件仿真器的费用。14.1.3 C51与标准与标准C的主要区别的主要区别不同的嵌入式处理器的C编译系统与标准与标准C的不同之的不同之处处,主要是它们所针对的嵌入式处理器的硬件系统不同。KeilC51的基本语法与标准C相同,但对标准C进行了扩展。深入理解KeilC51对标准C的扩展部分扩展部分是掌握KeilC51的关键之一。11C51与与标准标准C的主要区别如下:的主要区别如下:(1)头文件的差异。)头文件的差异。51系列单片机厂家有多个,它们的差异在于内部资源如定时器、中断、差异在于内部资源如定时器、中断
10、、I/O等数量以及功能等数量以及功能的不同的不同,而对使用者来说,只需要将相应的功能寄存器的头文件加载在程序内,就可实现所具有的功能。因此,Keil C51系列的头文件系列的头文件集中体现了各系列芯片的不同资源及功能。(2)数据类型的不同)数据类型的不同。51系列单片机包含位操作空间和丰富的位操作指令,因此KeilC51与ANSIC相比又扩展了4种类型,以便能够灵活地进行操作。12(3)数据存储类型的不同)数据存储类型的不同。C语言最初是为通用计算机设计的,在通用计算机中只有一个程序和数据统一寻址的内存空间,而而51系列单片机系列单片机有片内、外程序存储器,还有有片内、外程序存储器,还有片内、
11、外数据存储器。片内、外数据存储器。标准标准C并没有提供这部分存储器的并没有提供这部分存储器的地址范围的定义。地址范围的定义。此外,对于AT89C51单片机中大量的特殊功能寄存器也没有定义。(4)标准)标准C语言语言没有没有处理处理单片机中断单片机中断的定义的定义。(5)KeilC51与标准C的库函数库函数有较大的不同。由于标准C的中的部分库函数部分库函数不适于嵌入式处理器系统不适于嵌入式处理器系统,因此被排除在KeilC51之外,如字符屏幕和图形函数。13有一些库函数可以继续使用,有一些库函数可以继续使用,但这些库函数都必须针对51单片机的硬件特点来作出相应的开发,与标准C库函数的构成与用法有
12、很大的不同。例如库函数printf和scanf,在标准C中,这两个函数通常用于屏幕打印和接收字符,而在KeilC51中,它们主要用于串行口数据的收发。(6)程序结构的差异)程序结构的差异。由于51单片机的硬件资源有限,它的编译系统不允许太多的程序嵌套。其次,标准C所具备的递归特性不被KeilC51支持,在C51中,要使用递归特性,必须用reentrant进行声明才能使用。14但是从数据运算操作、程序控制语句以及函数的使用但是从数据运算操作、程序控制语句以及函数的使用上来说,上来说,Keil C51与标准与标准C几乎没有什么明显的差别。几乎没有什么明显的差别。如果程序设计者具备了有关标准C的编程
13、基础,只要注意KeilC51与标准C的不同之处,并熟悉AT89S51单片机的硬件结构,就能够较快地掌握KeilC51的编程。1514.2 C51语言程序设计基础语言程序设计基础本节介绍C51语言程序设计的有关基础知识。14.2.1 C51语言中的数据语言中的数据1.数据类型数据类型KeilC51的基本数据类型如表表14-1所示。针对AT89S51单片机的硬件特点,C51在标准C的基础上,扩扩展了展了4种数据类型种数据类型(见表中最后4行)。注意:注意:扩展的4种数据类型,不能使用指针对它们存取。16表14-1KeilC51支持的数据类型17数据类型位数字节数取值范围signed char81-
14、128+127,有符号字符变量unsigned char810255,无符号字符变量signed int162-32768+32767,有符号整型数unsigned int162065535,无符号整型数signed long324-2147483648+2147483647,有符号长整型数unsigned long3240+4294967295,无符号长整型数float3243.402823 E+38,浮点数(精确到7位)double6481.175494E-308,浮点数(精确到15位)*2413对象指针bit10或1sfr810255sfr16162065535sbit1可进行位寻址的特
15、殊功能寄存器的某位的绝对地址2.C51的扩展数据类型的扩展数据类型下面对表表14-1中扩展的4种数据类型进行说明。(1)位变量)位变量bitbit的值可以是1(true),也可以是0(false)。(2)特殊功能寄存器)特殊功能寄存器sfrAT89S51特殊功能寄存器在片内RAM区的80HFFH之间,“sfr”数据类型数据类型占用一个内存单元。利用它可访问AT89S51内部的所有特殊功能寄存器。例如:例如:sfr P1=0 x90这一语句定义P1口在片内的寄存器,在后面语句中可用“P1=0 xff”(使P1的所有引脚输出18为高电平)之类的语句来操作特殊功能寄存器。(3)特殊功能寄存器)特殊功
16、能寄存器sfr16 “sfr16”数据类型数据类型占用两个内存单元。sfr16和sfr一样用于操作特殊功能寄存器。所不同的是它用于操作占两个字节的特殊功能寄存器。例如:例如:sfr16 DPTR=0 x82语句定义了片内16位数据指针寄存器DPTR,其低8位字节地址为82H。在后面的语句中可以对DPTR进行操作。19(4)特殊功能位)特殊功能位 sbitsbit是指AT89S51片内特殊功能寄存器的可寻址位。例如:例如:sfr PSW=0 xd0;/*定义定义PSW寄存器地址为寄存器地址为0 xd0*/sbit PSW 2=0 xd2;/*定义定义OV位为位为PSW.2*/符号“”前面是特殊功
17、能寄存器的名字,“”的后面数字定义特殊功能寄存器可寻址位在寄存器中的位置,取值必须是07。注意,不要把注意,不要把bit与与sbit混淆。混淆。bit用来定义普通的位变量,值只能是二进制的0或1。而sbit定义的是特殊功能20寄存器的可寻址位,其值是可进行位寻址的特殊功能寄存器的位绝对地址,例如例如PSW寄存器OV位的绝对地址0 xd2。3.数据的存储类型数据的存储类型C51完全支持51单片机硬件系统的所有部分。在51单片机中,程序存储器与数据存储器是完全分开的,且分为片内片内和片外片外两个独立的寻址空间,特殊功能寄存器与片内RAM统一编址,数据存储器与I/O端口统一编址。C51编编译器译器通
18、过将变量、常量定义成不同存储类型的方法将它们定义在不同的存储区中。21C51存储类型存储类型与与AT89S51的实际存储空间的实际存储空间的对应关系见表表14-2。下面对表表14-2作以说明。(1)片内数据存储器)片内数据存储器片内RAM可分为3个区域个区域:data:片内直接寻址区片内直接寻址区,位于片内RAM的低128字节。bdata:片内位寻址区片内位寻址区,位于片内RAM位寻址区20H2FH。idata:片内间接寻址区片内间接寻址区,片内RAM所有地址单元 (00HFFH)。2223(2)片外数据存储器)片外数据存储器pdata:片外数据存储器页,一页为256字节。xdata:片外数据
19、存储器RAM的64KB空间。(3 3)片外程序存储器)片外程序存储器code:外部程序存储器的64KB空间。对单片机编程,正确地正确地定义数据类型定义数据类型以及以及存储类型存储类型,是所有编程者在编程前都需要都需要首先考虑首先考虑的问题。的问题。在资源有限的条件下,如何节省存储单元并保证运行效率,是对开发者的一个考验。只有对C51中的各种数据类型以及存储类型非常熟练的掌握,才能运用自如。24定义定义变量类型变量类型应考虑如下问题:应考虑如下问题:程序运行时该变量可能的取值范围,是否有负值,绝对值有多大,以及相应需要的存储空间大小。在够用的情况下,尽量选择8位即一个字节的char型,特别是un
20、sigedchar。对于51系列这样的定点机而言,浮点类型变量将明显增加运算时间和程序长度,如果可以的话,尽量使用灵活巧妙的算法来避免浮点变量的引入。定义数据的存储类型通常遵循如下原则:定义数据的存储类型通常遵循如下原则:只要条件满足,尽量选择内部直接寻址的存储类型data,然后选择idata即内部间接寻址。对于那些经常使用的变量要使用内25部寻址。在内部数据存储器数量有限或不能满足要求的情在内部数据存储器数量有限或不能满足要求的情况下才使用外部数据存储器。况下才使用外部数据存储器。选择外部数据存储器可先选择pdata类型,最后选用最后选用xdata类型类型。需指出,扩展片外存储器,原理上虽很
21、简单,但在实际开发中,很多时候,会带来不必要的麻烦,如可能降低系统稳定性、增加成本、拉长开发和调试周期等,推荐充推荐充分利用片内存储空间。分利用片内存储空间。另外,通常的单片机应用都是面对小型的控制,代码比较短,对于程序存储区的大小要求很低,常常是片内RAM很紧张而片内FlashROM很富裕,因此如果实时性26要求不高,可考虑使用可考虑使用宏宏,以及将一些子函数的常量数据做成数据表,放置在程序存储区,当程序运行时,进入子函数动态调用下载至RAM即可,退出子函数后立即释放该内存空间。2714.2.2 C51的位变量定义的位变量定义由于AT89C51能够进行位操作,C51扩展了“bit”数据类型用
22、来定义位变量,这是C51与标准C的不同之处。C51中位变量bit的具体定义如下:1.位变量的位变量的C51定义方法定义方法C51通过关键字“bit”来定义位变量,格式格式为:bit bit-name;例如:例如:bitov-flag;/*将ov-flag定义为位变量*/282.C51程序函数的程序函数的“bit”参数及返回值参数及返回值C51程序函数可以包含类型为“bit”的参数,也可将其作为返回值。例如:bitfunc(bitb0,bitb1);/*位变量b0,b1作为函数func的参数*/return(b1);/*位变量b1作为函数的返回值*/293.位变量的限制位变量的限制位变量不能用来
23、定义指针和数组位变量不能用来定义指针和数组。例如:bit*ptr;/*错误,不能用位变量来定义指针错误,不能用位变量来定义指针*/bita-array;/*错误,不能用位变量来定义数组错误,不能用位变量来定义数组*/在定义位变量时,允许定义存储类型,位变量都被放入一个位段,此段总是位于AT89S51片内RAM中,因此其存储器类型限制为bdata,data或idata,如果将位变量定义成其他类型都会在编译时出错。3014.2.3 一个简单的一个简单的C51程序程序一个C51源程序是由一个个模块化的函数一个个模块化的函数所构成,函数是指程序中的一个模块,main()函数()函数为程序的主函数,其他
24、若干个函数可以理解为一些子程序。一个C51源程序无论包含了多少函数,它总是从总是从main()函数()函数开始执行开始执行,不论不论main()函数()函数位于程序的什么位于程序的什么位置位置。程序设计者就是编写一系列的函数模块编写一系列的函数模块,并在需要的时候调用这个函数,实现程序所要求的功能。311.C51程序与函数程序与函数下面通过一个简单C51程序,认识认识C51程序与函数。程序与函数。【例例14-1】在AT89S51的P1.0脚脚接有一只发光二极管,二极管的阴极接P1.0脚,阳极通过限流电阻接+5V,现在让发光二极管每隔800ms闪灭,占空比为50%。已知单片单片机时钟晶振为机时钟
25、晶振为12MHz,即每个机器周期1s,采用软件延时的方法,参考程序如下:32#include/*包含reg51.h头文件*/sbitP10=P10;/*定义位变量P1.0,也可使用sbitP10=0 x90*/voidDelay(unsignedinti)/*延时函数Delay(),i是形式参数*/*两个花括号之间为函数Delay()的函数体*/unsignedintj;/*定义变量j*/for(;i0;i-)/*如果i0,则i减1*/for(j=0;j333;j+)/*如果j333,则j加1*/;/*空函数*/33voidmain(void)/*主函数main()*/while(1)/*主程
26、序轮询*/P10=1;/*P1.0输出高电平,发光二极管灭*/Delay(800);/*将实际参数800传递给形式参数i,延时800ms*/P10=0;/*P1.0输出低电平,发光二极管亮*/Delay(800)/*将实际参数800传递给形式参数i,延时800ms*/34下面对程序进行程序进行简要说明简要说明。程序的第第1行行是“文件包含文件包含”,是将另一个文件“reg51.h”的内容全部包含进来。文件“reg51.h”包含了51单片机全部的特殊功能寄存器的字节地址及可寻址位的位地址定义。程序包含程序包含reg51.h的的目的目的就是为了使用P1这个符号,即通知程序中所写的P1是指AT89S
27、51的P1端口,而不是其他变量。35打开打开reg51.h文件可以看到文件可以看到“sfr P1=0 x90;”,即定义符号P1与地址0 x90对应,而P1口的地址就是0 x90。虽然这里的“文件包含”只有一行,但C编译器在处理的时候却要处理几十行或几百行。程序的第第2行行用符号P10来表示P1.0引脚。在C51中,如果直接写“P1.0”编译器并不能识别,而且P1.0也不是一个合法的C51语言程序变量名,所以必须给它起一个另外的名字,这里起的名字是P10,可是P10是否就是P1.0呢,所以必须给它们建立联系,这里使用了使用了C51的的关键字关键字“sbit”来进行定义。36第第3行行第第9行行
28、对函数函数Delay进行了事先定义事先定义,只有这样,才能在主程序中被主函数main()调用。自行编写的函数Delay()的用途是软件延时,调用时使用的这个“800”被称为“实际参数”,以延时800ms的时间。注意,注意,内层循环for(j=0;j0;i-)时的这1+2+i*(1002+1+2)可以近似为 i*1002,即i个ms。编程者可在一定范围内对i、j调整(不超过i、j的取值范围),来控制延时时间的长短。注意,注意,若Delay()的定义写在main函数的后面,则需要先作出声明,否则编译无法通过,因为编译到main函数中的Delay()语句时,找不到相应的函数体。38main为“主函数
29、主函数”,每一个C语言程序有且只有一个主函数,主函数后面一定有一对花括号“”,在花括号里面书写该函数的代码行。2.用户自定义函数与库函数用户自定义函数与库函数从结构上划分,函数分为主函数主函数main()和普通函数普通函数两种。对普通函数对普通函数,从用户使用的角度划分有两种有两种:一种是标准库函数标准库函数;另一种是用户自定义函数用户自定义函数。(1)标准库函数)标准库函数KeilC51具有功能强大、资源丰富的标准库函数,由C51编译器提供。进行程序设计时,应该善于充分利用39这些功能强大、资源丰富的标准库函数,提高编程效率。用户可以直接调用用户可以直接调用C51的库函数的库函数而不需要为这
30、个函数写任何代码,只需要包含具有该函数说明的头文件即可。例如例如调用输出函数printf时,要求程序在调用输出库函数前包含以下的include命令:#include(2)用户自定义函数)用户自定义函数用户根据自己需要所编写的函数。如例例14-1中的Delay函数函数。编写时,需要注意以下几点。40函数的首部(函数的第首部(函数的第1行),行),包括函数名、函数类型、函数属性、函数参数(形式参数)名、参数类型。例如:例如:void Delay(unsigned int i)函数体,函数体,即函数首部下面的花括号“”内的部分。如果一个函数体内有多个花括号,则最外层的一对“”为函数体的范围。C51区
31、分大小写区分大小写,例如Delay与delay,编译时是不同的两个名称。每个语句最后必须有一个语句最后必须有一个分号分号,分号是C语句的必要组成部分。41从函数的定义的形式上划分可以有三种形三种形式:无参数函数、有参数函数和空函数。(1)无参数函数)无参数函数此种函数在被调用时,既无参数输入,也不返回结果给调用函数,只是为完成某种操作而编写的。(2)有参数函数)有参数函数调用此种函数时,必须提供实际的输入函数,必须说必须说明与实际参数一一对应的形式参数明与实际参数一一对应的形式参数,并在函数结束时返回结果,供调用它的函数使用。例如,例如,例14-1中的第第3行行的“i i”是形式参数形式参数。
32、42(3)空函数)空函数函数体内无语句,是空白的无语句,是空白的。调用空函数时,什么工什么工作也不做,不起任何作用。作也不做,不起任何作用。定义空函数的目的目的,是为以后为以后程序功能的扩充程序功能的扩充。程序最初设计时,往往只涉及最基本的功能模块的函数,其他模块的功能函数可以在以后补上。因此先将非基本模块的功能函数定义成空函数,用一个空语句“;”占好位置,并写好注释,以后再用一个编好的函数代替它。例如,例如,例14-1中的第8行的“;”就是一个空语句。433.函数调用函数调用程序设计者的任务程序设计者的任务就是编写一系列的用户自定义函数模块,并在需要的时候调用这些函数以及库函数,实现程序所要
33、求的功能。调用分为以下几种。(1)简单调用)简单调用调用格式为:调用格式为:函数名(实际参数函数名(实际参数1,实际参数,实际参数2,)例如,例例如,例14-1中主函数main()里的子函数调用语句“Delay(800);”,其中800为实际参数。44(2)嵌套调用)嵌套调用是在被调用的函数中又调用其他函数的语句又调用其他函数的语句。(3)递归调用)递归调用函数的递归调用就是一个函数在其函数体内有调用自身。再入函数再入函数是一种可以在函数体内直接或间接调用自身的一种函数,在KeilC51中递归函数必须是可重入的,可重入的函数需要加上reentrant。再入函数有以下几点规定。再入函数有以下几点
34、规定。45l 再入函数不能包括位操作以及51单片机的未寻址区。l 在同一个程序中可以定义和使用不同存储模式的再入函数,任何模式的再入函数不能调用不同模式的再入函数。l 在参数的传递上,实际参数可以传递给间接调用的再入函数。14.2.4 C51的运算符的运算符在程序中实现运算,要熟悉常用的运算符。本节对C51中用到的标准标准C运算符进行复习运算符进行复习,为C51的程序设计打下基础。1.算术运算符算术运算符如表表14-3所示。46表表14-3算术运算符及其说明47符号说明+加法运算-减法运算*乘法运算/除法运算%取模运算+自增1-自减1对于“/”和“%”往往会有疑问。这两个符号两个符号都涉及除法
35、运算,但“/”运算是取商,而“%”运算为取余数。例如例如“5/3”的结果(商)的结果(商)为1,而“5%3”的结果的结果为2(余数)。表14-3中的自增和自减运算符是使变量自动加1或减1,自增和自减运算符放在变量前和变量之后是不同的。+i,-i:在使用:在使用i之前,先使之前,先使i值加(减)值加(减)1。i+,i-:在使用:在使用i之后,再使之后,再使i值加(减)值加(减)1。48例如:例如:若i=4,则执行x=+i时,先使i加1,再引用结果,即x=5,运算结果为i=5,x=5。再如:再如:若i=4,则执行x=i+时,先引用i值,即x=4,再使i加1,运算结果为i=5,x=4。在例14-1中
36、可以看到有关自增和自减运算符的应用。2.逻辑运算符逻辑运算符逻辑运算符及其说明如表表14-4所示。49 表表14-4逻辑运算符及其说明3.关系运算符关系运算符判断两个数之间的关系。关系运算符及其说明如表表14-5。50符号说明&逻辑与逻辑或!逻辑非 表表14-5关系运算符及其说明51符号说明大于=大于或等于=小于或等于=等于!=不等于4.位运算位运算位运算符及其说明如表表14-6所示。表表14-6位运算其说明52符号说明&位逻辑与位逻辑或位异或位取反位右移5.赋值、指针和取值运算符赋值、指针和取值运算符是对变量操作的基本运算。赋值、指针和取值运算符及其说明如表表14-7所示。表表14-7位运算
37、其说明53符号说明=赋值*指向运算符&取地址14.2.5 C51的分支与循环程序结构的分支与循环程序结构程序结构上可把程序分为可把程序分为三类三类,即顺序、分支顺序、分支和循环循环结构。顺序结构是程序的基本结构,程序自上而下,从main()的函数开始一直到程序运行结束,程序只有一条路可走,没有其他的路径可以选择。顺序结构比较简单和便于理解,这里重点介绍重点介绍分支结构分支结构和循环结构循环结构。541.分支结构程序分支结构程序(1)只有两条分支的时候用只有两条分支的时候用If(条件)分支1else分支2(2)分支较多时分支较多时在分支较多时的情况下使用分支较多时的情况下使用switch语句。5
38、5switch()case():语句;break;case():语句;break;default:语句;break;56注意:注意:每个每个switch分支必须有一个分支必须有一个break语句,语句,否则程序并不能跳出switch,就会继续执行case后面的case语句。如果看一下上述结构的程序对应的汇编语言源程序如果看一下上述结构的程序对应的汇编语言源程序可看到,每一条break语句对应了汇编语言中的一条SJMP指令,而没有SJMP指令程序会继续向下执行,并不能跳出分支选择语句。实际上在对应的汇编语言源程序中,case(0),case(1)只是确定了分支的地址,真正的判断是在switch语
39、句开始的。572.循环结构程序循环结构程序循环语句有以下三种三种。(1)for循环循环格式:格式:for(循环体初始化;循环体执行条件;循环体执行后操作)(循环体初始化;循环体执行条件;循环体执行后操作)花括号中为循环体内容。这里有一个值得注意的现象,能够反映出C51在编译中对于执行时间和占用的存储单元的权衡。例如,例如,for(i=0;i10;i+)对应的汇编语句为:58CLRA;1个机器周期MOVR7,A;2个机器周期LOOP:INCR7;1个机器周期CJNER7,#0AH,LOOP;2个机器周期而for(i=2;i10;i+)对应的汇编语句为:MOVR7,#02H;2个机器周期LOOP:
40、INCR7;一个机器周期CJNER7,#0AH,LOOP;两个机器周期59为什么当i=0时,编译器要多花一个机器周期对for循环初始化?这是因为在使用立即数时,单片机需要在代码空间(程序存储器)中为该立即数申请一个存储单元,用来存放该立即数,作为MOV指令的操作数;而累加器A是单片机中的寄存器,使用A可以节省一个字节的存储空间,从而实现以时间换取空间。(2)while循环循环格式为:while(循环体执行条件),花括号中为循环体内容。60(3)do while循环循环格式为:do ,花括号,花括号 中为循环体内容中为循环体内容while(循环体执行条件)(循环体执行条件)前两种循环前两种循环是
41、先进行循环条件是否满足的判断,才决定循环体是否执行;而而“do while循环循环”是在执行完循环是在执行完循环体后再判断条件是否满足,再决定循环体是否继续执行。体后再判断条件是否满足,再决定循环体是否继续执行。三种循环中,经常使用的是三种循环中,经常使用的是for语句语句。下面来说明for语句的应用。61【例例14-2】求求1到到100之间整数的和。之间整数的和。程序如下:程序如下:#include#includemain()intnVar1,nSum;for(nVar1=0,nSum=1;nSum=100;nSum+)nVar1+=nCount;/*累加求和*/while(1);62关于循
42、环,需说明的是,在无操作系统的控制器和处理器上运行的程序,主体通常采用主体通常采用轮询方式轮询方式,即把所有的操作包含在一个while(1)中,如例14-1。这样的无限循环在面向通用计算机的软件设计中是不被允许的,然而嵌入式系统软件设计中,则由于其硬件构成和使用需求,常常采用这种无限循环。6314.2.6 AT89C51不同存储区的不同存储区的C51定义定义AT89C51有不同的存储区。利用绝对地址的头文件利用绝对地址的头文件absace.h可对不同的存储区进行访问。可对不同的存储区进行访问。该头文件的函数包头文件的函数包括括:CBYTE(访问访问code区,字符型区,字符型)DBYTE(访问
43、访问data区,字符型区,字符型)PBYTE(访问访问pdata区或区或I/O口,字符型口,字符型)XBYTE(访问访问xdata区或区或I/O口,字符型口,字符型)另外还有CWORD、DWORD、PWORD、XWORD四个函数,它们的访问区域同上,只是访问的数据类型为只是访问的数据类型为int型型。64注意:注意:AT89S51片内的4个并行I/O口(P0P3),都是SFR,故对故对P0P3采用采用定义定义SFR的方法的方法。而AT89S51在在片外扩展的并行片外扩展的并行I/O口口,这些扩展的I/O口与片外扩展的RAM是统一编址的,即把一个外部I/O端口当作外部RAM的一个单元来看待。可根
44、据需要来选择为pdata类型或xdata类型。对于对于片外扩展的片外扩展的I/O端口端口,根据硬件译码地址,根据硬件译码地址,将其看作片外将其看作片外RAM的一个单元,使用的一个单元,使用语句语句#define进行定进行定义义。例如:#includ;/*不可缺少*/#definePORTBXBYTE0 xffc2;/*定义外部I/O口PORTB的地址为xdata区的0 xffc2*/65也可把片外片外I/O口口的定义放在一个头文件中一个头文件中,然后在程序中通过通过#include语句语句调用调用。一旦在头文件或程序中通过使用#define语句对片外I/O口进行了定义,在程序中就可以自由使用变
45、量名(例如:PORTB)来访问这些片外I/O端口了。14.2.7 C51中断服务函数的定义中断服务函数的定义由于标准C没有处理单片机中断单片机中断的定义,为直接编写中断服务程序,C51编译器对函数的定义进行了扩展,增增加了一个加了一个扩展关键字扩展关键字interrupt,使用该关键字可以将一个函数定义成中断服务函数。由于C51编译器在编译时对66声明为中断服务程序的函数自动添加了相应的现场保护自动添加了相应的现场保护、阻断其他中断、返回时恢复现场等处理的程序段,因而在编写中断服务函数时可不必考虑这些问题,减轻了用汇编语言编写中断服务程序的繁琐程度,而把精力放在如何处理引发中断请求的事件上。中
46、断服务函数的一般形式为:函数类型 函数名(形式参数表)interruptnusingn关键字interrupt后面的 n是中断号中断号,对于AT89S51,取值为取值为04,编译器从从8n+3处产生中断向量处产生中断向量。AT89S51中断源对应的中断号和中断向量见表表14-3。67 表表14-3AT89S51中断号和中断向量AT89S51在内部RAM中有4个工作寄存器区个工作寄存器区,每个寄存器区包含包含8个工作寄存器(个工作寄存器(R0-R7)。)。C51扩展了一个关键字关键字using,专门用来选择选择AT89S51的的4个不同的工作寄存器区个不同的工作寄存器区。在定义一个函数时,usi
47、ng是一个选项,如果不选用该项,是一个选项,如果不选用该项,则由编译器选择一个寄存器区作为绝对寄存器区访问。68中断号n中断源中断向量(8n+3)0外部中断00003H1定时器0000BH2外部中断10013H3定时器1001BH4串行口0023H其他值保留8n+3关键字关键字using对函数目标代码的影响如下对函数目标代码的影响如下:在中断函数的入口处将当前工作寄存器区内容保护到堆栈中,函数返回前将被保护的寄存器区的内容从堆栈中恢复。使用关键字关键字using在函数中确定一个工作寄存器区时必须小心,要保证工作寄存器区切换都只在指定的控制区指定的控制区域中发生域中发生,否则将产生不正确的函数结
48、果。还要注意,带带using属性的函数原则上不能返回属性的函数原则上不能返回bit类型类型的值的值,且关键字键字using和关键字关键字interrupt都不允许用于外部函数,另外也都不允许有一个带运算符的表达式。69例如例如,外中断外中断1()的中断服务函数书写如下:voidint1()interrupt2using0/*中断号n=2,选择0区工作寄存器区*/编写编写AT89S51中断程序时,应遵循以下规则中断程序时,应遵循以下规则:(1)中断函数没有返回值)中断函数没有返回值,如果定义了一个返回值,将会得到不正确的结果。因此建议在定义中断函数时,将其定义为void类型,以明确说明没有返回值
49、。(2)中断函数不能进行参数传递,)中断函数不能进行参数传递,如果中断函数中包含任何参数声明都将导致编译出错。70(3)在任何情况下都不能直接调用中断函数,)在任何情况下都不能直接调用中断函数,否则会产生编译错误。因为中断函数的返回是由指令指令RETI完成的。RETI指令会影响指令会影响AT89S51中的硬件中断系统内的不可寻址的中断优先级寄存器的状态。如果在没有实际的中断请求的情况下,直接调用中断函数,也就不会执行RETI指令,其操作结果有可能产生一个致命的错误。(4)如果在中断函数中再调用其他函数)如果在中断函数中再调用其他函数,则被调用的函数所使用的寄存器区必须与中断函数使用的寄存器区不
50、同。7114.3 C51的程序设计举例的程序设计举例本节重点介绍对AT89S51片内各种功能部件及硬件接口的C51例程例程,读者应仔细阅读并理解这些例程。14.3.1 中断程序的编写中断程序的编写为响应中断请求而进行中断处理的程序称为中断程序。由中断初始化程序中断初始化程序和中断服务程序中断服务程序两部分组成。中断初始化程序中断初始化程序的位置位于主程序中,主要包括选择外选择外部中断的触发方式、开中断、设置中断优先级部中断的触发方式、开中断、设置中断优先级等。72【例例14-3】在单片机系统的P1口上接有8只只LED,LED的阳极接+5V,阴极接P1口的引脚。当P1口某一口线输出为0时,将LE