单片机原理与c51编程课件3第三章C51程序设计.ppt

上传人:小飞机 文档编号:6041782 上传时间:2023-09-17 格式:PPT 页数:98 大小:669.50KB
返回 下载 相关 举报
单片机原理与c51编程课件3第三章C51程序设计.ppt_第1页
第1页 / 共98页
单片机原理与c51编程课件3第三章C51程序设计.ppt_第2页
第2页 / 共98页
单片机原理与c51编程课件3第三章C51程序设计.ppt_第3页
第3页 / 共98页
单片机原理与c51编程课件3第三章C51程序设计.ppt_第4页
第4页 / 共98页
单片机原理与c51编程课件3第三章C51程序设计.ppt_第5页
第5页 / 共98页
点击查看更多>>
资源描述

《单片机原理与c51编程课件3第三章C51程序设计.ppt》由会员分享,可在线阅读,更多相关《单片机原理与c51编程课件3第三章C51程序设计.ppt(98页珍藏版)》请在三一办公上搜索。

1、第章C51单片机编程语言,3.1 单片机编程语言概述3.2 C51程序设计基础3.3 函数、数组、指针的应用 3.4 C51程序设计,3.1 单片机编程语言概述,51单片机的编程语言可以是汇编语言,也可以是高级语言,如由C语言演变而成的C51语言等。汇编语言产生的目标代码短,占用的存储空间小,执行速度快,能充分发挥单片机的硬件功能。但对于复杂的应用来讲使用汇编语言编程复杂,程序的可读性和可移植性不强。高级语言产生的目标代码长,占用的存储空间大,执行速度慢。但这是相对于汇编语言来讲的,其实C语言在大多数情况下的机器代码生成效率和汇编语言相当,但可读性和可移植性却远远超过汇编语言,编程效率也大大高

2、于汇编语言。,如果应用系统的存储空间比较小,且对实时性的要求很高,则应选用汇编语言。如果系统的存储空间比较大,且对实时性的要求不是很高,则应选用C51语言。如果系统中有部分模块对实时性的要求很高,而其它模块对实时性的要求不是很高,则可以将两种语言结合,程序的主体部分使用C51编程,对实时性的要求高的模块用汇编语言编程,然后将汇编语言程序模块嵌入到C51语言程序当中。无论是高级语言还是汇编语言写的源程序都必须转换成目标程序,单片机才能执行。目前很多公司都将编辑器、汇编器、编译器、连接/定位器、符号转换程序做成了软件包,称为集成开发环境,如Keil uVision、MedWin等。,3.1 单片机

3、编程语言概述,使用C语言有以下的优点:C 语言具有结构化和模块化特点,便于阅读和维护。C 语言可移植性好,很多微控制器都支持C 编译器。功能化的代码能够很方便的从一个工程移植到另一个工程,从而减少了开发时间。C 语言编写的程序比汇编语言编写的程序更符合人们的思考习惯,开发者可以更专心的考虑算法,而不是考虑一些细节问题。这样可以减少编程出错的机率,从而提高开发效率,减少调试的时间。C 语言和微控制器是相对独立的,开发者不必知道处理器的具体内部结构和处理过程。当基于新型的微控制器开发程序时,可以很快上手,减少学习时间和程序开发时间。,3.2C51程序设计基础,3.2.1 C51扩展关键字,3.2.

4、1 C51扩展关键字,位运算符,例:unsigned char x=0 x55;unsigned char y=0 x37;unsigned char z;z=x则z=0 x2A,位运算符举例,3.2.2 C51数据类型,3.2.2 C51数据类型,3.2.3 常量及变量,在C51中变量定义的格式如下:存储种类 数据类型 存储器类型 变量名表;变量定义中可使用的存储种类(Storage Classes)有四种:自动(auto)外部(extern)静态(static)寄存器(register)变量定义时,如果省略存储种类选项,则该变量将为自动(auto)变量(默认)。,auto类型:在定义它的函

5、数体内部有效;进入定义它的函数体时动态分配内存,退出函数体时所占用的内存区域被释放(局部变量);变量的默认存储种类。register类型:作用域与寿命和auto型相同;编译器尽可能将该类变量放在CPU的寄存器中,以提高存储速度。通常选择访问频率较高的变量定义为该类型,以提高效率。,存储种类,extern类型可以在一个应用的多个程序文件中均有效;存放在内存的静态存储区。直到该程序结束,分配的内存才被释放(全局变量)。可在函数间传递信息,在函数内被修改时,修改值会传递给其它函数。static类型局部变量作用域与auto类相同;寿命与extern类相同。数据值在两次调用之间一直保持,占用的内存空间在

6、程序结束才释放。全局变量仅在定义它的程序文件内有效;寿命与extern类相同。作用范围受限的全局变量,仅在定义它的文件中有效。,存储种类,存储区域,8051系列微处理器采用了哈佛结构,即程序存储器和数据存储器是分离的。8051系列微处理器提供了三种不同类型的存储区域(memory areas):程序存储区(program memory)内部数据存储区(internal data memory)外部数据存储区(external data memory)这三种存储区域均从地址0开始编址,通过采用不同的寻址指令来解决地址重叠的问题。,存储区域的划分,存储器类型的变量声明举例,char data va

7、r1;char code text=ENTER PARAMETER:;unsigned long xdata array100;float idata x,y,z;unsigned int pdata dimension;unsigned char xdata vector1044;char bdata flags;,说明:声明变量时存储区修饰符和数据类型修饰符的位置可以互换,即char data x;和 data char x;是完全等效的。不过从兼容性考虑,建议使用前一种格式。,存储模式(memory models),如果在变量声明时未声明变量的存储器类型,则该变量的存储器类型,由程序的存

8、储模式来决定。小模式(small model):默认data区紧凑模式(compact model):默认pdata区大模式(large model):默认xdata区注意:除非应用在特殊的场合,否则SMALL存储模式可以提供最快和最有效的代码。,3.2.4 C51中的特殊数据类型,C51中有几种ANSI C所没有的特殊数据类型,这些数据类型是和存储区域和存储器类型的概念密切相关的。位变量可位寻址的对象特殊功能寄存器,位变量,位变量(Bit Types)是指用一个二进制位表示的变量。位数据类型可以用来说明变量,参数表,函数返回值等。位数据变量声明和基本的数据类型声明一样。所有的位变量都存储在内

9、部数据区的位寻址段中。因为该段只有16个字节长,所以在一个作用域内最多只能声明128个位变量。注意:由于位变量只能存储在内部数据存储区的位变量区内,因此只能使用data 和idata两种存储器类型修饰符,其它存储器类型是非法的。,例3.15 位变量的使用static bit done_flag=0;/*位变量*/bit testfunc(/*位函数返回类型*/bit flag1,/*位类型参数*/bit flag2)return(0);/*位类型返回值*/,特殊功能寄存器,8051系列的微控制器提供了一个独立的内存区,用来存放特殊功能寄存器(special function register,

10、SFR)。SFR用来在程序中控制定时器,计数器,串行I/O,端口I/O操作,以及外设的操作。SFR驻留在地址0X80到0XFF空间,可按字节寻址或按字寻址,某些寄存器还可以按位寻址。8051系列微控制器中SFR的个数和类型是变化的。C51没有预先定义SFR的名字,而是提供了许多8051兼容芯片的包含文件,这些文件对芯片的SFR进行了定义。CX51编译器用sfr,sfr16,sbit来进行SFR定义。,sfr:定义8位特殊功能寄存器sfr可以用来定义8051单片机的8位特殊功能寄存器。sfr占用一个字节内存单元,取值范围是0 255。SFR的声明和C变量的声明格式是一样的,只不过使用的修饰符不是

11、char 或int 而是sfr。例如:sfr P0=0 x80;/*Port-0,address 80h*/sfr P1=0 x90;/*Port-1,address 90h*/sfr P2=0 xA0;/*Port-2,address 0A0h*/sfr P3=0 xB0;/*Port-3,address 0B0h*/P0,P1,P2,P3是sfr声明的特殊功能寄存器的名称。特殊功能寄存器名称是一个合法的C标识符。等号后的地址必须是数值常量,不允许使用带运算符的表达式。,sfr16:定义16位特殊功能寄存器8051芯片可以将两个8位SFR作为一个16位寄存器来访问。条件是这两个SFR必须处在

12、相邻地址上,并且是低字节在高字节地址的前面。C51提供了sfr16数据类型来进行16位特殊功能寄存器的声明,声明时低字节地址被用来作为sfr16的地址。例如:sfr16 T2=0 xCC;/*Timer 2:T2L 0CCh,T2H 0CDh*/sfr16 RCAP2=0 xCA;/*RCAP2L 0CAh,RCAP2H 0CBh*/在这个例子中,T2和RCAP2被声明为16位的特殊功能寄存器。sfr16声明和sfr声明的规则相同。,sbit:定义特殊功能位sbit用来访问SFR中的可寻址位和其它可位寻址对象的可寻址位。在8051应用中,经常需要对SFR中的可寻址位(特殊功能位)进行独立访问。

