-
C51
中变量和函数的绝对地址定位问题
:
p>
1.
变量绝对地址定位
1)
在定义变量时使用
_at_
关键字加上地址就可
.
e.g.
unsigned char
idata myvar _at_ 0x40;
把变量
myvar
定义在
idata
的
0x40
处
,
在
M51
文件中可以找到这麽
一行
IDATA 0040H 0001H
ABSOLUTE
表示有变量在
idata
的
0x0040
处绝对地址定位
.
2)
使用
KeilC
编译器定义绝对地址的变量
,
方法待查
.
2.
函数绝对地址定位
1)
在程序中编写一函数
myTest
void myTest(void)
{
// Add your code here
}
2)
使用
KeilC
编译器定位绝对地址的函数
,
打开
Project -> Options for
Target
菜单
,
选中
BL51 Locate
选项卡
,
在
Code:
中输入
:
?PR?myTest?MAIN(0x4000)
把函数
myTest
定位到程序区的
0x4000
处
,
再次编译就可以了
.
3)
一次定位多个函数的方法
同样地
,
在程序中再编写另外一个函数
myTest1
void myTest1(void)
{
// Add your code here
}
在
Options for Target
菜单的
BL51
Locate
选项卡的
Code:
中输入
:
?PR?myTest1?MAIN(0x3900), ?PR?myTest?MAIN(0x4000)
把函数
myTest1
定位在程序区的
0x3900
处
,
把函数
myTest
定义在程
序区的
0x4000
处
,
重新编译就可以了
.
在
M51
文件中可以找到下面的内容
:
>> TO Reader RAMSIZE
(256) CODE (?PR?MYTEST1?MAIN
(0X3900),
?PR?MYTEST?MAIN (0X4000))
3665H 029BH *** GAP ***
CODE 3900H 0014H
UNIT ?PR?MYTEST1?MAIN
3914H 06ECH *** GAP ***
CODE 4000H 0014H
UNIT ?PR?MYTEST?MAIN
4)
函数的调用
:
程序中直接调用函数的方式就不说明了
,
这里重点讲使用函数指针调用
绝对地址处的函数的方法
.
(1)
定义调用的函数原形
typedef void (*CALL_MYTEST)(void);
这是一个回调函数的原形
,
参数为空
.
(2)
定义相应的函数指针变量
CALL_MYTEST
myTestCall = NULL;
(3)
函数指针变量赋值
,
指向我们定位的绝对地址的函数
myTestCall = 0x3900;
指向函数
myTest1
(4)
函数指针调用
if (myTestCall != NULL)
{
myTestCall(); //
调用函数指针处的函
数
myTest1,
置
PC
指针为
0x3900
}
检查编译生成的
bin
文件
,
到
0x3900
处可以看到
myTest1
的内
容
,
在
0x4000
处可以看到
myTest
的内容
,
(5)
其它说明
:
如果在
0x3000
到
0x3900
的程序空间没有内容
时
,
把
myTestCall
的地址指针指到
0x3800
(
在
0x3000
到
0x3900
之间
)
时
,
会从
0x3900
处开始执行
.
至於在
Load
中调用
AP
中的函数的方法与此类似
,
但是相应的参数传
递可能要另寻方法
.
************************************
keil
C51
绝对地址访问
在利用
keil
进行
80
51
单片机
编程的时
,
常常需要进行绝对地址进行访问
.
特别是
对硬件操作
,
如
DA AD
采样
,LCD
液晶操作
,
打印操作
.
等等
.
C51
提供了三种访问绝对地址的方法:
1.
绝对宏:
在程序中,
用
“
#
include”
即可使用其中定义的宏来访问绝对地址,
包
括:
CBYTE
< br>、
XBYTE
、
PWORD
p>
、
DBYTE
、
C
WORD
、
XWORD
、
PBYTE
、
DWORD
具体使用可看一看
absacc.h
便知
例如:
rval=CBYTE[0x0002];
指向程序存贮器的
< br>0002h
地址
rval=XWORD [0x0002];
指向外
RAM
的
0004h
地址
2.
_at_
关键字
直接在数据定义后加上
_at_
const
即可,但是注意:
(1)
绝对变量不能被初使化;
<
/p>
(2)bit
型函数及变量不能用
_at
_
指定。
例如:
idata struct link list _at_ 0x40;
指定
list
结构从
40h
开始。
xdata char
text[25b] _at_0xE000
;指定
text<
/p>
数组从
0E000H
开始
提示:如果外部绝对变量是
I/O
端口等可自行变化数据,需要使用
volatile
关
键
字进行描述,请参考
absacc.h
。
3.
连接定位控制
此法是利用连接控制指令
code xdata pdata
data bdata
对
“
段
”
地址进行,如要
指定某具体变量地址,则很有
局限性,不作详细讨论。
附
:(c51)
/*-------------------------
------------------------------------------
< br>-------
ABSACC.H
Direct access
to 8051, extended 8051 and Philips 80C51MX memory
areas.
Copyright (c)
1988-2002 Keil Elektronik GmbH and Keil Software,
Inc.
All
rights
reserved.
-----
--------------------------------------------------
--------------
-----*/
#ifndef
__ABSACC_H__
#define
#define
#define
#define
#define
#define
#define
#define
__ABSACC_H__
CBYTE
DBYTE
PBYTE
XBYTE
((unsigned
((unsigned
((unsigned
((unsigned
char
char
char
char
volatile
volatile
volatile
volatile
code
data
pdata
xdata
*)
*)
*)
*)
0)
0)
0)
0)
CWORD
DWORD
PWORD
((unsigned
((unsigned
((unsigned
int
int
int
volatile
volatile
volatile
code
data
pdata
*)
*)
*)
0)
0)
0)
#define
XWORD
((unsigned
int
volatile
xdata
*)
0)
#ifdef
__CX51__
#define
FVAR(object,
addr)
(*((object
volatile
far
*)
(addr)))
#define
FARRAY(object,
base)
((object
volatile
far
*)
(base))
#define
FCVAR(object,
addr)
(*((object
const
far
*)
(addr)))
#define
FCARRAY(object,
base)
((object
const
far
*)
(base))
#else
#define
FVAR(object,
addr)
(*((object
volatile
far
*)
((addr)+0x10000L)))
#define
FCVAR(object,
addr)
(*((object
const
far
*)
((addr)+0x810000L)))
#define
FARRAY(object,
base)
((object
volatile
far
*)
((base)+0x10000L))
#define
FCARRAY(object,
base)
((object
const
far
*)
((base)+0x810000L))
#endif
#endif
附
:(c166)
/*-------------------------
------------------------------------------
< br>-------
ABSACC.H
Direct
access
to
166
memory
areas
for
C166/EC++
Version
5.
Copyright
(c) 1992-2004 Keil Elektronik GmbH and Keil
Software, Inc.
All
rights
reserved.
---------------------------
------------------------------------------
< br>-----*/
#ifndef
#define
#if
#define
#define
#else
#define
#define
#define
#define
#define
#define
#endif
__ABSACC_H__
__ABSACC_H__
(__MODEL__
==
0)
MVAR(object,
addr)
(*((object
volatile
*)
(addr)))
MARRAY(object,
base)
((object
volatile
*)
(base))
MVAR(object,
addr)
(*((object
volatile
far
*)
(addr)))
MARRAY(object,
base)
((object
volatile
far
*)
(base))
HVAR(object,
addr)
(*((object
volatile
huge
*)
(addr)))
HARRAY(object,
base)
((object
volatile
huge
*)
(base))
XVAR(object,
addr)
(*((object
volatile
xhuge
*)
(addr)))
XARRAY(object,
base)
((object
volatile
xhuge
*)
(base))
#endif
以下来自转载:
使用
KeilC51
软件,可以很方便地将代码或者数据绝对定位到某个地址。
p>
1
、代码定位:
方法
1
:
使用
伪指令
CSEG
。
比如要将
MyFunc1
定位到代码区
C:0x1000<
/p>
,
则新
建一个
A
51
文件,添加以下内容:
PUBLIC MYFUNC1
CSEG AT 1000H
MYFUNC1:
;
其它代码
RET
在其它源文件中,就可以调用
MyFunc()
函数了。需要注意的是,编译器不检测
传递参
数的数目,仅检测函数是否有返回值。
方法
< br>2
:使用
BL51 Locate
选项。比如在
main.c
中定义了一个
MyFunc2
函数,
并且要将该函数定位到代码区
C:0x2000
,则从菜单中选择
Proj
ect->Options for
Target
'Target1'
,在弹出的对话框中选择
BL51 Loc
ate
页,在下面的
code
栏中
p>
写上
?PR?MYFUNC2?MAIN(0x2000)
即可。
如果想定位多个函数,也可以使用
p>
*
通配符。
2
、变量定位:
只有全局变量可以绝对定位,局部变量无法实现绝对定位。
<
/p>
方法
1
:
使用<
/p>
_at_
关键字。
声明一个全局变量
p>
unsigned char data MyBuf1[8] _at_
0x20;
方法
2
< br>:使用
BL51 Locate
选项。比如将
main.c
中定义的所有
data
型的全局变
量定位到数据区
D:0x28
开始的空间,则从菜单中
选择
Project->Options for
Target 'Target1'
,
在弹出的对话框中选择<
/p>
BL51 Locate
页,在下面的
d
ata
栏中写上
?DT?MAIN(0x28)
即可。
如果是
idata<
/p>
,
则使用
?ID?MAIN(0x28)
,
如果是
xdata
< br>,
则使用
?XD?MAIN(0x28)
,
如果是
pdata
,则使用
?PD?MAIN(0x28)
3
、堆栈定位:
在
STARTUP.A51
文件中定义了堆栈区
?STACK
,其起始地址同样可以在
BL51
Locate
页中设置,在
Stack
栏写上
?STACK(0x80)
BL51 locate
选项卡中
code range
和
xdata range
如果不填
写,
编译默认将程序中相应代码和变量从空
间前面取起
网上看到有人提到在
keil
中使用
_at_
进行绝对地
址定位问题,
我简单介绍一下它
的用法。
使用
_at_
关键字对存储器进行绝对地址定位程序如下
#
i nclude
char xdata LED_Data[50]
_at_ 0x8000;
main()
{
LED_Data[0] = 0x23;
}
在
ke
il
中运行以上程序可以在存储器窗口中输入
x
:
0x8000
可以看到
0x8000
地址中的值为
0x23.
值得指出的几点是
1.
在给变量
LED_Data[50]
定位绝对地址空间时,不能对其赋初值。
< br>
xdata LED_Data[50]
_at_ 0x8000;
这条语句不能主函数中。有些网友提
到在按着
keil
说明中用
_at_<
/p>
进行绝对地址定位时,
编译会出现错误
2
74
,
就是将
这条语句放在主函数中的
原因。
中
地址是自动分配的,所以除非特殊情况否则不提倡使用绝对地址定位。
初学者因帖别注意
。不要把
c
当作汇编使用。
对需要
/RST
复位后要保持变量不变,防止意外改变(比如升级到新程序,变量
地址可能被编译器
优化到其他地方),比较有用!!!!
STARTUP.A51
这个文件有什么用,有必要添加到工程吗?
如果不添加
文件,编译器就会自动加入一段初始化内存以及堆栈等
的代码,
这时的内存初始化部分你就无法去控制了,
当然这在大部分情况下没什
< br>么关系。
但是如果你想你的程序在复位后,
内存里面的信
息依然还保存着
(所说
的
“
热复位
”
)
,
那么你就需要添加该启动文件,
并且去里面修改内存初始化部分,
不要初始化你需要保留的部分内存。
p>
请问如何在
keil
编译器里,编程时指定
函数的绝对地址
(
无内容
)
不好意思啊,
我还从来没有接触过有这样要求情况,
不过从网上其他地方找了一
篇你参考一下吧,、函数定位:
假如要把
C
源文件
tools.c
中的函数
int
BIN2HEX(int xx)
{
...
}
放在
CODE MEMORY
的
0x1000
处,先编译该工程,然后打开该工程的
< br>M51
文件,
在
* * *
C O D E
M E M O R Y
* * *
行下找出要定位的函数的名称,应该形如:
CODE
xxxxH
xxxxH
UNIT
?PR?_BCD2HEX
?TOOLS
然后在:
Project->Options for Target ...->BL51
Locate
:
Code
中填写如下内容:
?PR?_BCD2HEX?TOOLS(0x1000)
再
次
Build
,在
M51
中会发现该函数已放在
CODE MEMORY
的<
/p>
0x1000
处了
2
、赋初值的变量定位:
要将某变量定位在一绝对位置且要赋初值,
此时用
_at_
不能完成,
则如下操
作:
在工程中建立一个新的文件,
如
InitVars.c
,
在其中对要处理的变量赋初值
(假
设是
code
变
量):
char code
myVer = {
然后将该文件加入工程,编译,打开
M51
文件,若定义的是
code
型,则在<
/p>
* * *
C O D E
M E M O
R Y
* * *
下可找到:
CODE
xxxxH
xxxxH
UNIT
?CO?INITVARS
然后在:
Project->Options for Target ...->BL51
Locate
:
Code
中填入:
?CO?INITVARS(0x200)
再次编译即可。
相应地,如为
xdata
变量,则
InitVars.c<
/p>
中写:
char xdata
myVer = {
然后将该文件加入工程,编译,打开
M51
文件,在
* * *
X D A T A
M E M O R Y
* *
*
下可找到:
XDATA
xxxxH
xxxxH
UNIT
?XD?INITVARS
然后在:
Project->Options for Target ...->BL51
Locate
:
Xdata
中填入:
?XD?INITVARS(0x200)
再次编译即可。相
应地,若定义的是
data/idata
等变量,则相应处理即
可。
3
、
若
有
多个变量或函数要进行绝对地址定位,
则应按地址从低到高的
顺序排列
。
**********************************
PIC 51
混编
C18
指定数据绝对地址
51:
RSEG
是段选择指令,要想明白它的意思就要了解段的意思。
段是程序代码或数据对象的存储单位。
程序代码放到代码段,
数据对象放到数据
段。段分两种,一是绝对段,一是再定位段。绝对段在汇编语言中指定
,在用
L51
联接的时候,地址不会改变。用于如访问一个固定
存储器的
i/o,
或提供中断
向量的入
口地址。而再定位段的地址是浮动的。它的地址有
L51
对程序
模块连
接时决定
,C51
对源程序编译
所产生的段都是再定位段,
它都有段名和存储类型。
绝对段没有
段名。
说了这么多,大家可能还是不明白段是什么意思。别急
,接着往下看。
例如,你写用
C
p>
写了一个函数
void test_fun(void) {
…
} ,
存在
test.c
中,用编译
器编译以后
. SRC FILE
中看到
:
?PR?test_fun?TEST SEGMENT CODE
//(
函数放到代码段中
)
写这个函数体的时候
:
RSEG ?PR?test_fun?TEST
//
选择已定位的代码段为
当前段
test_fun:
……
//
代码
所以函数的表达模式是这样:
?PR?
函数名
?
文件名
而函数名又分
:
1:
无参函数
?PR?
函数名
?
文件名
2:
有参函数
?PR?_
函数名
?
文件名
3:
再入函数
?PR?_?
函数名
?
文件名
又例如
你定义了全局变量
unsigned char data
temp1,temp2; unsigned char
xdata temp3;
在
test.c
文件中,编译器会为每
个文件分
0
到多个全局数据段,相
同类
型的全局变量被存到同一段中。所以上面会编译成如下
:
RSEG ? DT? TEST
. temp1: DS
1
. temp2: DS 1
;
RSEG ?XD? TEST
. temp3: DS 1
//
下面是各个类型的数据全局段的表示
?CO?
文件名
//
常数段
?XD?
FILE_NAME //XDATA
数据段
?DT? FILE_NAME //DATA
数据段
?ID?
FILE_NAME //IDATA…..
?BI?
FILE_NAME // BIT …..
?BA?
FILE_NAME //BDATA….
?PD?
FILE_NAME //PDATA…..
看到这里大家应
该明白段的意思了吧。
也许你会问,
这有什么作用哪?它就是用
在当你需要用汇编语言写一部份程序的时候,把汇编写的函数放在这个问件中,
改名
xxx.a51,
按上面的规则写。
编译就好。
既然
知道了段的意思,现在我们回到
SEG
的用法上来。
A51
中有两种段选择指令
:
再定位段选择指令
和
绝对段选择指令
.
它们用来选
择当前段是再定位段还是绝对段。
使用不同的段选择指令,
将使程序定位在不同
的地址空间之内。
1:
再定位段的选择指令是:
RSEG
段名
它用来选择一个在前面已经定义过的再定位段作为当前段。
<
/p>
用法就像我们上面的例子,先申明了一个函数段,后面写这个函数段。
2:
绝对段选择指令
CSEG [AT
绝对地址表达式
]
//
绝对代码段
DSEG [AT
绝对地址表达式
]
//
内部绝对数据段
XSEG
[AT
绝对地址表达式
]
//
外部绝对数据段
ISEG
[AT
绝对地址表达式
]
//
内部间接寻址绝对数据段
BSEG [AT
绝对地址表达式
]
//
绝对位寻址段
它们的用法我举一个例子
:
例如我们
写串口中断程序,起始地址是
0x23.
就这样写
CSEG AT 0X23
LJMP
serialISR
RSEG ?PR?serialISR?TEST
. serialISR:
PIC:
汇编函数使用同一个工
程
C
文件中的变量
,
< br>例如
ICFLAG
在
C
文件中定义
,
则汇
编文
件中定义方式为
;
定义外部变量
EXTERN ICFLAG
定义函数
例如
CARDATR: