微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > Keil C51 中的函数指针和再入函数

Keil C51 中的函数指针和再入函数

时间:11-27 来源:互联网 点击:

现如下:

SEGMENTDATA_GROUP

+--> CALLED SEGMENTSTARTLENGTH

----------------------------------------------

?C_C51STARTUP----------

+--> ?PR?MAIN?FPT_MAIN

+--> ?C_INITSEG

?PR?MAIN?FPT_MAIN0008H0001H

?C_INITSEG----------

+--> ?PR?FUNC1?FP_TAB

+--> ?PR?FUNC2?FP_TAB

+--> ?PR?FUNC3?FP_TAB

?PR?FUNC1?FP_TAB0008H0008H

?PR?FUNC2?FP_TAB0008H0008H

?PR?FUNC3?FP_TAB0008H0008H

三个函数通过列表被调用,FUNC1,FUNC2 和FUNC3被C_INITSEG调用。但是这是错误的,C_INITSEG按照常规的方式在程序中初始化。这些函数被引入初始化代码中,因为函数指针列表被初始化成这些函数的地址值。

注意这些变量(FUNC1,FUNC2 和FUNC13)和MAIN函数的起始地址都是0008H。这样不能正常工作,因为MAIN函数调用FUNC1,FUNC2 和FUNC3(通过函数指针类表)。

C51编译器和BL51连接器联合工作,当使用函数指针列表时,使得函数变量空间覆盖很容易。但是,你必须合理的声明指针列表。如果你这样做了,就可以避免使用“OVERLAY”指令。下面的函数指针列表的定义,C51和BL51可以自动处理:

code long (code *fp_tab []) (void) = { func1, func2, func3 };

注意唯一不同的是存储列表在CODE空间。现在,连接映射文件如下:

SEGMENTDATA_GROUP

+--> CALLED SEGMENTSTARTLENGTH

----------------------------------------------

?C_C51STARTUP----------

+--> ?PR?MAIN?FPT_MAIN

?PR?MAIN?FPT_MAIN0008H0001H

+--> ?CO?FP_TAB

?CO?FP_TAB----------

+--> ?PR?FUNC1?FP_TAB

+--> ?PR?FUNC2?FP_TAB

+--> ?PR?FUNC3?FP_TAB

?PR?FUNC1?FP_TAB0009H0008H

?PR?FUNC2?FP_TAB0009H0008H

?PR?FUNC3?FP_TAB0009H0008H

现在,初始化代码中没有引入FUNC1,FUNC2 和FUNC3。但是,MAIN函数中引入一个常数段FP_TAB。这是一个函数指针列表。因为函数指针列表引入了FUNC1,FUNC2 和FUNC3,所以调用树是正确的。

只要把函数指针列表放在一个独立的源文件中,在调用树中,C51和BL51就能正确的连接。

函数指针的建议和技巧

有些函数指针的应用技巧。
使用指定空间的指针

把函数指针从一个普通的指针变成一个指定空间的指针。用一个字节保存指针。因为函数属于CODE存储区(在8051里),一个字节可以用来保存声明的函数指针作为CODE指针。例如:

void (code *function_ptr) (void) = another_function;

如果你选择在你的函数指针声明中包含code关键字,就可以在任何地方使用它。如果你声明一个函数,它接收一个3字节的普通指针,通过指定空间传递,2字节函数指针,坏事将要产生。

再入函数和指针

Keil C51 为函数的再入提供关键字“reentrant”。再入函数的参数通过模拟栈来传递。模拟栈对于small存储模式位于IDATA,对于compact存储模式位于PDATA,对于large存储模式位于XDATA。如果你使用再入函数,在STARTUP.A51中你必须初始化再入栈的指针。参考下面的启动代码:

;----------------------------------------------------------------------

;Reentrant Stack Initilization

;

;The following EQU statements define the stack pointer for reentrant

;functions and initialized it:

;

;Stack Space for reentrant functions in the SMALL model.

IBPSTACKEQU0; set to 1 if small reentrant is used.

IBPSTACKTOPEQU0FFH+1; set top of stack to highest location+1.

;

;Stack Space for reentrant functions in the LARGE model.

XBPSTACKEQU0; set to 1 if large reentrant is used.

XBPSTACKTOPEQU0FFFFH+1; set top of stack to highest location+1.

;

;Stack Space for reentrant functions in the COMPACT model.

PBPSTACKEQU0; set to 1 if compact reentrant is used.

PBPSTACKTOPEQU0FFFFH+1; set top of stack to highest location+1.

;----------------------------------------------------------------------

你必须设置你使用的存储模式的堆栈和设置栈顶。当有入栈时,再入函数的栈指针减少(向下移动)。为了保护内部的数据区,有一个技巧就是把所有的再入函数放在一个独立的存储模式,像large或compact。

用reentrant声明再入函数。

void reentrant_func (long arg1, long arg2, long arg3) reentrant

{

}

用large和reentrant声明一个large模式的再入函数。

void reentrant_func (long arg1, long arg2, long arg3) large reentrant

{

}

声明一个再入函数的函数指针,必须使用reentrant关键字。

void (*rfunc_ptr) (long, long, long) reentrant = reentrant_func;

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top