13、可以用sbit数据类型来将SFR中的可寻址位声明为特殊功能位。sbit EA=0 xAF;上例中将EA定义为地址0XAF,对8051而言这是中断使能寄存器(IE)的中断许可位。,有三种方法来声明位地址:方法一:sfr_name int_constant,即SFR寄存器名整形常量。这种方法使用已经定义的sfr作为sbit的基地址。该SFR的地址必须能被8整除,符号后的表达式定义了可寻址位的位地址。位地址必须是0-7之间的数。sfr PSW=0 xD0;/声明寄存器名sfr IE=0 xA8;sbit OV=PSW 2;/声明特殊功能位sbit CY=PSW 7;sbit EA=IE 7;,方法二

14、:int_constant int_constant,即整形常量整形常量。这种方法使用整形常数作为基地址。该地址必须可以被8整除,符号后的表达式定义了可寻址位的位地址。位地址必须是0-7之间的数。sbit OV=0 xD0 2;sbit CY=0 xD0 7;sbit EA=0 xA8 7;方法三:int_constant用绝对位地址来声明sbit。sbit OV=0 xD2;sbit CY=0 xD7;sbit EA=0 xAF;,注意:sbit、bit和位域是三种不同的数据类型。使用sbit声明时,基对象必须可位寻址变量或者是可以位寻址的特殊功能寄存器。,绝对变量地址,开发者有时候希望把变

15、量存储在指定的地址单元中。可用 _at_ 关键词来将变量定位在一个绝对的内存地址单元。使用方法如下:数据类型 存储器类型 变量名 _at_ 变量所在绝对地址;在 _at_ 后面的绝对地址必须符合存储器类型的物理边界限制,即不超过存储区域的最大可寻址范围,该地址必须为常数。,绝对变量定位遵循以下约束:绝对变量不能初始化。类型为bit的函数和变量不能用绝对地址定位。绝对变量必须是全局变量,不能是局部变量。,例3.18struct linkstruct link idata*next;char code*test;struct link list idata _at_ 0 x40;/*list at

16、 idata 0 x40*/char xdata text256 _at_ 0 xE000;/*array at xdata 0 xE000*/int xdata i1 _at_ 0 x8000;/*i1 at xdata 0 x8000*/void main(void)list.next=(void*)0;i1=0 x1234;text 0=a;,有时需要在不同的模块之间调用变量,可使用下列的语句来在另一个源文件中访问上例中用 _at_修饰的变量。例3.19struct linkstruct link idata*next;char code*test;extern struct link

17、idata list;/*list at idata 0 x40*/extern char xdata text256;/*array at xdata 0 xE000*/extern int xdata i1;/*int at xdata 0 x8000*/,3.3.1 数组和指针,数组是一个由同种类型的变量组成的集合,它保存在连续的存储区域中,第一个元素保存在最低地址中,最末一个元素保存在最高地址中。数组可以是一维的也可以是多维的。数组的定义方式如下:数据类型 数组名常量1常量2常量n;这里的n是数组的维数。在定义时可以进行数组元素的初始化,初始化的值放在 中,每个元素值用逗号分开。如果是

18、对多维数组进行初始化,还可以使用 将元素维的大小分成组。,例如:char a23=0,1,2,3,4,5;char a23=0,1,2,3,4,5;以上两条语句的功能是相同的,执行完成后各元素的值如下:(设初始地址为ADDR),对于特殊的字符串数组,初始化时不仅可以采用每个元素分别赋值,还可以以字符串的形式赋值。如char array20=”hello world”;还可以使维数的大小为空,由初始化字串的长度决定数组的长度。如char array=”hello world”;上例中,数组array的长度为12个字节(字符串赋值时会增加一个0字符,作为字符串的结束标志)。,注意:C语言中的数组元

