第10章 80C51单片机的C语言程序设计_第1页
第10章 80C51单片机的C语言程序设计_第2页
第10章 80C51单片机的C语言程序设计_第3页
第10章 80C51单片机的C语言程序设计_第4页
第10章 80C51单片机的C语言程序设计_第5页
已阅读5页,还剩120页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第10章 80C51单片机的C语言程序设计 C51的数据类型 10.2C51流程控制语句10.4C51的运算符和表达式310.3 单片机C语言概述310.110.5C51的构造数据类型10.6C51编程实例310.7C51的函数10.1单片机C语言概述10.1.1 C51的程序结构 返回 C51的程序结构与标准C语言根本相同。其结构特点如下:1.函数是C51程序的根本单位。标准的C51程序通常是多个函数的集合,在这个集合当中,有且只有一个main函数主函数。不管main函数在整个程序中所处的位置如何,C51程序总是从main函数开始执行。2.C51程序书写格式自由,一行内可以写几个语句。3.分

2、号是C51语句的重要组成局部,每个语句和数据定义函数除外的最后必须有一个分号。4.C51本身没有输入输出语句。标准的输入和输出是由scanf和printf等库函数来完成的。对于用户定义的输出,比方直接以输出端口读取键盘输入或驱动LED,那么需要自行编制输出函数。5.可以用/*/对C51程序中的任何局部作注释。10.1.2 C51编译器介绍单片机不能直接执行C51程序,执行前必须经过编译,形成相应的可执行代码,目前开发的编译器种类繁多,并非所有的C51编译器产生高效代码。AMERICAN AUTOMATION编译器:该编译器通过#asm和endasm预处理选择支持汇编语言,此编译器的编译速度较慢

3、。IAR编译器:IAR编译器是由瑞典开发的,它和ANSI C兼容,但需要一个较复杂的链接程序才能运行。BSOTASKing编译器:它是基于windows下的集成开发环境软件(EDE),支持鼠标操作,人机界面友好,它的汇编器和Intel汇编器兼容KEIL编译器:KEIL编译器效率很高。它支持浮点和长整数、重入和递归,支持DOS和Window环境,但它不提供库源代码,只能产生混合代码,假设使用汇编语言,必须分开汇编程序,然后手工连接。假设使用单片模式,它是最好的选择。10.1.3 C51的特点(1) C语言采用与人的思维更接近的关键字和操作函数。(2) C语言提供了大量的标准库文件 (3) C语言

4、采用模块化编程思想(4)C语言可移植性好 (5)通用性好(6)存放器分配和寻址方式由编译器进行管理很多系统特别是实时时钟系统都是用C 语言和汇编语言联合编写的,尤其是对时序要求很严格的驱动程序来说使用汇编语言是唯一的方法。10.2 C51的数据类型返回 数据是具有一定格式的数字或数值,是计算机操作的对象。任何语言编写的程序最终在计算机中运行的只有数据流。编程的目的就是让CPU按照程序流程对各种数据进行相应的操作,并得到编程者所期望的运算结果。数据操作会因数据类型不同而有较大的差异,其差异主要表达在取值范围、存储位置和存储空间大小等几个方面。数据类型长度取值范围字符型Unsigned char8

5、0255Signed char8-128127整型Unsigned int16065535Signed int16-3276832767长整型Unsigned long3204294967295Signed long32-21474836482147483647浮点型float321.18e-383.40e38位型bit10或1sbit10或1SFRsfr80-255Sfr1616065535C51的数据类型 C51和标准C的区别:1. C语言的根本数据类型有char、int、short、long、float、double等六种类型。对于C51来说,short、int、float和double

6、类型都是一样的。因为C51主要针对控制类的应用,所以C51不支持复杂的双精度浮点运算double。2 .float也与标准C一样符合IEEE-754标准,但float的使用和运算,需要调用数学库“math.h函数的支持。3 .布尔处理器是8051单片机的特色,位类型bit可以定义一个位变量,由C51编译器在8051内部RAM区20H-2FH的128个位地址中分配一个位地址。需要注意的是,位类型不能定义指针和数组。4 .特殊功能存放器sfr和sfr16:8051及其兼容产品的特殊功能存放器必须采用直接寻址的方式来访问,sfr可以对8051的特殊功能存放器进行定义,数据占用一个字节。sfr16为1

7、6位特殊功能存放器,8051及其兼容产品的16位特殊功能存放器如DPTR可以用sfr16来定义,数据占用两个字节。5. 在C51编译器提供的头文件reg51.h中已经把所有的特殊功能存放器进行了定义,我们可以直接用include命令使其包括在程序中。注意:在使用时,所有的sfr的名称都必须大写。6. 可寻址位类型sbit:利用sbit可以对8051内部RAM的位寻址空间及特殊功能存放器的可寻址位进行定义。7.数据类型的转换: 不同类型的数据是可以相互转换的,可以通过赋值或者强制转换。赋值转换次序为:bit-char-int-long-float,如果反向赋值,那么结果丢弃高位。10.2.2 C

8、51数据的存储类型 C51是面向51单片机的程序语言,所以任何数据都必须以一定的方式存储在不同的位置。数据分为常量和变量两种形式。常量可以用一个标志符号来代表。变量由变量名和变量值组成,每一个变量占据一定的存储空间用以存放变量的值。存储器类型长度存储位置说 明data8片内RAM直接寻址:007FH,速度最快bdata8可位寻址:20H2FHidada8间接寻址:00FFHpdata8片外RAM分页间址:00FFH, MOVX Ri指令访问xdata16间接寻址:0000FFFFH MOVX DPTR访问code16ROM间接寻址:0000FFFFH MOVC A+DPTR访问C51对变量定义

9、时,既可以定义数据类型,还可以定义存储类型。其格式为: “数据类型 存储类型 变量名。例如: char data name_var; /*字符变量name_var存储在片内RAM区*/ float idata x ; /*浮点型变量x存储在片内间址RAM区*/ bit bdata flags; /*位变量flags存储在片内RAM位寻址区*/存储类型为可选项,如果不做存储类型的定义,系统将选择默认存储模式来存储,默认类型由编译控制命令指令限制。存储模式与默认存储类型存储模式默认存储类型特点说明SMALLdata(小模式)存储于片内RAM,速度快COMPACTpdata(紧凑模式)存储于片外分页

