教学课件PPT单片机C51语言程序设计基础.ppt_第1页
教学课件PPT单片机C51语言程序设计基础.ppt_第2页
教学课件PPT单片机C51语言程序设计基础.ppt_第3页
教学课件PPT单片机C51语言程序设计基础.ppt_第4页
教学课件PPT单片机C51语言程序设计基础.ppt_第5页
已阅读5页,还剩52页未读 继续免费阅读

下载本文档

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

文档简介

第10章 单片机c51语言程序设计基础,学习目标,掌握c51程序设计的基本方法及特点制,掌握c语言在单片机开发中程序的语法规则,了解c语言在单片机开发中程序的结构特点,学习重点,c语言在单片机应用系统中程序设计实例的分析和讨论,“c51”程序设计的特点,c语言程序设计的基本方法,主要内容, 任务1:学习c51程序的结构,10 单片机c51语言程序设计基础, 任务2:c51数据类型、常量和变量的定义, 任务3:c51程序的运算符及其语法规则, 任务4:掌握c51程序设计特点, 任务5:理解c51程序设计举例,10.1 c51程序的结构,c51与标准c的区别,c51在语法规则、程序结构及程序设计方法等与标准的c语言程序设计相同。 主要区别在于: (1)c51语言编写单片机应用程序时,需根据单片机存储结构及内部资源定义相 应的数据类型和变量,而标准的c语言程序不需要考虑这些问题 。 (2)c51中变量的存储模式是与mcs-51单片机的存储器紧密相关; (3)c51中的数据类型与标准c的数据类型也有一定的区别,在c51中还增加了几种针对mcs-51单片机特有的数据类型; (4)c51与标准c的输入输出处理不一样,c51中的输入输出是通过mcs-51串行口来完成的,输入输出指令执行前必须要对串行口进行初始化; (5)c51与标准c在函数使用方面也有一定的区别,c51中有专门的中断函数。,10.1 c51程序的结构,10.1.1 c51程序的结构特点,c51语言与标准c语言的不同之处在于,c51语言根据单片机存储结构及内部资源定义了相应的c语言中的数据类型和变量,c51程序结构与一般c语言没有什么差别。一个c51程序大体上是一个函数定义的集合,在这个集合中有且仅有一个名为main()函数(主函数)。主函数是程序的入口,主函数中的所有语句执行完毕,则程序执行结束。 在c51中,函数定义由类型、函数名、参数表和函数体四部分组合而成函数名是一个标识符,标识符是大小写可区别的,最长为255个字符。参数表是用圆括号括起来的若干参数,项与项之间用逗号隔开。函数体是用大括号括起来的若干c语句,语句与语句之间用分号隔开,最后一个语句一般是return(在主函数中可以省略)。每一个函数都返回一个值,该值由return语句中的表达式指定(省略时为零)。函数的类型就是返回值的类型,函数类型(除整型外)均需在函数名前加以指定。,2.1 c51程序的结构,以87c51单片机最小系统的p1.0口控制一个led发光管的亮和灭为例来介绍c51程序的一般格式。图10-1为其控制电路图。,图10-1 87c51 p1.0口控制led灯电路图,10.1 c51程序的结构,#include / 包括一个51标准内核的头文件 sbit p10=p10; / 头文件中没有定义的i/o口就要自己来定义 void main() / 主程序 unsigned int n; do for(n=0;n20000;n+); / 延时 p10=0; / 设p1.0口为低电平,点亮led for(n=0;n20000;n+); / 延时 p10=1; / 设p1.0口为高电平,熄灭led while (1); / 程序循环 ,对应的c51控制程序如下:,c51函数的一般格式如下: 函数类型 函数名(函数形式参数表 / 函数说明部分 数据说明部分; / 函数体部分 执行语句部分; 其中加 时,表示其中的内容可以省略。 一个函数在程序中可以以三种形态出现。函数定义、函数调用和函数说明。函数定义相当于汇编中的一般子程序。函数调用相当于汇编中调用子程序的call语句,在c语言中,更普遍地规定函数调用可以出现在表达式中。函数定义和函数调用不分先后,但若调用在定义之前,那么在调用前必须先进行函数说明。函数说明是一个没有函数体的函数定义,而函数调用则要求有函数名和实参数表。 c51中函数分为两大类。一类是库函数,一类是用户定义的函数。库函数是c51在库文件中已定义的函数,其函数说明在相关的头文件中。对于这类函数,用户在编程时只要用include预处理指令将头文件包含在用户文件中,直接调用即可。用户函数是用户自己定义、自己调用的一类函数。从某种意义上来看,c编程实际上是对一系列用户函数的定义。,10.1 c51程序的结构,10.1.2 c51函数的一般格式,10.2 c51数据类型、常量和变量的定义,10.2.1 c51数据类型,用c51编写程序,虽不像用汇编语言那样具体地组织、分配存储器资源和处理端口数据,但对数据类型与变量的定义必须要与单片机的存储结构相关联,否则编译器不能正确地映射定位。每写一个程序,总离不开数据的应用,在学习c51语言的过程中掌握理解数据类型也是很关键的。,c51的数据类型分为基本数据类型和组合数据类型,情况与标准c中的数据类型基本相同,float型与double型相同,另外,c51中还有专门针对于mcs-51单片机的特殊功能寄存器型和位类型。,表10-1 keil uvision2 c51 编译器所支持的数据类型,常量是指在程序执行过程中其值不能改变的量。在c51中支持整型常量、浮点型常量、字符型常量和字符串型常量。,整型常量也就是整型常数,根据其值范围在计算机中分配不同的字节数来存放。在c51中它可以表示成以下几种形式: 十进制整数。如234、-56、0等。 十六进制整数。以0x开头表示,如0x12表示十六进制数12h。 长整数。在c51中当一个整数的值达到长整型的范围,则该数按长整型存放,在存储器中占四个字节,另外,如一个整数后面加一个字母l,这个数在存储器中也按长整型存放。如123l在存储器中占四个字节。,10.2 c51数据类型、常量和变量的定义,10.2.2常量的定义,浮点型常量也就是实型常数。有十进制表示形式和指数表示形式。 十进制表示形式,由数字和小数点组成。如 0.123、34.645等都是十进制数表示形式的浮点型常量,整数或小数部分为 0,能省略但必须有小数点 。 指数表示形式为: 数字 .数字 e 数字 例如:123.456e-3、-3.123e2等都是指数形式的浮点型常量, 中的内容为可选项,其中内容根据具体情 况可有可无,但其余部分必须有。,字符型常量是用单引号引起的字符,如a、1、f等。可以是可显示的ascii字符,也可以是不可显示的控制字符。对不可显示的控制字符须在前面加上反斜杠“”组成转义字符。利用它可以完成一些特殊功能和输出时的格式控制。,10.2 c51数据类型、常量和变量的定义,字符串型常量由双引号内的字符组成,如“test“,“ok“等。当引号内没有字符时,为空字符串。在使用特殊字符时同样要使用转义字符如双引号。在c中字符串常量是做为字符类型数组来处理的,在存储字符串时系统会在字符串尾部加上0 转义字符以作为该字符串的结束符。字符串常量“a“和字符常量a是不同的,前者在存储时多占用一个字节的空间。,它的值是一个二进制数。,10.2 c51数据类型、常量和变量的定义,常量可用在不必改变值的场合,如固定的数据表,字库等。常量的定义方式有几种,下面加以说明。 #difine false 0x0; / 用预定义语句可以定义常量 #difine true 0x1; / 这里定义false为0,true为1在程序中用到 / false 编译时自动用0替换,同理true替换为1 unsigned int code a=100; / 这一句用code把a定义在程序存储器中并赋值 const unsigned int c=100; / 用const定义c为无符号int 常量并赋值 以上代码中常量的值都保存在程序存储器中,而程序存储器在运行中是不允许被修改的,所以如果在这两句后面用了类似a=110,a+这样的赋值语句,编译时将会出错。,10.2 c51数据类型、常量和变量的定义,10.2 c51数据类型、常量和变量的定义,10.2.3 变量的定义,变量是一种在程序执行过程中其值能不断变化的量。要在程序中使用变量必须先用标识符作为变量名,并指出所用的数据类型和存储模式,这样编译系统才能为变量分配相应的存储空间。在c51中规定变量名可以由字母、数字和下划线三种字符组成,且第一个字母必须为字母或下划线。定义一个变量的格式如下: 存储种类 数据类型 存储器类型 变量名表 在定义格式中除了数据类型和变量名表是必要的,其他都是可选项。存储种类有四种: 自动(auto); 作用范围:在定义它的函数内或复合语句内部。当定义它的函数 或复合语句执行时,c51才为变量分配存储空间,结束时所占用的存储空间释放。 外部(externad);用extern声明的变量为外部变量,是在其它文件定义过的全局变量。用extern声明后,便可以在所声明的文件中使用。 静态(static);在函数内可以任意使用和修改,函数运行结束后会一直存在,但在函数外不可见,即在函数体外得到保护,不能被访问。 寄存器(register)。用register定义的变量为寄存器变量,告诉编译器他所定义的变量使用率较高。寄存器变量存放在cpu的寄存器中,这种变量处理速度快,但数目少。 缺省类型为自动(auto)。,而这里的数据类型则是和前面学习到的各种数据类型的定义是一样的。说明了一个变量的数据类型后,还可选择说明该变量的存储器类型。存储器类型的说明就是指定该变量在c51硬件系统中所使用的存储区域,并在编译时准确的定位。表10-2列出了keil uvision 2所能识别的存储器类型。注意:在87c51芯片中ram只有低128位,位于80h到ffh的高128 位则在52芯片中才有用,并和特殊寄存器地址重叠。,表10-2 keil c51能识别的存储器类型,10.2 c51数据类型、常量和变量的定义,10.2 c51数据类型、常量和变量的定义,10.2.4 c51定义sfr字节和位单元,1.对特殊功能寄存器sfr的定义,sfr和sfr16可以直接对51单片机的特殊功能寄存器sfr进行定义,定义方法如下: sfr特殊功能寄存器名= 功能寄存器地址常数; sfr用于定义8位的特殊功能寄存器。sfr关键字后面是一个要定义的名字,可任意选取,但要符合标识符的命名规则,名字最好有一定的含义如p1口可以用p1为名,这样便于程序读写。等号后面必须是常数,不允许有带运算符的表达式,而且该常数必须在特殊功能寄存器的地址范围之内(80hffh)。 例如:sfr p1 = 0x90; / 定义p1 i/o口,其口地址为90h sfr16特殊功能寄存器名= 殊功能寄存器地址常数; sfr16是用来定义16位特殊功能寄存器。 例如:sfr16 t2=0xcc; / 这里定义8052定时器t2,地址为 /t2l=cch,t2h=cdh 用sfr16 定义16位特殊功能寄存器时,等号后面是它的低位地址,高位地址一定要位于物理低位地址之上。,2.对位地址单元的定义,sbit 定义可位寻址对象。如访问特殊功能寄存器中的某位。 如要访问p1口中的第2个引脚p1.1。定义方法有三种: 第一种方法:sbit 位变量名位地址。例如:sbit p11 = 0x91; 这种方法是把位的绝对地址赋给位变量。同sfr一样sbit的位地址必须位于80hffh之间 第二种方法:sbit 位变量名特殊功能寄存器名位位置。 例如: sfr p1=0x90; sbit p11=p11;/ 先定义一个特殊功能寄存器名再指定位变量名所在的位置 当可寻址位位于特殊功能寄存器中时可采用这种方法。 第三种方法:sbit 位变量名字节地址位位置 例如: sbit p11=0x90 1; 第三种种方法其实和第二种是一样的,只是把特殊功能寄存器的地址直接用常数表示。通常这些可以直接使用,系统提供的预处理文件里面已定义好各特殊功能寄存器的简单名字,直接引用可以省去一点时间,没有定义的可以自己定义。 在c51存储器类型中提供有一个bdata的存储器类型,这个是指可位寻址的数据存储器,位于单片机的可位寻址区中,可以将要求可位寻址的数据定义为bdata,例如: unsigned char bdata ib;/ 在可位寻址区定义unsigned char类型的变量ib,10.2 c51数据类型、常量和变量的定义,10.3 c51程序的运算符、表达式及其语法规则,10.3.1 c51算术运算符及其表达式,赋值运算符“=”,在c51中,它的功能是将一个数据的值赋给一个变量,如x=10。利用赋值运算符将一个变量与一个表达式连接起来的式子称为赋值表达式,在赋值表达式的后面加一个分号“;”就构成了赋值语句,一个赋值语句的格式如下: 变量=表达式; 执行时先计算出右边表达式的值,然后赋给左边的变量。例如: x=8+9; /*将8+9的值赋绐变量x*/ x=y=5; /*将常数5同时赋给变量x和y*/ 在c51中,允许在一个语句中同时给多个变量赋值,赋值顺序自右向左。,1.运算符,(1)赋值运算符,(2)算术运算符,c51中支持的算术运算符有: + 加或取正值运算符 - 减或取负值运算符 * 乘运算符 / 除运算符 % 取余运算符(定时器赋初值) (1)加、减、乘运算相对比较简单,而对于除运算,如相除的两个数为浮点数,则运算的结果也为浮点数,如相除的两个数为整数,则运算的结果也为整数,即为整除。如25.0/20.0结果为1.25,而25/20结果为1。 (2)对于取余运算,则要求参加运算的两个数必须为整数,运算结果为它们的余数。例如:x=5%3,结果x的值为2。,(3)算术运算符,算术表达式用算术运算符和括号将运算对象连接起来的式子称为算术表达式,其中的运算对象包括常量、变量、函数、数组、结构等等。 例如:a+b;a+b*cd;a*(b+c)-(d-e)f;a+bc-2.5+b:,10.3 c51程序的运算符、表达式及其语法规则,(4)算术运算符的优先级和结合性,优先级:指当运算对象两侧都有运算符时,执行运算的先后次序。按运算符优先级别的高低顺序执行运算。 结合性:指一个运算对象两侧的运算符优先级别相同时的运算顺序。 算术运算符的优先级规定为:先乘除模,后加减,括号优先。即在算术运算符中,乘、除、模运算符的优先级相同,并高于加减运算符。在表达式中若出现括号,则括号中的内容优先级最高。 例如:a + b/c; 这个表达式中除号的优先级高于加号,故先运算b/c所得的结果再与a相加。 例如:(a + b) * (c-d) - e; 在这个表达式中括号的优先级最高,*次之,减号优先级最低,故先运算(a+b) 和(c-d),然后再将两者的结果相乘,最后再与e相减。 算术运算符的结合性规定为自左至右方向,又称为“左结合性”。即当一个运算对象两侧的算术运算符优先级别相同时,运算对象先与左面的运算符结合。 例如:a + b + c; b两边是“+”、“-”,运算符优先级别相同,故按左结合性先执行a + b再与c相减。,10.3 c51程序的运算符、表达式及其语法规则,10.3 c51程序的运算符、表达式及其语法规则,10.3.2 c51关系运算符、表达式及优先级,1.关系运算符,c51中有6种关系运算符: 大于 = 大于等于 3,结果为真(1),而10= =100,结果为假(0)。 注意:关系运算符等于“= =”是由两个“=”组成。,10.3 c51程序的运算符、表达式及其语法规则,2.关系运算符的优先级,图10-4 各种运算符优先级规则图,前四种关系运算符 (,=) 优先级相同, 后两种也相同;前四种优先级高于后两种。 关系运算符的优先级低于算术运算符。 关系运算符的优先级高于赋值运算符。 各种运算符的优先级规则如图10-4所示。 【例10.1】 c a+b 等效于c(a+b) a b ! = c 等效于(ab)!=c a= = bc 等效于a = (bc) 关系运算符的结合性为左结合。 关系表达式:用关系运算符将两个表达式(算术表达式、关系表达式、逻辑表达式及字符表达式等)连接起来的式子,称为关系表达式。 关系表达式的结果:由于关系运算符总是二目运算符,它作用在运算对象上产生的结果为一个逻辑值(即真或假)。c语言以“1”代表真,以“0”代表假。,10.3 c51程序的运算符、表达式及其语法规则,10.3.3 c51逻辑运算符、表达式及优先级,1. 逻辑运算符,&:逻辑与(and); :逻辑或(or); !:逻辑非(not)。 “&”和“”是双目运算符,要求有两个运算对象;而“!”是单目运算符,只要求一个运算对象。 c51逻辑运算符与算术运算符、关系运算符、赋值运算符之间的关系:其中“!”运算符优先级最高,算术运算符次之,关系运算符再次之,之后是&和,赋值运算符最低。,2.逻辑表达式,用逻辑运算符将关系表达式或逻辑量连接起来的式子称为逻辑表达式。 逻辑表达式的值应该是一个逻辑量“真”或“假”。逻辑表达式的结合性为自左向右。 逻辑表达式的值与关系表达式的值相同,以0代表假,以1代表真。,10.3 c51程序的运算符、表达式及其语法规则,【例10.2】若a = 4,b = 5,则: 因为a = 4为真,所以!a为假(0)。 因为a、b为真,两者相或也为真。a & b为真(1)。!a & b为假(0)。因为!优先级高于&,故先执行!a,值为假(0),而0 & b为0,故结果为假(0)。 通过上面的例子可以看出,系统给出的逻辑运算结果不是0就是1,不可能是其他值。这与后面讲到的位逻辑运算是截然不同的,应该注意区别逻辑运算与位逻辑运算这两个不同的概念。 在由多个逻辑运算符构成的逻辑表达式中,并不是所有逻辑运算符都被执行,只是在必须执行下一个逻辑运算符后才能求出表达式的值时,才执行该运算符。由于逻辑运算符的结合性为自左向右,所以对于&(逻辑与)运算符来说,只有左边的值不为假(0)才继续执行右边的运算。对于(逻辑或)运算符来说只有左边的值为假才继续进行右边的运算。 【例10.3】若a = 1,b = 2,c = 3,d = 4;m,n原值为1, 则求表达式(m = a b ) & (n = c d ) 由于a b为假(0),即m = 0,故无须再执行右边的&(n = cd)运算,即可确定表达式值为假(0);而表达式(m = a b) (n = c d ) 由于a d为假(0),即m = 0,故需继续向右执行,又由于c d为假(0),即n = 0,两者相或()结果为0,故表达式值为0。,10.3 c51程序的运算符、表达式及其语法规则,10.3.4 c51位操作及其表达式, 1. 位操作运算符,&:按位与。 :按位或。 :按位异或。 :按位取反。 :位右移。 除了按位取反运算符以外,以上位操作运算符都是两目运算符,即要求运算符两侧各一个运算对象。 位运算对象只能是整型或字符型数,不能为实型数据。 【例10.4】若a=54h=01010100b,b=3bh=00111011b 则表达式c=a&b的值为10h, 【例10.5】若a = a5h = 10100101,b = 37h = 00110111,则表达式c = a b的值为92h。,10.3 c51程序的运算符、表达式及其语法规则, 2. “位左移”和“位右移”运算符( ),位左移、位右移运算符 ,用来将一个数各二进制位的全部左移或右移若干位,移位后,空白位补0,而溢出的位舍弃。移位运算并不能改变原变量本身,除非我们将移的结果赋给另一变量。 【例10.6】若a =11000011b = 0c3h,将a值右循环位移两位。 其实现过程分析如下: a右循环n位,即将a中原来左面 (8-n) 位右移n位,而将原来右端的n位移到最左面n位。 上述问题可以由下列步骤来实现: (1)将a的右端n位先放到b的高n位中 b=an; (3)将b,c进行或运算 a=cb;,故对a进行循环右移2位的程序可这样编写: main() unsigned char a=0xc3,b,c; int n=2; b=an; a=cb; 结果:循环右移前a = 11000011。 循环右移后a=11110000。 对于二进制数来说,左移1位相当于对该数乘2,而右移1位相当于该数除以2,利用这一性质我们可以用移位来做快速乘除法。例如,假如要对某数乘10,使用这种方法将比直接做乘法效率更高先将该数右移2位再与该数本身相加,然后再左移1位。,10.3 c51程序的运算符、表达式及其语法规则,10.3 c51程序的运算符、表达式及其语法规则,【例10.7】对片外i/o接口的位操作。 在控制系统中,位操作方式比算术方式更频繁地被使用。以8051片外i/o接口为例,这种i/o接口的字长为1b(8位),在实际控制应用中,人们常常想要改变i/o接口中某一位的值而不影响其他位,当这个口的其他位正在点亮报警灯,或命令a/d转换器开始转换的时候,用这一位可以启动或关闭一部电动机。正像前面已经提过的那样,有些i/o接口是可以位寻址的(例如8051片内i/o接口),但大多数片外附加i/o接口只能对整个字节作出响应。因此要想在这些地方实现单独位控制(或线控制),就要采用位操作。 单片机内部端口用sfr定义,外部并行口用指针定义,指针的定义在absacc.h头文件中。例如: #include #define porta xbyte 0xffc0 void main () porta=(porta & 0xbf) 0x04; 在上面的程序片段中,定义了一个片外i/o接口变量porta,其地址在片外数据存储区的0xffc0上。在main()函数中,porta= (porta & 0xbf) 0x04;作用是先用&运算符将porta.6位置成低电平,然后使用0x04运算将porta.2位置成高电平。,10.3 c51程序的运算符、表达式及其语法规则,10.3.5自增减运算符、复合运算符及其表达式,1.自增减运算符,自增减运算符的作用是使变量值自动加1或减1。例如: + i,- i在使用i之前,先使i值加(减)1。 i +,i -在使用i之后,再使i值加(减)1。 粗略地看,+ i 和i +的作用都相当于i = i +1,但不同之处在于+ i先执行i = i +1,再使用i的值,而i +则是先使用i的值,再执行i = i +1。 【例10.8】若i值原来为5,则 j = + i:j值为6,i值也为6;j = i +:j值为5,i值为6 【例10.9】若i原值为3,则表达式k = (+ i ) + (+ i ) + (+ i )的值为18。这是因为,+ i最先执行,先对表达式进行扫描,对i进行三次自加 (+ i) 则此时i = 6,然后执行k = 6+6+6=18,故k值为18。 而表达式k = (i +) + (i +) + (i +)其结果是:k值为9,而i值为6。因为先对i进行三次相加,再执行三次i的自加。 注意: (1)自增运算 (+ +)和自减运算(-)只能用于变量而不能用于常量表达式。 (2)(+ +)和(-)的结合方向是“自右向左”。,10.3 c51程序的运算符、表达式及其语法规则,2.复合运算符及其表达式,凡是二目运算符,都可以与赋值运算符“=”一起组成复合赋值运算符。c51共提供了10种复合赋值运算符。即: + =,- =,* =,=, =, =,& =, =, = 采用这种复合赋值运算的目的,是为了简化程序,提高c程序编译效率。如: a+=b 相当于a=a+b a%=b 相当于a=ab a-=b 相当于a=a-b a=8 相当于a=a8 a/=b 相当于a=ab porta & = 0xf7相当于porta = porta & 0xf7,其作用是使用&位运算,将porta.3位置0。,【例10.7】使用p1口控制八路led流水灯。 电路图如10-6所示。这里87c51 的p1 引脚为低电平才会点亮led灯,p1口的八个引脚刚好对应p1口特殊寄存器的八个二进制位,如向p1口送数据0xfe,转换成二进制就是11111110,最低位d0为0 这里p1.0 引脚输出低电平,led1被点亮。以此类推,根据自己想要做的效果就可以给端口送对应的值了。 现在要求八个led灯依次从p1.0到p1.7点亮,然后又从p1.7到p1.0依次点亮,循环往复。,10.3 c51程序的运算符、表达式及其语法规则,参考程序如下: #include void delay1ms(unsigned int count) / 延时程序 unsigned int i,j; for(i=0;icount;i+) for(j=0;j120;j+); ,main() unsigned char ledindex=0; bit leddirection=1; / 点亮led灯的方向标志 while(1) if(leddirection) p1=(0x01ledindex); / 方向标志为0时,从p1.7到p1.0点亮 if(ledindex=7) leddirection=!leddirection; ledindex=(ledindex+1)%8; delay1ms(100); ,10.3 c51程序的运算符、表达式及其语法规则,10.3 c51程序的运算符、表达式及其语法规则,图10-6 八路led流水灯电路图,10.4 c51程序设计特点,10.4.1c51函数的分类,1.c51库函数,c51编译器提供了丰富的库函数,使用这些库函数可大大提高编程效率,用户可根据自己程序的需要随时调用编译器提供的库函数。每个库函数都在相应的头文件中给出了函数原型,使用时只需在源程序的开头用编译预处理命令#include将相关的头文件包含进来即可。 例如,要进行绝对地址访问,只需要在程序开头使用“#include ”将头文件包含即可。要访问sfr或sfr的位,则只需要在程序开头使用“#include ”或“#include ”将头文件包含即可。,c语言函数分为主函数main()和普通函数两类,对于普通函数,从用户的角度又划分为标准库函数和用户自定义函数。,用户自定义函数是根据需要编写的函数。从其定义形式上划分为三种形式:无参数函数、有参数函数和空函数。 无参数函数:此种函数被调用时,既无参数输入,也不返回结果给调用函数,它是为完成某种操作过程而编写的。 有参数函数:在定义此类函数时,必须定义与实际参数一一对应的形式参数,并在函数结束时返回结果给调用该函数的程序使用,函数的返回值是通过函数中的return语句获得的。调用时必须提供实际的输入参数。 空函数:此种函数体内无语句,是空白的。调用此种空函数时,什么工作也不做,不起任何作用,定义此种函数的目的并不是为了执行某种操作,而是为了以后程序功能的扩充。在程序的设计过程中,往往根据需要确定若干模块,分别由一些函数来实现。而在程序设计的第一阶段,往往只设计最基本的功能模块函数,而将其他非基本模块的功能函数定义为空函数,留待以后完善。,2.用户自定义函数,10.4 c51程序设计特点,10.4 c51程序设计特点,10.4.2 中断子程序的设计,1.中断函数的定义与使用举例,c51 语言扩展了函数的定义使它可以直接编写中断服务函数,扩展的关键字是interrupt和using,用于定义中断服务程序。具体格式如下: 函数类型 函数名 (形式参数) interrupt n using n interrupt 关键字是不可缺少的,由它告诉编译器该函数是中断服务函数,并由后面的n 指明所使用的中断号。n的取值范围为031,但具体的中断号要取决于芯片的型号,像87c51 实际上就使用04号中断。每个中断号都对应一个中断向量,具体地址为8n+3,中断源响应后处理器会跳转到中断向量所处的地址执行程序,一般会在这地址上安排一个无条件跳转语句,以保证转到中断服务函数所在的地址执行程序。using这个选项是指定选用51芯片内部4 组工作寄存器中的那个组,初学者可以不必去做工作寄存器设定,而由编译器自动选择,避免产生不必要的错误。表10-3所示为51芯片的中断向量和中断号。,表10-3 87c51 芯片中断号和中断向量,10.4 c51程序设计特点,【例10.8】在图10-6所示的8路led流水灯电路中多加一个按键,接在p3.2(12引脚外部中断)和地线之间。当接在p3.2 引脚的按键接下时,就可以触发一个中断,也就会执行相应的中断服务程序。,#include sbit k1=p32; unsigned char n=0; void main(void) / 主程序 it0=1; / 设置外中断0跳变产生中断 ex0=1; / 开外部中断0 ea=1; / 打开cpu中断 while (1) / 主程序循环 / 外中断0程序 void int0()interrupt 0 / 在中断里点亮led p1=(0x01n); n=(n+1)%8; /每按一下k1(p3.2)之后,就会触发一次中断,就会点亮一个led灯。如果连/续按/键,就会依次循环点亮led灯。,10.4 c51程序设计特点, 中断函数不能进行参数传递; 中断函数没有返回值; 在任何情况下,都不能直接调用中断函数; 如果中断函数调用了其他函数,则被调用函数所使用的寄存器组必须与中断函数相同。, 2.使用中断函数的注意事项,10.4 c51程序设计特点,10.4 c51程序设计特点,10.4.3 c51与汇编混合编程,1.在c51中直接嵌入汇编语句,一般情况下,由c51具有很强的数据处理能力,编程中对51单片机寄存器和存储器的分配由编译器自动完成,因此常用c51编写主程序及一些运算较复杂的程序。而汇编语言对硬件的控制较强,运行速度快,灵活性更强,因此常用汇编语言实现与硬件接口及对时间要求高的子程序设计。由此导致某些时候需要进行c51语言和汇编语言的混合编程。在此以keil c51为例,简单介绍c51与汇编语言混合编程的方法。,在c51中可以通过直接插入“#prapma asm/endasm”预处理指令,实现汇编语言程序的内嵌。具体步骤如下: 在c文件中要嵌入汇编代码片以如下方式加入汇编代码: #pragma asm 汇编语句 #pragma endasm 在project窗口中包含汇编代码的c文件上右击,选择“options for .”命令,单击右边的“generate assembler src file”和“assemble src file”选项,使检查框由灰色变成黑色(有效)状态; 根据选择的编译模式,把相应的库文件(small 模式时是keilc51libc51s.lib)加入工程中,该文件必须作为工程的最后文件。, 编译,即可生成目标代码。 例如: #include / 包括一个51标准内核的头文件 sbit p10=p10; / 头文件中没有定义的io就要自己来定义了 void main() / 主程序 unsigned int n; do / 程序循环 #pragma asm / 加入汇编指令延时 mov r7,#250 del: mov r6,#250 djnz r6,$ djnz r6,$del #pragma endasm p10=0; / 设p1.0 口为低电平,点亮led for (n=0;n20000; n+) / 延时 p10=1; / 设p1.0 口为高电平,熄灭led while (1); ,10.4 c51程序设计特点,10.5 c51程序设计举例,1 “求和”的c51程序设计,【例10.9】已知x=10,y=20,计算z=x+y的结果。 # include main () / 主函数名 / 主函数体开始 int x,y,z; / 主函数的内部变量类型说明 x=10; / 变量赋值 y=20; z=x+y; / 计算z=x+y的值 printf (“z=d “,z); / 输出变量z的值 / 程序结束 本例的程序是很简单的,它只有一个主函数main ()。在更一般情况下,一个c语言程序除了必须有一个主函数之外,还可能有若干个其他的功能函数。,10.5 c51程序设计举例,2 求最大值的c51程序设计,【例10.10】求两个数中的最大值,并输出。 # include / 预处理命令 # include main () / 主函数名 / 主函数体开始 int a,a,c; / 主函数的内部变量类型说明 int max (int x,int y); / 功能函数max及其形式参数说明 scon=0x52; / 8051单片机串行口初始化 tmod=0x20; tcon=0x69; thl=0x0f3: scanf (“ d d “,&a,&a); / 输入变量a和a的值 c = max (a,a); / 调用max函数 printf (“ max=d ”,c); / 输出变量c的值 / 主程序结束,10.5 c51程序设计举例,int max (int x,int y); / 定义max函数,x、y为形式参数 / max函数体开始 int z: / max函数内部变量类型说明 if (xy) z=x; / 计算最大值 else z=y; return (z); / 将计算得到的最大值z返回到调用处 / max函数结束 本例在main()函数中调用了库函数(各种子程序)scanf()和printf(),它们分别是输入库函数和输出库函数。c语言本身没有输入输出功能,输入输出是通过函数调用来实现的。需要说明一点的是:c51提供的输人输出库函数是通过8051系列单片机的串行口来实现输入输出的,因此在调用库函数scanf()和printf()之前,必须先对8051单片机的串行口进行初始化。另外,我们在程序中还可以看到小写字母a和大写字母a,它们分别是两种变量。c语言规定同一个字母由于其大小写不同可以代表两个不同的变量,这也是c语言的一个特点。一般的习惯是在普通情况下采用小写字母,对于一些具有特殊意义的变量或常数采用大写字母,如本例中所用到的8051单片机特殊功能寄存器scon、tmod、tcon和th1等。但是必须注意,在c语言程序中同一字母的大小写是有区别的,例如scon和scon在c语言程序中会被认为是两个完全不同的变量。,10.5 c51程序设计举例,3 按秒进行led闪烁,【例10.11】p1.7驱动led按秒进行闪烁,时钟频率为6mhz。 分析:时钟频率为6mhz,则一个机器周期t=2s,而定时时间x为5105个t,而定时器最大只能为65536个t,不能满足要求,必须借助硬件计数器或软件循环。 本题采用硬件方式,如图10-7所示。 t0定时,定时10ms。 t1计数t0的定时跳变信号p1.0的负跳变次数,计满50个跳变为1s。 t0定时初值:(方式1) t=10ms,x=5000d t=(216-t0初值)机器周期t t1计数初值:(方式2) x=50d,图10-7 led灯按秒闪烁电路图,程序如下: #include sbit p1_0=p10; sbit p1_7=p17; void timer0() interrupt 1 using 1 / t/c 0中断服务程序 p1_0=!p1_0; / 10ms定时时间到,p1.0反相 th0=(65536-5000)/256; / 计数初值重装载 tl0=(65536-5000)%256; void timer1() interrupt 3 using 2 / t/c1中断服务程序入口 p1_7=! p1_7; / 1s定时时间到, 灯改变状态 ,10.5 c51程序设计举例,main() p1_7=0; / 置灯初始灭 p1_0=1; / 保证第一次反相便开始计数 tmod=0x61; / 定时器0工作在方式1定时,定时器1工作在方式2计数 th0=(65536-5000)/256; / 预置计数初值 tl0=(65536-5000)%256; th1=256-50; tl1=256-50; ip=0x08; / 置优先级寄存器 ea=1; / cpu开中断 et0=1; / 开t/c0中断 et1=1; / 开t/c1中断 tr0=1; / 启动t/c0 tr1=1; / 启动t/c1 for (;) ,10.5 c51程序设计举例,10.5 c51程序设计举例,4 直流电机的控制,【例10.20】按照开环控制方式实现对直流电机的控制 此例中直流电机采用pwm控制,pwm(pulse width modulation)即脉冲宽度调制,脉冲宽度调制波通常由一列占空比不同的矩形脉冲构成。占空比就是输出的pwm波中,高电平保持的时间与该pwm的时钟周期的时间之比。通过改变pwm的占空比可以调节直流电机的速度。产生pwm信号可以由硬件方法和软件方法实现,传统的硬件模拟方法是把调制信号和载波(一般是三角波)同时接入运算放大器的两个输入端作比较而得到。而软件的实现,特别是基于单片机的软件实现方法,主要是利用其内部提供的定时器,通过改变定时器的定时初值获得不同的脉冲持续时间。,/ -函数声明,变量定义- #include #include #include / -定义管脚- sbit pwm=p10; /

温馨提示

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

评论

0/150

提交评论