C51语言编程基础

上传人:痛*** 文档编号:252941886 上传时间:2024-11-26 格式:PPT 页数:136 大小:908KB
返回 下载 相关 举报
C51语言编程基础_第1页
第1页 / 共136页
C51语言编程基础_第2页
第2页 / 共136页
C51语言编程基础_第3页
第3页 / 共136页
点击查看更多>>
资源描述
,*,单击此处编辑母版标题样式,单击此处编辑母版文本样式,第二级,第三级,第四级,第五级,第,3,章,C51,语言编程基础,1,内容概要,目前单片机应用设计与开发,多使用,C51,语言来编程。,C51,语言是在标准,C,的基础上,根据单片机存储器硬件结构及内部资源,扩展相应的数据类型和变量,而在语法规定、程序结构与设计方法上,都与标准,C,相同。,本章介绍,C51,语言的基础知识、对,C51,集成开发环境,Keil,Vision3,以及单片机虚拟仿真平台,Proteus,也作以介绍。,2,在单片机应用开发中,软件编程占有非常重要的地位。要求编程人员在短时间内编写出执行效率高、运行可靠的程序代码。同时,由于实际系统的日趋复杂,对程序的可读性、升级与维护以及模块化的要求越来越高,以方便多个工程师协同开发。,C51,语言是近年来在国内外的,51,单片机开发中,普遍使用的一种程序设计语言。,C51,能直接对单片机硬件进行操作,既有高级语言的特点,又有汇编语言的特点,因此在单片机应用的程序设计中,得到非常广泛的使用。,3.1,编程语言,Keil,C51,简介,Keil,C51(,简称,C51),,是在标准,C,语言的基础上发展的。,3.1.1,Keil,C51,简介,Keil,C51,语言是在标准,C,的基础上针对,51,单片机的硬件特点进行的扩展,并向,51,单片机上移植,经多年努力,,C51,语言已成为公认的高效、简洁的,51,单片机的实用高级编程语言。,与汇编语言相比,用,C51,语言进行软件开发,有如下优点:,(,1,)可读性好,。,C51,语言程序比汇编语言程序的可读性好,因而编程效率高,程序便于修改、维护以及程序升级。,4,(,2,)模块化开发与资源共享。,C51,开发的模块可直接被其他项目所用,能很好地利用已有的标准,C,程序资源与丰富的库函数,减少重复劳动,也有利于多个工程师的协同开发。,(,3,)可移植性好。,为某型单片机开发的,C51,程序,只需将与硬件相关之处和编译链接的参数进行适当修改,就可方便地移植到其它型号的单片机上。例如,为,51,单片机编写的程序通过改写头文件以及少量的程序行,就可以方便地移植到,PIC,单片机上。,(,4,)生成的代码效率高。,代码效率比直接使用汇编语言,低,20%,左右,,如使用,优化编译选项,,最高,可达,90%,左右,效果会更好。,5,3.1.2 C51,与标准,C,的比较,C51,标准,C,语言有许多相同的地方,但也有自身特点。不同的嵌入式,C,语言编译系统与标准,C,语言的不同,主要是由于它们所针对的硬件系统不同。对于,51,单片机,目前广泛使用的是,Keil,C51,语言,简称,C51,语言。,C51,的基本语法与标准,C,相同,,C51,在标准,C,的基础上进行了适合于,51,系列单片机硬件的扩展。深入理解,Keil,C51,对标准,C,的扩展部分以及不同之处,是掌握,C51,语言的关键之一。,C51,与标准,C,的,主要区别如下,:,6,(,1,)库函数的不同。,标准,C,中的部分库函数不适合于嵌入式控制器系统,被排除在,Keil,C51,之外,如,字符屏幕,和,图形函数,。有些库函数可继续使用,但这些库函数都必须针对,51,单片机的硬件特点做出相应的开发。,例如库函数,printf,和,scanf,,,在标准,C,中,这两个函数通常用于屏幕打印和接收字符,而在,Keil,C51,中,主要用于串行口数据的收发。,(,2,)数据类型有一定的区别。,在,C51,中增加了几种针对,51,单片机特有的数据类型,在标准,C,的基础上,又扩展了,4,种类型,。例如,,51,单片机包含位操作空间和丰富的位操作指令,因此,,C51,语言与标准,C,相比就要增加位类型。,7,(,3,),C51,的变量存储模式与标准,C,中的变量存储模式数据不一样。,标准,C,是为通用计算机设计的,计算机中只有一个程序和数据统一寻址的内存空间,而,C51,中变量的存储模式与,51,单片机的存储器紧密相关。,(,4,)数据存储类型的不同,。,51,单片机存储区可分为内部数据存储区、外部数据存储区以及程序存储区。,内部数据存储区可分为,3,个不同的,C51,存储类型:,data,、,idata,和,bdata,。,外部数据存储区分为,2,个不同的,C51,存储类型:,xdata,和,pdata,。程序存储区只能读不能写,在,51,单片机内部或外部。,C51,提供了,code,存储类型来访问程序存储区。,8,(,5,)标准,C,语言没有处理单片机中断的定义。,C51,中有专门的中断函数。,(,6,),C51,语言与标准,C,语言的输入,/,输出处理不一样。,C51,语言中的输入,/,输出是通过,51,单片机的串行口来完成的,输入,/,输出指令执行前必须对串行口进行初始化。,(,7,)头文件的不同。,C51,语言与标准,C,头文件的差异是,C51,头文件必须把,51,单片机内部的外设硬件资源如定时器、中断、,I/O,等所相应的功能寄存器的写入头文件内。,(,8,)程序结构的差异。,由于,51,单片机硬件资源有限,它的编译系统不允许太多的程序嵌套。其次,标准,C,所具备的递归特性不被,C51,语言支持。,9,但是从数据运算操作、程序控制语句以及函数的使用上来说,,Keil,C51,与,标准,C,几乎没有什么明显的差别,。,如果程序设计者具备了有关标准,C,的编程基础,只要注意,Keil,C51,与标准,C,的不同之处,并熟悉,51,单片机的硬件结构,就能够较快地掌握,C51,的编程。,3.2,Keil,C51,的开发工具,Keil,C51,是德国,Keil,software,公司开发的用于,51,单片机的,C51,语言开发软件,在兼容标准,C,的基础上,又增加了很多与,51,单片机硬件相关的编译特性,使得在,51,系列单片机上开发应用程序更为方便和快捷,生成的程序代码运行速度快,所需的存储器空间小,完全可以和汇编语言相媲美。,10,它支持众多的,8051,架构的芯片,同时集编辑、编译、仿真等功能于一体,具有强大的软件调试功能,是众多的单片机应用开发软件中的最优秀软件之一。,3.2.1,集成开发环境,Keil Vision3,简介,Keil,C51,已集成到一个功能强大的,集成开发环境,(,IDE,Intergrated,Development,Eviroment,),Vision3,中,用于,51,单片机的,Windows,下的集成开发环境,提供对,8051,内核的各种型号的支持,为,51,单片机软件开发提供全新的,C,语言开发环境。该开发环境下集成了文件编辑处理、编译链接、项目(,Project,)管理、窗口、工具引用和仿真软件模拟器以及,Monitor51,硬件目标调试器等多种功能,,,所有功能均,11,可在,Keil,Vision3,的开发环境中极为简便地进行操作。,Keil,Vision3,完全兼容先前的,Keil,Vision2,版本。,Keil,公司目前已经推出了,v7.0,以上版本的,C51,编译器,较新的版本为,Keil,C51 v8.08a,。,经常用到,Keil,C51,和,Keil,Vision3,两个术语。,Keil,C51,一般简写为,C51,,指的是,51,单片机编程所用的,C51,语言;而,Keil,Vision3,,可简写为,Vision3,,指的是用于,51,单片机的,C51,程序编写、调试的集成开发环境。,Vision3,内部集成了源程序编辑器,并允许用户在编辑源文件时就可设置程序调试断点,便于在程序调试过程中快速,检查和修改程序。此外,,Vision3,还支持,软件模拟仿真,(,Simulator,)和,用户目标板调试,(Monitor51),两种工作方式。在软件模拟仿真方式下不需要任何,51,单片机及其外围硬件即可完成用户程序仿真调试。在用户目标板调试方式下,利用硬件目标板中的监控程序可以直接调试目标硬件系统,使用户节省购买硬件仿真器的费用。,C51,程序的程序开发是在,Keil Vision3,开发环境下进行的。开发者可购买,Keil Vision3,软件,,,也可到,Keil software,公司的主页免费下载,Eval,(,评估,),版本。该版本同正式版本一样,但有一定的限制,最终生成的代码不能超过,2KB,,但用,13,于学,习已足够。开发者还,可到,Keil,公司网站申请免费的软件,试用光盘。,Keil,Vision3,内集成了功能强大,源程序编辑器,和,调试器,。编辑器允许用户在编辑源文件时就可设置程序调试断点,便于在程序调试过程中快速检查和修改程序,可像一般文本编辑器一样对源代码进行编辑。,用户启动,Vision3,调试器之后,断点即被激活。断点可被设置为条件表达式、变量或存储器访问,断点被触发后,调试器的命令或调试功能即可执行,因此可在编辑器内调试程序,快速地检查和修改程序。还可在编辑器中选中变量和存储器来观察其值。并可以在双层窗口中显示,还可对其进行适当,14,的调整。此外,,Vision3,调试器具有符号调试特性以及历史跟踪,代码覆盖,复杂断点等功能。,Keil,Vision,环境下还有,串口调试器软件,comdebug.exe,,用于在电脑端能够看到单片机发出的数据,串口调试器软件无需安装,可直接运行这个软件。若读者需要最新版,可到有关搜索网站输入关键词“串口调试器”,找到一个合适的下载网站,可即下载“串口调试器”的最新版本。当然,使用,Windows,自带的“超级终端”也是不错的选择。,15,3.2.2,Keil,Vision3,软件的安装、启动和运行,1.,软件安装,集成开发环境安装,同大多数软件安装一样,根据提示进行。安装完毕后,可在桌面上看到,Keil,Vision3,的,快捷图标,。,2.,软件启动,点击桌面上的,Keil,Vision3,软件的快捷图标,即可启动该软件,几秒种后,出现编辑界面。,3.,软件的运行,Keil,Vision3,把用户的每一个应用程序设计都当作一个项目,,16,用,项目管理,方法可把一个应用程序设计中所用到的、互相关联的程序链接在同一项目中。,打开一个项目时,所需要的关联程序也都进入了调试窗口,方便用户对项目中各程序的编写、调试和存储。用户也可能开发了多个项目,每个项目用到了相同或不同的程序文件和库文件,采用项目管理,就很容易区分不同项目中所用到的程序文件和库文件,非常容易管理。因此,在编写一个新的应用程序前,一定先要建立项目。下面首先介绍如何建立一个新的项目。,在,编辑界面下,,首先要建立一个点击“,Project”,菜单,选择下拉式菜单中的“,New Project”,,弹出文件对话窗口,选择要,17,保存的路径,在“文件名”中输入一个程序项目名称,保存后的文件扩展名为“,.uv2”,,这是,Keil,Vision3,项目文件的扩展名,以后可直接点击此文件就可打开先前做的项目。,点击“保存”后,这是会弹出一个对话框,要求选择单片机的型号,用户可根据所使用的单片机来选择。,Keil,Vision3,支持几乎所有的,51,内核单片机。,然后编写第一个程序。点击“,File”,菜单,单击“,New”,。这时,用户可输入代码。输入完毕,单击菜单上的“,File”,,在下拉菜单中单击“,Save As”,,在“文件名”栏的编辑框中,键入文件名,,同时,必须键入正确的扩展名,(,.C,或,.ASM),,然后,单击“保存”按钮。,18,上述工作完成后,还有有关项目的设置,程序的编译,链接,调试。这些内容,可按照,Keil,Vision3,开发环境的帮助功能。,3.3 C51,语言程序设计基础,在标准,C,的基础上了解掌握,C51,的数据类型和存储类型、基本运算与流程控制语句、,C51,语言构造数据类型、,C51,函数以及,C51,程序设计的其它问题,为,C51,程序设计打下基础。,3.3.1 C51,语言中的数据类型与存储类型,1.,数据类型,数据是单片机操作的对象,是具有一定格式的数字或数值,,19,数据的不同格式就称为,数据类型,。,Keil,C51,支持的基本数据类型如表,3-1,所示。针对,AT89S51,单片机的硬件特点,,C51,在标准,C,的基础上,扩展了,4,种数据类型(表,3-1,中最后,4,行)。,注意:,扩展的,4,种数据类型,不能使用指针对它们存取。,20,2. C51,的扩展数据类型,对扩展的,4,种数据类型说明。,(,1,)位变量,bit,bit,的值可以是,1,(,true,),也可以是,0,(,false,)。,(,2,)特殊功能寄存器,sfr,特殊功能寄存器分布在片内数据存储区的地址单元,80H,FFH,之间,“,sfr,”,数据类型占用一个内存单元。利用它可以访问,AT89S51,单片机内部的所有特殊功能寄存器。例如:,sfr,P1=0x90,这一语句定义了,P1,端口在片内的寄存器,在程序后续的语句中可以用“,P1=0xff”,使,P1,的所有引脚输出为高电平之类的语句来操作特殊功能寄存器。,22,(,3,)特殊功能寄存器,sfr16,“sfr16”,数据类型占两个内存单元。它用于操作占两个字节的特殊功能寄存器。,例如: “,sfr16 DPTR=0x82,”,语句定义了片内,16,位数据指针寄存器,DPTR,,其低,8,位字节地址为,82H,,高,8,位字节地址为,83H,。,(,4,)特殊功能位,sbit,sbit,片内特殊功能寄存器的可寻址位。例如:,sfr,PSW=0xd0,;,/*,定义,PSW,寄存器地址为,0xd0*/,sbit,PSW2 = 0xd2,;,/*,定义,OV,位为,PSW.2*/,符号“,”,前是特殊功能寄存器的名字,“,”,的后面数字是特殊功能寄存器可寻址位在寄存器中的位置,取值必须是,0,7,。,23,注意,不要把,bit,与,sbit,混淆。,bit,是定义普通的位变量,值只能是二进制的,0,或,1,。而,sbit,定义的是特殊功能寄存器的可寻址位,它的值是可进行位寻址的特殊功能寄存器的某位的绝对地址,例如,,PSW,寄存器,OV,位的绝对地址,0xd2,。,3.,数据存储类型,在讨论,C51,的数据类型时,必须同时提及它的存储类型,以及它与,51,单片机存储器结构的关系,因为,C51,定义的任何数据类型必须以一定的方式定位在,51,单片机的某一存储区中,否则没有任何实际意义。,51,单片机有片内、外数据存储区,还有程序存储区。,51,单片机片内的数据存储区是可读写的,,51,单片机的衍生系列最多可,24,有,256,个字节的内部数据存储区,其中低,128,字节可直接寻址,高,128,字节(,80H,FFH,)只能间接寻址,从,20H,开始的,16,字节可位寻址。内部数据存储区可分为,3,个不同的数据存储类型:,data,、,idata,和,bdata,。,访问片外数据存储区比访问片内数据存储区慢,因为片外数据存储区是通过数据指针加载地址来间接寻址访问的。,C51,提供两种不同数据存储类型,xdata,和,pdata,来访问片外数据存储区。,程序存储区只能读不能写,可能在,51,单片机内部或者外部,或者外部和内部都有,由,51,单片机的硬件决定,,C51,提供了,code,存储类型来访问程序存储区。,25,C51,存储类型与,AT89S51,单片机实际的存储空间的对应关系如,表,3-2,所示。,26,下面对,表,3-2,中的各种存储区作以说明。,(,1,),DATA,区。,寻址是最快的,应该把经常使用的变量放在,DATA,区,但是,DATA,区的存储空间是有限的,,DATA,区除了包含程序变量外,还包含了堆栈和寄存器组。,DATA,区声明中的存储类型标识符为,data,,通常指片内,RAM,的,128,字节的内部数据存储的变量,可直接寻址。,声明举例如下:,unsigned char data,system_status,=0;,unsigned,int,data unit_id8;,char data inp_string20,;,标准变量和用户自声明变量都可存储在,DATA,区中,只要不超过,DATA,区的范围即可。由于,C51,使用默认的寄存器组来传递参数,这样,DATA,区至少失去了,8,字节的空间。另外,当内部堆栈溢出的时候,程序会莫名其妙地复位。这是因为,51,单片机没有报错的机制,堆栈的溢出只能以这种方式表示,因此要留有较大的堆栈空间来防止堆栈溢出。,(,2,),BDATA,区。,是,DATA,中的位寻址区,在这个区中声明变量就可进行位寻址。,BDATA,区声明中的存储类型标识符为,bdata,,,指的是内部,RAM,可位寻址的,16,字节存储区(字节地址为,20H,2FH,)中的,128,个位,。,下面是在,BDATA,区中声明的位变量和使用位变量的例子:,28,unsigned char,bdata,status_byte,;,unsigned,int,bdata,status_word,;,sbit,stat_flag,= status_byte4;,if(status_word15), ,stat_flag,=1;,C51,编译器不允许在,BDATA,区中声明,float,和,double,型变量。,(,3,),IDATA,区。,IDATA,区使用寄存器作为指针来进行间接寻址,常用来存放使用比较频繁的变量。与外部存储器寻址相比,它的指令执行周期和代码长度相对较短。,IDATA,区声明中的存储类型标识符为,idata,,指的是片内,RAM,的,256,字节的存储区,只能间接寻址,速度比直接寻址慢。,声明举例如下:,29,unsigned char,idata,system_status,=0;,unsigned,int,idata,unit_id8;,char,idata,inp_string16,;,float,idata,out_value,;,(,4,),PDATA,区和,XDATA,区。,PDATA,区和,XDATA,区位于片外存储区,,PDATA,区和,XDATA,区声明中的存储类型标识符分别为,pdata,和,xdata,。,PDATA,区只有,256,字节,仅指定,256,字节的外部数据存储区。但,XDATA,区最多可达,64KB,,对应的,xdata,存储类型标识符可以指定外部数据区,64KB,内的任何地址。,对,PDATA,区的寻址要比对,XDATA,区寻址快,因为对,PDATA,区,寻址,只需要装入,8,位地址,而对,XDATA,区寻址要装入,16,位地址,所以要尽量把外部数据存储在,PDATA,区中。,对,PDATA,区和,XDATA,区的声明举例如下:,unsigned char,xdata,system_status,=0;,unsigned,int,pdata,unit_id8;,char,xdata,inp_string16,;,float,pdata,out_value,;,由于外部数据存储器与外部,I/O,口是统一编址的,外部数据存储器地址段中除了包含存储器地址外,还包含外部,I/O,口的地址。对外部数据存储器及外部,I/O,口的寻址将在后面的绝对地址寻址中详细介绍。,31,(,5,)程序存储区,CODE,。程序存储区,CODE,声明的标识符为,code,,储存的数据是不可改变的。在,C51,编译器中可以用存储区类型标识符,code,来访问程序存储区。,声明举例如下:,unsigned char code a =0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08;,以上介绍了,C51,的数据存储类型,,C51,的数据存储类型及其大小和值域如,表,3-3,所示。,32,33,单片机访问片内,RAM,比访问片外,RAM,相对快一些,所以应当尽量把频繁使用的变量置于片内,RAM,。即采用,data,、,bdata,或,idata,存储类型,而将容量较大的或使用不太频繁的那些变量置于片外,RAM,,即采用,pdata,或,xdata,存储类型。常量只能采用,code,存储类型。,变量存储类型定义举例:,(,1,),char data a1,;/*,字符变量,a1,被定义为,data,型,分配在片内,RAM,低,128,字节中*,/,(,2,),float,idata,x,y,;/*,浮点型变量,x,和,y,被定义为,idata,型,定位在片内,RAM,中,只能用间接寻址方式寻址*,/,34,(,3,),bit,bdata,p;,/*,位变量,p,被定义为,bdata,型,定位在片内,RAM,中的位寻址区*,/,(,4,),unsigned,int,pdata,var1;,/*,无符号整型变量,var1,被定义为,pdata,型,定位在片外,RAM,中,相当于使用,Ri,间接寻址*,/,(,5,),unsigned char,xdata,a2 4;,/*,无符号字符型二维数组变量,a24,被定义为,xdata,存储类型,定位在片外,RAM,中,占据,2,4=8,个字节,相当于使用,DPTR,间接寻址*,/,35,4,数据存储模式,如在变量定义时略去存储类型标识符,编译器会自动默认存储类型。默认的存储类型进一步由,SMALL,、,COMPACT,和,LARGE,存储模式指令限制。例如,若声明,char var1,,则在使用,SMALL,存储模式下,,var1,被定位在,data,存储区,在使用,COMPACT,模式下,,var1,被定位在,idata,存储区;在,LARGE,模式下,,var1,被定位在,xdata,存储区中。,在固定的存储器地址上进行变量的传递,是,C51,标准特征之一。在,SMALL,模式下,参数传递是在片内数据存储区中完成的。,LARGE,和,COMPACT,模式允许参数在外部存储器中传递。,C51,也支持混合模式。例如,在,LARGE,模式下,生成的程序,36,可以将一些函数放入,SMALL,模式中,从而加快执行速度。下面对存储模式作进一步的说明。,(,1,),SMALL,模式。,本模式,所有变量都默认位于,51,单片机内部的数据存储器,这与使用,data,指定存储器类型的方式一样。本模式,变量访问的效率高,但所有数据对象和堆栈,必须使用内部,RAM,。,(,2,),COMPACT,模式。,本模式,所有变量都默认在外部数据存储器的,1,页内,这与使用,pdata,指定存储器类型是一样的。该存储器类型适用于变量不超过,256,字节的情况,此限制是由寻址方式决定,相当用数据指针,Ri,进行寻址。与,SMALL,模式相比,该存储模式的效率比较低,对变量访问的速度也慢一些,但比,LARGE,模式快。,37,(,3,),LARGE,模式。,在,LARGE,模式中,所有变量都默认位于外部数据存储器,相当于使用数据指针,DPTR,进行寻址。通过数据指针访问外部数据存储器的效率较低,特别是当变量为,2,字节或更多字节时,该模式要比,SMALL,和,COMPACT,产生更多的代码。,3.3.2 C51,语言的特殊功能寄存器及位变量定义,介绍,C51,如何对特殊功能寄存器以及位变量进行定义并访问。,1,特殊功能寄存器的,C51,定义,C51,语言允许使用关键字,sfr,、,sbit,或直接引用编译器提供的头文件来对特殊功能寄存器(,SFR,)进行访问,特殊功能寄存器在片内,RAM,的高,128,字节,只能采用直接寻址方式。,38,(1),使用关键字定义,sfr,为了能直接访问特殊功能寄存器,SFR,,,C51,语言提供了一种定义方法,即引入关键字,sfr,,语法如下:,sfr,特殊功能寄存器名字,=,特殊功能寄存器地址;,例如:,sfr,IE=0xA8;/*,中断允许寄存器地址,A8H*/,sfr,TCON=0x88;/*,定时器,/,计数器控制寄存器地址,88H*/,sfr,SCON=0x98;/*,串行口控制寄存器地址,98H*/,例如要访问,16,位,SFR,,可使用关键字,sfr16,。,16,位,SFR,的低,39,字节地址必须作为“,sfr16”,的定义地址,例如:,sfr16 DPTR=0x82/*,数据指针,DPTR,的低,8,位地址为,82H,,高,8,位地址为,83H*/,(2),通过头文件访问,SFR,各种衍生型的,51,单片机的特殊功能寄存器的数量与类型有时是不相同的,对单片机特殊功能寄存器的访问可以通过头文件的访问来进行。,为了用户处理方便,,C51,语言把,51,单片机(或,52,单片机)的常用的特殊功能寄存器和其中的可寻址位进行了定义,放在一个,reg51.h,(或,reg52.h,)的头文件中。当用户要使用,40,时,只需在使用之前用一条预处理命令,#include,把这个头文件包含到程序中,就可以使用特殊功能寄存器名和其中的可寻址位名称了。用户可以通过文本编辑器对头文件进行增减。,头文件引用举例如下:,#include/*,头文件,为,51,型单片机的头文件,*,/,void,main(void,),TL0=0xF0;/*,给定时器,T0,低字节,TL0,设置时间常数,已在,reg51.h,中定义*,/,TH0=0x3F;/*,给,T0,高字节,TH0,设时间常数,*,/,TR0=1;/*,启动定时器,0 */,41,(3),特殊功能寄存器中的位定义,对,SFR,中的可寻址位的访问,要使用关键字来定义可寻址位,共有,3,种方法。,sbit,位名,=,特殊功能寄存器,位置;,例如:,sfr,PSW=0xD0,;,/*,定义,PSW,寄存器的字节地址,0xD0H*/,sbit,CY= PSW7,;,/*,定义,CY,位为,PSW.7,,地址为,0xD0*/,sbit,OV= PSW2,;,/*,定义,OV,位为,PSW.2,,地址为,0xD2*/,sbit,位名,=,字节地址,位置;,例如:,sbit,CY= 0xD07,;,/* CY,位地址为,0xD7*/,sbit,OV= 0xD02,;,/* OV,位地址为,0xD2*/,42,sbit,位名,=,位地址;,这种方法将位的绝对地址赋给变量,位地址必须在,0x80,0xFF,之间。,例如:,sbit,CY= 0xD7,;,/* CY,位地址为,0xD7*/,sbit,OV= 0xD2,;,/* OV,位地址为,0xD2*/,【,例,】,片内,I/O,口,P1,口的各寻址位的定义如下:,sfr,P1=0x90,;,sbit,P1_7= P17,;,sbit,P1_6= P16,;,sbit,P1_5= P15,;,sbit,P1_4= P14,;,43,sbit,P1_3= P13,;,sbit,P1_2= P12,;,sbit,P1_1= P11,;,sbit,P1_0= P10,;,2.,位变量的,C51,定义,(1),位变量的,C51,定义,由于,51,单片机能够进行位操作,,C51,扩展的“,bit”,数据类型用来定义位变量,这是,C51,与标准,C,的不同之处。,C51,采用关键字“,bit”,来定义位变量,一般格式为:,bit,bit_name,;,例如:,44,bit,ov_flag,;,/*,将,ov_flag,定义为位变量*,/,bit,lock_pointer,;,/*,将,lock_pointer,定义为位变量*,/,(2),函数可以包含类型为,bit,的参数,也可将其作为返回值,C51,程序函数可以包含类型为“,bit”,的参数,也可将其作为返回值。例如:,bit,func(bit,b0, bit b1),;,/*,位变量,b0,与,b1,作为函数,func,的参数*,/,return(b1),;,/*,位变量,b1,作为函数的返回值*,/,45,(3),位变量定义的限制,位变量不能用来定义指针和数组。例如:,bit *,ptr,;,/*,错误,不能用位变量来定义指针*,/,bit array ,;,/*,错误,不能用位变量来定义数组,array */,在定义位变量时,允许定义存储类型,位变量都被放入一个位段,此段总是位于,51,单片机的片内,RAM,中,因此其存储类型限制为,DATA,或,IDATA,,如果将位变量定义成其他类型都会导致编译时出错。,46,3.3.3 C51,语言的绝对地址访问,如何对,51,单片机的片内,RAM,、片外,RAM,及,I/O,进行访问,,C51,语言提供了两种比较常用的访问绝对地址的方法。,1.,绝对宏,C51,编译器提供了一组宏定义来对,code,、,data,、,pdata,和,xdata,空间进行绝对寻址。在程序中,用“,#include”,来对,absacc.h,中声明的宏来访问绝对地址,包括,CBYTE,、,CWORD,、,DBYTE,、,DWORD,、,XBYTE,、,XWORD,、,PBYTE,、,PWORD,,具体使用方法参考,absacc.h,头文件。其中:,47,CBYTE,以字节形式对,code,区寻址;,CWORD,以字形式对,code,区寻址;,DBYTE,以字节形式对,data,区寻址;,DWORD,以字形式对,data,区寻址;,XBYTE,以字节形式对,xdata,区寻址;,XWORD,以字形式对,xdata,区寻址;,PBYTE,以字节形式对,pdata,区寻址;,PWORD,以字形式对,pdata,区寻址。,例如:,48,#include,#define PORTA XBYTE0xFFC0 /*,将,PORTA,定义为外部,I/O,口,地址为,0xFFC0,,长度,8,位*,/,#define NRAM DBYTE0x50/*,将,NRAM,定义为片内,RAM,,地址为,0x50,,长度,8,位*,/,【,例,】,片内,RAM,、片外,RAM,及,I/O,的定义的程序如下:,#include,#define PORTA XBYTE0xFFC0 /*,将,PORTA,定义为外部,I/O,口,地址为,0xFFC0*/,#define NRAM DBYTE0x40 /*,将,NRAM,定义为片内,RAM,,地址为,0x40*/,49,main( ), PORTA=0x3D; /*,数据,3DH,写入地址,0xFFC0,的外部,I/O,端口,PORTA */,NRAM=0x01; /*,将数据,01H,写入片内,RAM,的,40H,单元*,/,2. _at_,关键字,使用关键字,_at_,可对指定的存储器空间的绝对地址进行访问,格式如下:,存储器类型,数据类型说明符 变量名,_at_,地址常数,其中,存储器类型为,C51,语言能识别的数据类型;数据类型为,C51,支持的数据类型;地址常数用于指定变量的绝对地址,必须位于有效的存储器空间之内;使用,_at_,定义的变量必须为全局变量。,50,【,例,】,使用关键字,_at_,实现绝对地址的访问,程序如下:,void,main(void,), data unsigned char y1_at_0x50; /*,在,data,区定义字节变量,y1,,它的地址为,50H*/,xdata,unsigned,int,y2_at_0x4000; /*,在,xdata,区定义字变量,y2,,地址为,4000H*/,y1=0xff;,y1=0x1234;,while(1);,51,【,例,】,将片外,RAM 2000H,开始的连续,20,个字节单元清,0,。,程序如下:,xdata,unsigned char buffer20_at_0x2000;,void,main(void,), unsigned char i;,for(i,=0; i20; i+),bufferi,=0,如果把片内,RAM 40H,单元开始的,8,个单元内容清,0,,则程序如下:,xdata,unsigned char buffer8_at_0x40;,void,main(void,), unsigned char j ;,for(j,=0; j8; j+),bufferj,=0,52,3.3.4 C51,的基本运算,C51,语言的基本运算与标准,C,类似,主要包括算术运算、关系运算、逻辑运算、位运算和赋值运算及其表达式等。,1.,算术运算符,算术运算的算术运算符及其说明如表,3-4,所示。,53,54,读者对表,3-4,中的运算符“,+,、,-,、*”,运算比较熟悉,但是对于“,/”,和“,%”,往往会有疑问。这两个符号都涉及除法运算,但“,/”,运算是取商,而“,%”,运算为取余数。例如“,5/3”,的结果(商)为,1,,而“,5%3”,的结果为,2,(余数)。表,3-4,中的自增和自减运算符是使变量自动加,1,或减,1,,自增和自减运算符放在变量前和变量之后是不同的。例如:,+i,,,-i,:在使用,i,之前,先使,i,值加(减),1,。,i+,,,i-,:在使用,i,之后,再使,i,值加(减),1,。,例如:若,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,。,55,2.,逻辑运算符,逻辑运算符及其说明如表,3-5,所示。,56,3.,关系运算符,关系运算符就是判断两个数之间的关系。关系运算符及其说明如表,3-6,所示。,57,4.,位运算,位运算符及其说明如表,3-7,所示。,58,在实际的控制应用中,人们常常想要改变,I/O,口中的某一位的值,而不影响其它位,如果,I/O,口是可位寻址的,这个问题就很简单。但有时外扩的,I/O,口只能进行字节操作,因此要想在这种场合下实现单独的位控,就要采用位操作。,【,例,】,编写程序将扩展的某,I/O,口,PORTA,(只能字节操作)的,PORTA.5,清,0,,,PORTA.1,置为,1,,程序如下:,#define ,#define PORTA XBYTE0xFFC0,void main( ),PORTA=( PORTA,上面程序段中,第,1,行定义了一个片外,I/O,口变量,PORTA,,其地址为片外数据存储区的,0xFFC0,。在,main( ),函数中,“,PORTA=( PORTA&0xDF)0x02,”,的作用是先用运算符“,&”,将,PORTA.5,置成,0,,然后再用“,0x02,”,运算将,PORTA.1,置为,1,。,5.,指针和取地址运算符,指针是,C,语言中一个十分重要的概念,将在后面介绍。在这里,先来了解,C,语言中提供的两个专门用于指针和取地址运算符,如表,3-8,所示。,取内容和取地址的一般形式分别为:,变量,=*,指针变量,指针变量,=&,目标变量,取内容运算是将指针变量所指向的目标变量的值赋给左边的变量;取地址运算是将目标变量的地址赋给左边的变量。注意,指针变量中只能存放地址(也就是指针型数据),一般情况下不要将非指针类型的数据赋值给一个指针变量。,3.3.5 C51,的分支与循环程序结构,在,C51,的程序结构上可以把程序分为三类,即顺序、分支和循环结构。顺序结构是程序的基本结构,程序自上而下,从,main,()的函数开始一直到程序运行结束,程序只有一条路可走,没有其它的路径可以选择。顺序结构比较简单和便于理解,这里仅介绍分支结构和循环结构。,3.3.5.1,分支控制语句,实现分支控制的语句有:,if,语句和,switch,语句。,1. if,语句,if,语句是用来判定所给定的条件是否满足,根据判定结果决定执行两种操作之一。,if,语句的基本结构如下:,if (,表达式,) ,语句,括号中的表达式成立时,程序执行大括号内的语句,否则程序跳过大括号中的语句部分,而直接执行下面其它语句。,C51,语言提供,3,种形式的,if,语句:,(1),形式,1,if (,表达式,) ,语句,例如:,if (xy) max=x; min=y;,即如果,xy,,则,x,赋给,max,,,y,赋给,min,。如果,xy,不成立,则不执行大括号中的赋值运算。,(2),形式,2,if (,表达式,) ,语句,1; else ,语句,2;,例如:,if (xy),max=x; ,else min=y;,本形式相当于双分支选择结构。,(3),形式,3,if (,表达式,1) ,语句,1;,else if (,表达式,2) ,语句,2;,else if (,表达式,3) ,语句,3;,else ,语句,n;,例如:,if (x100) y=1;,else if (x50) y=2;,else if (x30) y=3;,else if (x20) y=4;,else y=5;,本形式相当于串行多分支选择结构。,在,if,语句中又含有一个或多个,if,语句,这称为,if,语句的嵌套。应当注意,if,与,else,的对应关系,,else,总是与它前面最近的一个,if,语句相对应。,2. switch,语句,if,语句只有两个分支可供选择,而,switch,语句是多分支选择语句。,switch,语句的一般形式如下:,switch (,表达式,1),case,常量表达式,1,:,语句,1,;,break,;,case,常量表达式,2,:,语句,2,;,break,;,case,常量表达式,n,:,语句,n,;,break,;,default,:,语句,n+1,;,上述,switch,语句的说明如下:,(,1,)每一个,case,的常量表达式必须是互不相同的,否则将出现混乱。,(,2,)各个,case,和,default,出现的次序,不影响程序执行的结果。,(,3,),switch,括号内的表达式的值与某,case,后面常量表达式的值相同时,就执行它后面的语句,遇到,break,语句则退出,switch,语句。若所有的,case,中的常量表达式的值都没有与,switch,语句表达式的值相匹配时,就执行,default,后面的语句。,(,4,)如果在,case,语句中遗忘了,break,语句,则程序执行了本行之后,不会按规定退出,switch,语句,而是将执行后续的,case,语句。在执行一个,case,分支后,使流程跳出,switch,结构,即中止,switch,语句的执行,可以用一个,break,语句完成。,switch,语句的最后一个分支可以不加,break,语句,结束后直接退出,switch,结构。,【,例,】,在单片机程序设计中,常用,switch,语句作为键盘中按键按下的判别,并根据按下键的键号跳向各自的分支处理程序。,input:,keynum,=,keyscan,( ),switch,(,keynum,),;break;,case 1:key1( );/*,如果按下键的键值为,1,,则执行函数,key1( )*/,case 2:key2( ); /*,如果按下键的键值为,2,,则执行函数,key2( )*/,case 3:key3( ); /*,如果按下键的键值为,3,,则执行函数,key3( )*/,case 4:key4( ); /*,如果按下键的键值为,4,,则执行函数,key4( )*/,default,:,goto,input,例子中的,keyscan,( ),是另行编写的一个键盘扫描函数,如果有键按下,该函数就会得到按下按键的键值,将键值赋予变量,keynum,。如果键值为,1,,则执行键值处理函数,key1( ),后返回;如果键值为,4,,则执行,key4( ),函数后返回。执行完一个键值处理函数后,则跳出,switch,语句,从而达到按下不同的按键来进行不同的键值处理的目的。,3.3.5.2,循环结构流程控制语句,许多的实用程序都包含有循环结构,熟练地掌握和运用循环结构的程序设计,是,C51,语言程序设计的基本要求。,实现循环结构的语句有以下三种:,while,语句、,do-while,语句和,for,语句,1,while,语句,while,语句的语法形式为:,while,(表达式),循环体语句;,表达式是,while,循环能否继续的条件,如果表达式为真,就重复执行循环体语句;反之,则终止循环体内的语句。,while,循环结构的特点在于,循环条件的测试在循环体的开头,要想执行重复操作,首先必须进行循环条件的测试,如条件不成立,则循环体内的重复操作一次也不能执行。,例如:,while,(,P1&0x80,),=0,),while,中的条件语句对,AT89S51,单片机的,P1,口,P1.7,进行测试,如果,P1.7,为低电平(,0,),则由于循环体无实际操作语句,故继续测试下去(等待),一旦,P1.7,的电平变高(,1,),则循环终止。,2,do-while,语句,do while,语句的语法形式为:,do,循环体语句;,while,(表达式);,do-while,语句的特点是先执行内嵌的循环体语句,再计算表达式,如果表达式的值为非,0,,则继续执行循环体语句,直到表达式的值为,0,时结束循环。,由,do-while,构成的循环与,while,循环十分相似,它们之间的重要区别是:,while,循环的控制出现在循环体之前,只有当,while,后面表达式的值非,0,时,才可能执行循环体,在,do-while,构成的循环中,总是先执行一次循环体,然后再求表达式的值,因此无论表达式的值是,0,还是非,0,,循环体至少要被执行一次。,和,while,循环一样,在,do-while,循环体中,要有能使,while,后表达式的值变为,0,的操作,否则,循环会无限制地进行下去。根据经验,do-while,循环用的并不多,大多数的循环用,while,来实现会直观。,【,例,】,实型数组,sample,存有,10,个采样值,编写程序段,要求返回其平均值(平均值滤波)。程序如下:,float,avg(float,*sample),float sum=0;,char n=0;,do,sum+=samplen,;,n+;, while,(,n10,),;,return(sum/10);,3,基于,for,语句的循环,在,3,种循环中,经常使用的是,for,语句构成的循环。它不仅可以用于循环次数已知的情况,,也可用于循环次数不确定而只给出循环条件的情况,它完全可以替代,while,语句。,for,循环的一般格式为:,for,(表达式,1,;表达式,2,;表达式,3,),循环体语句;,for,是,C51,的关键字,其后的括号中通常含有三个表达式,各表达式之间用“;”隔开。这三个表达式可以是任意形式的表达式,通常主要用于,for,循环的控制。紧跟在,for,(,)之后的循环体,在语法上要求是一条语句;若在循环体内需要多条语句,应该用大括号括起来组成复合语句。,for,的执行过程如下:,(,1,)计算“表达式,1”,,表达式,1,通常称为“初值设定表达式”。,(,2,)计算“表达式,2”,,表达式,2,通常称为“终值条件表达式”,若满足条件,转下一步,若不满足条件,则转步骤(,5,)。,(,3,)执行一次,for,循环体,.,(,4,)计算“表达式,3”,, “表达式,3”,通常称为“更新表达式”转向步骤,(,2,)。,(,5,)结束循环,执行,for,循环之后的语句。,下面对,for,语句的几个特例进行说明。,(,1,),for,语句中的小括号内的,3,个表达式全部为空。,例如:,for,(;),循环体语句;,在小括号内只有两个分号,无表达式,这意味这没有设初值,无判断条件,循环变量为增值,它的作用相当于,while,(,1,),这将导致一个无限循环。一般在编程时,需要无限循环时,可采用这种形式的,for,循环语句。,(,2,),for,语句的三个表达式中,表达式,1,缺省。,例如:,for,(;,i=100,;,i+,),sum=,sum,+ i;,即不对,i,设初值。,(,3,),for,语句的三个表达式中,表达式,2,缺省。,例如:,for,(,i=1,;,i+,),sum=,sum+i,;,即不判断循环条件,认为表达式始终为真,循环将无休止地进行下去。,(,4,),for,语句的三个表达式中,表达式,1,、表达式,3,省略。,例如:,for,(,;i=100;,),sum=,sum+i,;,i+,;,(,5,)没有循环体的,for,语句。,例如:,int,a=1000;,for,(,t=0,;,ta,;,t+,),;,本例的一个典型应用就是软件延时。、在程序的设计中,经常用到时间延迟,可用循环结构来实现,即循环执行指令,消磨一段已知的时间。,AT89S51,单片机指令的执行时间是靠一定数量的时钟周期来计时的,如果使用,12MHz,晶振,则,12,个时钟周期花费的时间为,1s,。,【,例,】,编写一个延时,1ms,程序。,void,delayms,( unsigned char,int,j),unsigned char i,;,while(j,-),for(i,=0;i125;i+),;,如果把上述程序段编译成汇编语言代码进行分析,用,for,进行的内部循环大约延时,8 ms,,但不是特别精确。不同的编译器会产生不同的延时,因此,i,的上限值,125,应根据实际情况进行补偿调整。,【,例,】,求,1+2+3+,+100,的累加和。,用,for,语句编写的程序如下:,#include ,#include ,main( ),int,nvar1,nsum,;,for(nvar1=0,nsum=1,;,nsum=100,;,nsum+),nVar1+=,ncount,;,
展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 管理文书 > 施工组织


copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!