10、RAMLARGExdata(大模式)存储于片外64K的RAM,速度慢例如,假设声明char v,在SMALL模式下,v被定位在data区;在使用COMPACT模式下,那么v被定位在pdata区;在使用LARGE模式情况下,v被定位在xdata区中。10.2.3 8051单片机特殊功能存放器的C51定义一 、特殊功能存放器的声明如何对SFR操作对SFR的操作,只能用直接寻址方式。为了能直接访问这些特殊功能存放器SFR,C51提供了一种独特的定义方法,这种定义方法与标准C语言不兼容,只适用于对8051系列单片机进行编程。其定义语法如下:sfr nameadress “sfr为保存关键字,其后面必须

11、跟一个特殊存放器名称, “后面的地址必须是常数,不允许带有运算表达式,其常数值范围必须在特殊功能存放器地址范围0 x80到0 xFF之间。例: sfr SCON0 x90; *串口控制存放器地址 90H*特殊功能存放器位的声明在8051单片机的应用中,经常需要对SFR进行位操作,特殊位的定义利用关键字sbit进行说明,与sfr定义一样,用关键字“sbit定义某些持殊位时能接受任何符号名称,这种地址分配有三种方式:第1种方式: sbit 位变量=特殊功能存放器名位位置0-7有效例如:sfr PSW0 xD0; /*定义PSW存放器地址为0 xD0*/第2种方式: sbit 位变量名=字节地址位位

12、置0-7例如:sbit OV0 xD02; *OV位地址为0 xD2* 这种方法以一个整常数作为基地址,该值必须在0X800 xFF之间,并能被8整除。第3种方式:sbit 位变量名=位地址 这种方法将位的绝对地址赋给变量,地址必须位于0 x80到0 xFF之间。例如:sbit OV0XD2;10.3 C51的运算符和表达式运算符就是完成某种特定运算的符号,按运算符在表达式中与运算对象的数量可分为单目运算符,双目运算符和三目运算符。单目就是指需要有一个运算对象双目就要求有两个运算对象表达式那么是由运算及运算对象所组成的具有特定含义的式子。返回10.3.1 赋值运算符将数据赋给变量:变量 = 表

13、达式例如:a=0 xFF;/将常数0 xFF赋于变量a b=c=33;/同时将33赋值给变量b,c f=a+b;/将变量a+b的值赋于变量f先计算出=右边的表达式的值,然后将得到的值赋给左边的变量。而且右边的表达式可以是一个赋值表达式。注意:赋值符号“=与关系运算符“=相等不能混淆。10.3.2 算术运算符+ 加或取正值运算符 - 减或取负值运算符* 乘运算符 / 除运算符% 取余运算符 算术表达式的形式: 表达式1 算术运算符 表达式2算术运算符的优先级: 先乘除模,后加减,括号最优先。 算术运算符的结合性:算术运算符的结合性规定为自左至右方向,又称为“左结合性。10.3.3 关系运算符当指

14、定的条件满足时结果为1真,不满足时结果为0假。 关系运算符的优先级别: 前四个具有相同的优先级,后两个也具有相同的优先级,但是前四个的优先级要高于后两个。例 假设a=4,b=3,c=2 那么 ab的值为“真,表达式值为1;b+cbc,由于关系运算符的结合性为左结合,故ab值为1。而1c位为0,故f值为0。10.3.4 逻辑运算符 逻辑运算符那么是用于求条件式的逻辑值,逻辑运算结果只有真与假两种。& :逻辑与AND| :逻辑或(OR)! :逻辑非(NOT)常用运算符的优先级 “与逻辑和“或逻辑是双目运算符,而“非逻辑是单目运算符。逻辑运算符优先级别:逻辑非最高,逻辑与次之,逻辑或最低。C51逻辑

