第六章函数单片机CWord格式.docx
- 文档编号:17347802
- 上传时间:2022-12-01
- 格式:DOCX
- 页数:15
- 大小:23.06KB
第六章函数单片机CWord格式.docx
《第六章函数单片机CWord格式.docx》由会员分享,可在线阅读,更多相关《第六章函数单片机CWord格式.docx(15页珍藏版)》请在冰豆网上搜索。
外部函数可以在定义它的文件和其它文件中被调用。
可以在函数定义和调用时使用extern说明是外部函数。
但必须注意:
在一个文件中,若将主程序放到前面,对后面出现的函数,就必须在文件开始进行说明,说明方式同普通C语言。
否则出现警告如下:
函数**.C(5):
warningC206:
'
func'
:
missingfunction-prototype文档收集自网络,仅用于个人学习
所以编程时,要习惯将main()放到最后。
(见“外部函数”)
例:
文件1:
#include<
AT89X51.H>
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函数属性。
中断函数在运行过程中自动完成以下工作:
1)当中断产生时,将特殊功能寄存器ACC、B、DPH、DPL、PSW的值将被保存在堆栈中。
2)如果中断函数未使用using属性进行修饰,中断函数中所使用的寄存器的值将保存在堆栈中。
3)中断函数运行完成退出时,堆栈中保存的数据将被恢复。
4)中断函数退出时,其对应的汇编代码使用RETI指令退出。
中断函数应遵循以下规则:
(1)中断函数不能进行参数传递。
(要传递参数怎么办)
(2)中断函数没有返回值。
(要返回值怎么办)
(3)不能在其它函数中直接调用中断函数
(4)如果在中断中调用了其他函数,必须保证这些函数和中断函数使用了相同的寄存器组,并且这些函数应为可重入函数。
(5)C51编译器从绝地址8n+3产生一个中断向量,其中n为中断号。
该向量包含一个到中断函数入口地址的绝对跳转。
(因为外部中断0从0003H开始,然后每个中断占8个字节)文档收集自网络,仅用于个人学习
例1:
定义了一个函数
intfunc(x1,x2,x3);
//函数声明
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)
returnp2;
a4=func(a1,a2,a3);
二、函数与指针
函数在编译时,编译器为每个函数分配一个入口地址,这个入口地址就称为函数的指针。
函数的指针可以赋给函数指针变量,并能通过函数指针变量调用它所指向的函数。
指向函数指针变量的定义格式如下:
存储器类型数据类型(*指针变量名)(参量列表)
存储器类型是定义将指针变量存储的位置。
说明:
1)函数指针变量定义时,两侧的()是必须的。
2)指向函数的指针变量可以指向任何一个格式相同的函数的入口地址。
3)C语言约定,函数名本身就是函数的入口地址。
4)当函数指针变量指向函数时,即可用它来调用所指的函数。
调用格式为(*指针变量名)(实参表)
函数指针的使用
intadd(inta,intb)
{returna+b;
intsub(inta,intb)
{returna-b;
int(*pFunc)(int,int);
//定义函数指针变量,注意要定义数据类型
intx,y;
pFunc=add;
//对函数指针变量赋值
x=(*pFunc)(3,4);
pFunc=sub;
//由于两个函数格式相同,故定义了一个函数指针
y=(*pFunc)(5,3);
函数的指针类型为普通指针。
(函数指针定义)
三、函数的调用
函数的调用,必须保证被调用函数是已经存在的函数或者是库函数;
如果是库函数,必须用#include将所用函数信息包含到程序中。
如#include<
math.h>
。
对于无参数和无返回值的函数调用很简单,不再重复。
在函数调用中,最关键的是参数的传递与返回值,同普通c语言一样,调用时,可以由实元传递多个参数到函数中,但在返回时,只能返回一个值给函数名。
1、数组作为函数的参数(函数1)
下面调用函数时,实元传递给哑元的是数组名,将数组传递过去,实际是传递地址。
at89x52.h>
intfunc(x)
intx[3];
//注意说明方法,定义了一个数组,在花括号外
{intp2;
p2=x[0]+x[1]+x[2];
{inta[3]={3,6,9},a2;
a2=func(a);
//数组a作为函数的参数,传数组地址
将上式的数组,也可以改为指针。
(函数2)
int*x;
//定义指针,也就是数组的首地址,以下要按指针运算
{chari;
intp2=0;
for(i=0;
i<
3;
i++)
p2+=*(x+i);
//*为间接访问运算符
main()
inta[3]={3,6,9},a2;
2、指针作为函数的参数(函数3)
intfunc(x)
//必须还要定义相同类型的指针
intp2;
p2=2*(*x);
//传过来的内容乘2
inta=5,a2;
int*p=&
a;
//定义指针并赋值
a2=func(p);
}//用指针将a的地址传到函数
3、用指针作为返回值(函数4)
由于返回值只有一个,所以要返回多个值时,就要用数组或者指针。
注意指针函数的概念。
int*func()//由于返回值为地址,要定义指针函数
{intp2[3]={2,4,6};
}//将数组的首地址返回
{unsignedchari;
inta;
int*a2;
//接收的是地址
a2=func();
//将传递过来的地址送入a2中
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返回
单个字节类型通过R7返回
R6,R7
高字节在R6,低字节在R7
long
R4-R7
最高字节在R4,最低字节在R7
float
32位IEEE格式
R1-R3
1)返回值必须用return语句返回,返回值的类型是定义时的类型;
2)调用1次只能返回1个值;
参数传递
at89x51.h>
charfunc(x1,x2,x3)
chardatap2;
chara1=1,a2=2,a3=3;
chardataa4;
}(调参数传递)
下面是编译连接后的汇编语言,可以看到参数在寄存器中的传递
C:
0x000002001ELJMPC:
001E
8:
main()
9:
10:
11:
chardataa4;
0x00037F01MOVR7,#0x01
0x00057D02MOVR5,#0x02
0x00077B03MOVR3,#0x03
【调用前,将参数按规定存入寄存器R7,R5,R3】
12:
0x0009EFMOVA,R7
………
0x001812002ALCALLfunc(C:
002A)
0x001B8F08MOV0x08,R7
【从R7中接收返回值,送堆栈0x08】
13:
}
2:
charfunc(x1,x2,x3)
3:
4:
5:
【注意:
R7,R5,R3中分别为主函数中传过来的数据】
0x002AEFMOVA,R7
0x002B2DADDA,R5
0x002C2BADDA,R3
【函数返回前,将返回值按规定送入R7】
0x002DFFMOVR7,A
6:
0x002E22RET
(调用函数4指针返回)
在下面,函数的参数是数组,按指针传递,传递参数用R1、R2、R3。
其中R1为存储类型,R2、R3为地址。
intdataa[3]={3,6,9},a2;
0x016C1200D3LCALLC?
COPY(C:
00D3)
a2=func(a);
//a为数组
0x016F7B00MOVR3,#0x00
0x01717A00MOVR2,#0x00
0x01737908MOVR1,#0x08
【普通指针占3字节,由R1,R2,R3传递】
0x017512017DLCALLfunc(C:
017D)
0x01788E0EMOV0x0E,R6
0x017A8F0FMOV0x0F,R7
14:
0x017C22RET
以下是指针作为参数传递的例子。
(见例3)
0x002E750800MOV0x08,#0x00;
//地址
0x0031750905MOV0x09,#0x05
//将a的地址送入指针
0x00347B00MOVR3,#0x00;
指针参数
0x00367A00MOVR2,#0x00
0x00387908MOVR1,#0x08//地址
//指针作为函数的参数
0x003A12004ELCALLfunc(C:
004E)
0x003D8E0AMOV0x0A,R6;
返回值
0x003F8F0BMOV0x0B,R7
15:
4:
intfunc(x)//要放在前面,对有参数传递的函数说明
//注意说明位置
7:
//中间是乘号
0x004E120003LCALLC?
ILDPTR(C:
0003)
0x005125E0ADDA,ACC(0xE0)
0x0053FFMOVR7,A
0x0054E5F0MOVA,B(0xF0)
0x005633RLCA
0x0057FEMOVR6,A
【返回值在R6,R7】
}
0x005822RET
下面是函数指针定义的例子中,两个整型参数的传递与返回一个整型参数的过程。
x=(*pFunc)(3,4);
//传递3和4
0x0014F582MOVDPL(0x82),A
0x00168A83MOVDPH(0x83),R2
0x00187D04MOVR5,#0x04;
第一个低位送R5
0x001A7C00MOVR4,#0x00;
高位送R4
0x001C7F03MOVR7,#0x03;
第二个低位送R7
0x001E7E00MOVR6,#0x00;
高位送R6
0x002012006BLCALLC?
ICALL2(C:
006B)
0x00238E08MOV0x08,R6;
返回高位送R6
0x00258F09MOV0x09,R7;
返回低位送R7
11:
pFunc=sub;
//由于两个函数格式相同,故定义了一个函数指针
0x00277BFFMOVR3,#0xFF;
函数指针的存储器类型,程序代码为code,故为0XFF文档收集自网络,仅用于个人学习
0x00297A00MOVR2,#0x00;
被调用函数的地址高位
0x002B7958MOVR1,#0x58;
被调用函数的地址低位
0x002D900000MOVDPTR,#0x0000
0x0030EBMOVA,R3
0x0031F0MOVX@DPTR,A
0x0032A3INCDPTR
0x0033EAMOVA,R2
0x0034F0MOVX@DPTR,A
0x0035A3INCDPTR
0x0036E9MOVA,R1
0x0037F0MOVX@DPTR,A
0x004B22RET
0x004C787FMOVR0,#0x7F
0x004EE4CLRA
0x004FF6MOV@R0,A
0x0050D8FDDJNZR0,C:
004F
0x005275810BMOVSP(0x81),#0x0B
0x0055020003LJMPmain(C:
intsub(inta,intb)//被调用时的参数传递关系,返回为整型
0x0058C3CLRC
0x005EFEMOVR6,A;
结果送R6
0x005F22RET
2:
{returna+b;
}//定义了一个加法函数
0x0060EFMOVA,R7
0x00612DADDA,R5
0x0062FFMOVR7,A
0x0063EEMOVA,R6
0x00643CADDCA,R4
0x0065FEMOVR6,A
0x006622RET
C?
ICALL:
⑴本征库函数(intrinsic
routines)和非本征证库函数
本征函数:
指编译时直接将固定的代码插入当前行,不是用ACALL和LCALL语句来实现。
非本征函数:
必须由ACALL及LCALL调用文档收集自网络,仅用于个人学习
crol_,_cror_
将char型变量循环向左(右)移动指定位数后返回
iror_,_irol_
将int型变量循环向左(右)移动指定位数后返回
lrol_,_lror_
将long型变量循环向左(右)移动指定位数后返回
nop_
相当于插入NOP
testbit
相当于JBC
bitvar测试该位变量并跳转同时清除
_chkfloat_
测试并返回源点数状态
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第六 函数 单片机