19、素的下标总是从0开始的。多维数组在内存中保存时,下标1变化最慢,下标n变化最快。,指针与地址的概念,程序中的变量经过编译处理后都对应着内存中的一个地址。编译器根据变量的类型,为其分配不同大小的内存单元来存放变量的数据。所谓指针,就是某个变量所占用存储单元的首地址。用来存放指针值的变量称为指针变量。指针变量的定义格式如下:类型说明符 存储器类型*指针变量名其中:“*”表示定义的是指针变量,类型说明符表示该指针变量指向的变量的类型。,C51的指针和标准C中的指针功能相同。但是由于8051体系结构的特殊性,C51提供了两种不同类型的指针:通用指针(Generic Pointers)具体指针(Memo

20、ry-specific Pointers)。通用指针的声明是和标准C中的指针声明是相同的,例如:char*s;/*指向字符类型的指针*/int*numptr;/*指向整型类型的指针*/long*state;/*指向长整型类型的指针*/,通用指针总是占用三个字节。第1个字节保存存储器类型编码值,第2个字节保存地址的高字节,第3 个字节保存地址的低字节。许多C51的库例程使用这种指针类型,通用指针类型可以访问任何存储区域内变量。,具体指针是在声明时指定了存储器类型的指针,是指向特定存储区域中的指针变量。char data*str;/*ptr to string in data*/int xdata

21、*numtab;/*ptr to int(s)in xdata*/long code*powtab;/*ptr to long(s)in code*/具体指针不需要保存存储器类型字节。具体指针可以保存在一个字节(idata,data,bdata,pdata类型指针)或2个字节(code 和xdata类型指针)中。具体指针可以用来访问8051声明的存储区内的变量。具体指针的效率高,但灵活性较差。,指定具体指针本身的存储类型的定义,例:char data*xdata ptr;/*ptr in xdata to data char*/int xdata*data numtab;/*numtab in

22、 data to xdata int*/long code*idata powtab;/*powtab in idata to code long*/注意:本例中变量定义时使用了两个存储器类型,*前的存储器类型修饰指针指向的数据,*后的存储器类型修饰指针本身,即指针所占据的存储区域类型。,注意:完成相同的功能,使用通用指针类型的代码与使用具体指针类型的代码相比,前者的运行速度要慢很多。原因:这是因为通用指针类型只有在程序运行时才能知道实际的变量存储区类型,因此编译器就不能对内存访问进行优化,从而只能生成可以访问任意存储区的通用代码。如果必须优先考虑程序的运行速度,那么只要有可能就应该使用具体指

23、针来替代通用指针,指针变量的引用,在利用指针变量进行间接访问时,必须使它指向一个确定的变量。指针变量只能存放地址,不能将一个非地址量赋给指针变量。C语言中有两个与指针相关的运算符:*:指针运算符,作用是通过指针变量间接访问它所指向的变量,来存取数据。&:取地址运算符,作用是取得变量所占用存储单元的首地址,即指针。,例子,数组的指针一个数组包含多个元素,每个数组元素都在内存中占用存储单元,都有相应的地址,并且这些存储单元都是连续的。指针变量可以指向数组和数组的任意元素。引用数组元素可以使用:下标法(即 运算符和对应元素的下标)指针法(即通过指向数组元素的指针找到所需元素)。,例如:float a

24、10;float*p1,*p2;p1=a;p2=上例运行完成后,指针p1和p2的值是相同的,均指向数组的首地址,即第一个元素。注意:指向数组的指针的值和指向数组首元素的指针的值是相同的,C语言规定,如果指针变量pointer已经指向数组中的一个元素,则pointer+1指向同一个数组元素的下一个元素,而不是pointer的值简单加1。实际增加的大小,由指针指向变量的类型决定。,函数的指针,在C语言中,指针变量除了能指向数据对象外,也可以指向函数。函数在编译时,编译器为每个函数分配一个入口地址,这个入口地址就称为函数的指针。函数的指针可以赋给函数指针变量,并能通过函数指针变量来调用它所指向的函数

25、。指向函数指针变量的定义格式如下:类型标识符(*指针变量名)(参量列表),int add(int a,int b)return a+b;int sub(int a,int b)return a-b;main()int(*pFunc)(int,int);/定义函数指针变量!int x,y;pFunc=add;/对函数指针变量赋值x=(*pFunc)(3,4);pFunc=sub;y=(*pFunc)(5,3);上例运行完成后,x=7,y=2。,说明:函数指针变量定义时,两侧的()是必须的,表示变量名先于*结合,是一个指针变量。然后再与后随的()结合,表示指针变量指向的对象是函数。指向的函数的指针

