参考来源:Keil > Help > uVision Help > Cx51 Compiler User’s Guide > Language Extensions > Function Declarations
函数声明/定义时,与标准C语言存在一定差异,主要为以下几个方面:
- 声明函数为中断处理函数入口
- 选择函数使用的8051通用寄存器组
- 配置函数的内存模型
- 声明可重入的函数
函数声明/定义规范
<return_type> funcname (<args>) <small / compact / large>
<reentrant>
<interrupt x>
<using y>
/*
return_type 函数返回值类型,如果没有指定类型,编译器默认返回类型为int型;
funcname 函数名
arg 函数形参
small 内存模式:small
compact 内存模式:compact
large 内存模式:large
reentrant 可重入函数
interrupt 中断函数入口
x 中断号
using 函数使用指定的工作寄存器组
y 工作寄存器组编号
*/
函数形参和堆栈的关系
在8051架构中,堆栈指针只能访问内部RAM空间;
Cx51编译器在内部RAM中的所有变量空间之后,开辟堆栈空间;
堆栈指针间接寻址内部RAM区域,并限制寻址的最大地址为0xFF,最大堆栈空间被限制在256字节以内;
8051的堆栈是向上生长的;
函数的形参和实参,均不占用堆栈空间,Cx51编译器为每个函数形参分配一个固定的内存空间。
当一个函数被调用时,首先把实参数据拷贝到形参所处的内存空间,然后再执行函数代码。
执行函数代码过程中,从形参所在的内存区域读写数据。
在此过程中,只有返回地址会临时存放在堆栈中。
调用中断处理函数时,需要更多的堆栈空间。因为,必须切换寄存器组,并且在堆栈中保存几个寄存器的数据。
默认情况下,Cx51在寄存器中最多传递3个函数实参,这样可以提高执行速度。
使用data/idata变量时需要注意占用空间大小,因为这直接决定了给堆栈预留的空间大小。
函数形参和寄存器
使用Cx51编译器时,MCU的寄存器最多传递3个函数实参。这个机制会显著提高性能,因为实参不会在RAM中读写。
使用REGPAARMS
和MOREGPARMS
指令传递函数形参/实参。
下图详细说明了不同数量和类型的实参所用到的寄存器:
实参数量 | char/1-byte ptr | int/2-byte ptr | long/float | 3-byte ptr |
---|---|---|---|---|
1 | R7 | R6&R7 | R4~R7 | R1~R3 |
2 | R5 | R4&R5 | R4~R7 | R1~R3 |
3 | R3 | R2&R3 | R1~R3 |
如果函数的第一个形参时bit型,那么其他形参不会通过寄存器传递。这是因为寄存器中传递的参数和上面表格中列出的不一致,所以如果有bit型形参时,应该放到函数形参的后面。
如果没有足够的寄存器来传递函数形参,那么RAM空间也可以用来传递参数。
函数返回值
函数的返回值总是被放在寄存器中,下表列出了返回值类型和对应使用的寄存器:
Return Type | Registers | Storge Format |
---|---|---|
bit | Carry Flag | |
char, unsigned char, 1-byte ptr | R7 | |
int, unsigned int, 2-byte ptr | R6 & R7 | MSB in R6, LSB in R7 |
long, unsigned long | R4-R7 | MSB in R4, LSB in R7 |
float | R4-R7 | 32-Bit IEEE format |
generic ptr | R1-R3 | Memory type in R3, MSB R2, LSB R1 |
函数与内存模型
函数实参和局部变量默认存储在内存模型隐式指定的存储空间。
针对单个函数声明/定义时,可以使用small
, compact
, large
关键字指定这个函数使用那种内存模型。
#pragma small /* Default to small model */
extern int calc (char i, int b) large reentrant;
extern int func (int i, float f) large;
extern void *tcp (char xdata *xp, int ndx) compact;
int mtest (int i, int y) /* Small model */
{
return (i * y + y * i + func(-1, 4.75));
}
int large_func (int i, int k) large /* Large model */
{
return (mtest (i, k) + 2);
}
使用small
模型的好处是,函数形参/实参和局部变量存储在内部RAM中,因此访问速度快。
但是,实际应用时,small
模型满足不了大型程序的需要,这时候有必要针对部分函数使用compact
/large
模型。
工作寄存器组
在8051单片机中,内部RAM的低32个字节空间(0x00~0x1F)被划分给4个工作寄存器组,每个组包含8个寄存器(R0 ~ R7)。程序状态字(PSW)寄存器的其中两位用来选择使用那个工作寄存器组。
当处理中断任务或使用RTOS时,工作寄存器组非常有用,因为MCU可以为单个任务或中断切换工作寄存器组使用,而不是使用堆栈保存8个寄存器数据。切换任务或处理完中断后,MCU可以把工作寄存器组切换回原来的状态。
可以在函数定义后使用关键字using x
,给这个函数指定使用某一寄存器组,例如:
void rb_function (void) using 3
{
/* code */
}
关于作者
微信公众号:萤火虫的电子笔记
分享电子产品开发软、硬件方面知识,51单片机、STM32、ARM、AltiumDesigner PCB设计、开发平台、软件工具等知识分享。
欢迎转发,请注明出处及作者。关注微信公众号,获取最新文章动态。