




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第三章Cx51数据与运算
第三章Cx51数据与运算
3.1数据与数据类型3.2常量与变量3.3Cx51数据的存储结构3.4SFR及其Cx51定义3.5并行接口及其Cx51定义3.6位变量及其Cx51定义3.7Cx51的运算规则3.1数据与数据类型
KeilC有ANSIC的所有标准数据类型,除此之外为了更加有利地利用8051的结构,还加入了一些特殊的数据类型。除了这些标准数据类型外,编译器还支持一种位数据类型,一个位变量存在于内部RAM的可位寻址区中,可像操作其它变量那样对位变量进行操作,而位数组和位指针是违法的。数据类型大小char/unsignedchar8bitint/unsignedint16bitlong/unsigned
long32bitfloat/double32bit一般指针genericpointer24bit
Cx51实际上不支持双精度浮点数double,即便你定义了double,也是按照float处理的。1、基本数据类型数组(array)结构体(struct)联合体(union)枚举(enum)2、构造数据类型3、指针类型4、空类型3.2常量与变量
C语言中的数据有常量和变量之分。
常量—在程序运行的过程中,其值始终不能改变的量。
变量—在程序运行的过程中其值可以改变的量。
常量和变量一样可以有不同的数据类型。可以用一个标识符const来声明一个量是常量。unsignedchardatasystem_status=0;unsignedintdataunit_id[2];constchardatasamp_string[16];floatdataoutport_value;
我们还要经常用到使用预编译语句#define定义的符号常量:#defineCONST 100#defineNULL 0#defineTRUE 1#defineFALSE 0
符号常量和变量的区别在于,符号常量的值在其作用域(例如所在的函数)中不能改变,也不能用等号赋值,它仅仅是代表某个固定常数的一个符号而已。
习惯上符号常量通常用大写,而变量着用小写,注意养成良好的编程习惯。1、位变量
位变量(bit)的类型是位,其值可以是1(true)或者0(false)。这些位变量定义在内部RAM的可位寻址区。2、字符变量字符变量(char)的类型是一个字节共8位,是带符号的单字节数,其值范围为-128~+127。无符号字符变量(unsignedchar)也是一个字节,但是不带符号,其值范围为0~+255。3、整型变量整型变量(int)的类型是双字节共16位,其取值范围是-32768~。无符号整型变量(unsignedint)为双字节但不带符号,其值范围为0~65535。KeilCx51中整型变量(int)的字节存放次序:高8位存放在低地址,低8位则存放在高地址。例如位于地址0x1100的整数0x1234:低地址0x1100存放0x12,高地址0x1101存放0x34。4、浮点变量
浮点变量(float)的类型是4字节共32位,其存放格式为:地址+0+1+2+3内容SEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMMKeilCx51中浮点变量(float)采用IEEE-754标准,由1位符号、8位指数和23位尾数(最高位始终为1无需保存)组成,具有24位精度
浮点变量(float)的字节次序和编译器及其版本有关。
KeilC编译器能从你的程序源代码中产生高度优化的代码,但你可以帮助编译器产生更好的代码。下面将讨论这方面的一些问题。5、使用KeilC时应做的和应该避免的
一个提高代码效率的最基本的方式就是减小变量的长度。使用C编程时,我们都习惯于对循环控制变量使用int类型,这对8位的单片机来说是一种极大的浪费。你应该仔细考虑你所声明的变量值可能的范围,然后选择合适的变量类型。很明显,经常使用的变量应该是unsignedchar,只占用一个字节:(1)采用短变量unsignedchardata
n;
为什么要使用无符号类型呢?原因是8051不支持符号运算,程序中也不要使用含有带符号变量的外部代码。除了根据变量长度来选择变量类型之外,你还要考虑变量是否会用于负数的场合,如果你的程序中可以不需要负数,那么把变量都定义成无符号类型的变量:(2)使用无符号类型
unsignedintdata
n1,n2;
在8位操作系统上使用32位浮点数是得不偿失的。你可以这样做,但会浪费大量的时间。所以当你要在系统中使用浮点数的时候,你要问问自己这是否一定需要。可以通过提高数值数量级和使用整型运算来消除浮点指针。处理int和long比处理double和float要方便得多,你的代码执行起来会更快,也不用连接处理浮点指针的模块。如果你一定要采用浮点指针的话,你应该采用西门子80517和达拉斯半导体公司的80320,这些已经对数处理进行过优化的单片机。(3)避免使用浮点数(4)使用位变量对于某些标志位应使用位变量而不是unsignedchar,这将节省你的内存。你不用多浪费7位存储区,而且位变量在RAM中访问他们只需要一个处理周期。
把变量定义成局部变量比全局变量更有效率,编译器为局部变量在内部存储区中分配存储空间,而为全局变量在外部存储区中分配存储空间,这会降低你的访问速度。另一个避免使用全局变量的原因是你必须在你系统的处理过程中调节使用全局变量,因为在中断系统和多任务系统中,不止一个过程会使用全局变量。(5)用局部变量代替全局变量
局部变量和全局变量可被定义在你想要的存储区中,根据先前的讨论,当你把经常使用的变量放在内部RAM中时,可使你的程序的速度得到提高。除此之外,你还缩短了你的代码,因为外部存储区寻址的指令相对要麻烦一些。考虑到存储速度,按下面的顺序使用存储器:data、idata、pdata、xdata,但要记得留出足够的堆栈空间。(6)为变量分配内部存储区3.3Cx51数据的存储结构
因为KeilCx51是面向8051系列单片机及其硬件控制系统的开发工具,所以在讨论KeilCx51的数据类型的时候,必须同时提及它的存储类型以及它与8051单片机存储器结构的关系。它定义的任何数据类型必须以一定的存储类型的方式定位在8051的某一存储器中,否则便没有任何的实际意义。1、通用寄存器区(00~1FH)
通用寄存器区由4个寄存器组构成:0组(00~07H)、
1组(08~0FH)、
2组(10~17H)、
3组(18~1FH)。
可以通过PSW中RS0和RS1的不同取值来选择不同的寄存器组。在函数定义中则可以使用关键词using来指明寄存器号。
上电复位后默认使用寄存器组0。当其它组不用时,它们都可以当作普通的RAM使用。2、可位寻址区(20~2FH)
这个区共16个字节128位,分别占有位地址0~127;另外内部地址尾数为8或者0的SFR共16字节128位,分别占有位地址128~255。
内部低128字节的地址30~7FH作为用户存取临时数据区。许多单片机的高128字节除了直接寻址的SFR外还有128字节间接寻址区RAM,它们也作为用户区。通常堆栈区就设在这里,堆栈指针SP可以设置这个区域。3、用户(RAM)区
Keil允许使用者指定程序变量的存储区,这使使用者可以控制存储区的使用。编译器可识别以下存储区:存储区描述dataRAM的低128个字节,可在一个周期内直接寻址bdatadata区的16个字节的可位寻址区idataRAM区的高128个字节,必须采用间接寻址pdata外部存储区的256个字节,一般是片外RAM第一页空间;通过P0口的地址对其寻址,使用指令MOVX@Ri,需要两个指令周期xdata外部存储区,使用DPTR寻址code程序存储区,使用PC或DPTR寻址4、存储类型unsignedchardatasystem_status=0;unsignedintdataunit_id[2];chardatainp_string[16];floatdataoutp_value;(1)data区
对data区的寻址是最快的,所以应该把使用频率高的变量放在data区。由于空间有限,必须注意使用data区;除了包含程序变量外,还包含了堆栈和寄存器组在data区的声明。
标准变量和用户自定义变量都可存储在data区中,只要不超过data区的范围。因为Cx51使用默认的寄存器组来传递参数,1个工作寄存器组就是8个单元字节。你至少失去了8个字节。另外要定义足够大的堆栈空间。当你的内部堆栈溢出的时候,你的程序会莫名其妙地复位,实际原因是8051系列微处理器没有硬件报错机制,堆栈溢出只能以这种方式表示出来。(2)bdata区
可以在data区的位寻址区定义变量,这个变量的每个位就可进行位寻址。这对状态寄存器来说是十分有用的,因为它需要单独地使用变量的每一位。不一定要用位变量名来引用位变量,下面是一些在bdata段中声明变量和使用位变量的例子:unsignedcharbdatastatus_byte;unsignedintbdatastatus_word;unsignedlongbdatastatus_dword;sbitstat_flag=status_byte^4;if(status_word^15){…}stat_flag=1;
idata区也可存放使用比较频繁的变量,使用寄存器作为指针进行寻址。在寄存器中设置8位地址进行间接寻址,和外部存储器寻址比,它的指令执行周期和代码长度都比较短:(3)idata区unsignedcharidatasystem_status=0;unsignedintidataunit_id[2];charidatainp_string[16];floatidataoutp_value;
在这两个区声明变量和在其它段的语法是一样的。pdata段只有256个字节,而xdata段可达65536个字节。下面是一些例子:(4)pdata和xdata区unsignedcharxdatasystem_status=0;unsignedintpdataunit_id[2];charxdatainp_string[16];floatpdataoutp_value;
对pdata和xdata的操作是相似的,对pdata段寻址比对xdata段寻址要快,因为对pdata段寻址只需要装入8位地址,而对xdata段寻址需装入16位地址,所以尽量把外部数据存储在pdata段中。对pdata和xdata寻址要使用MOVX指令,需要两个处理周期。inp_byte=XBYTE[0x8500]; //从8500H读字节inp_word=XWORD[0x4000];//从4000H读字c=*((charxdata*)0x0000); //从0000读字节XBYTE[0x7500]=out_val; //写字节到7500H
在外部地址段中,除了包含存储器地址外,还包含I/O器件的地址,对外部器件寻址可通过指针或Cx51提供的宏。建议使用宏对外部器件进行寻址,因为这样更有可读性,宏定义使得存储段看上去像char和int类型的数组。下面是一些绝对寄存器寻址的例子:unsignedint
code
unit_id[2]=1234;unsignedchar
codeNum[]={
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10,0x11,0x12,0x13,0x14,0x15};(5)code区
代码区的数据是不可改变的,8051的代码段不可重写。一般代码段中可存放数据表、跳转向量和状态表,对code段的访问和对xdata段的访问的时间是一样的。代码段中的对象在编译的时候初始化,否则你就得不到你想要的值。下面是代码段的声明例子:5、存储器模式
Cx51提供了3种存储器模式来存储变量过程参数和分配再入函数堆栈:存储模式说明small参数及局部变量放入data区,默认存储类型是data,栈空间位于片内idata(片内RAM)compact参数及局部变量放入pdata区,默认存储类型是pdata(片外RAM);栈空间位于片内idata(片内RAM)large参数及局部变量放入xdata区,默认存储类型是xdata,模拟堆栈位于片外xdata(片外RAM)
一般来说如果系统所需要的内存数小于内部RAM数时,都应以small模式进行编译。在这种模式下,data段是所有内部变量和全局变量的默认存储段,所有参数传递都发生在data段中。
这种模式的优势就是数据的存取速度很快,但只有1个字节寻址的存储空间供你使用(总共有128,但至少有8个字节被寄存器组使用)。(1)small模式。
如果你的系统有256字节或更少的外部RAM,你可以使用compact模式,这样一来,如果不加说明,这种模式将扩充你能够使用的RAM数量。对xdata段以外的数据存储仍然是很快的,变量的参数传递将在内部RAM中进行,这样存储速度会比较快。对pdata段的数据的寻址是通过R0和R1进行间接寻址的,比使用DPTR要快一些。(2)compact模式
在large模式中所有变量的默认存储区是xdata段,KeilC尽量使用内部寄存器组进行参数传递。在寄存器组中可以传递参数的数量和compact模式一样。对xdata段数据的访问是最慢的,所以要仔细考虑变量应存储的位置,使数据的存储速度得到优化。(3)large模式3.4特殊功能寄存器SFR及其Cx51定义
特殊功能寄存器用关键词sfr来定义,sfr16用来定义16位的特殊功能寄存器,如DPTR。通过名字或地址来引用特殊功能寄存器地址必须高于80H。可位寻址的字节地址的位变量定义用关键字sbit。对于大多数8051成员,Keil提供了一个包含了所有特殊功能寄存器和它们的位的定义的头文件,通过包含头文件可以很容易地进行新的扩展。sfrSCON =0x98; //定义SCONsbitSM0 =0x9F; //定义SCON的各位sbitSM1 =0x9E;sbitSM2 =0x9D;sbitREN =0x9C;sbitTB8 =0x9B;sbitRB8 =0x9A;sbitTI =0x99;sbitRI =0x98;
注意,用sfr16定义的16位特殊功能寄存器的字节顺序,前面的为低字节,后面的为高字节,这个和8x86中的顺序相同,但是int的高低字节顺序却正好相反。(P64、P65)3.5并行接口及其Cx51定义
8051单片机具有P0、P1、P2和P3四个端口,许多增强型单片机还有P4端口。没有针对这些端口的专用IO操作指令,8051对IO口的操作是通过设置其输出锁存器SFR来实现的。
部分针对IO口的读取指令是读取端口锁存器,部分是读取端口的输入状态,通常那些针对端口的读—修改—写指令读取的是端口锁存器(对应SFR的地址单元)而不是引脚状态,这样可以防止因外部环境不稳定而引起的误操作。
P0-3端口的SFR地址的尾数不是0就是8,所以它们也是可以位寻址的SFR。端口在Cx51中可以直接用sfr来定义:sfrP0 =0x80; //定义IO口P0sfrP1 =0x90; //定义IO口P1sfrP2 =0xA0; //定义IO口P2sfrP3 =0xB0; //定义IO口P3
端口中的每一个IO口线还可以用sbit来定义其每一个位:sbit
P00 =P0^0; //定义IO线P0.0sbit
P01 =P0^1; //定义IO线P0.1sbit
P02 =P0^2; //定义IO线P0.2sbit
P03 =P0^3; //定义IO线P0.3sbit
P04 =P0^4; //定义IO线P0.4sbit
P05 =P0^5; //定义IO线P0.5sbit
P06 =P0^6; //定义IO线P0.6sbit
P07 =P0^7; //定义IO线P0.73.6位变量及其Cx51定义
位变量是Cx51中针对其硬件特点引入的一种新变量,它并不是标准C语言的变量。
内部data区地址20~2FH共128位占有位地址0-127,而地址尾数为0或者8的SFR共128位占有位地址128-255。
使用C51编程时,定义了位变量后,就可以用定义了的变量来表示MCS-51的位寻址单元。位变量的C51定义的一般语法格式如下:
位类型标识符(bit)位变量名;
这里,bit是一个数据类型例如:bitdirection_bit;/*把direction_bit定义为位变量*/bitlook_pointer;/*把look_pointer定义为位变量*/
(1)位变量C51定义。(2)函数可包含类型为“bit”的参数,也可以将其作为返回值。例如:
bitfunc(bitb0,bitb1) /*变量b0,b1作为函数的参数*/{return(b1); /*变量b1作为函数的返回值*/}
注意,使用禁止中断(#pragmadisable)或包含明确的寄存器组切换(usingn)的,函数不能返回位值。否则编辑器将会给出一个错误信息。位变量不能定义成一个指针,如不能定义:
bit*bit_pointer。(错)不存在位数组,如不能定义:bitb_array[]。(错)
(3)对位变量定义的限制。可位寻址字节变量的某一个位使用关键词sbit定义;bdata区(片内RAM单元20-2F)的独立位变量则用关键词bit定义:
关键词sbit和bit对位变量定义仅仅是形式上的区别,在实际操作中式完全一样的。例1先定义变量的数据类型和存储类型(P69):bdataintibase; /*定义ibase为bdata整型变量,16位*/bdatacharbary[4]; /*bary[4]定义为bdata字符型数组*/然后可使用"sbit"定义可独立寻址访问的对象位:sbitmybit0=ibase^0; /*mybit0定义为ibase的第0位*/sbitmybit15=ibase^15; /*mybit0定义为ibase的第15位*/sbitAry07=bary[0]^7; /*Ary07定义为abry[0]的第7位*/sbitAry37=bary[3]^7; /*Ary37定义为abry[3]的第7位*/
对象ibase和bary也可以字节寻址:ary37=0; /*bary[3]的第7位赋值为0*/bary[3]='a'; /*字节寻址,bary[3]赋值为'a'*/问bary[3]中放了哪个数据?
“^”操作符后的最大值依赖于指定的数据类型,对于char/uchar而言是0~7;#defineucharunsignedchar
对于int/uint而言是0~15;#defineuintunsignedchar
对于long/ulong而言是0~31;#defineulongunsignedchar3.7Cx51的运算规则3.7.1算术运算及其优先级3.7.2关系运算及其优先级3.7.3逻辑运算及其优先级3.7.4位操作及其表达式3.7.5自减、自加和复合运算一、Cx51最基本的5种算术运算符
+:加法运算,或者正值符号;
-:减法运算,或者负值符号;
*:乘法运算;
/:除法运算,取商;
%:取模(求余)运算符,取余数P69。3.7.1
算术运算及其优先级二、算术表达式、优先级与结合性
用算术运算符和括号将运算对象连接起来的式子称之为算术表达式。运算优先级,指当运算对象两侧都有运算符时,执行运算的先后次序:先乘、除、模(3者等同),后加、减(2者等同),括号最优先。(a+b)*(c-d)-e运算结合性,指当运算对象两侧的运算符具有相同优先级时,执行运算的先后次序:先左后右。a+b-c三、数据类型转换
如果一个运算符的两侧的数据类型不同,则必须先通过数据类型转换,将两侧的数据转换成位相同类型。转换的方法有两种:
自动(缺省)类型转换,即在程序编译时由C编译器自动从低类型到高类型进行类型转换;P70
强制类型转换,需要使用强制类型转换符。unsignedchara,b,c;c=a+(unsignedint)b;
首先(unsignedint)强制把b转换为类型unsignedint,编译器在编译加法代码时自动把a转换为unsignedint,最后存放运算结果时,编译器又自动把a转换为unsignedchar3.7.2
关系运算及其优先级优先级相同(高)优先级相同(低)<——小于<=——小于等于>——大于>=——大于等于==——测试等于!=——测试不等于一、关系运算符二、关系运算符的优先级
前4种关系运算符的优先级相同,后2种关系运算符的优先级也相同,前4中关系运算符的优先级高于后2种。
关系运算符的优先级低于算术运算符的优先级,但是高于赋值运算符(=)的优先级。
优先级算术运算符高关系运算符赋值运算符低关系运算符的结合性为左结合。
关系表达式:用关系运算符将两个表达式(算术表达式、关系表达式、逻辑表达式及字符表达式)连接起来的式子。
关系表达式的结果:关系运算符总是二目运算符,它作用在运算对象上产生的结果为一个逻辑值,即真(1)或者假(0)。(P71例题)3.7.3
逻辑运算及其优先级“&&”和“||”是双目运算符,要求有两个运算对象;而“!”是单目运算符,只要求有一个运算对象。Cx51提供3种逻辑运算符
&&:逻辑“与”(AND);
||:逻辑“或”(OR);
!:逻辑“非”。逻辑运算的优先级很特殊,有的很高,有的则很低:
优先级!(逻辑非)高算术运算符关系运算符&&和||赋值运算符低逻辑运算符的结合性为左结合。P72例题逻辑表达式:用逻辑运算符将关系表达式或者逻辑量连接起来的式子;逻辑表达式的结果:其值和逻辑关系表达式的值一致,是一个逻辑量真(1)或者假(0);例:unsignedchar
a=4,b=3,c=1;a>b&&b>c==c;
//-->1(真)
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年乡村旅游与非物质文化遗产传承报告
- 玛利亚vip门诊协议书
- 聘用退休人员签合同范本
- 猪场合作养殖协议书范本
- 首封人债权转让合同范本
- 淘宝与快递合作合同范本
- 汽油餐饮车转让合同范本
- 涂料机低价转让协议合同
- 签订借款合同后的协议书
- 篮球互租合同协议书范本
- 退学费和解协议书模板
- 【课件】2025届高三生物一轮复习备考策略研讨
- 某集团国企改革三年行动工作台账
- HJ 636-2012 水质 总氮的测定 碱性过硫酸钾消解紫外分光光度法
- 《公平竞争审查条例》微课
- 2024-2029年中国热成型钢行业市场现状分析及竞争格局与投资发展研究报告
- 2024年四川成都市第八人民医院人员招聘13人历年高频考题难、易错点模拟试题(共500题)附带答案详解
- 广东省韶关市翁源县2023-2024学年七年级12月月考语文试题
- 工业设备故障预测与维护
- 2023福建宁德市公安局招聘警务辅助人员(21人)笔试题库及答案解析
- 特种设备作业人员体检表(叉车)
评论
0/150
提交评论