26、变量可以指向任何一个格式相同的函数的入口地址。C语言约定,函数名本身就是函数的入口地址。当函数指针变量指向函数时,即可用它来调用所指的函数。调用格式为(*指针变量名)(实参表),3.8 函数,在C语言中,函数是程序的基本组成单位。函数不仅可以实现程序的模块化,提高程序的可读性和可维护性,使程序设计变得简单和直观,还可以把程序中经常用到的一些计算或操作设计成通用的函数,以供随时调用。C程序由一个主函数main()和若干个其它函数组成。由主函数调用其它函数,其它函数也可以互相调用,同一个函数可以被调用多次。,函数定义函数定义的一般形式是:函数类型 函数名(形式参数列表)局部变量声明部分语句(有返回

27、值的要有return语句)函数类型定义了函数中返回语句(return)返回值的数据类型,返回值可以是任何一种有效的数据类型。如果没有使用函数类型说明符,函数返回值默认为整形值。,参数表是一个用逗号分隔的变量表,当函数被调用时这些变量接收调用参数的值。一个函数可以没有参数,这时函数参数表是空的。注意即使没有参数,括号仍是必须的。C51对函数的功能进行了扩展,函数定义的完整形式如下:函数类型 函数名(形式参数列表)small|compact|large reentrant interrupt m using n,函数返回值返回语句return用来回送一个数值给定义的函数,完成后从函数中退出。如果函

28、数没有返回值可以不使用return语句,或使用不带返回值的return语句。关于返回值有以下几条注意点:返回值是通过return语句返回的。返回值的类型如果与函数定义的类型不一致,那么返回值将被自动转化为函数定义的类型。如果没有return语句,函数会返回一个不确定的值。因此如果函数无需返回值,可以用”void”类型说明符指明函数无返回值。,形式参数与实际参数,与使用变量一样,在调用一个函数之前,必须对该函数进行声明,即先声明后调用。函数声明的一般形式是:函数类型 函数名(形式参数列表);函数定义时参数列表中的参数称为形式参数,简称形参,它们同函数内部的局部变量作用相同,形参的定义是在函数名后

29、的括号中。函数调用时所使用的替换参数,是实际参数,简称实参。定义的形参与调用函数的实参类型应该一致,书写顺序应该相同。使用函数的注意事项:1、函数定义时要同时声明其类型。2、调用函数前要先声明该函数。3、传给函数的参数值,其类型要与函数原定义一致。4、接受函数返回值的变量,其类型也要与函数一致。,在C语言中对不同类型的实际参数,有三种不同的参数传递方式:基本数据类型的参数传递当函数的参数是一般类型变量时,主调函数将实际参数的值传递给被调用函数中的形式参数,这种方式称为传值调用。这种参数传递方式下形式参数的值发生改变时,实际参数的值不会受到影响。因此值传递是一种单向传递。数组类型的参数传递当函数

30、的参数是数组类型的变量时,主调函数将实际参数数组的起始地址传递到被调用函数中的形式参数中,这种方式称为地址传递。这种参数传递方式下形式参数的值发生改变时,实际参数的值也会改变,因此地址传递是一种双向传递。指针类型的参数传递当函数的参数是指针类型的变量时,主调函数将实际参数的地址传递给被调用函数的形式参数,因此也是地址传递。这种参数传递方式下形式参数的值发生改变时,实际参数的值也会改变。,函数调用方式,函数调用格式:函数名(实参列表)在一个函数中调用另一函数需要具备下面的条件。被调用的函数必须是已经存在的函数,即已经声明或定义的函数。(库函数或自定义函数)如果是库函数,应该在程序开头用#incl

31、ude命令将有关库函数所需用到的信息包含到本程序中来。如果是用户定义的函数,一般还应该对被调用的函数做函数声明。,自定义函数的声明:函数原型格式为:extern 函数类型 函数名(形式参数列表);函数的声明是指明:函数的名字、类型以及形参的类型、个数和顺序P45例3-26、3-27函数的调用方式,如下:,函数调用方式,函数的调用方式:,调用函数的方式可以是以下几种:函数作为语句:把函数调用作为一个语句,不使用函数返回值,只是完成函数所定义的操作。函数作为表达式:函数调用出现在一个表达式中,使用函数的返回值进行相关运算。函数作为一个参数:函数调用作为另一个函数的实参,即使用函数的返回值做为另一个