15、运算符、算术运算符、关系运算符和赋值运算符之间优先级的次序中,!(非)运算符优先级最高,算术运算符次之,关系运算符再次之,与和或再次之,最低为赋值运算符。 例: a=1,b=2,c=3,d=4,m=0,n=0,那么m=ab: 因为ab为假(0),即m=0。(m=cd): 因为=运算符的优先级比运算符的低,所以先判断cb& (n=cd): 因为ab为假(0),即m=0为真1,故需继续向右执行,又因为cd为假(0),即n=0为真1,两者相与结果为真1,故表达式值为1。10.3.5 位运算符位运算符:是按位对变量进行运算,但是并不改变参与运算和变量的值。 & 按位与;| 按位或; 按位异或; 按位取

16、反; 位右移;位运算一般的表达形式为:变量1 位运算符 变量2位运算符的优先级:高到低依次是: (按位取反)(右移) &(按位与)(按位异或)|(按位或)位运算符对象只能是整型或字符型数据,不能为实型数据例:a=0 xff,b=0 x01,那么c=a & b :c=0 x01c=a | b :c=0 xffc=ab:c= 0 xfec=a:c=0 x00c=ab:c=0 x7f10.3.6 其他运算符1.条件运算符C51中有一个三目运算符,它就是条件运算符?。逻辑表达式? 表达式1 : 表达式2; 当逻辑表达式的值为真时1时,整个表达式的值为表达式1的值; 当逻辑表达式的值为假0时,整个表达式

17、的值为表达式2的值。返回例:将a和b两数中的较小的值放入min变量中。程序1if (ab)min = a;elsemin = b; 程序2min =(a= 右移位赋值 -= 减法赋值 &= 逻辑与赋值 *= 乘法赋值 |= 逻辑或赋值 /= 除法赋值 = 逻辑异或赋值 %= 取模赋值 != 逻辑非赋值 20|x-10) if(yx) printf(Good); else printf(Bad); 4. 可用阶梯式if-else-if结构 if(表达式1) 语句1; else if(表达式2) 语句2; else if(表达式3) 语句3; else 语句n; 这种结构是从上到下逐个对条件进行判

18、断, 一旦发现条件满点足就执行与它有关的语句, 并跳过其它剩余阶梯; 假设没有一个条件满足, 那么执行最后一个else语句n。10.4.2 循环语句一 .for循环for(; ; ) 语句; 初始化一般是一个赋值语句, 它用来给循环控制变量赋初值; 条件表达式是一个关系表达式, 它决定什么时候退出循环; 更新表达式说明循环控制变量每循环一次后按什么方式变化,假设更新后条件表达式为假FALSE那么退出循环。 例如: for(i=1; i10)时结束循环。注意: 1.for循环中语句可以为语句体, 但要用“将参加循环的语句括起来。 2.for循环中的初始化、条件表达式和更新表达式都是选择项,可缺省

19、, 但分号“;不能缺省。 3.for循环可以多层嵌套。二 、 while循环 while(条件) 语句; 假设while循环条件为真时, 那么执行语句,为假那么循环结束。与for循环一样,while循环总是在循环的开始检验条件,如果一开始条件就不满足,那么意味着循环语句可能一次也不执行就退出。注意: 1、在while循环体内允许空语句。 2、可以有多层循环嵌套。 3、语句可以是语句体, 此时必须用“括起来。4、当条件恒为真时,那么程序进入一个死循环。单片机系统中经常利用这一特性实现前后台系统的调用。三、do-while 循环 do 语句; while(条件); 该循环与while循环的不同在于

20、它先执行循环中的语句, 然后再判断条件是否为真,如果为真那么继续循环,如果为假, 那么终止循环。10.4.3 开关语句switch(变量) case 常量1: 语句1或空; case 常量2: 语句2或空; . . . case 常量n; 语句n或空; default: 语句n+1或空; 执行switch开关语句时, 将变量逐个与case后的常量进行比较, 假设与其中一个相等, 那么执行该常量下的语句, 假设不与任何一个常量相等, 那么执行default 后面的语句。 注意: 1. switch中变量可以是数值, 也可以是字符。 2. 可以省略一些case和default。 3. 每个case

21、或default后的语句可以是语句体,同样要使用“括起来。 10.4.4 break、continue 和 goto 语句一.break语句break语句可以跳出循环而执行循环后面的语句,通常用在循环语句和开关语句中配合使用。break用于开关语句switch中时, 可使程序跳出switch而执行switch以后的语句;如果没有break语句, 那么将成为一个死循环而无法退出。当break语句用于for、do-while、while循环语句中时, 可使程序终止循环而执行循环后面的语句, 通常break语句总是与if语句联在一起,即满足条件时便跳出循环。 二. continue 语句 conti

22、nue语句的作用是跳过本次循环中剩余的语句而强行执行下一次循环。这是与break语句的最大差异。continue语句通常只用在for、while、do-while等循环体中,常与if条件语句一起使用,用来加速循环。三.goto 语句 是一种无条件转移语句goto 语句的使用格式为: goto标号: 标号是一个有效的标识符, 这个标识符加上一个“:一起出现在函数内某处, 执行goto语句后, 程序将跳转到该标号处并执行其后的语句。 另外标号必须与goto语句同处于一个函数中,但可以不在一个循环层中。 在编程时goto语句要尽量少用,因为它将使程序层次不清,但在多层嵌套退出时, 用goto语句那么

