V1.2第10章 80C51单片机的C语言程序设计-宋121203.ppt_第1页
V1.2第10章 80C51单片机的C语言程序设计-宋121203.ppt_第2页
V1.2第10章 80C51单片机的C语言程序设计-宋121203.ppt_第3页
V1.2第10章 80C51单片机的C语言程序设计-宋121203.ppt_第4页
V1.2第10章 80C51单片机的C语言程序设计-宋121203.ppt_第5页
已阅读5页,还剩121页未读 继续免费阅读

下载本文档

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

文档简介

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

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

3、编译器: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的数据类型,返回,数据是具有一定格式的数字或数值,是计算机操作的对象。 数据操作会因数据类型不同而有较大的差异,其差异主要体现在取值范围、存储位置和存储空间大小等几个方面。,C51的数据类型,C51和标准C的区别: 1 C语言的基本数据类型有char、int、short、long、float、double等六种类型。对于C51来说,short、int、f

5、loat和double类型都是一样的。因为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的特殊功能寄存器进行定

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

7、t,如果反向赋值,则结果丢弃高位。,10.2.2 C51数据的存储类型,C51是面向51单片机的程序语言,所以任何数据都必须以一定的方式存储在不同的位置。数据分为常量和变量两种形式。 常量可以用一个标志符号来代表。变量由变量名和变量值组成,每一个变量占据一定的存储空间用以存放变量的值。,C51对变量定义时,既可以定义数据类型,还可以定义存储类型。其格式为: “数据类型 存储类型 变量名” 例如: char data name_var; /*字符变量name_var存储在片内RAM区*/ float idata x ; /*浮点型变量x存储在片内间址RAM区*/ bit bdata flags;

8、 /*位变量flags存储在片内RAM位寻址区*/ 存储类型为可选项,如果不做存储类型的定义,系统将选择默认存储模式来存储,默认类型由编译控制命令指令限制。,存储模式与默认存储类型,例如,若声明char v: 在SMALL模式下,v被定位在data区; 在使用COMPACT模式下,则v被定位在pdata区; 在使用LARGE模式情况下,v被定位在xdata区中。,10.2.3 8051单片机特殊功能寄存器的C51定义一 、特殊功能寄存器的声明,如何对SFR操作,对SFR的操作,只能用直接寻址方式。 为了能直接访问这些特殊功能寄存器SFR,C51提供了一种独特的定义方法,这种定义方法与标准C语言

9、不兼容,只适用于对8051系列单片机进行编程。,“sfr”为保留关键字,其后面必须跟一个特殊寄存器名称 “”后面的地址必须是常数,不允许带有运算表达式,其常数值范围必须在特殊功能寄存器地址范围0 x80到0 xFF之间。 例: sfr SCON0 x90; *串口控制寄存器地址 90H*,其定义语法如下: sfr nameadress,特殊功能寄存器位的声明,在8051单片机的应用中,经常需要对SFR进行位操作,特殊位的定义利用关键字sbit进行说明,与sfr定义一样,用关键字“sbit”定义某些持殊位时能接受任何符号名称,这种地址分配有三种方式: 第1种方式: sbit 位变量=特殊功能寄存

10、器名位位置(0-7有效) 例如:sfr PSW0 xD0; /*定义PSW寄存器地址为0 xD0*/,第2种方式: sbit 位变量名=字节地址位位置(0-7) 例如:sbit OV0 xD02; *OV位地址为0 xD2* 这种方法以一个整常数作为基地址,该值必须在0X800 xFF之间,并能被8整除。 第3种方式: sbit 位变量名=位地址 这种方法将位的绝对地址赋给变量,地址必须位于0 x80到0 xFF之间。 例如:sbit OV0XD2;,10.3 C51的运算符和表达式,运算符就是完成某种特定运算的符号,按运算符在表达式中与运算对象的数量可分为单目运算符,双目运算符和三目运算符。