32、函数的实参。函数递归调用:函数可以自我调用。如果函数内部一个语句调用了函数自己,则称为递归。,返回,内部函数和外部函数,C语言程序可以由多个函数组成,这些函数可以在同一个程序文件中,也可以在多个不同的程序文件中。根据这些函数的使用范围,可以把它们分为静态函数和外部函数。静态函数(内部函数)静态函数只能在定义它的文件中被调用,而不能在其他文件中的函数所调用。定义格式为:static 类型说明符 函数名(形式参数列表)外部函数(默认)外部函数可以在定义它的文件和其它文件中被调用。可以在函数定义和调用时使用extern说明是外部函数。关键字extern可省略。extern 类型说明符 函数名(形式参

33、数列表),reentrant-定义可重入函数,可重入函数可以在同一个时刻由多个进程共享。当一个进程正在执行一个可重入函数,另一个进程可以中断该进程,并可以开始执行同一个可重入函数,而不影响函数的运行结果。ANSI C 语言中,调用函数时会把函数的参数和函数中使用的局部变量入栈。因为8051 内部堆栈空间的限制,为了提高效率,C51 没有提供这种堆栈,而是提供一种压缩栈。每个函数有一个给定存储空间,用于存放局部变量。函数中的每个变量都存放在这个空间的固定位置。当递归调用该过程时会导致变量被覆盖,所以通常情况下CX51中的函数是不能重入的。,为此必须使用reentrant函数属性来声明函数是可重入

34、的。格式如下:函数类型 函数名(形式参数列表)reentrant与不可重入函数的参数传递和局部变量的存储分配方法不同,C51编译器为再入函数生成一个模拟栈,通过这个模拟栈来完成参数传递和存放局部变量。这样每次函数调用时的局部变量都会被单独保存,再入函数一般占用较大的内存空间,运行起来也比较慢,并且不允许传递bit 类型的变量,也不能定义局部位变量。可重入函数经常在实时应用系统中应用,也可在中断函数和非中断函数同时调用同一个函数时使用。,reentrant修饰符-定义可重入函数,interrupt m修饰符-定义中断函数,8051的标准中断有5种,某些8051兼容类型可以有更多的中断,C51最大

35、支持32个中断。中断函数的定义:函数类型 函数名(形式参数列表)interrupt m注意:仅能在函数定义时使用interrupt函数属性,不能在函数声明时使用interrupt函数属性。它将函数定义为中断处理函数。中断属性带一个m为0-31的整形参数,用来表示中断处理函数所对应的中断号,该参数不能是带运算符的表达式。对应的中断m=05,其他预留。情况如下:0外部中断01定时/计数器02-外部中断13-定时/计数器14-串行口5-定时/计数器2,编写8051单片机中断函数时应遵循以下规则:,(1)中断函数不能进行参数传递,如果中断函数中包含任何参数声明都将导致编译出错。(2)中断函数没有返回值

36、,如果企图定义一个返回值将得到不正确的结果。(3)在任何情况下都不能直接调用中断函数,否则会产生编译错误。(4)如果中断函数中用到浮点运算,必须保存浮点寄存器的状态,当没有其他程序执行浮点运算时可以不保存。(5)如果在中断函数中调用了其它函数,则被调用函数所使用的寄存器组必须与中断函数相同。(6)C51编译器从绝对地址8n3处产生一个中断向量,其中n为中断号。该向量包含一个到中断函数入口地址的绝对跳转。(7)中断函数一般放在文件的尾部,并且禁止使用extern存储类型说明。防止其他程序调用。,中断服务函数与寄存器组定义 C51编译器支持在C语言源程序中直接编写8051单片机的中断服务函数程序。

37、定义中断服务函数的一般形式为:1函数类型 函数名(形式参数表)interrupt n using n 2关键字using对函数目标代码的影响如下:在函数的入口处将当前工作寄存器组保护到堆栈中;指定的工作寄存器内容不会改变;函数返回之前将被保护的工作寄存器组从堆栈中恢复。3关键字interrupt也不允许用于外部函数,它对中断函数目标代码的影响如下:在进入中断函数时,特殊功能寄存器ACC、B、DPH、DPL、PSW将被保存入栈;如果不使用寄存器组切换,则将中断函数中所用到的全部工作寄存器都入栈;函数返回之前,所有的寄存器内容出栈;中断函数由8051单片机指令RETI结束。,可使用using函数说

38、明属性来规定函数所使用的寄存器组。格式如下:函数类型 函数名(形式参数列表)using n using属性使用一个0-3的整形参数,这个参数表示使用的寄存器组的编号,这个参数不能使用带运算符的表达式。using属性只能在函数定义中使用,不能在函数原型声明中使用。使用using属性的函数将自动完成以下操作:进入函数前,将当前使用的寄存器组的标号保存在堆栈中。更改PSW的寄存器组选择位,选择设定的寄存器组作为当前的寄存器组。函数退出时,将寄存器组恢复成进入函数前的寄存器组。,using n修饰符-定义寄存器组,n=0-3,using n修饰符-定义寄存器组,n=0-3,规定函数使用的寄存器组805

