




已阅读5页,还剩114页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第4章 单片机的C语言编程 单片机应用系统是由硬件和软件组成的,这是与一般的数字逻辑电路系统的不同之处。 汇编语言是能够利用单片机所有特性直接控制硬件的唯一语言,对于一些需要直接控制硬件的场合,汇编语言是必不可少的。 但汇编语言不是一种结构化的程序设计语言,对于较复杂的单片机应用系统,它的编写效率很低。 为了提高软件的开发效率,许多软件公司致力于单片机高级语言的开发研究,许多型号的单片机内部ROM已经达到64KB甚至更大,且具备在系统编程(ISP, In System Programmable)功能,进一步推动了高级语言在单片机应用系统开发中的应用。 51系列单片机支持三种高级语言: PL/M、 C、 BASIC,BASIC语言适用于简单编程而对编程效率、运行速度要求不高的场合,8052单片机内固化有BASIC语言解释器。 PL/M是一种结构化的语言,很象PASCAL,PL/M编译器好象汇编器一样产生紧凑的机器代码,可以说是高级汇编语言,但它不支持复杂的算术运算,无丰富库函数支持,学习PL/M无异于学习一种新的语言。 C语言是一种通用的程序设计语言,其代码率高,数据类型及运算符丰富,并具有良好的程序结构,适用于各种应用的程序设计,是目前使用最广的单片机编程语言。 C语言作为一种非常方便的语言而得到广泛的支持,很多硬件开发都用C语言编程,如:各种单片机、DSP、ARM等。 C语言程序本身不依赖于机器硬件系统,基本上不作修改或仅做简单修改就可将程序从不同的单片机中移植过来直接使用。 C语言提供了很多数学函数并支持浮点运算,开发效率高,故可缩短开发时间,增加程序可读性和可维护性。,单片机的C语言编程称为C51编程,应用C51编程具有以下优点: (1)C51编译器管理内部寄存器和存贮器的分配,编程时,无需考虑不同存储器的寻址和数据类型等细节问题; (2)程序有规范的结构,可分成不同的函数,这种方式具有良好的模块化结构,使已编好程序容易移植; (3)有丰富的子程序库可直接引用,具有较强的数据处理能力,从而大大减少用户编程的工作量; (4)C语言和汇编语言可以交叉使用。 汇编语言程序代码短、运行速度快、但复杂运算编程耗 时。用汇编语言编写与硬件有关的部分程序,用C语言编写与硬件无关的运算部分程序,充分发挥两种语言的长处,提高开发效率。,C51编译器(Keil C)与标准ANSI C编译器的主要区别 单片机C编译器之所以与ANSI C有所不同,主要是由于它们所针对的硬件系统有其各自不同的特点。C51的特点和功能主要是80C51单片机自身特点引起的。 (1)头文件:51单片机有不同的厂家和系列,不同单片机的主要区别在于内部资源,为了实现内部资源功能,只需将相应的功能寄存器的头文件加载在程序中,就可实现指定的功能。因此,C51系列头文件集中体现了各系列芯片的不同功能。 (2)数据类型:由于51系列器件包含了位操作空间和丰富的位操作指令,因此 C51比ANSI C多一种位类型。 (3)数据存储类型:51系列单片机有程序存储器和数据存储器。数据存储器又分片内和片外数据存储器。片内数据存储器还分直接寻址区和间接寻址区,分别对应code、data、idata、xdata以及根据80C51系列特点而设定的pdata类型。,(4)数据运算操作和程序控制:从数据运算操作和程序控制语句以及函数的使用上来讲,它们几乎没有什么明显的区别。只是在函数的使用上,由于单片机系统的资源有限,它的编译系统不允许太多的程序嵌套。 由于51系列单片机是8位机,所以扩展16位字符Unicode不被C51支持。 ANSI C所具备的递归特性不被C51支持,所以在C51中如果要使用递归特性,必须用REETRANT关键字声明。 (5)C51与标准ANSI C库函数:部分库函数不适合单片机处理系统,因此被排除在外,如字符屏幕和图形函数。 也有一些库函数继续使用,但这些库函数是厂家针对硬件特点相应开发的,与ANSI C的构成和用法有很大的区别,如printf和scanf。在ANSI C中,这两个函数通常用作屏幕打印和接收字符,而在C51中,主要用于串口数据的发送和接收。,4.1 C51程序结构特点,同标准C一样,C51的程序是由函数组成。C语言的函数以“”开始,以“”结束。 其中必须有一个主函数main(),程序的执行从主函数main() 开始,调用其他函数后返回主函数main(),最后在主函数中结束整个程序,而不管函数的排列顺序如何。 C语言的语句规则: 1.每个变量必须先说明后引用。 2.C语言程序一行可以书写多条语句,但每个语句必须以“;”结尾,一个语句也可以多行书写。 3.C语言的注释用/*/表示。 4.“”花括号必须成对,位置随意,多个花括号可同行书写,也可逐行书写。 为层次分明,增加可读性,同一层的“”花括对齐,采用逐层缩进方式书写。,C语言程序的组成结构: 全局变量说明 /*可被各函数引用*/ main( ) /*主函数*/ 局部变量说明 /*只在本函数引用*/ 执行语句(包括函数调用语句); fun1(形式参数表) /*函数1*/ 形式参数说明 局部变量说明 执行语句(包括调用其他函数语句) funn(形式参数表) /*函数n*/ 形式参数说明 局部变量说明 执行语句 ,标识符用来标识源程序中某个对象的名字,这些对象可以是语句、数据类型、函数、变量、数组等。标识符区分大小写,第一个字符必须是字母或下划线。 C51中有些库函数的标识符是以下划线开头的,所以一般不要以下划线开头命名标识符。 C51编译器规定标识符最长可达255个字符,但只有前面32个字符在编译时有效,因此在编写源程序时标识符的长度不要超过32个字符,这对于一般应用程序来说已经足够了。 关键字是编程语言保留的特殊标识符,有时又称为保留字,它们具有固定名称和含义,在C语言的程序编写中不允许标识符与关键字相同。 与其他计算机语言相比,C语言的关键字较少,ANSI C标准一共规定了32个关键字。 Keil C51编译器的关键字除了有ANSI C标准的32个关键字外还根据51单片机的特点扩展了相关的关键字。在Keil C51开发环境的文本编辑器中编写C程序,系统可以把保留字以不同颜色显示,缺省颜色为蓝色。,4.2 C51的标识符和关键字,ANSI C标准关键字(1),ANSI C标准关键字(2),Keil C51编译器扩展的关键字(1),Keil C51编译器扩展的关键字(2),4.3 C51的数据类型,C51的数据有常量和变量之分。 常量 在程序运行中其值不变的量。 数值型常量:可以为十进制数、 十六进制数( 用0x表示)和字符( 括号括起)。 符号型常量:用符号表示常量,此符号需用宏定义指令(#define)对其进行 定义(相当于汇编的“EQU”伪指令) 。 变量 在程序运行中其值可以改变的量。 定义一个变量,编译系统就会自动为它安排一个存贮区,具体的地址值 ,用户不必在意。 一个变量由变量名和变量值构成. 变量名:存贮单元地址的符号表示。 变量的值:变量所在地址单元存放的内容。 数据类型:数据的长度。,C51编译器支持数据类型,1、在C51语言程序中,有可能会出现在运算中数据类型不一致的情况。C51允许任何标准数据类型的隐式转换,隐式转换的优先级顺序如下: bitcharintlongfloat signedunsigned 2、也就是说,当char型与int型进行运算时,先自动对char型扩展为int型,然后与int型进行运算,运算结果为int型。C51除了支持隐式类型转换外,还可以通过强制类型转换符“()”对数据类型进行人为的强制转换。 3、字符型(char)、整型(int)和长整型(long)均有符号型(signed)和无符号型(unsigned)两种,如果不是必须,尽可能选择unsigned型,这将会使编译器省却符号位的检测,使生成的程序代码比signed类型短得多。 C5l编译器除了能支持以上这些基本数据类型之外,还能支持一些复杂的组合型数据类型,如数组类型、指针类型、结构类型、联合类型等这些复杂的数据类型。,4.4 数据的存储类型和存储模式,同ANSI C一样,C51规定变量必须先定义后使用。C51对变量的进行定义的格式如下: 存储种类 数据类型 存储器类型 变量名表 其中,存储种类和存储类型是可选项。 一、变量的存储种类 按变量的有效作用范围可以将其划分为局部变量和全局变量;还可以按变量的的存储方式为其划分存储种类。 在C语言中变量有四种存储种类,即自动(auto)、外部(extern)、静态(static)和寄存器(register)。 这四种存储种类与全局变量和局部变量之间的关系如图所示,1、自动变量(auto),定义一个变量时,在变量名前面加上存储种类说明符“auto”,即将该变量定义为自动变量。自动变量是C语言中使用最为广泛的一类变量。 自动变量的作用范围在定义它的函数体或复合语句内部,只有在定义它的函数内被调用,或是定义它的复合语句被执行时,编译器才为其分配内存空间,开始其生存期。当函数调用结束返回,或复合语句执行结束时,自动变量所占用的内存空间就被释放,变量的值当然也就不复存在,其生存期结束。自动变量始终是相对于函数或复合语句的局部变量。,一、变量的存储种类 2、外部变量(extern) 使用存储种类说明符“extern”定义的变量称为外部变量。 按照缺省规则,凡是在所有函数之前,在函数外部定义的变量都是外部变量,定义时可以不写extern说明符。但是,在一个函数体内说明一个已在该函数体外或别的程序模块文件中定义过的外部变量时,则必须要使用extern说明符。 一个外部变量被定义之后,它就被分配了固定的内存空间。 外部变量的生存期为程序的整个执行时间,即在程序的执行期间外部变量可被随意使用,当一条复合语句执行完毕或是从某一个函数返回时,外部变量的存储空间并不被释放,其值也仍然保留。因此,外部变量属于全局变量。 C语言允许将大型程序分解为若干个独立的程序模块文件,各个模块可分别进行编译,然后再将它们连接在一起。,2、外部变量(extern) 在这种情况下,如果某个变量需要在所有程序模块文件中 使用,只要在一个程序模块文件中将该变量定义成全局变量,而在其它程序模块文件中用extern说明该变量是已被定义过的外部变量就可以了。 函数是可以相互调用的,因此函数都具有外部存储种类的 属性。 定义函数时如果冠以关键字extern即将其明确定义为一个外部函数。例如extern int func2(char a,b)。如果在定义函数时省略关键字extern,则隐含为外部函数。如果要调用一个在本程序模块文件以外的其它模块文件所定义的函数,则必须要用关键字extern说明被调用函数是一个外部函数。对于具有外部函数相互调用的多模块程序,可用C51编译器分别对各个模块文件进行编译,最后L51连接定位器将它们连接成为一个完整的程序。,一、变量的存储种类 3、静态变量(static) 使用存储种类说明符“static”定义的变量称为静态变量。静态变量分为局部静态变量和全局静态变量。 局部静态变量不象自动变量那样只有当函数调用它时才存在,局部静态变量始终都是存在的,但只能在定义它的函数内部进行访问,退出函数之后,变量的值仍然保持,但不能进行问。 全局静态变量,它是在函数外部被定义的,作用范围从它的定义点开始,一直到程序结束。 当一个C语言程序由若干个模块文件所组成时,全局静态变量始终存在,但它只能在被定义的模块文件中访问,其数据值可为该文件内的所有函数共享,退出该文件后,虽然变量的值仍然保持着,但不能被其它模块文件访问。局部静态变量是一种在两次函数调用之间仍能保持其值的局部变量。有些程序需要在多次调用之间仍然保持变量的值,使用自动变量无法实现这一点,使用 全局变量有时又会带来意外的副作用,这时就可采用局部静态变量。,一、变量的存储种类 4、寄存器变量(register) 为了提高程序的执行效率,C语言允许将一些使用频率最高的 那些变量,定义为能够直接使用硬件寄存器的所谓寄存器变量。 定义一个变量时在变量名前而冠以存储种类符号“register”即将 该变量定义成为了寄存器变量。 寄存器变量可以被认为是自动变量的一种,它的有效作用范围也与自动变量相同。 C51编译器能够识别程序中使用频率最高的变量,在可能的情况下,即使程序中并未将该变量定义为寄存器变量,编译器也会自动将其作为寄存器变量处理。 因此,用户无须专门声明寄存器变量。,4.4 数据的存储类型和存储模式,二、数据的存储类型 C51是面向8XX51系列单片机及硬件控制系统的开发语言,它定义的任何变量必须以一定的存储类型的方式定位在8XX51的某一存储区中,否则便没有意义。因此在定义变量类型时,还必须定义它的存储类型,C51的变量有如下几种存储类型:,如果在变量定义时省略了存储器类型标识符,C51编译器会选择默认的存储器类型。默认的存储器类型由SMALL、COMPACT和LARGE存储模式指令决定。 1)data区 对data区的寻址是最快的,所以应该把使用频率高的变量放在data区,由于空间有限,必须注意使用data区,data区除了包含程序变量外,还包含了堆栈和寄存器组data区。 在SMALL存储模式下,未说明存储器类型时,变量默认被定位在data区。 2)bdata区 当在DATA区的位寻址区定义变量,这个变量就可进行位寻址,并且声明位变量。这对状态寄存器来说十分有用,因为它可以单独使用变量的每一位,而不一定要用位变量名引用位变量。 3)idata区 idata区也可以存放使用比较频繁的变量,使用寄存器作为指针进行寻址。在寄存器中设置8位地址进行间接寻址,与外部存储器寻址比较,它的指令执行周期和代码长度都比较短。,4)pdata和xdata区 在这两个区声明变量和在其他区的语法是一样的,pdata区只有256B,而xdata区可达65536B 5)code区 code区即80C51的程序代码区,所以代码区的数据是不可改变的,80C51的代码区不可重写。一般代码区中可存放数据表,跳转向量和状态表。 如果用户不对变量的存贮 类型定义,则编译器承认默认存贮类型,默认的存贮类型由编译控制命令的存贮的模式部分 决定。,三、数据的存储模式 在固定的存贮器地址进行变量参数传递是C51的一个标准特征,定义了变量、参数传递区的存贮器模式,也就是默认了变量和参数传递区存贮器类型、无需再对变量和参数传递区的存贮器类型进行说明。 存贮器模式决定了变量的默认存贮器类型、参数传递区和无明确存贮区类型的说明。 有三种存贮器模式:SMALL、LARGE和COMPACT,1小(SMALL)模式 所有变量都默认在8051的内部数据存储器中。这和用data显式定义变量起到相同的作用。 2紧凑(COMPACT)模式 此模式中,所有变量都默认在8051的外部数据存储器的一页中。 3大(LARGE)模式 在大模式下,所有的变量都默认在外部存储器中(xdata)。,为了能够直接访问这些特殊功能寄存器 ,C51编译器扩充了关 键字sfr和sfrl6,利用这种扩充关键字可以在C语言源程序中直 接对805l单片机的特殊功能寄存器进行定义。定义方法如下: sfr特殊功能寄存器名地址常数; 例如:sfr P00x80;* 定义IO口P0,其地址为80H *,例如: 设C语言源程序为PROR.C,若使程序中的变量类型和参数传递区限定在外部数据存贮区 ,有两种方法: 方法1:在程序的第一句加预处理命令 #pragma compact。 方法2:用C51对PROR.C进行编译时,使用编译控制命令: C51 PROR.C COMPACT。,四、变量说明举例,data char var; /*字符变量var定位在片内RAM区 char code MSG=ENTER PARAMETER: /*字符数组定位在程序存贮区*/ unsigned long xdata array100; /* 无符号长 型数组定位在片外RAM区,每 元素占4bytes*/ float idata x,y,z; /*实型变量x,y,z,定位在片内用 间址访问的内部RAM区*/ bit lock; /*位变量Lock定位在片内RAM可位寻址区*/ unsigned int pdata dimension; /* 无符号整型变量 dimension定位在分页的外部RAM区 */ unsigned char xdata vector 1044; /* 无符号字符型三 维数组, 定位在片外RAM区*/,sfr P0=0x80; /*定义P0口,地址为80H*/ char bdata flags; /*字符变量flags定位在 可位寻址内部RAM区*/ sbit flag0=flags0; /*定义flag0为flags.0 如果在变量说明时略去存贮器类型标志符,编译器会自动选择默认的存贮器类型。默认的存 贮器类型进一步由控制指令SMALL、COMPACT和LARGE限制。 例:如果声明char var,则默认 的存贮器模式为SMALL,var放在data存贮区; 如果使用COMPACT模式,var放入idata存贮区 ; 使用LARGE模式,var被放入外部存贮区(xdata存贮区)。,一、指针与指针变量 指针就是存储单元地址,存储这个地址的变量称为指针变量。 (1)指针变量 在汇编语言程序中,要取存贮单元m的内容可用直接寻址方式,也可用寄存器间接寻址方式 ,如果用R1寄存器指示m的地址,用R1就是取m单元的内容。 相对应的在C语言中可用变量名表示取变量的值(相当于直接寻址),也可用另一个变量(如P)存放m的地址,P就相当于R1寄存器 。用*P取得m单元的内容(相当于汇编的间接寻址方式)这里P即为指针型变量。,4.5 指针,注: 汇编语言程序中对符号地址n和m需用EQU伪指令进行地址定义。 C语言应对变量n、m和指针变量P需进行类型定义。 表中&为取地址运算符,*为取内容运算符。,下面表格表示两种语言将m单元的内容送n单元的对照语句。,(2)指针型变量的类型 51单片机的不同存贮空间,有不同的地址范围,即使对于同一外部数据存贮器,又有用Ri分页寻址(Ri为八位)和用DPTR寻址(DPTR为十六位)两种寻址方式。 指针是指示变量的地址的,因此,在指针类型的定义中要说明被指的变量的数据类型和存贮类型。同时指针变量本身也是一个变量,有它存放的存贮区和数据长度。即指针变量本身有它的存贮类型和数据长度,其数据长度是由被指的变量的存贮类型而定的。,指针变量存储类型: data、idata、pdata 以上均为八位地址指示,所以指针长度为1Byte。 code、xdata 这些均为十六位地址指示,所以指针长度为2Byte。,指针变量存储类型: 如果指针的存储类型缺省,指针定义为通用型指针,表示指针可指向任何存贮空间,此时指针长度为3字节。 通用型指针的存贮类型编码如下: 表中v4.0、v5.0表示 C51的版本,例如指针变量px值为0x021203 (v4.0版)或为0x001 203 (v5.0版) ,即指针指向xdata 区的1203H地址单元。 如: char *pd; /* pd 定义为通用型指针*/,指针变量的数据类型: 同样有char、int、long等类型, 表示指针指向的数据的长度是占1个单元、2个单元还是4个单元。,(2) 被指数据类型 被指存贮类型 *指针变量存贮类型 指针变量 例如 long code * xdata px; /*和上面定义等同*/,指针变量说明有两种格式: (1) 指针变量存贮类型 被指数据类型 被指存贮类型 *指针变量名; ( 其中 为可选项 ) 如:,(3) 指针变量说明举例,例如 char xdata *data pd; /* 指针变量pd指向字符型xdata区,自 身在data区,长度2字节*/ long xdata *px; /*指针变量px指向long型xdata区(被指的数据在 xdata区,每个数据占四个单元,指针自身在默认存贮器(如不指定编译模式在data区),指针长度为2个字节*/ data char xdata *pd; /*与上例等效*/ 说明: 1.” * ”号不可少,它表示变量为指针变量。 2.指针变量说明格式中的 为可选项 ,如 被指存贮类型 缺省,则指针定义为通用型,如 指针变量存贮类型 缺省、指针变量则存放在默认存贮区或者在data区。,(3) 指针变量说明举例,二、指向数组的指针变量,如果用一个变量存放一个数组的地址,这个变量就称为指向数组的指针变量。 数组的起始地址称为数组指针,一个数组a 的起始地址用a表示。 (1)指向数组的指针变量的定义和赋值 设定义了一个数组a5和一个指针变量ap: char data a5; char data *ap; 仅此两句并不能说明变量ap是指向数组的,还必须将数组的起始地址赋给该变量: ap=a; /*数组a的起始地址赋给指针变量ap*/ 或 ap=,2) 利用指向数组的指针变量引用数组元素 指向数组的指针变量引用数组元素有两种方法: *(ap+i) 或 ap(i) ,它们等同于*(a+i)或a (i) 例 main() char a5=11,22,33,44,55; char b,c,d; char *ap; ap=a; /* ap等于数组a5的起始地址*/ b=a+2; /* b等于数组元素a2的地址 */ c=ap+3; /* c等于数组元素a3的地址 */ d=*(ap+3); /* d等于数组元素a3的值, 即d=44 ,等同于d=a (3) */ ,4.6 C51对 SFR、可寻址位、存储器和I/O口的定义,一、特殊功能寄存器SFR定义 C51对特殊功能寄存器SFR有两种定义方法 (1)使用特定关键字sfr自主形式的定义方式, sfr 寄存器名=寄存器地址 其中寄存器地址必须大写 如 sfr SCON=0x98; /*串行通信控制寄存器 地址98H*/ sfr TMOD=0x89; /*定时器模式控制寄存器地址89H*/ sfr ACC=0xe0; /*A累加器地址E0H*/ sfr P1=0x90; /*P1端口地址90H*/ 定义了以后,程序中就可以直接引用寄存器。,(2)使用头文件 C51建有头文件 reg51.h、 reg52.h ,在该头文件中对51或52系列单片机所有的特殊功能寄存器的进行了sfr定义,对特殊功能寄存器的有位名称的可寻址位进行了sbit定义,因此,只要用包含语句#include 就可以直接引用特殊功能寄存器名,或直接引用位名称。 要特别注意:在引用时特殊功能寄存器或者位名称必须大写。,二、对位变量的定义,C51对位变量的定义方法有三种: 1.将变量用bit类型的定义符定义为bit类型 如 bit mn;mn为位变量,其值只能是“0”或“1”,其位地址C51自行安排在可位寻址区的bdata区。 2.采用字节寻址变量.位的方法 如 bdata int ibase; /*ibase定义为整型变量*/ sbit mybit=ibase15; /*mybit定义为 ibase的D15位*/ 这里位是运算符 “” 相当于汇编中的 “”,其后的最大取值依赖于该位所在的变量的类型,如定义为char最大值只能为7。,二、对位变量的定义,C51对位变量的定义方法有三种: 3.对特殊功能寄存器的位的定义 方法1:使用头文件及sbit定义符;多用于无位名的可寻址位。 如 #include sbit P1_1=P11; /*P1_1为P1口的第1位*/ sbit ac=ACC7;/*ac定义为累加器A的第7位*/ 方法2:使用头文件reg51.h,再直接用位名称。 例如 #include RS1=1; RS0=0; 方法3:用字节地址位表示 如 sbit OV=0xD02; 方法4:用寄存器名.位定义 如 sfr PSW=0xd0; /*定义PSW地址为d0H*/ sbit CY=PSW7; /*CY为PSW7*/,三、对存贮器和外接I/O口的绝对地址访问 C51对存贮器和外接I/O口的绝对地址访问可以通过指针访问,也可以通过函数访问。 1.对存贮器的绝对地址访问 利用绝对地址访问的头文件absacc.h可对不同的存贮区进行访问。该头文件的函数有: CBYTE (访问code区字符型) DBYTE (访问data区字符型) PBYTE (访问pdata或I/O) XBYTE (访问xdata或I/O) 还有CWORD、DWORD、PWORD和XWORD四个函数,它们的访问区域同上,只是访问的类型为int 型 例: #include #define com XBYTE0x07ff,那么后面程序com变量出现的地方,就是对地址为07ffH 外部RAM或I/O口进行访问。 例: XWORD0=0x9988; 即将9988H(int类型)送入外部RAM的0号和1号单元。 使用中要注意: absacc.h一定要包含进程序。 CBYTE、DBYTE、XBYTE等函数名必须大写。 2.对外部I/O口的访问 由于单片机的I/O口和外部RAM统一编址,因此对I/O口地址的访问可用 XBYTE(MOVX DPTR ) 或 PBYTE(MOVX Ri) 进行。,4.7 C51的运算符及表达式,一、赋值运算符 赋值运算符“=”,在C51中,它的功能是将一个数据的值赋给一个变量,如x=10。利用赋值运算符将一个变量与一个表达式连接起来的式子称为赋值表达式,在赋值表达式的后面加一个分号“;”就构成了赋值语句,一个赋值语句的格式如下: 变量=表达式; 执行时先计算出右边表达式的值,然后赋给左边的变量。例如: x=8+9; /*将8+9的值赋绐变量x*/ x=y=5; /*将常数5同时赋给变量x和y*/ 在C51中,允许在一个语句中同时给多个变量赋值,赋值顺序自右向左。,4.7 C51的运算符及表达式,二、算术运算符 C51中支持的算术运算符有: + 加或取正值运算符 - 减或取负值运算符 * 乘运算符 / 除运算符 % 取余运算符 加、减、乘运算相对比较简单,而对于除运算,如相除的两个数为浮点数,则运算的结果也为浮点数,如相除的两个数为整数,则运算的结果也为整数,即为整除。如25.0/20.0结果为1.25,而25/20结果为1。 对于取余运算,则要求参加运算的两个数必须为整数,运算结果为它们的余数。例如:x=5%3,结果x的值为2。,3 关系运算符 C51中有6种关系运算符: 大于 = 大于等于 = 小于等于 = = 等于 != 不等于,关系运算用于比较两个数的大小,用关系运算符将两个表达式连接起来形成的式子称为关系表达式。关系表达式通常用来作为判别条件构造分支或循环程序。关系表达式的一般形式如下: 表达式1 关系运算符 表达式2 关系运算的结果为逻辑量,成立为真(1),不成立为假(0)。其结果可以作为一个逻辑量参与逻辑运算。例如:53,结果为真(1),而10= =100,结果为假(0)。 注意:关系运算符等于“= =”是由两个“=”组成。,4 逻辑运算符 C51有3种逻辑运算符: | 逻辑或 & 逻辑与 ! 逻辑非 关系运算符用于反映两个表达式之间的大小关系,逻辑运算符则用于求条件式的逻辑值,用逻辑运算符将关系表达式或逻辑量连接起来的式子就是逻辑表达式。,逻辑与,格式: 条件式1 & 条件式2 当条件式1与条件式2都为真时结果为真(非0值),否则为假(0值)。 逻辑或,格式: 条件式1 | 条件式2 当条件式1与条件式2都为假时结果为假(0值),否则为真(非0值)。 逻辑非,格式: !条件式 当条件式原来为真(非0值),逻辑非后结果为假(0值)。当条件式原来为假(0值),逻辑非后结果为真(非0值)。 例如:若a=8,b=3,c=0,则!a为假,a & b为真,b & c为假。,5 位运算符,C51语言能对运算对象按位进行操作,它与汇编语言使用一样方便。位运算是按位对变量进行运算,但并不改变参与运算的变量的值。如果要求按位改变变量的值,则要利用相应的赋值运算。C51中位运算符只能对整数进行操作,不能对浮点数进行操作。C51中的位运算符有: & 按位与 | 按位或 按位异或 按位取反 右移 【例4-10】设a=0x45=01010100B,b=0x3b=00111011B,则a&b、a|b、ab、a、a2分别为多少? a&b=00010000b=0x10。 a|b=01111111B=0x7f。 ab=01101111B=0x6f。 a=10101011B=0xab。 a2=00001110B=0x0e。,6 复合赋值运算符 C51语言中支持在赋值运算符“=”的前面加上其它运算符,组成复合赋值运算符。下面是C51中支持的复合赋值运算符: += 加法赋值 + 减法赋值 *= 乘法赋值 /= 除法赋值 %= 取模赋值 &= 逻辑与赋值 |= 逻辑或赋值 = 逻辑异或赋值 = 逻辑非赋值 = 右移位赋值 =2相当于x=x2。,7 逗号运算符 在C51语言中,逗号“,”是一个特殊的运算符,可以用它将两个或两个以上的表达式连接起来,称为逗号表达式。逗号表达式的一般格式为: 表达式1,表达式2,表达式n 程序执行时对逗号表达式的处理:按从左至右的顺序依次计算出各个表达式的值,而整个逗号表达式的值是最右边的表达式(表达式n)的值。例如:x=(a=3,6*3)结果x的值为18。 8 条件运算符 条件运算符“?:”是C51语言中唯一的一个三目运算符,它要求有三个运算对象,用它可以将三个表达式连接在一起构成一个条件表达式。条件表达式的一般格式为: 逻辑表达式?表达式1:表达式2 其功能是先计算逻辑表达式的值,当逻辑表达式的值为真(非0值)时,将计算的表达式1的值作为整个条件表达式的值;当逻辑表达式的值为假(0值)时,将计算的表达式2的值作为整个条件表达式的值。例如:条件表达式max=(ab)?a:b的执行结果是将a和b中较大的数赋值给变量max。,9 指针与地址运算符 指针是C51语言中的一个十分重要的概念,在C51中的数据类型中专门有一种指针类型。指针为变量的访问提供了另一种方式,变量的指针就是该变量的地址,还可以定义一个专门指向某个变量的地址的指针变量。为了表示指针变量和它所指向的变量地址之间的关系,C51中提供了两个专门的运算符: * 指针运算符 & 取地址运算符 指针运算符“*”放在指针变量前面,通过它实现访问以指针变量的内容为地址所指向的存储单元。例如:指针变量p中的地址为2000H,则*p所访问的是地址为2000H的存储单元,x=*p,实现把地址为2000H的存储单元的内容送给变量x。 取地址运算符“&”放在变量的前面,通过它取得变量的地址,变量的地址通常送给指针变量。例如:设变量x的内容为12H,地址为2000H,则&x的值为2000H,如有一指针变量p,则通常用p=&x,实现将x变量的地址送给指针变量p,指针变量p指向变量x,以后可以通过*p访问变量x。,4.8 表达式语句及复合语句,1 表达式语句,在表达式的后边加一个分号“;”就构成了表达式语句 ,如:,a=+b*9; x=8;y=7; +k;,可以一行放一个表达式形成表达式语句,也可以一行放多个表达式形成表达式语句,这时每个表达式后面都必须带“;”号,另外,还可以仅由个分号“;”占一行形成一个表达式语句,这种语句称为空语句。,空语句在程序设计中通常用于两种情况: (1)在程序中为有关语句提供标号,用以标记程序执行的位置。例如采用下面的语句可以构成一个循环。 repeat:; ; goto repeat;,(2)在用while语句构成的循环语句后面加一个分号,形成一个不执行其它操作的空循环体。这种结构通常用于对某位进行判断,当不满足条件则等待,满足条件则执行。 【例1】下面这段子程序用于读取8051单片机的串行口的数据,当没有接收到则等待,当接收到,接收数据后返回,返回值为接收的数据。 #include char getchar() char c; while(!RI); /当接收中断标志位RI为0则等待,当接收中断标志位为1则结束等待。 c=SBUF; RI=0; return(c); ,2 复合语句,复合语句是由若干条语句组合而成的一种语句,在C51中,用一个大括号“”将若干条语句括在一起就形成了一个复合语句,复合语句最后不需要以分号“;”结束,但它内部的各条语句仍需以分号“;”结束。复合语句的一般形式为: 局部变量定义; 语句l; 语句2; 复合语句在执行时,其中的各条单语句按顺序依次执行,整个复合语句在语法上等价于一条单语句,因此在C51中可以将复合语句视为一条单语句。通常复合语句出现在函数中,实际上,函数的执行部分(即函数体)就是一个复合语句;复合语句中的单语句一般是可执行语句,此外还可以是变量的定义语句(说明变量的数据类型)。在复合语句内部语句所定义的变量,称为该复合语句中的局部变量,它仅在当前这个复合语句中有效。利用复合语句将多条单语句组合在起,以及在复合语句中进行局部变量定义是C51语言的一个重要特征。,4.9 C51的输入输出,在C51语言中,它本身不提供输入和输出语句,输入和输出操作是由函数来实现的。在C51的标准函数库中提供了一个名为“stdio.h”的一般I/O函数库,它当中定义了C51中的输入和输出函数。当对输入和输出函数使用时,须先用预处理命令“#include ”将该函数库包含到文件中。,在C51的一般I/O函数库中定义的I/O函数都是通过串行接口实现,在使用I/O函数之前,应先对MCS-51单片机的串行接口进行初始化。选择串口工作于方式2(8位自动重载方式),波特率由定时器/计数器1溢出率决定。例如,设系统时钟为12MHZ,波特率为2400,则初始化程序如下: SCON=0x52; TMOD=0X20; TH1=0xf3; TR1=1;,1 格式输出函数printf(),printf()函数的的作用是通过串行接口输出若干任意类型的数据,它的格式如下: printf(格式控制,输出参数表) 格式控制是用双引号括起来的字符串,也称转换控制字符串,它包括三种信息:格式说明符、普通字符和转义字符。 (1)格式说明符,由“%”和格式字符组成,它的作用是用于指明输出的数据的格式输出,如%d、%f等,它们的具体情况见表4-4。 (2)普通字符,这些字符按原样输出,用来输出某些提示信息。 (3)转义字符,就是前面介绍的转义字符(表4-2),用来输出特定的控制符,如输出转义字符n就是使输出换一行。 输出参数表是需要输出的一组数据,可以是表达式。,2 格式输入函数scanf(),scanf()函数的作用是通过串行接口实现数据输入,它的使用方法与printf()类似,scanf()的格式如下: scanf(格式控制,地址列表) 格式控制与printf()函数的情况类似,也是用双引号括起来的一些字符,可以包括以下三种信息:空白字符、普通字符和格式说明。 (1)空白字符,包含空格、制表符、换行符等,这些字符在输出时被忽略。 (2)普通字符,除了以百分号“%”开头的格式说明符而外的所有非空白字符,在输入时要求原样输入。 (3)格式说明,由百分号“%”和格式说明符组成,用于指明输入数据的格式,它的基本情况与printf()相同,具体情况见表4-5。 地址列表是由若干个地址组成,它可以是指针变量、取地址运算符“&”加变量(变量的地址)或字符串名(表示字符串的首地址)。,【例2】 使用格式输入输出函数的例子 #include /包含特殊功能寄存器库 #include /包含I/O函数库 void main(void) /主函数 int x,y; /定义整型变量x和y SCON=0x52; /串口初始化 TMOD=0x20; TH1=0XF3; TR1=1; printf(“input x,y:n”); /输出提示信息 scanf(“%d%d”, /结束 ,4.10 C51程序基本结构与相关语句,1 C51的基本结构,一顺序结构,顺序结构是最基本、最简单的结构,在这种结构中,程序由低地址到高地址依次执行,图4.3给出顺序结构流程图,程序先执行A操作,然后再执行B操作。,图4.3 顺序结构流程图,选择结构可使程序根据不同的情况,选择执行不同的分支,在选择结构中,程序先都对一个条件进行判断。当条件成立,即条件语句为“真”时,执行一个分支,当条件不成立时,即条件语句为“假”时,执行另一个分支。如图4.4,当条件S成立时,执行分支A,当条件P不成立时,执行分支B。,二选择结构,在C51中,实现选择结构的语句为if/else,if/else if语句。另外在C51中还支持多分支结构,多分支结构既可以通过if和else if语句嵌套实现,可用swith/case语句实现。,在程序处理过程中,有时需要某一段程序重复执行多次,这时就需要循环结构来实现,循环结构就是能够使程序段重复执行的结构。循环结构又分为两种:当(while)型循环结构和直到(do.while)型循环结构。 (1)当型循环结构 当型循环结构如图4-3,当条件P成立(为“真”)时,重复执行语句A,当条件不成立(为“假”)时才停止重复,执行后面的程序。,三循环结构,图4.5 当型循环结构,(2)直到型循环结构 直到型循环结构如图4-4,先执行语句A,再判断条件P,当条件成立(为“真”)时,再重复执行语句A,直到条件不成立(为“假”)时才停止重复,执行后面的程序。,图4.6 直到型循环结构,构成循环结构的语句主要有:while、do while、for、goto等。,2 if语句 if语句是C51中的一个基本条件选择语句,它通常有三种格式: (1)if (表达式) 语句; (2)if (表达式) 语句1; else 语句2; (3)if (表达式1) 语句1; else if (表达式2) (语句2;) else if (表达式3) (语句3;) else if (表达式n-1) (语句n-1;) else 语句n,【例3】 if语句的用法。 (1)if (x!=y) printf(“x=%d,y=%dn”,x,y); 执行上面语句时,如果x不等于y,则输出x的值和y的值。 (2)if (xy) max=x; else max=y; 执行上面语句时,如x大于y成立,则把x送给最大值变量max,如x大于y不成立,则把y送给最大值变量max。使max变量得到x、y中的大数。 (3)if (score=90) printf(“Your result is an An”); else if (score=80) printf(“Your result is an Bn”); else if (score=70) printf(“Your result is an Cn”); else if (score=60) printf(“Your result is an Dn”); else printf(“Your result is an En”); 执行上面语句后,能够根据分数score分别打出A、B、C、D、E五个等级。,3 switch/case语句,if语句通过嵌套可以实现多分支结构,但结构复杂。switch是C51中提供的专门处理多分支结构的多分支选择语句。它的格式如下: switch (表达式) case 常量表达式1:语句1;break; case 常量表达式2:语句2;break; case 常量表达式n:语句n;break; default:语句n+1; 说明如下: (1)switch后面括号内的表达式,可以是整型或字符型表达式。 (2)当该表达式的值与某一“case”后面的常量表达式的值相等时,就执行该“case”后面的语句,然后遇到break语句退出switch语句。若表达式的值与所有case后的常量表达式的值都不相同,则执行default后面的语句,然后退出switch结构。 (3)每一个case常量表达式的值必须不同否则会出现自相矛盾的现象。,(4)case语句和default语句的出现次序对执行过程没有影响。 (5)每个case语句后面可以有“break”,也可以没有。有break语句,执行到break则退出switch结构,若没有,则会顺次执行后面的语句,直到遇到break或结束。 (6)每一个case语句后面可以带一个语句,也可以带多个语句,还可以不带。语句可以用花括号括起,也可以不括。 (7)多个case可以共用一组执行语句。 【例4-14】 switch/case语句的用法。 对学生成绩划分为AD,对应不同的百分制分数,要求根据不同的等级打印出它的对应百分数。可以通过下面的switch/case语句实现。 switch(grade) case A;printf(”90100n”);break; case B;printf(”8090n”);break; case C;printf(”7080n”);break; case D;printf(”6070n”);break; case E;printf(”60n”);break; default;printf(”error”n) ,4 while语句,while语句在C51中用于实现当型循环结构,它的格式如下: while(表达式) 语句; /*循环体*/ while语句后面的表达式是能否循环的条件,后面的语句是循环体。当表达式为非0(真)时,就重复执行循环体内的语句;当表达式为0(假),则中止while循环,程序将执行循环结构之外的下一条语句。它的特点是:先判断条件,后执
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2024年烟草四川公司招聘考试真题及答案
- 中级计量经济学知到智慧树答案
- 计算机三级(信息安全技术)考试题(含答案)
- 中外名建筑赏析知到智慧树答案
- 中西方文化比较知到智慧树答案
- 中西医结合基础思路研究与方法知到智慧树答案
- 中学生物实验教学知到智慧树答案
- 2025版水电安装工程合同履行与维护合同
- 2025年仓储配送一体化大数据分析服务合同
- 2025版土地储备项目合作开发中介服务合同
- 勉县一中小升初数学试卷
- 2025一建《建设工程经济》计算、时间、数字考点笔记
- 校园基孔肯雅热防控措施课件
- 第1课 中国古代政治制度的形成与发展 课件 统编版高中历史选择性必修1
- 药师考试历年真题综合测试试卷(含答案)
- 2025年村级防疫员考试模拟试题及答案
- 快餐公司门店设备夜间关闭管理制度
- 以童心为笔:基于儿童心理发展需求的小学校园公共活动空间设计
- 生猪屠宰兽医卫生检验人员理论考试题库及答案
- 《护理伦理学》教学大纲(本科)
- 板带轧机刚度对热轧板形的影响
评论
0/150
提交评论