23、比较合理。 10.5 C51的构造数据类型10.5.1 数组数组是一组具有固定数目和相同类型成分分量的有序集合,其成分分量的类型为该数组的根本类型。常用的有整型数组、字符型数组等。数组的各元素必须是同一类型的变量。1.数组的定义数组是用同一个名字的不同下标访问的,数组的下标放在方括号中,是从0开始的一组有序整数。例如数组bi,当i=0,1,2,n时,b0,b1,,bn分别是数组b的元素。一维数组类型说明 存储器类型 数组名整型 =初始值 例: char a6=“shiwei; /*字符数组a0=s, ,a5=i*/ int idata b5=0,1,2,3,4; /*字符数组b存储于片内RAM

24、,且b0=0, ,b4=4 */二维数组类型说明 存储器类型 数组名整型整型 =初始值二维数组的存取顺序是按行存取,即先依次存取第1行元素的所有列,再存储第2行,依次类推。例 int a22=1,2,3,4 /*整型数组a有4个元素,且a00=1, a01=2, a10=3, a11=4*/。二维数组的初始化有多种形式,下面的初始化结果是等效的:int a22=1,2,3,4; int a22=1,2,3,4;2.数组的应用例:设单片机的P0引脚和数码管代码段相连,P1.0和数码管公共段相连共阴,请在数码管上依次显示0-9个数字,显示时间间隔为1秒,试利用数组编写程序。#includeint

25、code=0 x3f,0 x06,0 x5b,0 x4f,0 x66,0 x6d,0 x7d,0 x07,0 x7f,0 x6f;void main() for(i=0; i=9; i+) /*数组的访问下标从0开始*/P1=0 xff; /*消隐*/ P0=codei; /*段译码*/P1=0 xfe; /*使能*/ Delay(1000); /*延时1秒*/ 10.5.2 指针指针就是指变量或数据所在的存储区的地址,它为变量的访问提供了一个特殊的方式。返回1.指针的根本概念如果程序中定义了一个变量,C51编译器在编译时就给这个变量在内存中分配相应的存储空间。变量有变量名和变量值两个概念,其

26、中变量名是数据的标号,变量值那么是数据的内容。内存单元也有两个概念,一个是内存单元的地址,一个是内存单元的内容。前者是内存对该单元的编号,它表示在内存中的位置。后者指的是在该内存单元中存放的数据。在变量与内存单元的对应关系中,变量的变量名与内存单元的地址相对应。变量的变量值与内存单元的内容相对应。假设程序中定义了两个整型变量a和b,它们的值分别为2、3,而C51编译系统将地址为1000和1001的两个字节内存单元分配给了变量a,将地址为1002和1003的两字节内存单元分配给了变量b,那么变量a和b的地址为1000和1002,在内存中变量名a、b是不存在的,对变量值的存取是通过地址进行的。存取

27、的方式有两种:1)直接访问方式例:x=a,找到变量a在内存中的位置,即地址l000,然后由地址1000开始的两个字节中取出2并赋给x。2)间接访问方式例如:char data *bp ; /*定义指针变量bp */bp=&b ; /*将b的地址赋给指针变量bp*/X=*bp ; /*将b的内容3送给X*/要读取变量b的值时,可以将变量b的地址放在另一个内存单元中(如放在2021、2021中),访问时,先找到存放变量b的地址的内存单元的地址(2021、2021),然后从中取出变量b的地址(1002),然后从地址为(1002、1003)的两字节内存单元中取出变量b的值3,该读取过程中就使用了指针。

28、为了使用指针进行间接访问,必须弄清关于指针的两个根本概念,即变量的指针和指向变量的指针变量(简称指针变量)。变量的指针:变量的指针就是变量的地址。对于上面提到的变量a而言,其指针就是1000。指向变量的指针变量:假设有一个变量专门用来存放另一个变量的地址(即指针),那么该变量称为指向变量的指针变量(简称指针变量)。上例中提到的地址为2021的内存单元,如果我们定义一个变量ap,并使其定位在地址为2021的这个内存单元上,那么ap就是一个指针变量。因为ap中(即2021址址单元中)存放着变量a的地址1000。上例中我们可以说变量a的指针(地址)为1000,不能说a的指针变量是1000。变量a的指

29、针变量应该是ap,ap的指针是2021。2. 指针的根本类型变量的指针就是变量的地址,用取地址运算符“&取得。语句ap = &a 就能把所取得的a指针地址存放在ap指针变量中。ap的值就变为 1000H。可见指针变量的内容是另一个变量的地址。指针定义的一般的格式: 数据类型指向对象的存储器类型*指针存储器类型变量名;一般指针类型当指向对象的存储类型缺省时指针变量为一般类型,一般指针占用3个字节,第一个字节存放指针的存储类型编码,第二、三字节分别存放指针的高位和低位的地址。存储类型data/bdata/idataxdatapdatacode编码0 x000 x010 xfe0 xff例如: ch