39、1单片机的内部存储器的低32个字节被划分成4个寄存器组,每个寄存器组8个寄存器。寄存器组可以通过PSW中的两个位进行选择,任何时刻仅有一个寄存器组处于工作状态,该寄存器组称为当前寄存器组。寄存器组切换在处理中断和使用实时操作系统时很有用。通常在调用函数时,须要将当前寄存器组的值保存在堆栈中,并在退出函数时将保存在堆栈中的值恢复到寄存器组中去。入栈和出栈操作均需要2个指令周期,保存和恢复8个寄存器的值,共需32个周期。通过寄存器组切换来保护寄存器组中的数据,省去堆栈操作,从而可以提高程序的运行速度。为中断程序指定工作寄存器组的缺点是所有被中断调用的函数都必须使用同一个寄存器组,否则参数传递会发生

40、错误。,中断函数应遵循以下规则:中断函数不能进行参数传递。中断函数没有返回值。不能在其它函数中直接调用中断函数如果在中断中调用了其他函数,必须保证这些函数和中断函数使用了相同的寄存器组,并且这些函数应为可重入函数。C51编译器从绝地址8m+3产生一个中断向量,其中m为中断号。该向量包含一个到中断函数入口地址的绝对跳转。,3.4 程序设计的三种基本结构,计算机程序是由若干条有序的语句组成的。在程序执行过程中一般有以下几种情况:在程序执行过程中,程序按语句的顺序逐条执行,这称为的顺序结构。在程序执行过程中,根据特定的条件选择执行某些语句,即程序执行的顺序根据条件来选择,这称为选择结构。在程序执行过

41、程中,根据某个条件是否成立重复执行一段程序,直到该条件不成立为止,即程序的执行顺序在某处循环,这称为循环结构。程序是由顺序、选择、循环这三种结构构成的复杂组合。,C语言的语句用来向计算机系统发出操作指令,C语言中语句是以分号为结束标志的。C语言中语句可分为以下几类:控制语句:控制程序运行的语句。空语句:只有一个分号的语句,称为空语句,它不进行任何操作。表达式语句:表达式后面加上分号就构成一个表达式语句。函数调用语句也是表达式语句,由函数调用加分号构成。复合语句:可以把多个语句用括起来,就成为复合语句,多用于选择或循环结构中。注释语句:在C51中,由/*、*/符号对包含的内容以及双斜杠/后面的内

42、容均表示注释语句。编程人员使用注释语句来解释程序的功能,标注修改时间等。,顺序结构顺序结构是从前往后依次执行语句。从整体上看所有程序都是顺序结构,只不过中间某些部分是由选择结构或循环结构构成,选择结构或循环结构部分执行完成后,程序重新按顺序结构向下执行。选择结构选择结构的基本特点是程序由多路分支构成,在程序的一次执行中根据指定的条件,选择执行其中的一条分支,而其他分支上的语句被直接跳过。循环结构循环结构是根据某个或某些条件是否成立,来重复运行一段相同的程序。,C语言中可由if语句和switch语句构成选择结构。if语句有3种基本形式:if(表达式)语句if(表达式)语句1else 语句2if(

43、表达式1)语句1else if(表达式2)语句2else if(表达式3)语句3else 语句n在选择结构中,else默认与最靠近它的if配对,可用改变配对。if语句中,如果需要执行的语句不止一条,可用组成复合语句。,switch语句专门用来处理多路分支的情形,形式如下:switch(表达式)case 常量表达式1:语句1:break;case 常量表达式2:语句2:break;case 常量表达式n:语句n:break;default:语句n+1;break;,对switch语句需要注意:case分支中的常量表达式的值必须是整形、字符型或枚举类型,不能使用条件运算符。break语句用于跳出s

44、witch结构。若case分支中未使用break语句,则程序将继续执行下一个case分支中的语句直至遇到break语句或整个switch语句结束。这可以用于多个分支需要执行相同的语句进行处理的情况。,for循环语句的一般格式为:for(表达式1;表达式2;表达式3)循环体语句for循环语句的执行过程为:求解表达式1。求解表达式2。若其值为真,则执行循环体;若其值为假,则循环语句结束,执行后续语句。求解表达式3。并转到第2步继续执行,直至条件为假时结束循环。注意:若第一次求解表达式2,其值就不成立,则循环体将一次都不执行。,while循环while语句格式为:while(表达式)循环体语句whi

