第六章 单片机编程 函数.docx
- 文档编号:10752386
- 上传时间:2023-05-27
- 格式:DOCX
- 页数:20
- 大小:23.25KB
第六章 单片机编程 函数.docx
《第六章 单片机编程 函数.docx》由会员分享,可在线阅读,更多相关《第六章 单片机编程 函数.docx(20页珍藏版)》请在冰点文库上搜索。
第六章单片机编程函数
第六章函数
一、函数的分类与定义
1、函数的分类
从C语言程序的结构上划分,C语言函数分为主函数main()和普通函数两种,而对于普通函数,又可以分为标准库函数和用户自定义函数。
1)标准库函数
标准库函数是由C编译系统提供的库函数,在C编译系统中将一些独立的功能模块编写成公用函数,并将它们集中存放在系统的函数库中,供程序设计时使用,称之为标准库函数。
丰富的可直接调用的库函数是C51功能及其高效率的重要体现之一,多使用库函数使程序代码简单,结构清晰,易于调试和维护。
C51几类重要库函数及简要说明
对于标准C中原有的,在此不再说明,如MATH.H中的各数学运算函数。
① 专用寄存器include文件
包括了所有8051的SFR及其位定义,一般系统都必须包括本文件。
如REG51.H,AT89X51.H等。
② 绝对地址文件absacc.h,见P337
该文件中实际只定义了几个宏,以确定各存储空间的绝对地址,如定义了XBYTE宏,允许用户访问8051外部数据存储器中的某一字节。
③存储器分配函数,位于stdlib.h中,见P340表B-11.
④字符串操作函数位于“string.h”中,见P341
其中包括拷贝比较移动等函数如:
memccpy、memchr、memcmp、memcpy、memmove、memset
这些函数对缓冲区进行处理很方便。
⑤ 流函数输入输出,位于“stdio.h”中,见P344。
流函数缺省为8051串口,如要修改,比如改为LCD显示,可修改lib目录中的getkey.c及putchar.c源文件,然后在库中替换它们即可定义用户的I/O口数据读写。
2)用户自定义函数
用户自定义函数是用户根据自己的需要而编写的函数。
从函数定义的形式上可以将其划分无参数函数、有参数函数和空函数。
无参数函数被调用时,既无参数输入,也不返回结果给调用函数,它是为完成某种操作而编写的函数。
有参数函数在被调用时,必须提供实际的输入参数,必须说明与实际参数一一对应的形式参数,并在函数结束时返回结果供调用它的函数使用。
定义空函数的目的是为了以后程序功能的扩充。
2、函数的定义
C51对函数的功能进行了扩展,函数定义的完整形式如下:
返回数据类型函数名(形式参数列表)[reentrant][interruptn][usingm]{函数体;}
(注意对于原型函数的说明,和定义函数相似,但无函数体,也不能说明工作寄存器组的切换usingn和中断说明interrupt函数。
)
其中:
1)函数类型
有静态函数和外部函数。
C语言程序可以由多个函数组成,这些函数可以在同一个程序文件中,也可以在多个不同的程序文件中。
根据这些函数的使用范围,可以把它们分为静态函数和外部函数。
静态函数(内部函数)
静态函数只能在定义它的文件中被调用,而不能在其他文件中的函数所调用。
外部函数(默认)
外部函数可以在定义它的文件和其它文件中被调用。
可以在函数定义和调用时使用extern说明是外部函数。
但必须注意:
在一个文件中,若将主程序放到前面,对后面出现的函数,就必须在文件开始进行说明,说明方式同普通C语言。
否则出现警告如下:
函数**.C(5):
warningC206:
'func':
missingfunction-prototype
所以编程时,要习惯将main()放到最后。
(见“外部函数”)
例:
文件1:
#include
externadd(x1,x2);
unsignedchardatax1=12,x2=2,y;
main()
{
y=add(x1,x2);
}
文件2:
datacharx3;
add(x1,x2)
{
x3=x1+x2;
returnx3;
}
2)返回数据类型和函数返回值
如果返回数据,进行说明;如果不返回,一般用void说明,也可以不说明。
函数返回值通过returnx返回,返回值是通过函数名带回的,所以一个函数只能有一个返回值。
上例中的x3和y。
3)形参与实参
形参:
在定义函数时,函数名后面括号中的变量称为“形参”,定义时不赋值,由调用函数将值传过来。
实参:
主调用函数后面括号中的表达式为“实参”,实参必须有确定的值。
该值在调用时按对应关系传递给形参。
C语言中参数传递是单向的。
4)可重入函数
可重入函数可以在同一个时刻由多个进程共享。
当一个进程正在执行一个可重入函数,另一个进程可以中断该进程,并可以开始执行同一个可重入函数,而不影响函数的运行结果。
因为8051内部堆栈空间的限制,为了提高效率,C51没有提供这种堆栈,而是提供一种压缩栈。
每个函数有一个给定存储空间,用于存放局部变量。
函数中的每个变量都存放在这个空间的固定位置。
当递归调用该过程时会导致变量被覆盖,所以通常情况下C51中的函数是不能重入的。
可重入函数为此必须使用reentrant函数属性来声明函数是可重入的。
与不可重入函数的参数传递和局部变量的存储分配方法不同,C51编译器为再入函数生成一个模拟栈,通过这个模拟栈来完成参数传递和存放局部变量。
这样每次函数调用时的局部变量都会被单独保存,再入函数一般占用较大的内存空间,运行起来也比较慢,并且不允许传递bit类型的变量,也不能定义局部位变量。
可重入函数经常在实时应用系统中应用,也可在中断函数和非中断函数同时调用同一个函数时使用。
5)规定函数使用的寄存器组切换usingm
可使用using函数说明属性来规定函数所使用的寄存器组。
m是一个0-3的整形参数,分别对应0-3组工作寄存器。
这个参数表示使用的寄存器组的编号,这个参数不能使用带运算符的表达式。
using属性只能在函数定义中使用,不能在函数原型声明中使用。
使用using属性的函数将自动完成以下操作:
进入函数前,将当前使用的寄存器组的标号保存在堆栈中。
更改PSW的寄存器组选择位,选择设定的寄存器组作为当前的寄存器组。
函数退出时,将寄存器组恢复成进入函数前的寄存器组。
6)中断函数说明interruptn
C51最大支持32个中断,在单片机中n常用0-5。
对应中断源见P177。
注意:
仅能在函数定义时使用interrupt函数属性,不能在函数声明时使用interrupt函数属性。
中断函数在运行过程中自动完成以下工作:
当中断产生时,中断函数被系统所调用,ACC、B、DPH、DPL、PSW这些特殊功能寄存器的值将被保存在堆栈中。
如果中断函数未使用using属性进行修饰,中断函数中所使用的寄存器的值将保存在堆栈中。
中断函数运行完成退出时,堆栈中保存的数据将被恢复。
中断函数退出时,其对应的汇编代码使用RETI指令退出。
中断函数应遵循以下规则:
(1)中断函数不能进行参数传递。
(2)中断函数没有返回值。
(3)不能在其它函数中直接调用中断函数
(4)如果在中断中调用了其他函数,必须保证这些函数和中断函数使用了相同的寄存器组,并且这些函数应为可重入函数。
(5)C51编译器从绝地址8n+3产生一个中断向量,其中n为中断号。
该向量包含一个到中断函数入口地址的绝对跳转。
(因为外部中断0从0003H开始,然后每个中断占8个字节)
例1:
定义了一个函数
intfunc(x1,x2,x3);
main()
{
inta1=1,a2=2,a3=3,a4;
a4=func(a1,a2,a3);
}
intfunc(x1,x2,x3)
{
intdatap2;
p2=x1+x2+x3;
returnp2;
}
在单片机c语言中,用下面的方法定义更方便:
intfunc(x1,x2,x3)
{
intdatap2;
p2=x1+x2+x3;
returnp2;
}
main()
{
inta1=1,a2=2,a3=3,a4;
a4=func(a1,a2,a3);
}
二、函数与指针
函数在编译时,编译器为每个函数分配一个入口地址,这个入口地址就称为函数的指针。
函数的指针可以赋给函数指针变量,并能通过函数指针变量调用它所指向的函数。
指向函数指针变量的定义格式如下:
存储器类型数据类型(*指针变量名)(参量列表)
存储器类型是定义将指针变量存储的位置。
说明:
1)函数指针变量定义时,两侧的()是必须的。
2)指向函数的指针变量可以指向任何一个格式相同的函数的入口地址。
3)C语言约定,函数名本身就是函数的入口地址。
4)当函数指针变量指向函数时,即可用它来调用所指的函数。
调用格式为(*指针变量名)(实参表)
例1:
函数指针的使用
intadd(inta,intb)
{returna+b;}
intsub(inta,intb)
{returna-b;}
main()
{
int(*pFunc)(int,int);//定义函数指针变量,注意要定义数据类型
intx,y;
pFunc=add;//对函数指针变量赋值
x=(*pFunc)(3,4);
pFunc=sub;//由于两个函数格式相同,故定义了一个函数指针
y=(*pFunc)(5,3);
}
函数的指针类型为普通指针。
(函数指针定义)
三、函数的调用
函数的调用,必须保证被调用函数是已经存在的函数或者是库函数;如果是库函数,必须用#include将所用函数信息包含到程序中。
如#include
对于无参数和无返回值的函数调用很简单,不再重复。
在函数调用中,最关键的是参数的传递与返回值,同普通c语言一样,调用时,可以由实元传递多个参数到函数中,但在返回时,只能返回一个值给函数名。
1、数组作为函数的参数(函数1)
#include
intfunc(x)
intx[3];//注意说明方法,定义了一个数组,在花括号外
{
intp2;
p2=x[0]+x[1]+x[2];
returnp2;
}
main()
{
inta[3]={3,6,9},a2;
a2=func(a);//数组a作为函数的参数
}
将上式的数组,也可以改为指针。
(函数2)
#include
intfunc(x)
int*x;//定义指针,也就是数组的首地址
{chari;
intp2=0;
for(i=0;i<3;i++)
p2+=*(x+i);
returnp2;}
main()
{
inta[3]={3,6,9},a2;
a2=func(a);
}
2、指针作为函数的参数(函数3)
#include
intfunc(x)
int*x;
{
intp2;
p2=2*(*x);//传过来的内容乘2
returnp2;}
main()
{
inta=5,a2;
int*p=&a;//定义指针并赋值
a2=func(p);}//用指针将a的地址传到函数
3、用指针作为返回值(函数4)
由于返回值只有一个,所以要返回多个值时,就要用数组或者指针。
注意指针函数的概念。
#include
int*func()//定义了一个指针函数
{
intp2[3]={2,4,6};
returnp2;}//将数组的首地址返回
main()
{unsignedchari;
inta;
int*a2;
a2=func();//a2中是数组的首地址
for(i=0;i<3;i++)
a+=*(a2+i);//分别取数组的3个数相加
}
四、函数调用时参数的传递规定
为了便于混合编程,在单片机c语言中,对函数调用和返回,参数的传递都有严格的规定,参数的传递途径有:
寄存器、存储器和堆栈(重入函数);其返回参数均通过寄存器传递。
利用寄存器传递参数的规则如下:
参数编号
char
int
Long,float
一般指针
第1个参数
R7
R6(高),R7(低)
R4(高)-R7(低)
R1,R2,R3
第2个参数
R5
R4(高),R5(低)
使用固定地址
存储类型在R3,地址高位在R2,低位在R1
第3个参数
R3
R2(高),R3(低)
例如:
(1)func(chara)
a在R7中传递到函数。
(2)intfunc(inta,intb,char*c)
参数a、b、c分别通过R6、R7;R4、R5;R1、R2、R3传递。
(3)func(floatg,charh)
g在R4、R5、R6、R7中传递,h就不能在R7中传递,只能在堆栈中传递。
返回参数传递规则:
返回类型
使用的寄存器
说明
Bit
Cy
单个位通过进位标志Cy返回
char
R7
单个字节类型通过R7返回
int
R6,R7
高字节在R6,低字节在R7
long
R4-R7
最高字节在R4,最低字节在R7
float
R4-R7
32位IEEE格式
一般指针
R1-R3
存储类型在R3,地址高位在R2,低位在R1
注意:
1)返回值必须用return语句返回,返回值的类型是定义时的类型;
2)调用1次只能返回1个值;
(调参数传递)
#include
charfunc(x1,x2,x3)
{
chardatap2;
p2=x1+x2+x3;
returnp2;
}
main()
{
chara1=1,a2=2,a3=3;
chardataa4;
a4=func(a1,a2,a3);
}
C:
0x000002001ELJMPC:
001E
8:
main()
9:
{
10:
chara1=1,a2=2,a3=3;
11:
chardataa4;
C:
0x00037F01MOVR7,#0x01
C:
0x00057D02MOVR5,#0x02
C:
0x00077B03MOVR3,#0x03
12:
a4=func(a1,a2,a3);
C:
0x0009EFMOVA,R7
C:
0x000A33RLCA
C:
0x000B95E0SUBBA,ACC(0xE0)
C:
0x000DFEMOVR6,A
C:
0x000EEDMOVA,R5
C:
0x000F33RLCA
C:
0x001095E0SUBBA,ACC(0xE0)
C:
0x0012FCMOVR4,A
C:
0x0013EBMOVA,R3
C:
0x001433RLCA
C:
0x001595E0SUBBA,ACC(0xE0)
C:
0x0017FAMOVR2,A
C:
0x001812002ALCALLfunc(C:
002A)
C:
0x001B8F08MOV0x08,R7
13:
}
C:
0x001D22RET
C:
0x001E787FMOVR0,#0x7F
C:
0x0020E4CLRA
C:
0x0021F6MOV@R0,A
C:
0x0022D8FDDJNZR0,C:
0021
C:
0x0024758108MOVSP(0x81),#0x08
C:
0x0027020003LJMPmain(C:
0003)
2:
charfunc(x1,x2,x3)
3:
{
4:
chardatap2;
5:
p2=x1+x2+x3;
C:
0x002AEFMOVA,R7
C:
0x002B2DADDA,R5
C:
0x002C2BADDA,R3
C:
0x002DFFMOVR7,A
6:
returnp2;
C:
0x002E22RET
(调用函数4刊指针返回)
在下面,函数的参数是数组,传递参数用R1、R2、R3。
其中R1为存储类型,R2、R3为地址。
12:
intdataa[3]={3,6,9},a2;
C:
0x015C7808MOVR0,#0x08
C:
0x015E7C00MOVR4,#0x00
C:
0x01607D00MOVR5,#0x00
C:
0x01627BFFMOVR3,#0xFF
C:
0x01647A01MOVR2,#0x01
C:
0x016679A8MOVR1,#IE(0xA8)
C:
0x01687E00MOVR6,#0x00
C:
0x016A7F06MOVR7,#0x06
C:
0x016C1200D3LCALLC?
COPY(C:
00D3)
13:
a2=func(a);
C:
0x016F7B00MOVR3,#0x00
C:
0x01717A00MOVR2,#0x00
C:
0x01737908MOVR1,#0x08
C:
0x017512017DLCALLfunc(C:
017D)
C:
0x01788E0EMOV0x0E,R6
C:
0x017A8F0FMOV0x0F,R7
14:
}
C:
0x017C22RET
下面是函数指针定义的例子中,两个整型参数的传递与返回一个整型参数的过程。
10:
x=(*pFunc)(3,4);//传递3和4
C:
0x0014F582MOVDPL(0x82),A
C:
0x00168A83MOVDPH(0x83),R2
C:
0x00187D04MOVR5,#0x04;第一个低位送R5
C:
0x001A7C00MOVR4,#0x00;高位送R4
C:
0x001C7F03MOVR7,#0x03;第二个低位送R7
C:
0x001E7E00MOVR6,#0x00;高位送R6
C:
0x002012006BLCALLC?
ICALL2(C:
006B)
C:
0x00238E08MOV0x08,R6;返回高位送R6
C:
0x00258F09MOV0x09,R7;返回低位送R7
11:
pFunc=sub;//由于两个函数格式相同,故定义了一个函数指针
C:
0x00277BFFMOVR3,#0xFF;函数指针的存储器类型,程序代码为code,故为0XFF
C:
0x00297A00MOVR2,#0x00;被调用函数的地址高位
C:
0x002B7958MOVR1,#0x58;被调用函数的地址低位
C:
0x002D900000MOVDPTR,#0x0000
C:
0x0030EBMOVA,R3
C:
0x0031F0MOVX@DPTR,A
C:
0x0032A3INCDPTR
C:
0x0033EAMOVA,R2
C:
0x0034F0MOVX@DPTR,A
C:
0x0035A3INCDPTR
C:
0x0036E9MOVA,R1
C:
0x0037F0MOVX@DPTR,A
13:
}
C:
0x004B22RET
C:
0x004C787FMOVR0,#0x7F
C:
0x004EE4CLRA
C:
0x004FF6MOV@R0,A
C:
0x0050D8FDDJNZR0,C:
004F
C:
0x005275810BMOVSP(0x81),#0x0B
C:
0x0055020003LJMPmain(C:
0003)
3:
intsub(inta,intb)//被调用时的参数传递关系
C:
0x0058C3CLRC
C:
0x0059EFMOVA,R7;1参数低位
C:
0x005A9DSUBBA,R5;2参数低位
C:
0x005BFFMOVR7,A;结果送R7
C:
0x005CEEMOVA,R6;1参数高位
C:
0x005D9CSUBBA,R4;2参数高位
C:
0x005EFEMOVR6,A;结果送R6
C:
0x005F22RET
2:
{returna+b;}//定义了一个加法函数
C:
0x0060EFMOVA,R7
C:
0x00612DADDA,R5
C:
0x0062FFMOVR7,A
C:
0x0063EEMOVA,R6
C:
0x00643CADDCA,R4
C:
0x0065FEMOVR6,A
C:
0x006622RET
C?
ICALL:
以下是指针作为参数传递的例子。
(见例3)
C:
0x0000020042LJMPC:
0042
C?
ILDPTR:
C:
0x0003BB010ACJNER3,#0x01,C:
0010;指针地址的处理
C:
0x00068982MOVDPL(0x82),R1
C:
0x00088A83MOVDPH(0x83),R2
C:
0x000AE0MOVXA,@DPTR
C:
0x000BF5F0MOVB(0xF0),A
C:
0x000DA3INCDPTR
C:
0x000EE0MOVXA,@DPTR
C:
0x000F22RET
C:
0x00105006JNCC:
0018
C:
0x001287F0MOVB(0xF0),@R1
C:
0x001409INCR1
C:
0x0015E7MOVA,@R1
C:
0x001619DECR1
C:
0x001722RET
C:
0x0018BBFE07CJNER3,#0xFE,C:
0022
C:
0x001BE3MOVXA,@R1
C:
0x001CF5F0MOVB(0xF0),A
C:
0x001E09INCR1
C:
0x001FE3MOVXA,@R1
C:
0x002019DECR1
C:
0x002122RET
C:
0x00228982MOVDPL(0x82),R1
C:
0x00248A83MOVDPH(0x83),R2
C:
0x0026E4CLRA
C:
0x002793MOVCA,@A+DPTR
C:
0x0028F5F0MOVB(0xF0),A
C:
0x002A7401MOVA,#0x01
C:
0x002C93MOVCA,@A+DPTR
C:
0x002D22RET
10:
main()
11:
{
12:
inta=5,a2;
C:
0x002E750800MOV0x08,#0x00;
C:
0x0031750905MOV0x09,#0x05
13:
int*p=&a;//将a的地址送入指针
C:
0x00347B00MOVR3,#0x00;指针参数
C:
0x00367A00MO
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第六章 单片机编程 函数 第六 单片机 编程