第5章单片机C51程序设计基础5.1C51数据与运算C语言中有如下几个基本数据类型:5.1.1C51的数据类型Char:单字节,可以存放一个字符。Int:整数,对大多数单片机开发软件而言是双字节大小float:单精度浮点数。double:双精度浮点数。关键字:shortintdata;longintadder;16位32位signedunsigned0~255-128~+127单片机特有的数据类型bit位变量,值为0或1sbit声明可位寻址空间的一个位sfr特殊功能寄存器,8位sfr16特殊功能寄存器,16位表5.1C51数据类型数据类型位宽字节数数值范围bit10~1char81-128~+127unsignedchar810~255short162-32768~+32767unsignedshort1620~65535int162-32768~+32767unsignedint1620~65535long324-2147483648~+2147483647unsignedlong3240~4294967295float324±1.175494E-38~±3.402823E+38(6位数字)double648±1.175494E-38~±3.402823E+38(10位数字)sbit10~1sfr810~255sfr161620~655355.1.2C51的存储类型3种存储模式:Small模式,所有缺省变量参数均装入内部RAM。优点是速度快,缺点在于空间有限,仅适用于小规模程序设计。Compact模式,所有缺省变量均位于外部RAM区的一页(256个字节),具体哪一页可由P2口指定(在STARTUP.A51文件中说明,也可用pdata指定)。优点是可用空间较Small宽裕,速度比Small慢但比Large要快。Large模式,所有缺省变量可放在多达64KB的外部RAM区。优点在于空间大,可存变量多,缺点是速度较前两种模式要慢。表5.251单片机存储类型及存储区类型关键字存储区描述dataDATA单片机内部RAM空间的低128字节,可在一个周期内直接寻址。bdataBDATADATA区中可以字节、位混合寻址的16字节位寻址区。idataIDATA内部RAM256字节,必须采用间接寻址。xdataXDATA片外RAM,地址范围64KB,使用DPTR间接寻址。pdataPDATA片外RAM的低256字节,可通过P0口的地址对其寻址。codeCODE程序存储区,内容只读,使用DPTR寻址。5.1.38051特殊功能寄存器的C51定义(1)sfr:声明字节寻址的特殊功能寄存器,例如:sfrP0=0x80;(2)sfr16该关键字对于一些8051派生系列单片机,可以声明其内两个连续地址的特殊功能寄存器(SFR),例如:sfr16T2=0xCC;(3)sbit:声明可位寻址的特殊功能寄存器和别的可位寻址目标。“=”号后将绝对地址赋给变量名,其声明形式又有以下3种:①用已声明的sfr寄存器名^整数常量,例如:sfrKEYS=0x80;//声明KEYS为特殊功能寄存器,地址为0x80。sbitKEY_UP=KEYS^1;//指定KEYS的第1位连接UP按键。sbitKEY_DOWN=KEYS^2;//指定KEYS的第2位连接DOWN按键。sbitKEY_SET=KEYS^3;②用一个整数常量作为基地址^后一个整数常量作为指定位,例如:地址0x88对应的是TCON寄存器,第5位表示定时器0计数溢出标志位,第4位表示定时器0计数允许位,第1位表示定时器0中断允许位:sbitTF0=0x88^5;sbitTR0=0x88^4;sbitIE0=0x88^1;sbitEA=0xA8^7;//指定0xA8的第7位为EA,即全局中断允许③直接用一个整数常量作为绝对地址,例如:sbitTF0=0x8D;sbitTR0=0x8C;sbitIE0=0x89;5.1.48051并行接口及位变量的C51定义REG51.h和REG52.h这两个头文件sfrP0=0x80;sfrP1=0x90;sfrP2=0xA0;sfrP3=0xB0;AT89X51.H头文件sbitP0_0=0x80;sbitP0_1=0x81;sbitP0_2=0x82;sfrSEGDATA=P0;/*数码管数据端口*/sbitSEL0=P1^0;/*数码管0片选端口*/sbitSEL1=P1^1;/*数码管1片选端口*/…sbitKEY_SET=P1^7;/*设置按键*/5.2C51运算符、表达式及其规则5.2.1(复合)赋值运算符例如:count=5;total1=total2=0;Total=Total+3;Total+=3;5.2.2算术运算符5.2.3自增和自减运算符例如:--Couter;Couter--;++Amount;Amount++;放前后都一样num1=4;num2=8;a=++num1;b=num2++;放前后不一样5.2.4关系运算符5.2.5逻辑运算符5.2.6位运算符&运算的规则是当两个位都为1时,结果为1,否则为0;|运算的规则是当两个位都为0时,结果为0,否则为1;^运算的规则是当两个位相同时,结果为0,否则为1;~运算的规则是当为1时结果为0,当为0时,结果为1。(1)设置位:设置某位为1,而其他位保持不变,可以使用位逻辑或运算charc;c=c|0x40;(2)清除位:设置某位为0,而其他位保持不变,可以使用位逻辑与运算c=c&0xBF;5.2.7条件运算符表达式1?表达式2:表达式3首先对第一个表达式进行检验,如果为真,则返回表达式2的值;如果为假,则返回表达式3的值。5.2.8指针和地址运算符指针数据类型是一种存放指向另一个数据的地址的变量类型。变量=*指针变量(将指针变量所指向的目标变量的值赋给等号左边的变量)指针变量=&目标变量(将目标变量的地址赋给等号左边的变量)unsignedintdataA_at_0x0028;//变量A存放在地址0x0028unsignedintdataB_at_0x002A;//变量B存放在地址0x002Aunsignedintdata*P_at_0x002C;//指针变量P存放在地址0x002CA=10;//变量A初值为10B=20;//变量B初值为20P=&B;//指针P现在指向变量B*P=100;//指针P所指变量(变量B)存放内容更改为100P=&A;//指针P现在指向变量AB=*P;//变量B的值现在更改为105.2.9优先级和结合性表5.3C51运算符的优先级和结合性优先级运算符结合性最高↑()[]-.自左向右!~++--+-*&sizeof自右向左*/%自左向右+-自左向右自左向右==自左向右==!=自左向右&自左向右^自左向右|自左向右&&自左向右||自左向右?:自右向左=+=-=*=/=%=&=^=|===自右向左最低,自左向右5.3C51流程控制语句5.3.1C51程序的基本结构及其流程图三种基本结构:顺序结构、选择结构和循环结构1.顺序结构及其流程图2.选择结构及其流程图选择结构还有两种派生结构:串行多分支结构和并行多分支结构(1)串行多分支结构流程图(2)并行多分支结构3.循环结构及其流程图循环结构又有“while”型和“dowhile”型两类(1)while型循环结构(2)dowhile型循环结构5.3.2选择语句选择语句有三种:if语句、switch/case语句和条件表达式5.3.3循环语句三种基本的循环语句:for语句、while语句和do-while语句5.4C51构造数据类型5.4.1数组5.4.2指针5.4.3结构体5.4.4共用体5.4.5枚举5.5函数includestdio.h//预处理命令longfun1();//函数说明floatfun2();intx,y;//全程变量说明floatz;func1()//功能函数1{局部变量说明;函数体;}Main()//主函数{局部变量说明;主函数体;}func2()//功能函数2{局部变量说明;函数体;}5.5.1函数的定义1.无参数函数的定义无参数函数的定义形式为:返回值类型标识符函数名(){函数体语句;}无参数函数一般不带返回值,因此函数返回值类型识别符可以省略。voidprint_function(){printf(“HelloWorld!”);}2.有参数函数的定义返回值类型标识符函数名(形式参数列表)形式参数说明;{函数体语句;}inthcf(u,v)intu,v;{inta,b,t,r;if(uv){t=u;u=v;v=t;}a=u;b=v;while((r=b%a)!=0){b=a;a=r;}return(a);}3.空函数的定义返回值类型标识符函数名(){}例如:intempty(){}5.5.2函数的调用1.函数调用的一般形式函数名(实际参数列表);2.函数调用的方式(1)函数调用语句把被调用函数名作为主调用函数中的一个语句。print_function();//此时不要求被调用函数返回数值,//只求完成某种操作即可。(2)函数结果作为表达式的一个运算对象sum=3+hcf(a,b);要求被调用函数带有return语句,以便返回一个明确的数值参加表达式的运算。(3)函数参数m=max(a,hcf(u,v));其中hcf(u,v)是一次函数调用它的值作为另一个函数调用max()的实际参数之一最后的m变量值为a和u,v的最大公约数两者之中最大的一个3.对被调用函数的说明在一个函数中调用另一个函数必须具有以下条件:(1)被调用函数必须已经存在(库函数或用户自定义函数)(2)如果程序中使用了库函数,或使用了不在同一文件中的自定义函数,则应该在程序的开头处使用#include包含语句,将所调用函数的信息包括到程序中来。#include“stdio.h”//将标准输入、输出头文件(在函数库中)//包含到程序中,该文件中包含printf()等库函数。#include“math.h”//将函数库中专用数学库的函数包含到//程序中来。(3)如果程序使用自定义函数,且该函数与调用它的函数同在一个文件中,则应根据主调函数与被调函数在文件中的位置,决定是否对被调用函数作出说明。①如果被调用函数出现在主调用函数之后调用之前,对被调用函数的返回值类型做出说明返回值类型说明符被调用函数的函数名();②如果被调用函数出现在主调用函数之前可以不对被调用函数加以说明③如果在所有函数定义之前,在文件的开头处,在函数的外部已经说明了函数的类型则在主调用函数中不必对所调用的函数再作返回值类型说明5.5.3函数的嵌套调用与递归调用1.函数的嵌套调用函数的嵌套调用,即在调用一个函数的过程中,允许调用另外一个函数。每次调用都将使8051系统把2字节(调用指令的下一条指令地址)压入内部堆栈,而C编译器通常依靠堆栈来频繁地进行参数传递。所以在一个函数内应将嵌套调用的层次限制在4~5层以内。2.函数的递归调用函数的递归调用,即在调用一个函数的过程中,又直接或间接地调用该函数本身。举例:利用函数的递归调用计算一个数的阶乘!intfactorial(intn){intresult;if(n==0){result=1;}elseresult=n*factorial(n-1);//factorial函数的递归调用return(result);}5.5.4中断服务函数1.中断服务函数的定义返回值类型标识符函数名()interrupt中断号using寄存器组号{函数体语句;}①中断函数不能进行参数传递;②中断函数没有返回值;建议定义为void类型③在任何情况下都不能直接调用中断函数,否则会产生编译错误。④如果在中断函数中调用了其它函数,则被调用函数所使用的寄存器必须与中断函数相同。否则会产生不正确的结果。⑤C51编译器对中断函数编译时会自动在程序开始和结束处加上相应的内容,具体如下:在程序开始处对ACC、B、DPH、DPL和PS