45、le语句先求解循环条件表达式的值。如果为真,则执行循环体;否则跳出循环,执行后续操作。注意:一般来说在循环体中应该有使循环最终能结束的语句。如果表达式初始值为假,则循环体将一次都不执行。,dowhile循环dowhile语句格式为:do 循环体语句while(表达式);dowhile循环是先执行循环体一次,再判断表达式的值,若为真,则继续执行循环,否则退出循环。注意:dowhile语句至少执行循环体一次,goto语句goto语句的格式:goto 语句标号;goto语句是无条件转移语句,它将程序的运行转移 到指定的标号处。注意:goto语句使程序的转移控制变得非常灵活,但是也可能破坏程序良好结构

46、,因此应小心使用。,break语句在循环语句中,break语句的作用是在循环体中测试到指定条件为真时,其控制程序立即跳出当前循环结构,转而执行循环语句的后续语句。continue语句continue语句只能用于循环结构中,作用是结束本次循环。一旦执行了continue语句,程序就跳过循环体中位于该语句后的所有语句,提前结束本轮循环并开始下一轮循环。,3.10 预处理功能,预处理功能包括宏定义、文件包含和条件编译3个主要部分。预处理命令不同于C语言语句,具有以下特点:预处理命令以“#”开头,后面不加“;”。预处理命令在编译前执行,编译是对预处理的结果进行的,如词法、语法分析等。多数预处理命令习惯

47、上放在文件的开头。宏定义可以嵌套,即宏定义时,可以引用已定义的宏名,1.宏定义命令,使用#define关键字,作用是用宏符号名(标识符)来替代常数、字串和带参数的宏。宏符号名一般采用大写形式。分为两类:简单宏定义带参数的宏定义,简单宏定义的格式如下:#define 宏符号名 常量表达式当程序中出现宏符号名引用时,编译器用宏定义中的常量表达式来替代该宏符号。宏符号名一般采用大写形式,宏定义行的末尾不要加分号。可以使用以下格式结束宏符号名的定义:#undef 宏符号名宏符号名在宏定义开始到结束宏定义为止的这段时间内有效。如果没有结束宏定义命令,则在文件结束前宏定义均有效。,带参数的宏定义格式如下:

48、#define 宏符号名(参数表)宏体参数表由一个或多个参数组成,参数之间使用逗号分隔。宏体中包含参数表中所指定的参数,它可以是由若干个语句组成。替换带参数的宏时,不仅用宏体替换宏符号名,而且用实参替换形参。注意:使用带参数的宏定义时,宏符号名与左括号间不能出现空格符,否则空格符将作为宏体的一部分。由于运算符的优先级的影响,经常需要给宏体中各个参数加括号。,2.文件包含,包含文件的含义是在一个程序文件中包含其它文件的内容。用文件包含命令可以实现文件包含功能,命令格式如下:#include 或#include“文件名”其中,表示引用文件的目录在包含目录下;“”表示引用的文件在当前目录下。文件名可

49、以是绝对文件名,也可以是相对文件名(不包含目录信息)。文件名必须带文件后缀名(扩展名)。,一般情况下对C语言程序进行编译时,所有的程序行都参加编译,但是有时希望对其中一部分内容只在满足一定条件时才进行编译,这就是所谓的条件编译。条件编译可以选择不同的编译范围,从而产生不同的代码。条件编译命令格式:#if 标识符 程序段1#else 程序段2#endif,3.条件编译,如果表达式成立,则编译#if 下的程序,否则编译#else下的程序,#endif为结束条件表达式编译。#ifdef 宏名;如果宏名称已被定义过,则编译以下的程序。#ifndef 宏名;如果宏名称未被定义过,则编译以下的程序。条件表

50、达式编译通常用来调试,保留程序(但不编译),或者有两种状况而须做不同处理的程序编写时使用。,3.条件编译,使用C51时的注意事项,C51编译器能对C 程序源代码进行处理,产生高度优化的代码。注意下面一些问题,可以获取性能更好的代码。采用短变量。避免使用浮点运算。使用位变量。使用常量宏定义和枚举类型。用局部变量代替全局变量。尽量使用内部数据存储区使用库函数。对于小段代码,使用宏替代函数。利用条件编译简化外部变量的声明和定义使用合适的存储器模式,C51程序设计举例,例1:彩灯控制程序,要求P1口控制8个灯按一定要求实现彩灯的控制。线路原理图为:,方法一:直接对特殊寄存器P1进行操作,实现彩灯逐个点

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 生活休闲 > 在线阅读


备案号:宁ICP备20000045号-2

经营许可证:宁B2-20210002

宁公网安备 64010402000987号