11、 单目就是指需要有一个运算对象 双目就要求有两个运算对象 表达式则是由运算及运算对象所组成的具有特定含义的式子。,返回,10.3.1 赋值运算符,将数据赋给变量:变量 = 表达式 例如:a=0 xFF;/将常数0 xFF赋于变量a b=c=33;/同时将33赋值给变量b,c f=a+b;/将变量a+b的值赋于变量f 先计算出=右边的表达式的值,然后将得到的值赋给左边的变量。而且右边的表达式可以是一个赋值表达式。 注意:赋值符号“=”与关系运算符“=”(相等)不能混淆。,10.3.2 算术运算符,+ 加或取正值运算符 - 减或取负值运算符 * 乘运算符 / 除运算符 % 取余运算符 算术表达式的

12、形式: 表达式1 算术运算符 表达式2 算术运算符的优先级: 先乘除模,后加减,括号最优先。 算术运算符的结合性:算术运算符的结合性规定为自左至右方向,又称为“左结合性”。,10.3.3 关系运算符,当指定的条件满足时结果为1(真),不满足时结果为0(假)。,关系运算符的优先级别: 前四个具有相同的优先级,后两个也具有相同的优先级,但是前四个的优先级要高于后两个。 例 若a=4,b=3,c=2 则 ab的值为“真”,表达式值为1; b+cbc,由于关系运算符的结合性为左结合,故ab值为1。而1c位为0,故f值为0。,10.3.4 逻辑运算符,逻辑运算符则是用于求条件式的逻辑值,逻辑运算结果只有

13、真与假两种。 else min = b; 程序2 min =(ab)?a:b; 程序2比程序1的代码却序少很多,编译的效率也相对要高,但可读性相对较差。,2.指针和地址运算符,指针类型是一种存放指向另一个数据的地址的变量类型。 * 取内容, 结果是:char是多少个字节? 1字节,返回,4.自增减运算符,自增减运算符的作用是使变量值自动加1或减1。自增减运算有4种形式: +i,-i (在使用i之前,先使i值加(减)1) i+,i- (在使用i之后,再使i值加(减)1) 例: 若i值原来为5, 则 j+i , j位为6 i值也为6 ji+ , j值为5 i值为6,返回,5.复合赋值运算符,在赋值

14、运算符“=”的前面加上其他运算符。 += 加法赋值 = 右移位赋值 -= 减法赋值 unsigned int b; b=100*4; a=b; while(1); b=100*4就可以得知b=0 x190,然而a的值为0 x90,也就是b的低8位。因为b是int型,a是char型数据,只能存储8位,所以在将b赋值给a时执行了数据类型的隐式转换。,返回,隐式转换规则,1. 把所有的char类型的操作数转换成为int类型。 2. 当强制类型转换运算符连接两个操作数时,如果有一个是float类型的,另外一个也转换成float类型;如果有一个是long类型的,另外的一个要转换成long类型;如果有个是

15、unsigned类型的,另一个也转换成unsigned。 3. 如果强制类型转换运算符连接的两个数据是对变量的赋值,则仅将赋值号右边的表达式类型转换成为赋值号左边的类型。 从上面的规则可以知道,在C51中只有char,int,long及float这几种基本的数据类型可以被隐式转换,而其它的数据类型就只能用到显示转换。,强制转换运算符的表达形式: (类型) 表达式 例如: main() unsigned char a; unsigned long b; a=5; b=(unsigned long)a)*10000; 首先把a转换成为可以储存5*10000的数据类型,得到的结果才会正确。用显示类型

16、转换来处理不同类型的数据间运算和赋值是十分方便的,特别对指针变量赋值很有用。,10.4 C51流程控制语句10.4.1 条件语句,if(表达式) 语句1; else 语句2; 如果表达式的值为非0(TURE), 则执行语句1, 执行完语句1后跳过语句2开始继续向下执行;如果表达式的值为0(FALSE), 则跳过语句1而执行语句2。 表达式可以为关系表达式和逻辑表达式的结合式。,特殊说明: 1. 条件执行语句中else 语句2”部分为可选项, 当其缺省时条件语句变成: if(表达式) 语句1; 2. 如果语句1或语句2有多于一条语句要执行时, 必须使用“”把所有语句包括在其中。 3.条件语句可以

