




已阅读5页,还剩28页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
内 容 提 要 C51程序结构 C51的数据类型及运算符 C51的函 数 C51程序设计 小结,第4章 单片机的C语言编程,应用C51编程具有以下优点: (1)C51管理内部寄存器和存贮器的分配,编程时,无需考虑不同存储器的寻址和数据类型等细节问题; ()程序由若干函数组成,具有良好的模块化结构; ()有丰富的子程序库可直接引用,从而大大减少用户编程的工作量。 ()C51和汇编语言可以交叉使用. 汇编语言程序代码短、运行速度快、但复杂运算编程耗时。如果用汇编语言编写与硬件有关的部分程序,用C51编写与硬件无关的运算部分程序,充分发挥两种语言的长处,可以提高开发效率。,4.1 C51程序结构,同标准C一样,C51的程序由一个个函数组成,这里的函数和其他语言的“子程序”或“过程 ”具有相同的意义。其中必须有一个主函数main(),程序的执行从main()函数开始,调用其 他函数后返回主函数main(),最后在主函数中结束整个程序而不管函数的排列顺序如何。,全局变量说明 /*可被各函数引用*/ main() /*主函数*/ 局部变量说明 /*只在本函数引用*/ 执行语句(包括函数调用语句) fun1(形式参数表) /*函数1*/ 形式参数说明, 局部变量说明 执行语句(包括调用其他函数语句) funn(形式参数表) /*函数n*/ 形式参数说明 局部变量说明 执行语句 ,C语言程序的组成结构如下所示:,C语言的语句规则: 1. 每个变量必须先说明后引用,变量名英文大小写是有差别的。 2. C语言程序一行可以书写多条语句,但每个语句必须以“;”结尾,一个语句也可以多行书写为好。 3. C语言的注释用/*/表示。 4. “”花括号必须成对,位置随意,可在紧挨函数名后,也可另起一行,多个花括号可以同行书写,也可逐行书写,为层次分明,增加可读性,同一层的“”花括号对齐,采用逐层缩进方式书写。,4.2 C51的数据类型及运算符 4.2.1 C51的存贮类型,访问内部数据存贮器(idata)比访问外部数据存贮器(xdata)相对要快一些,因此,可将经常使用的变量置于内部数据存贮器中,而将较大及很少使用的数据变量置于外部数据存贮器中。例如定义变量x语句:data char x (等价于char data x)。如果用户不对变量的存贮类型定义,则编译器承认默认存贮类型,默认的存贮类型由编译控制命令的存贮的模式部分决定。,4.2.2 C51的存贮器模式,存贮器模式决定了变量的默认存贮器类型、参数传递区和无明确存贮区类型的说明。C51的存贮器模式有SMALL、LARGE和COMPACT。 在固定的存贮器地址进行变量参数传递是C51的一个标准特征,在SMALL模式下参数传递是在内部数据存贮区中完成的。LARGE和COMPACT模式允许参数在外部存贮器中传递。C51同时也支持混合模式,例如在LARGE模式下生成的程序可将一些函数分页放入SMALL模式中从而加快执行速度。,例如设C语言源程序为PROR.C,若使程序中的变量类型和参数传递区限定 在外部数据存贮区 ,有两种方法: 方法1:用C51对PROR.C进行编译时,使用命令C51 PROR.C COMPACT。 方法2:在程序的第一句加预处理命令 #pragma compact,存贮器模式表,4.2.3 C51的数据类型,无论哪种数据都是存放在存贮单元中的,每一个数据究竟要占用几个单元(即数据的长度)都要提供给编译系统,正如汇编语言中存放数据的单元要用DB或DW伪指令进行定义一样,编译系统以此为根据预留存贮单元,这就是定义数据类型的意义。C51编译器支持数据类型见下表。,C51的数据类型,对上表作如下说明: 1. 字符型(char)、整型(int)和长整型(long)均有符号型(signed)和无符号型(unsigned)两种,如果不是必须,尽可能选择unsigned型,这将会使编译器省却符号位的检测,使生成的程序代码比signed类型短得多。 2. 程序编译时,C51编译器会自动进行类型转换,例如将一个位变量赋值给一个整型变量时,位型值自动转换为整型值;当运算符两边为不同类型的数据时,编译器先将低级的数据类型转换为较高级的数据类型,运算后,运算结果为高级数据类型。 3. 51单片机内部数据存贮器的可寻址位(20H2FH)定义为bit型,而特殊功能寄存器的可寻址位(即地址为X0H和X8H的SFR的各位)只能定义为sbit类型。,4.2.4 C51的指针,(1)关于指针型变量 在汇编语言程序中,要取存贮单元m的内容可用直接寻址方式,也可用寄存器间接寻址方式 ,如果用R1寄存器指示m的地址,用R1取m单元的内容。相对应的在C语言中用变量名表示取变量的值(相当于直接寻址),也可用另一个变量(如P)存放m的地址,P就相当于R1寄存器 。用*P取得m单元的内容(相当于汇编的间接寻址方式)这里P即为指针型变量。下面表格表示两种语言将m单元的内容送n单元的对照语句。,注: 上表省略了汇编语言程序中对符号地址n和m用EQU伪指令进行具体地址定义的 语句以及C语言对变量n、m和指针变量P进行类型定义的语句,实际程序设计中,此步是不可 缺少的。表中&为取地址运算符,*为取内容运算符。,汇编语言和C语言的对照 表,(2)指针型数据的类型 由于C51是结合51单片机硬件的,51单片机的不同存贮空间,有不同的地址范围,即使对于同一外部数据存贮器,又有用Ri分页寻址(Ri为八位)和用DPTR寻址(DPTR为十六位)两种寻址方式,而指针本身也是一个变量,有它存放的存贮区和数据长度。因此,在指针类型的定义中要说明:被指的变量的数据类型和存贮类型;指针变量本身的数据类型(占几个字节)和存贮类型(即指针本身存放在什么存贮区)。 例如类型定义为data或idata,表示指针指示内部数据存贮器;而pdata表示指针指向外部数据存贮器,用Ri间址。以上均为八位地址;而类型code/xdata表示指针指向外部程序存贮器或外部数据存贮器指针,本身(即被指示地址)应为十六位长度。如果想使指针能适用于指向任何存贮空间,则可以定义指针为通用型,此时指针长度为3字节,第一字节表示存贮器类型编码,第二、三字节分别表示所指 地址的高位和低位。第一字节表示的存贮器类型编码见下表:,通用型指针的存贮类型编码表,变量说明举例, 非指针型变量说明 data char var; /*字符变量var定位在片内数据存贮区*/ char code MSG =PARAMETER:;/*字符数组MSG 定位在程序 存贮区*/ unsigned long xdata array100; /*无符号长型数组定位在片外 RAM区,每元素占4bytes*/ float idata x,y,z; /*实型变量x,y,z,定位在片内用间址访问 的内部RAM区*/ bit lock; /*位变量Lock定位在片内RAM可位寻址区*/ unsigned int pdata sion;/*无符号整型变量sion定位在分页的外部 RAM*/ unsigned char xdata vector10 4 4 /*无符号字符型三维数 组, 定位在片外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存贮区)。 指针变量说明 long xdata *px; /*指针px指向long型xdata区(每个数据占四个单 元,指针自身在默认存贮器(如不指定编译模式 在data区),指针长度为2个字节*/ char xdata *data pd;/*指针pd指向字符型xdata区,自身在data区, 长度2字 节*/ data char xdata *pd; /*与上例等效*/ data int *pn;(和int *data pn及intr*pn等效) /*定义一个类型为int 型的通用型指针,指针自身在data区长度为3字节*/,在上例的指针声明中包含如下几个内容: 1) 指针变量名(如px)前面冠以“*”,表示px为指针型变量,此处*不带取内容之意。 2) 指针指向的存贮类型,即指向哪个存贮区,它决定了指针本身的长度(见数据类型表)。存贮类型声明的位置在数据类型和指针名(如*px)之间,如无次项声明,则此指针型变量为通用型。 3) 指针指向的存贮区的数据类型,即被指向的存贮区以多少个单元作一个数据单位,当程序通过指针对该区操作时,将按此规定的单元个数的内容作为一个数据操作。 4) 指针变量自身的存贮类型,即指针处于什么区与自身的长度无关,该声明可位于声明语句的开头,也可在“*”和变量名之间。此项由编译模式放在默认区,如无规定编译模式,通常在data区。,4.2.5 C51对SFR、可寻址位、存储器和I/O口的定义,1. 特殊功能寄存器SFR定义 C51提供了一种自主形式的定义方式,使用特定关键字sfr 如 sfr SCON=0x98; /*串行通信控制寄存器地址98H*/ sfr TMOD=0x89; /*定时器模式控制寄存器地址89H*/ sfr ACC=0xe0; /*A累加器地址E0H*/ sfr P1=0x90; /*P1端口地址90H*/ 定义了以后,程序中就可以直接引用寄存器名。 C51也建立了一个头文件reg51.h (增强型为reg52.h),在该文件中对所有的特殊功能寄存器的进行了sfr定义, 对特殊功能寄存器的有位名称的可寻址位进行了sbit定义,因此,只要用包含语句#include,就可以直接引用特殊功能寄存器名,或直接引用位名称。 要特别注意:在引用时特殊功能寄存器或者位名称必须大写。,2.对位变量的定义 C51对位变量的定义有三种方法: 将变量用bit类型的定义符定义为bit类型: 如 bit mn; mn为位变量,其值只能是“0”或“1”,其位地址C51自行安排在可位寻址区的bdata区。 采用字节寻址变量.位的方法: 如 bdata int ibase; /*ibase定义为整型变量*/ sbit mybit=ibase15;/*mybit定义为ibase的D15位*/ 这里位是运算符“”相当于汇编中的“”,其后的最大取值依赖于该位所在的字节寻址变量的定义类型,如定义为char最大值只能为7。, 对特殊功能寄存器的位的定义 方法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*/,3.C51对存贮器和外接I/O口的绝对地址访问 对存贮器的绝对地址访问 利用绝对地址访问的头文件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一定要包含进程序, XWORD必须大写。 对外部I/O口的访问 由于单片机的I/O口和外部RAM统一编址,因此对I/O口地址的访问可用XBYTE (MOVX DPTR )或PBYTE (MOVX Ri)进行。 例: XBYTE0Xefff=0x10;将10H输出到地址为EFFFH端口,4.2.6 C51的运算符,1赋值运算符: 将“”的右边的值赋值给左边的变量. 2. C51的算术运算符: (加或正号); (减或负号); * (乘号); / (除号); % (求余) 优先级为:先乘除,后加减,先括号内,再括号外 3. C51的关系运算符有六种: (小于); (大于); = (小于等于); = (大于等于); =(相等);!= (不相等) 优先级:前四个高,后二个“=”和“!=”级别低。,4C51的逻辑运算符有三种: 逻辑表达式和关系表达式的值相同,以0代表假,以1代表真。 以上三种运算的优先级见右图所示。 5C51的按位操作的运算符有六种: &(按位与);(按位或); (按位异或); (位取反); (位右移 ) (注:补零移位) 例1. a=0xf0H; 表达式a=a值为0FH 例2. a=0xea; 表达式a2值为A8H,即a值左移两位,移位后空白位补0。 6. 自增、自减运算符: +i,-i (在使用i之前,先使i值加1,减1) i+,i- (在使用i之后,再使i值加1.,减1) 例设i原值为5 j=+i 则j值为6,i值也为6 j=i+ 则j值为5,i值为6,| 非 算术运算 关系运算 &和| = 赋值运算,运算符的优先级,7复合赋值运算符: +=;=;*=;/=;%=;=; result等于变量var1和var2的小者 9. 对指针操作的运算符: &取地址运算 *间址运算符 例 a=&b;取b变量的地址送变量a c=*b;将以b的内容为地址的单元的内容送c 这里要注意: “&”与按位与运算符的差别,如果“&”为“与”,&的两边必须为变量或常量; “*”与指针定义时指针前的“*”的差别。如char *pt,这里的“*”只表示pt为指针变量,不代表间址取内容的运算。,4.3 C51的函数,从用户使用角度划分,函数分为库函数和用户自定义函数。 库函数是编译系统为用户设计的一系列标准函数,用户只需调用,而无需自己去编写这些复杂的函数,如前面所用到的头文件reg51.h、absacc.h等,有的头文件中包括一系列函数,要使用其中的函数必须先使用#include包含语句,然后才能调用。 用户自定义函数是用户根据任务编写的函数 从参数形式上函数分为无参函数和有参函数。 有参函数即是在在调用时,调用函数用实际参数代替形式参数,调用完返回结果给调用函数。,4.3.1 C51函数的定义,C51中函数的定义格式与ANSI C类似,增加了以下内容: 将函数定义为中断服务子程序; 选择函数所使用的寄存器体; 选择存储模式(memory model); 说明函数是一个可重入函数。 函数定义格式为: 返回值类型 函数名(参数) 存储模式 reentrant interrupt n using n 1.格式中 中的内容为可省略的选项。如果函数没有返回值,返回值类型应该声明为void,省略时默认返回值类型为int。 2.存储模式:说明函数的存储模式,可以为small、compact或 lagre,省略时使用程序设定的存储模式。 3.reentrant:说明函数为可重入函数。 4.interrupt n:说明函数是中断类型号n的中断服务子程序。 5.using n:指定函数使用第n个寄存器组,n可以为0、1、2或3。函数声明中不能包括using n选项。,4.3.2 C51函数参数传递及返回值传递,1函数参数传递 为了避免函数调用占用过多的堆栈空间,C51编译器在调用函数时,只将函数的返回地址压入堆栈,而通过寄存器或固定地址的存储单元来传递参数。默认情况下,编译器最多可以通过寄存器传递3个参数,寄存器传递参数的具体情况如下表所示。 寄存器传递参数表,可以用指令REGPARMS和NOREGPARMS说明是否要通过寄存器传递参数。不用寄存器传递参数或传递3个以上参数时,C51编译器会通过固定地址的存储单元进行参数传递。此外,由于bit类型的参数不能用寄存器传递,函数中bit型参数之后的参数都不会用寄存器传递,因此bit型参数应该在参数列表的最后声明。,2函数返回值的传递 C51编译器始终通过寄存器传递函数的返回值,传递返回值的寄存器如下表所示。 传递函数返回值所用的寄存器,例1:定义函数sum,实现两个参数求和功能。 unsigned int sum(unsigned int var1, var2); void main( ) unsigned int rlt; bgn: rlt=sum(100,200); goto bgn; unsigned int sum(unsigned int var1, var2) return(var1+var2); ,在程序开始处添加代码 #pragma REGPARMS,通过寄存器传递参数,则指令编译后的汇编程序如下: 代码地址 机器码 汇编指令 C:0x0003 7DC8 MOV R5,#0xC8 C:0x0005 7C00 MOV R4,#0x00 C:0x0007 7F64 MOV R7,#0x64 C:0x0009 7E00 MOV R6,#0x00 C:0x000B 120020 LCALL sum(C:0020) C:0x000E 8E0A MOV 0x0A,R6 C:0x0010 8F0B MOV 0x0B,R7,在程序开始处添加代码 #pragma NOREGPARMS,通过存储单元传递参数,则指令编译后的汇编程序如下: 代码地址 机器码 汇编指令 C:0x0003 750A00 MOV 0x0A,#0x00 C:0x0006 750B64 MOV 0x0B,#0x64 C:0x0009 750C00 MOV 0x0C,#0x00 C:0x000C 750DC8 MOV 0x0D,#0xC8 C:0x000F 120024 LCALL sum(C:0024) C:0x0012 8E08 MOV 0x08,R6 C:0x0014 8F09 MOV 0x09,R7,参数传递情况分析,例2:定义中断函数。 unsigned int intercnt; unsigned char second; void timer0(void) interrupt 1 using 3 if(+intercnt=4000) second+; intercnt=0; 程序编译链接后,汇编程序如下: 代码地址 机器码 汇编指令 C:0x000B 02000E LJMP timer0(C:000E) /中断向量 /中断程序timer0, 压栈保护函数中使用了的ACC、PSW寄存器 C:0x000E C0E0 PUSH ACC(0xE0) C:0x0010 C0D0 PUSH PSW(0xD0) C:0x0012 75D018 MOV PSW(0xD0),#0x18 /修改PSW, 使用寄存器组3 /省略实现函数功能的代码 C:0x002E D0D0 POP PSW(0xD0) C:0x0030 D0E0 POP ACC(0xE0) C:0x0032 32 RETI,4.3.3 C51函数的调用,对被调函数的说明 :返回值类型 被调函数名(形参表列); 如果被调函数出现在主调函数之后,在主调函数前应对被调函数作以说明。如果被调函数出现在主调函数之前,可以不对被调函数说明。函数调用的形式为:函数名(实际参数表列); 实参和形参的数目相等类型一致,对于无参函数当然不存在实际参数表列。 函数的调用方式有三种: 函数调用语句:即把被调函数名作为调用函数的一个语句;如fun1()。 被调函数作为表达式的运算对象,如 result=2*get(a,b) 此时get函数中的a,b应为实参,其以返回值参予式中的运算。 被调函数作为另一个数的实际参数 如 m=max(a,get(a,b);函数get(a,b)作为函数max()的一个实际参数。,4.4 C51 程序设计,进行程序设计时,为使程序简明清晰,易于阅读、测试、交流、移植以及与其他程序连接和共享,通常需采用模块化程序设计方法。 设计
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 建筑桅杆拆除方案(3篇)
- 水箱维护方案模板(3篇)
- 中介国企安置方案(3篇)
- 店面租房布置方案(3篇)
- 图书馆文化服务活动方案
- 商场开业新颖活动方案
- 商会三八活动方案
- 团圆宴会活动方案
- 咖啡品鉴兴趣活动方案
- 周末组队活动方案
- GB/T 18860-2002摩托车变速V带
- GB/T 16604-2008涤纶工业长丝
- GB 38031-2020电动汽车用动力蓄电池安全要求
- 计算流体力学完整课件
- 国开作业《监督学》形成性考核(三)参考(含答案)238
- 人因工程学课后习题及解答
- 2022年广东省中考地理试卷(含答案)
- 机关档案管理工作培训课件
- 石材产品质量保证书
- 部编版五年级语文下册作文范文全套
- 衰老生物学ppt课件(PPT 57页)
评论
0/150
提交评论