第2章单片机C51语言基础2.1C51语言的基本知识2.2运算符与表达式2.3指针与绝对地址访问2.4本章小结2.5实训二发光二极管流水广告灯返回首页教学提示随着单片机开发技术的不断发展,目前已有越来越多的人从普遍使用汇编语言逐渐过渡到使用高级语言开发,其中又以C语言为主,市场上几种常见的单片机均有其C语言开发环境。应用于51系列单片机开发的C语言通常简称为C51语言。KeilC51是目前最流行的51系列单片机的C语言程序开发软件。本章重点介绍C51语言对标准ANSIC语言的扩展内容。深入理解并应用这些扩展内容是学习C51语言程序设计的关键。教学要求掌握C51语言的基本知识,特别是新增数据类型bit、sbit、sfr、sfr16的使用方法;理解C51语言中关于存储区域的划分;掌握C51语言中指针及绝对地址的使用方法;进一步熟悉KeilC51、ProteusISIS的使用方法。2.1C51语言的基本知识2.1.1标识符2.1.2常量2.1.3基本数据类型2.1.4存储区域与存储模式2.1.1标识符用来标识常量名、变量名、函数名等对象的有效字符序列称为标识符(Identifier)。合法的标识符由字母、数字和下划线组成,并且第一个字符必须为字母或下划线。在C51语言的标识符中,大、小写字母是严格区分的。对于标识符的长度(一个标识符允许的字符个数),一般取前8个字符,多余的字符将不被识别。C51语言的标识符可以分为3类:关键字、预定义标识符和自定义标识符。1.关键字关键字是C51语言规定的一批标识符,在源程序中代表固定的含义,不能另作它用。C51语言除了支持ANSI标准C语言中的关键字(见表2-1)外,还根据51系列单片机的结构特点扩展部分关键字,见表2-2。2.预定义标识符预定义标识符是指C51语言提供的系统函数的名字(如printf、scanf)和预编译处理命令(如define、include)等。C51语言语法允许用户把这类标识符另作它用,但将使这些标识符失去系统规定的原意。因此,为了避免误解,建议用户不要把预定义标识符另作它用。3.自定义标识符由用户根据需要定义的标识符,一般用来给变量、函数、数组或文件等命名。程序中使用的自定义标识符除要遵循标识符的命名规则外,还应做到“见名知意”,即选择具有相关含义的英文单词或汉语拼音,以增加程序的可读性。如果自定义标识符与关键字相同,程序在编译时将给出出错信息;如果自定义标识符与预定义标识符相同,系统并不报错。2.1.2常量在程序运行过程中其值始终不变的量称为常量。在C51语言中,可以使用整型常量、实型常量、字符型常量。整型常量又称为整数。在C51语言中,整数可以用十进制、八进制和十六进制形式来表示。但C51中数据的输出形式只有十进制和十六进制两种,并可在Watches对话框中进行切换,如图2.1所示。1.整型常量图2.1C51中数据输出形式选择(1)十进制数(2)八进制数(3)十六进制数在C51语言中,还可以用一个“特别指定”的标识符来代替一个常量,称为符号常量。定义了符号常量PI,就可以用下例语句计算半径为r的圆的面积S和周长L。S=PI*r*r;//在程序中引用符号常量PIL=2*PI*r;//在程序中引用符号常量PI符号常量通常用#define命令定义,如#definePI3.14159//定义符号常量PI=3.141592.实型常量(1)小数形式:由数字和小数点组成。例如,0.123、.123、123.、0.0等都是合法的实型常量。实型常量又称实数。在C51语言中,实数有两种表示形式,均采用十进制数,默认格式输出时最多只保留6位小数。(2)指数形式:小数形式的实数E[±]整数。例如,2.3026可以写成0.23026E1,或2.3026E0,或23.026E-1。3.字符型常量用单引号括起来的一个ASCII字符集中的可显示字符称为字符常量。例如,‘A’、‘a’、‘9’、‘#’、‘%’都是合法的字符常量。C51语言规定,所有字符常量都可作为整型常量来处理。字符常量在内存中占1Byte,存放的是字符的ASCII代码值。例如,下列程序片段的执行结果为z=16(或0x10)。unsignedcharx='A',y='a';unsignedz;z=(y-x)/2;2.1.3基本数据类型数据类型是指变量的内在存储方式,即存储变量所需的字节数以及变量的取值范围。C51语言中变量的基本数据类型见表2-3,其中bit、sbit、sfr、sfr16为C51语言新增的数据类型,可以更加有效地利用51系列单片机的内部资源。所谓变量,是指在程序运行过程中其值可以改变的量。变量应该先定义后使用,定义格式如下:数据类型变量标识符[=初值]变量定义通常放在函数的开头部分,但也可以放在函数的外部或复合语句的开头。以unsignedint为例,变量的定义方式主要有以下3种:unsignedintk;//定义一个变量unsignedinti,j,k;//定义多个变量unsignedinti=6,j;//定义变量的同时给变量赋初值当在一个表达式中出现不同数据类型的变量时,必须进行数据类型转换。C51语言中数据类型的转换有两种方式:自动类型转换和强制类型转换。(1)自动类型转换不同数据类型的变量在运算时,由编译系统自动将它们转换成同一数据类型,再进行运算。自动转换规则如下:bit→char→int→long→floatsigned→unsigned当赋值运算符左右两侧类型不一致时,编译系统会按上述规则,自动把右侧表达式的类型转换成左侧变量的类型,再赋值。(2)强制类型转换根据程序设计的需要,可以进行强制类型转换。强制类型转换是利用强制类型转换符将一个表达式强制转换成所需要的类型。其格式如下:(类型)表达式例如,(int)5.87=5。注意:无论是自动转换还是强制转换,都局限于某次运算,并不改变数据说明时对变量规定的数据类型。【例2.1】数据类型转换。在KeilμVision2的Watches窗口中可以观察程序运行的结果。下面重点介绍C51语言中新增的数据类型bit、sbit、sfr和sfr16。为方便讲解,图2.2给出了一个简单的、基于AT89C52的、用ProteusISIS绘制的单片机应用系统原理图。图2.2某单片机应用系统原理图1.bit在51系列单片机的内部RAM中,可以位寻址的单元主要有两大类:低128字节中的位寻址区(20H~2FH),高128字节中的可位寻址的SFR,有效的位地址共210个(其中位寻址区有128个,可位寻址的SFR中有82个),见表2-4。关键字bit可以定义存储于位寻址区中的位变量。位变量的值只能是0或1。bit型变量的定义方法如下:bitflag;//定义一个位变量flagbitflag=1;//定义一个位变量flag并赋初值1KeilC51编译器对关键字bit的使用有如下限制:(1)不能定义位指针。如bit*P;//非法定义,关键字bit不能定义位指针(2)不能定义位数组。如bitP[8];//非法定义,关键字bit不能定义位数组(3)用“#pragmadisable”说明的函数和用“usingn”明确指定工作寄存器组的函数,不能返回bit类型的值。【例2.2】基于图2.2所示的单片机应用系统,编写程序使发光二极管D0闪烁。2.sbit关键字sbit用于定义存储在可位寻址的SFR中的位变量,为了区别于bit型位变量,称用sbit定义的位变量为SFR位变量。SFR位变量的值只能是0或1。51系列单片机中SFR位变量的存储范围见表2-4。SFR位变量的定义通常有以下3种用法:(1)使用SFR的位地址:sbit位变量名=位地址(2)使用SFR的单元名称:sbit位变量名=SFR单元名称^变量位序号(3)使用SFR的单元地址:sbit位变量名=SFR单元地址^变量位序号在KeilμVision2中的ParallelPort1对话框和Memory对话框均可以观察程序运行的结果;如果将KeilμVision2生成的HEX文件装入图2.2中的AT89C51中,则可以在ProteusISIS中看到硬件仿真结果。例如,下列3种方式均可以定义P1口的P1.2引脚。sbitP1_2=0x92;//0x92是P1.2的位地址值sbitP1_2=P1^2;//P1.2的位序号为2sbitP1_2=0x90^2;//0x90是P1的单元地址【例2.3】基于图2.2所示的单片机应用系统,编写程序使发光二极管D0、D1、D2同时闪烁。3.sfr利用sfr型变量可以访问51系列单片机内部所有的8位SFR。51系列单片机内部共有21个8位的SFR,其中11个是可以位寻址的(见表2-4),10个是不可以位寻址的(见表2-5)。sfr型变量的定义方法如下:sfr变量名=某个SFR地址【例2.4】基于图2.2所示的单片机应用系统,编写程序使发光二极管D0、D1、D2同时闪烁。KeilC51编译器已经在相关的头文件中对51系列单片机内部的所有sfr型变量和sbit型变量进行了定义,在编写C51程序时可以直接引用,如本例中的“reg51.h”。因此,只要在程序的开头添加了#includereg51.h,对reg51.h中已经定义了的sfr型、sbit型变量,如无特殊需要则不必重新定义,直接引用即可。值得注意的是,在reg51.h中未给出4个I/O口(P0~P3)的引脚定义。4.sfr16与sfr类似,sfr16可以访问51系列单片机内部的16位特殊功能寄存器(如定时器T0和T1),在此不再赘述。2.1.4存储区域与存储模式51系列单片机应用系统的存储器结构如图2.4所示。图2.451单片机应用系统的存储器结构0000H←―→0FFFH1000H←――――――――――――→FFFFH片内EA1程序存储器(ROM)片外EA000H←―→1FH10H←―→2FH30H←―→7FH80H←――――→FFH片内工作寄存器组位寻址区用户RAM区SFR区0000H←―――――――――――――――――――――――――→FFFFH数据存储器(RAM)片外1.存储区域有了存储区域的概念后,变量的定义格式变为数据类型[存储区域]变量名称针对51系列单片机应用系统存储器的结构特点,KeilC51编译器把数据的存储区域分为6种:data、bdata、idata、xdata、pdata、code,见表2-6。在使用C51语言进行程序设计时,可以把每个变量明确地分配到某个存储区域中。由于对内部存储器的访问比对外部存储器的访问快许多,因此应当将频繁使用的变量存放在片内RAM中,而把较少使用的变量存放在片外RAM中。【例2.5】存储区域的使用。在使用存储区域时,还应该注意以下几点:(1)标准变量和用户自定义变量都可以存储在data区中,只要不超过data区范围即可。由于51系列单片机没有硬件报错机制,当设置在data区的内部堆栈溢出时,程序会莫名其妙地复位。为此,要根据需要声明足够大的堆栈空间以防止堆栈溢出。(4)程序存储区的数据是不可改变的,编译的时候要对程序存储区中的对象进行初始化;否则就会产生错误。(3)对pdata和xdata的操作是相似的。但是,对pdata区的寻址要比对xdata区的寻址快,因为对pdata区的寻址只需装入8位地址;而对xdata区的寻址需装入16位地址,所以要尽量把外部数据存储在pdata区中。(2)KeilC51编译器不允许在bdata区中声明float和double型的变量。2.存储模式存储模式用于决定没有明确指定存储类型的变量、函数参数等的默认存储区域。KeilC51编译器提供的存储模式共有3种:Small,Compact,Large。具体使用哪一种模式,可以在Target设置界面中的MemoryMode下拉列表框中进行选择。2.2运算符与表达式2.2.1算术运算符与算术表达式2.2.2赋值运算符与赋值表达式2.2.3关系运算符、逻辑运算符及其表达式2.2.4条件运算符与条件表达式2.2.5逗号运算符