17、嵌套,但条件嵌套语句容易出错, 其原因主要是不知道哪个if对应哪个else。 为此规定: else 语句与最近的一个if语句匹配,同时为了提高程序的可读性,通常要求利用缩进方式将层次感写出来。,例: if(x20|xx) printf(Good); else printf(Bad);,4. 可用阶梯式if-else-if结构 if(表达式1) 语句1; else if(表达式2) 语句2; else if(表达式3) 语句3; else 语句n; 这种结构是从上到下逐个对条件进行判断, 一旦发现条件满点足就执行与它有关的语句, 并跳过其它剩余阶梯; 若没有一个条件满足, 则执行最后一个else

18、语句n。,10.4.2 循环语句一 .for循环,for(; ; ) 语句; 初始化一般是一个赋值语句, 它用来给循环控制变量赋初值; 条件表达式是一个关系表达式, 它决定什么时候退出循环; 更新表达式说明循环控制变量每循环一次后按什么方式变化,若更新后条件表达式为假(FALSE)则退出循环。,例如: for(i=1; i10)时结束循环。 注意: 1.for循环中语句可以为语句体, 但要用“”将参加循环的语句括起来。 2.for循环中的初始化、条件表达式和更新表达式都是选择项,可缺省, 但分号“;”不能缺省。 3.for循环可以多层嵌套。,二 、 while循环,while(条件) 语句;

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

20、 开关语句,switch(变量) case 常量1: 语句1或空; case 常量2: 语句2或空; . . . case 常量n; 语句n或空; default: 语句n+1或空; ,执行switch开关语句时, 将变量逐个与case后的常量进行比较, 若与其中一个相等, 则执行该常量下的语句, 若不与任何一个常量相等, 则执行default 后面的语句。 注意: 1. switch中变量可以是数值, 也可以是字符。 2. 可以省略一些case和default。 3. 每个case或default后的语句可以是语句体,同样要使用“”括起来。,10.4.4 break、continue 和 g

21、oto 语句一.break语句,break语句可以跳出循环而执行循环后面的语句,通常用在循环语句和开关语句中配合使用。 break用于开关语句switch中时, 可使程序跳出switch而执行switch以后的语句;如果没有break语句, 则将成为一个死循环而无法退出。 当break语句用于for、do-while、while循环语句中时, 可使程序终止循环而执行循环后面的语句, 通常break语句总是与if语句联在一起,即满足条件时便跳出循环。,二. continue 语句,continue语句的作用是跳过本次循环中剩余的语句而强行执行下一次循环。这是与break语句的最大差别。 cont

22、inue语句通常只用在for、while、do-while等循环体中,常与if条件语句一起使用,用来加速循环。,三.goto 语句,是一种无条件转移语句 goto 语句的使用格式为: goto标号: 标号是一个有效的标识符, 这个标识符加上一个“:”一起出现在函数内某处, 执行goto语句后, 程序将跳转到该标号处并执行其后的语句。 另外标号必须与goto语句同处于一个函数中,但可以不在一个循环层中。 在编程时goto语句要尽量少用,因为它将使程序层次不清,但在多层嵌套退出时, 用goto语句则比较合理。,10.5 C51的构造数据类型10.5.1 数组,数组是一组具有固定数目和相同类型成分分

23、量的有序集合,其成分分量的类型为该数组的基本类型。常用的有整型数组、字符型数组等。数组的各元素必须是同一类型的变量。,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,且b0=0, ,b4=4 */,二维数组 类型说明 存储器类型 数组名整型整型 =初始值

24、 二维数组的存取顺序是按行存取,即先依次存取第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秒,试利用数组编写程序。,#include int code=0 x3f,0 x06,0 x5b,0 x4f,0 x6

25、6,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是不存在的,对变量值的存取是通过地址进行的。,存取的方式有两种: 1)直接访问方式 例:x=a

27、,找到变量a在内存中的位置,即地址l000,然后由地址1000开始的两个字节中取出2并赋给x。 2)间接访问方式 例如:char data *bp ; /*定义指针变量bp */ bp= 指针pi本身存于任意空间,它指向char 型数据,但只能将xdata区域的对象赋值给它,pi占用2字节。,明确定义指针对象的存储类型可以高效访问对象,还能节省存储器的开销,这在严格要求程序体积的项目中很有用处,但只能将符合定义条件的变量赋值与它,兼容性较差,初学者要慎用。,3.指针的应用,指针变量中只能存放指针型数据(即地址),不能将非指针型数据赋值给一个指针变量。如果有一个变量a,则可以利用 /*定义指针变

28、量P为char类型,且指向data存储器*/ P= /*将a的地址的内容送给X*/,10.5.3 结构,将字符,整型,浮点等简单数据类型按层次产生各种构造数据类型,这些组合在一起的数据是互相关联的,这种按固定模式聚集在一起而构成的数据就是结构。 结构的定义和使用: 1.将有共同属性的一组变量放在一个结构体里,既可方便理解和规范编程,也有利于程序的移植和维护。 2.用结构可以分配一连续内存,方便与指针结合起来使用。 3.同一结构中不同分量不能同名。,例:#include struct float amplitude; int frequency; int offset; waveform ; /

29、*结构体定义*/ main() int asizeof(waveform); unsigned char *ptr= /*将结构体的值分别存储到数组a中*/ 显然,结构体数据利用指针和数组就可以将数据读出来,当然也可以将这些数据保存到对应的存储区。,10.5.4 枚举,枚举是一个被命名的整型常数的集合。 例如表示星期的SUNDAY,MONDAY,TUESDAY, WEDNESDAY,THURSDAY,FRIDAY,SATURDAY。 枚举的说明: enum 枚举名 标识符=整型常数, 标识符=整型常数, 枚举变量;,如果枚举没有初始化, 即 “=整型常数”部分缺省,则从第一个标识符开始, 顺次

30、赋给标识符0,1,2,但当枚举中的某个成员赋值后, 其后的成员按依次加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在库文件中已定义的函数,其函数说明在相关的头文件中。对于这类函数,

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

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

33、内为空,但括号不能少。 函数体中能包含有局部变量的定义和程序语句,如函数要返回运算值则要使用return语句进行返回。 在函数体中可以没有任何语句的函数称为空函数。在一个程序项目中能写一些空函数,在以后的修改和升级中能方便地利用这些空函数中进行功能扩展。,int max(x,y) / 函数定义 if(xy) return x; else return y; 该函数的名称为max, 函数返回值类型为int,x和y是函数的形式参数。,10.6.2 C51函数调用,函数是能相互调用的,但在调用函数前,必须对函数的类型进行说明。 函数调用是指一个函数体中引用另一个已定义的函数来实现所需要的功能。 调用

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

35、的进行转换。 混合编程函数转换规则,2.C51函数名和段属性的命名规则,Keil编译器的编译过程是首先将项目中的源文件编译为目标代码(obj文件),然在再通过连接器产生为最终可执行的hex文件。 其中目标代码将其中的代码、数据、常量放在不同的“段”中,保存程序的段称为“代码段”,保存数据的段称为“数据段”,最终目标代码经过Keil的连接器按照“段”的要求转换为程序和数据地址固定的可执行文件。 “段”按定位属性分为:“可重定位段”和“绝对段”。,段命名规则,可重定位段:程序和数据在其分别所对应的存储单元(FLASH和RAM)中的存储地址是浮动的、可重定义的、相对可变的。 绝对段:其地址在连接前就

36、已确定不变,连接器据此为它分配地址。 段的命名格式随存储器模式的不同有所变化。,3.C51函数参数传递和返回,常用的参数传递方式有寄存器传递和固定存储器位置传递两种方式。 寄存器参数传递:寄存器参数传递指参数通过寄存器R1R7来传递的,这种形式可产生高效的代码,利用51单片机的工作寄存器最多传递3个参数。 固定存储位置参数传递:当无寄存器可用或是采用了编译控制指令“NOREGPARMS”,则参数的传递将发生在固定的存储器区域,该存储器区域称为参数传递段,其地址空间取决于编译时所选择的存储器模式。 在SMALL模式下参数传递在内部RAM中完成,在COMPACT和LARGER模式下参数传递要在外部