30、ar * xdata pi 指针pi本身存于xdata空间,它指向char 型数据,任何区域的对象都可以赋值给它,第一字节为0 x01,其余字节为地址。由于指针变量的指向对象没有确定存储位置,所以在编译不能确定对象的存储位置,只能在程序运行时才能确定,故程序运行速度比较慢,但由于一般指针可以存储任何变量而不必考虑变量在单片机的存储位置,所以在单片机编程时绝大多数的指针都可采用一般指针形式。基于存储器的指针类型当指向对象的存储器类型缺省时,指针变量为基于存储器的指针类型。由于不必为指针选择存储器,指针的长度可以为1字节idata,data,pdata或2字节code,xdata 例如: char

31、 data * xdata pi; 指针pi本身存于xdata空间,它指向char 型数据,且只能将data区域的对象赋值给pi,pi只占用1个字节。 char xdata * pi; 指针pi本身存于任意空间,它指向char 型数据,但只能将xdata区域的对象赋值给它,pi占用2字节。明确定义指针对象的存储类型可以高效访问对象,还能节省存储器的开销,这在严格要求程序体积的工程中很有用处,但只能将符合定义条件的变量赋值与它,兼容性较差,初学者要慎用。3.指针的应用指针变量中只能存放指针型数据即地址,不能将非指针型数据赋值给一个指针变量。如果有一个变量a,那么可以利用&a表示变量a的地址,如果

32、执行以下语句P=&a;那么表示将a的地址赋给了指针变量P,即P指向了变量a。为了获取指针所指向的内容,可利用指针运算符“*来实现。例:char data *P; /*定义指针变量P为char类型,且指向data存储器*/P=&a; /*将a的地址赋给指针变量P,a的地址为片内RAM区*/X=*P ; /*将a的地址的内容送给X*/10.5.3 结构将字符,整型,浮点等简单数据类型按层次产生各种构造数据类型,这些组合在一起的数据是互相关联的,这种按固定模式聚集在一起而构成的数据就是结构。结构的定义和使用: 1.将有共同属性的一组变量放在一个结构体里,既可方便理解和标准编程,也有利于程序的移植和维

33、护。 2.用结构可以分配一连续内存,方便与指针结合起来使用。 3.同一结构中不同分量不能同名。返回.例:#include structfloat amplitude; int frequency; int offset;waveform ; /*结构体定义*/main() int asizeof(waveform); unsigned char *ptr=&waveform;unsigned char i; waveform.amplitude=1 ; /*结构体赋值*/waveform.frequency=2 ; /*结构体赋值*/ waveform.offset=3 ; /*结构体赋值*/

34、for(i=0;isizeof(waveform);i+)ai=*(ptr+i) ; /*将结构体的值分别存储到数组a中*/显然,结构体数据利用指针和数组就可以将数据读出来,当然也可以将这些数据保存到对应的存储区。10.5.4 枚举枚举是一个被命名的整型常数的集合。例如表示星期的SUNDAY,MONDAY,TUESDAY, WEDNESDAY,THURSDAY,FRIDAY,SATURDAY。枚举的说明:enum 枚举名标识符=整型常数,标识符=整型常数,标识符=整型常数,枚举变量;返回如果枚举没有初始化, 即 “=整型常数局部缺省,那么从第一个标识符开始, 顺次赋给标识符0,1,2,但当枚举

35、中的某个成员赋值后, 其后的成员按依次加1的规那么确定其值。例如:enum string x1,x2,x3=50,x4 那么x1=0, x2=1, x3=50, x4=51注意:1.枚举中每个成员间用逗号隔离,不是分号,且最后一个逗号可省。2.初始化时可以赋负数,以后的标识符仍依次加1。3.枚举变量只能取枚举说明结构中的某个标识符常量。10.6 C51的函数 返回 main()也算是一个函数,只不过它比较特殊,编译时以它做为程序的开始段。C51函数分为: 库函数,自定义函数。库函数是C51在库文件中已定义的函数,其函数说明在相关的头文件中。对于这类函数,用户在编程时只要用#include预处理

36、指令将头文件包含在用户文件中就可直接调用。用户函数是用户自己定义和调用的一类函数。 一个函数在程序中可以有三种形态:函数定义、函数调用和函数说明。函数定义和函数调用不分先后,但假设调用的函数在定义之前,那么在调用前必须先进行函数说明。函数说明是一个没有函数体的函数定义,而函数调用那么要求有函数名和实参数表。#include / 使用include预处理伪指令将所需库函数包含进来unsigned int z ;/ 变量定义int max(x,y);/ 函数说明unsigned int a=2;unsigned int b=3;main()while(1)z=max(a,b) ;/函数调用int

37、max(x,y) / 函数定义if(xy) return x;elsereturn y;10.6.1 C51函数定义函数定义的形式:函数返回值类型 函数名形式参数函数体函数返回值其实就是一个变量,只要按变量类型来定义函数类型就行。函数体返回值的类型一定要和函数类型一致,否那么会造成错误。如果函数不需要返回值应用关键字“void明确表示。返回函数名的定义在遵循C语言变量命名规那么的同时,不能在同一程序中定义同名的函数。形式参数是指调用函数时要传入到函数体内参与运算的变量,它可以有多个或没有,没有形式参数时括号内为空,但括号不能少。函数体中能包含有局部变量的定义和程序语句,如函数要返回运算值那么要

38、使用return语句进行返回。在函数体中可以没有任何语句的函数称为空函数。在一个程序工程中能写一些空函数,在以后的修改和升级中能方便地利用这些空函数中进行功能扩展。int max(x,y) / 函数定义if(xy) return x;elsereturn y;该函数的名称为max, 函数返回值类型为int,x和y是函数的形式参数。10.6.2 C51函数调用函数是能相互调用的,但在调用函数前,必须对函数的类型进行说明。函数调用是指一个函数体中引用另一个已定义的函数来实现所需要的功能。调用函数的一般形式: 函数名 (实际参数表);函数名就是指被调用的函数名。假设被调用函数有参数,那么主调函数必须

39、把被调函数所需的参数传递给被调函数的参数,即实参,实参向形参的传递是单向的,且实参的类型、位置应与形参一一对应,否那么就会产生错误。返回main()while(1)z=max(a,b) ;/函数调用max函数调用时a和b为实际参数,其实际参数和形式参数的对应关系为: a=x,b=y。10.6.3 混合编程简介混合编程中,首先要了解C51函数名和汇编函数名之间的转换规律,其次要明白函数间参数的传递原那么。1.C51函数名的转换在KEIL C51中,编译器对C语言程序中的函数会自动的进行转换。 混合编程函数转换规那么C51函数名称转换后的函数名称说 明void func1(void)func1无参

40、数传递的函数void func2(int)_func2通过寄存器传递参数的函数void func3(char)reentrant_?func3可重入函数固定存储位置传递参数的函数2.C51函数名和段属性的命名规那么Keil编译器的编译过程是首先将工程中的源文件编译为目标代码obj文件,然在再通过连接器产生为最终可执行的hex文件。其中目标代码将其中的代码、数据、常量放在不同的“段中,保存程序的段称为“代码段,保存数据的段称为“数据段,最终目标代码经过Keil的连接器按照“段的要求转换为程序和数据地址固定的可执行文件。“段按定位属性分为:“可重定位段和“绝对段。 段命名规那么可重定位段:程序和数

41、据在其分别所对应的存储单元FLASH和RAM中的存储地址是浮动的、可重定义的、相对可变的。绝对段:其地址在连接前就已确定不变,连接器据此为它分配地址。段的命名格式随存储器模式的不同有所变化。段内容段类型段 名程序代码CODE?PR?函数名?模块名变量DATA?DT?函数名?模块名(SMALL模式)PDATA?PD?函数名?模块名(COMPACT模式)XDATA?XD?函数名?模块名(LARGE模式)BIT型变量BIT?BI?函数名?模块名3.C51函数参数传递和返回常用的参数传递方式有存放器传递和固定存储器位置传递两种方式。存放器参数传递:存放器参数传递指参数通过存放器R1R7来传递的,这种形

42、式可产生高效的代码,利用51单片机的工作存放器最多传递3个参数。固定存储位置参数传递:当无存放器可用或是采用了编译控制指令“NOREGPARMS,那么参数的传递将发生在固定的存储器区域,该存储器区域称为参数传递段,其地址空间取决于编译时所选择的存储器模式。在SMALL模式下参数传递在内部RAM中完成,在COMPACT和LARGER模式下参数传递要在外部RAM中完成。例如: Fun1int a:a是第一个参数,在R6,R7中传递; Fun2int b,int c, int *d:b在R6,R7中传递, c在R4,R5中传递, *d那么在R1,R2,R3中传递。在混合编程有时还要进行参数返回,在函

43、数或子程序返回时,依据返回数据的类型不同有较大的差异。存放器参数传递对应的存放器传递参数char、1字节指针int 、2字节指针Long、 float一般指针参数1R7R6,R7R4-R7R1,R2,R3参数2R5R4,R5R4-R7R1,R2,R3参数3R3R2,R3无R1,R2,R3混合编程返回值规那么返回类型所用寄存器返回类型所用寄存器BitC累加器Char,1字节指针R7longR4-R7int,2字节指针R6,R7floatR4-R7一般指针R1-R310.6.4 混合编程形式举例 混合编程主要有C51内嵌汇编代码和C51调用汇编函数等两种形式。 1.C51内嵌汇编代码要实现在C51

44、函数中直接加汇编代码,需要注意以下几个方面:1将要嵌入的汇编语句用#pragma ASM和#pragma ENDASM语句涵括。2在 Project 窗口中右击包含汇编代码的 C 文件,选择“Options for . ,然后单击右边的“Generate Assembler SRC File和“Assemble SRC File,使选择框由灰色变成黑色状态。 3根据选择的编译模式,把KeilC51Lib路径下相应的库文件参加工程中,假设未添加此库文件,那么会提示“ UNRESOLVED EXTERNAL SYMBOL 警告。通过以上三步设置,即可编译生成目标代码。库文件和编译模式的选择表库文件