37、RAM中完成。,例如: Fun1(int a):a是第一个参数,在R6,R7中传递; Fun2(int b,int c, int *d):b在R6,R7中传递, c在R4,R5中传递, *d则在R1,R2,R3中传递。 在混合编程有时还要进行参数返回,在函数或子程序返回时,依据返回数据的类型不同有较大的差别。,寄存器参数传递对应的寄存器,混合编程返回值规则,10.6.4 混合编程形式举例,混合编程主要有C51内嵌汇编代码和C51调用汇编函数等两种形式。 1.C51内嵌汇编代码 要实现在C51函数中直接加汇编代码,需要注意以下几个方面: (1)将要嵌入的汇编语句用#pragma ASM和#pra

38、gma ENDASM语句涵括。 (2)在 Project 窗口中右击包含汇编代码的 C 文件,选择“Options for . ”,然后单击右边的“Generate Assembler SRC File”和“Assemble SRC File”,使选择框由灰色变成黑色状态。,(3)根据选择的编译模式,把KeilC51Lib路径下相应的库文件加入工程中,若未添加此库文件,则会提示“ UNRESOLVED EXTERNAL SYMBOL ”警告。 通过以上三步设置,即可编译生成目标代码。,库文件和编译模式的选择表,例: include void main(void) P2=1; #pragma a

39、sm MOV R7,#10 DEL:MOV R6,#20 DJNZ R6, DJNZ R7,DEL #pragma endasm P2=0; ,注意事项: 1)在汇编语言中可以加标签以执行跳转指令,但标签不要与编译器产生的其他标签相同。 2)在遵循了Cx51参数的调用规则后,如要传递char参数时,编译器会将其编译成通过R7传递,此时若在汇编语言中直接调用R7,会出现定义的变量(形参)未调用警告。,2.C51调用汇编函数,采用C51调用汇编子程序时,被调用的汇编程序的名字、段名、参数读取方式和返回值的设置都要按照相应的规则进行,否则将无法完成调用。同时还要注意以下三个方面的设置: 要在C程序中

40、用extern语句声明即将调用的汇编函数; 要在汇编函数中用PUBLIC语句将被调用的汇编函数声明为公共函数; 在汇编程序前面要注意段属性的说明。,例如:example.c和example.a51为项目中的两个文件,只要将两个文件添加到同一个项目工程中就可以成功编译。,*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* ?P

41、R?DELAY100 SEGMENT CODE; / 在程序存储区中定义段 PUBLIC _DELAY100; /声明函数(带参数传递,故函数形式为_DELAY100) RSEG ?PR?DELAY100; /函数可被连接器放置在任何地方 _DELAY100: DJNZ R5,$ DEL:DJNZ R7,DEL RET END,10.6.4 C51库函数,标准库函数按功能不同写在各种头文件中,使用时只要在程序最前面用#include预处理语句予以说明相应的头文件即可。,C51常用头文件简介,10.7 C51编程实例10.7.1 中断服务函数编程,中断函数编程的完整形式如下: 函数返回值 函数名

42、(参数)编译模式 重入 interrupt n using m 编译模式为SMALL、COMPACT或LARGE,用来指定参数和变量的存储空间。 重入:用于定义可重入函数。 interrupt n 用于定义中断函数,n为中断号,取值范围为0-31,单片机系统中不同的中断号都有固定的程序入口地址。 using m 用于确定中断服务函数所使用的寄存器组,m的取值范围为0-3,常用中断源的中断号和入口地址,在编写C51的中断函数时,注意几个问题: 1.中断函数没有返回值,因此它必须是一个void类型的函数; 2.中断函数不允许利用形式参数进行数据传递,只能利用寄存器实现; 3.不允许直接调用中断函数