45、编译模式C51S.LIB没有浮点运算的 Small modelC51C.LIB没有浮点运算的 Compact modelC51L.LIB没有浮点运算的 Large modelC51FPS.LIB带浮点运算的 Small modelC51FPC.LIB带浮点运算的 Compact modelC51FPL.LIB带浮点运算的 Large model例:include void main(void)P2=1;#pragma asmMOV R7,#10DEL:MOV R6,#20DJNZ R6,DJNZ R7,DEL#pragma endasmP2=0;本卷须知:1在汇编语言中可以加标签以执行跳转指令

46、,但标签不要与编译器产生的其他标签相同。2在遵循了Cx51参数的调用规那么后,如要传递char参数时,编译器会将其编译成通过R7传递,此时假设在汇编语言中直接调用R7,会出现定义的变量形参未调用警告。2.C51调用汇编函数 采用C51调用汇编子程序时,被调用的汇编程序的名字、段名、参数读取方式和返回值的设置都要按照相应的规那么进行,否那么将无法完成调用。同时还要注意以下三个方面的设置:要在C程序中用extern语句声明即将调用的汇编函数;要在汇编函数中用PUBLIC语句将被调用的汇编函数声明为公共函数;在汇编程序前面要注意段属性的说明。 例如:example.c和example.a51为工程中

47、的两个文件,只要将两个文件添加到同一个工程工程中就可以成功编译。*example.c*#include extern void delay100();extern DELAY100(a,b); /声明外部函数,以便在C51中调用直接调用。unsigned int a=4;unsigned int b=3;main()DELAY100(a,b);*example.a51*?PR?DELAY100 SEGMENT CODE; / 在程序存储区中定义段PUBLIC _DELAY100; /声明函数带参数传递,故函数形式为_DELAY100RSEG ?PR?DELAY100; /函数可被连接器放置在任

48、何地方_DELAY100: DJNZ R5,$DEL:DJNZ R7,DELRETEND10.6.4 C51库函数标准库函数按功能不同写在各种头文件中,使用时只要在程序最前面用#include预处理语句予以说明相应的头文件即可。C51常用头文件简介 头文件名说 明reg51.h51单片机的特殊寄存器reg52.h52单片机的特殊寄存器stdio.h标准输入和输出程序math.h数学程序absacc.h允许直接访问8051不同存储区的宏定义string.h字符串操作程序、缓冲区操作程序10.7 C51编程实例10.7.1 中断效劳函数编程中断函数编程的完整形式如下:函数返回值 函数名(参数)编译

49、模式 重入 interrupt n using m编译模式为SMALL、COMPACT或LARGE,用来指定参数和变量的存储空间。重入:用于定义可重入函数。interrupt n 用于定义中断函数,n为中断号,取值范围为0-31,单片机系统中不同的中断号都有固定的程序入口地址。using m 用于确定中断效劳函数所使用的存放器组,m的取值范围为0-3返回常用中断源的中断号和入口地址中断号中断源中断向量0外部中断 00003H1 定时器/计数器 0000BH2外部中断 10013H3 定时器/计数器 1001BH4 串行口0023H 在编写C51的中断函数时,注意几个问题:1.中断函数没有返回值

50、,因此它必须是一个void类型的函数;2.中断函数不允许利用形式参数进行数据传递,只能利用存放器实现;3.不允许直接调用中断函数;4.中断函数对压栈和出栈的处理由编译器完成,无需人工管理;5.需要严格注意using m的使用,必须确保存放器组的正确切换;6.进入中断函数时,SFR中的A,B,DPH,DPL和PSW都需要入栈;7.函数退出前,所有的存放器内容要出栈;8.函数由8051的特定中断返回指令“RETI终止。例:设单片机的晶振频率为12MHz,要求在P1.0引脚上输出周期为2ms的方波。解:周期为2ms的方波要求定时的间隔1ms,定时时间到那么P1.0取反。定时器计数频率晶振频率12=1

51、MHz。计数周期1计数频率1us。1msl000us,故计算器要计数1000次。考虑到计数器工作时是向上计数,所以必须给定时器赋初值为65536-1000。 (1)用定时器0的方式1编程,采用查询方式。#include sbit P1_0=P10;void main()TMOD=0 x01 /*设置定时器0的工作方式为1*/TR0=1;/*开启T/C0*/For(;) TH0(65536-1000)256); /*装载计值*/ TL0(65536-1000)256); do while (!TF0); *查询等待TF0置位(计数溢出时TF置1)* P1_0!P1_0 ; /*时间到P1.0取反

52、* TF0=0 ; /*软件清TF0*(2)用定时器0的方式1编程,采用中断方式。# include sbit P1_0=P10 ;定义T/C中断函数*/void time0(void) interrupt 1 using 1 P1_0!P1_0 ; /* P1.0取反* /*装载计数初值*/TH0(65536-1000)256); TL0(65536-1000)256);void main(void) /*设置定时器0的工作方式为1*/TMOD=0 x01; /*装载计数初值*/ TH0(65536-1000)256); TL0(65536-1000)256);EA=1 ; /*开启总中断*

53、/ET0=1 ;/*开启T/C0中断*/TR0=1 ;/*启动T/C0定时*/ while (1) ; *死循环等待中断*10.7.2 串行口编程通常情况下,使用单片机的串行口时,选用的晶振比较固定,一般为6MHz,12MHz和11.0592MHz,常用于与微机的通信;选用的波特率也相对固定。 串行口常用波特率(波特率单位补充完整)串行口工作方式波特率fOSC= 6MHzfOSC= 12MHzfOSC=11.0592MHzSMOD TMOD TH1SMOD TMOD TH1SMOD TMOD TH1方式01Mb/s 方式2375Kb/s 1 1 187.5Kb/s 0 方式1或方式362.5K