43、; 4.中断函数对压栈和出栈的处理由编译器完成,无需人工管理;,5.需要严格注意using m的使用,必须确保寄存器组的正确切换; 6.进入中断函数时,SFR中的A,B,DPH,DPL和PSW都需要入栈; 7.函数退出前,所有的寄存器内容要出栈; 8.函数由8051的特定中断返回指令“RETI”终止。,例:设单片机的晶振频率为12MHz,要求在P1.0引脚上输出周期为2ms的方波。 解:周期为2ms的方波要求定时的间隔1ms,定时时间到则P1.0取反。定时器计数频率晶振频率12=1MHz。计数周期1计数频率1us。1msl000us,故计算器要计数1000次。考虑到计数器工作时是向上计数,所以

44、必须给定时器赋初值为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取反* TF0=0 ; /*软件清TF0* ,(2)用定时器0的方式1编程,采用中断方式。 # inclu

45、de 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 ; /*开启总中断*/ ET0=1 ;/*开启T/C0中断*/ TR0=1 ;/*启动T/C0定时*

46、/ while (1) ; *死循环等待中断* ,10.7.2 串行口编程 通常情况下,使用单片机的串行口时,选用的晶振比较固定,一般为6MHz,12MHz和11.0592MHz,常用于与微机的通信;选用的波特率也相对固定。,串行口常用波特率(波特率单位补充完整),例:请利用单片机串行口发送/接收程序,每接收到字节即刻发送出去,并利用串口调试助手将字符回显在屏幕上。 /* * Title 串口接收发送程序(字母)* */ #include #define uchar unsigned char void main() uchar a; TMOD=0 x20; / 定时器1的工作方式为1,即自动

47、重设初值的8位定时/计数器,TL1=0 xfd; / 采用11.0592MHz的晶振,波特率9600bps TH1=0 xfd; SCON=0 xd8 ; /串行口的工作方式3 PCON=0 x00; TR1=1 ; /启动定时器T1 while(1) while(RI=0) ; /串口接收 RI=0; a=SBUF; SBUF=a;while(TI=0);TI=0; 将该程序编译后写入单片机,并连好串口线。通过串口调试助手向单片机发送字符,则发送的内容被原样发送回来。,10.7.3 LCD接口编程,SMG12864A可以看成是两个64*64的屏幕,每个屏通过CS1,CS2两个片选信号来控制(

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

49、#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高=16x16 -*/ 0 x08,0 x31,0 x86,0 x60,0

50、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 x12,0 x1C,0 x10,0 x00,0 x00,0

51、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 x00,0 x7F,0 x00,0 x00,0 x00,0

52、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,0 x8C,0 xA4,0 xA4,0 xA4,0

53、 xA5,0 xA6,0 xA4,0 xA4,0 xA4,0 xA4,0 x94,0 x8C,0 x04,0 x00,0 x00,0 x80,0 x40,0 x20,0 x18,0 x07,0 x00,0 x00,0 x00,0 x3F,0 x40,0 x40,0 x40,0 x70,0 x00,0 x00; char code bi= /*- 文字: 毕 -*/ 0 x00,0 x00,0 xFF,0 x88,0 x48,0 x28,0 x08,0 x80,0 x7F,0 x88,0 x88,0 x88,0 x84,0 x84,0 xE0,0 x00,0 x04,0 x04,0 x04,0

54、x04,0 x04,0 x04,0 x04,0 xFF,0 x04,0 x04,0 x04,0 x04,0 x04,0 x04,0 x04,0 x00;,/*-延时子程序-*/ void delay(unsigned int t) unsigned int i,j; for(i=0;it;i+); for(j=0;j10;j+); /*-写命令到LCD-*/ void write_com(unsigned char cmdcode) RS=0;RW=0; Lcd_Bus=cmdcode;delay(0); Enable=1;delay(0);Enable=0; ,/*-写数据到LCD-*/ v

55、oid write_data(unsigned char Dispdata) RS=1;RW=0; Lcd_Bus=Dispdata;delay(0); Enable=1;delay(0);Enable=0; /*-清除内存-*/ void Clr_Scr() unsigned char j,k; Mcs=1;Scs=1; write_com(Page_Add+0); write_com(Col_Add+0); for(k=0;k8;k+) write_com(Page_Add+k); for(j=0;j64;j+)write_data(0 x00); ,/*-指定位置显示汉字16*16-*/ void hz_disp16(char pag, char co

温馨提示

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

评论

0/150

提交评论