54、b/s 1 20 FF H 19.2Kb/s 1 20 FDH9.6Kb/s 0 20 FDH4.8Kb/s 1 20 F3H 0 20 FAH2.4Kb/s1 20 F3H 0 20 F3H 0 20 F4H1.2Kb/s1 20 E6H 0 20 E6H 0 20 E8H600Kb/s1 20 CCH 0 20 CCH300Kb/s0 20 CCH137.5Kb/s1 20 1DH110b/s0 20 72H例:请利用单片机串行口发送/接收程序,每接收到字节即刻发送出去,并利用串口调试助手将字符回显在屏幕上。 /* Title 串口接收发送程序字母*/#include #define uc

55、har unsigned charvoid main()uchar a;TMOD=0 x20; / 定时器1的工作方式为1,即自动重设初值的8位定时/计数器 TL1=0 xfd; / 采用11.0592MHz的晶振,波特率9600bpsTH1=0 xfd; SCON=0 xd8 ; /串行口的工作方式3PCON=0 x00; TR1=1 ; /启动定时器T1while(1) while(RI=0) ; /串口接收RI=0; a=SBUF;SBUF=a;while(TI=0);TI=0;将该程序编译后写入单片机,并连好串口线。通过串口调试助手向单片机发送字符,那么发送的内容被原样发送回来。10.

56、7.3 LCD接口编程SMG12864A可以看成是两个64*64的屏幕,每个屏通过CS1,CS2两个片选信号来控制低电平有效,对于每一片左边和右边,分成8行*64列,每一行的高度为8位,从上到下对应的是DB的低位到高位。每写一个数据,光标自动向后移动,到行末的时候返回到下一行行首。具体信号如下:返回CS1,CS2:屏幕控制片选信号,低电平有效;RST:复位信号,低电平有效;DB0-DB7:双向并行指令/数据信号;E:使能信号,高电平有效;R/W:读写控制信号,低电平为写入,高电平为读出;RS:数据命令控制信号,低电平为指令,高电平为数据;例:要求在LCD上交替实现“测试开始和“测试完毕字样,试

57、编写程序。#include #define Disp_On 0 x3f/开显示指令码#define Disp_Off 0 x3e/关显示指令码#define Col_Add 0 x40/列指针指令码#define Page_Add 0 xb8/页指针指令码#define Start_Line 0 xc0/初始行指令码#define Lcd_Bus P1 sbit Mcs=P31;sbit Scs=P32; sbit Enable=P34; sbit RS=P30; sbit RW=P33;char code ce= /*- 文字: 测 -*/*- 宋体12; 此字体下对应的点阵为:宽x高=16

58、x16 -*/0 x08,0 x31,0 x86,0 x60,0 x00,0 xFE,0 x02,0 xF2,0 x02,0 xFE,0 x00,0 xF8,0 x00,0 x00,0 xFF,0 x00,0 x04,0 xFC,0 x03,0 x00,0 x80,0 x47,0 x30,0 x0F,0 x10,0 x67,0 x00,0 x07,0 x40,0 x80,0 x7F,0 x00;char code shi= /*- 文字: 试 -*/0 x40,0 x42,0 xDC,0 x08,0 x00,0 x90,0 x90,0 x90,0 x90,0 x90,0 xFF,0 x10,0

59、 x12,0 x1C,0 x10,0 x00,0 x00,0 x00,0 x7F,0 x20,0 x10,0 x20,0 x20,0 x1F,0 x10,0 x10,0 x01,0 x06,0 x18,0 x20,0 x78,0 x00;char code kai= /*- 文字: 开 -*/0 x40,0 x42,0 x42,0 x42,0 x42,0 xFE,0 x42,0 x42,0 x42,0 x42,0 xFE,0 x42,0 x42,0 x42,0 x42,0 x00,0 x00,0 x40,0 x20,0 x10,0 x0C,0 x03,0 x00,0 x00,0 x00,0 x

60、00,0 x7F,0 x00,0 x00,0 x00,0 x00,0 x00;char code shi3= /*- 文字: 始 -*/0 x10,0 x90,0 x70,0 x1F,0 x12,0 xF0,0 x00,0 x20,0 x70,0 x28,0 x27,0 x22,0 x28,0 x70,0 x20,0 x00,0 x40,0 x21,0 x12,0 x0C,0 x06,0 x09,0 x30,0 x00,0 x7F,0 x21,0 x21,0 x21,0 x21,0 x7F,0 x00,0 x00;char code wan= /*- 文字: 完 -*/0 x00,0 x90,

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论