KEIL arm C51中常量变量函数的绝对地址设定

编程 专栏收录该内容
104 篇文章 1 订阅

keil for arm中:

static const uint8_t s_acBmpLogo030[len] __attribute__((at(0X800F000)))={0x80,0xC0,0xC0,0xC0,0xC0,0x80,xxxxxxx}  

案例演示

下面我们用一个例子演示一下,比如:

将一个全局变量放到0x20000000处;
将一个const常量放置到0x00001000处
将func函数放置到0x00000100起始处
只需要照下面写:

int value __attribute__((section(".ARM.__at_0x20000000"))) = 0x33;
const char ziku[] __attribute__((section(".ARM.__at_0x00001000")))   = {0x1, 0x2, 0x3};
void func (void) __attribute__((section(".ARM.__at_0x00000100")));
 
void func (void) {
    int i;
    for (i = 0; i < 100; i++){
    }
}
也就是说:对于变量,在其后边加修饰;而对于函数,在声明处加修饰,注意,是在声明处,不是在函数定义处!!!

学习STM32也会遇到这样的绝对定位的问题如下:

uint8_t   UART_RX_BUF[1024]   __attribute__ ((at(0X20001000)));   //就是将串口接收的数据定位到RAM中起始地址为0X20001000;

绝对定位要么定位到flash、要么定位到RAM,这里我们将定位在flash进行说明。

MDK如何实现将数据存储到FLASH指定地址?

      我们在烧录数据的时候,一般是从0x08000000开始按照顺序烧录到flash里面的,如何让数据能够定义到绝对地址如0800F000,就必须保证文件内数据也是存储在该地址,为了实现这个目的,MDK在生成文件时会填充0x00字段,从而确保能够将数据定义到

从实际情况也能验证这个原理,我以我的测试代码为例:

const u16 gFlashDefValue4[512] __attribute__((at(0x0800F000))) =  {0x1111, 0x1111, 0x1111, 0x0111, 0x0111,0x0111};
 

KEILC51中

1. 绝对宏:

在程序中,用“#include<absacc.h>”即可使用其中定义的宏来访问绝对地址,包括:
CBYTE、XBYTE、PWORD、DBYTE、CWORD、XWORD、PBYTE、DWORD 
具体使用可看一看absacc.h便知
例如:
rval=CBYTE[0x0002];指向程序存贮器的0002h地址 
rval=XWORD   [0x0002];指向外RAM的0004h地址 


2. _at_关键字

直接在数据定义后加上_at_ const即可,但是注意:
(1)绝对变量不能被初使化; 
(2)bit型函数及变量不能用_at_指定。 

例如:

idata struct link list _at_ 0x40;指定list结构从40h开始。 
xdata char text[25b] _at_0xE000;指定text数组从0E000H开始 

提示:如果外部绝对变量是I/O端口等可自行变化数据,需要使用volatile关键字进行描述,请参考absacc.h。


3. 连接定位控制

此法是利用连接控制指令code xdata pdata \data bdata对“段”地址进行,如要指定某具体变量地址,则很有局限性,不作详细讨论。

2、变量定位:
只有全局变量可以绝对定位,局部变量无法实现绝对定位。
方法1:使用_at_关键字。声明一个全局变量unsigned char data MyBuf1[8] _at_ 0x20;
方法2:使用BL51 Locate选项。比如将main.c中定义的所有data型的全局变量定位到数据区D:0x28开始的空间,则从菜单中
选择Project->Options for Target 'Target1',在弹出的对话框中选择BL51 Locate页,在下面的data栏中写上?DT?MAIN(0x28)即可。
如果是idata,则使用?ID?MAIN(0x28),如果是xdata,则使用?XD?MAIN(0x28),如果是pdata,则使用?PD?MAIN(0x28)

Keil C51中变量和函数的绝对地址定位问题:
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 文件中可以找到下面的内容:
              >> 3.obj 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 中的函数的方法与此类似, 但是相应的参数传递可能要另寻方法.

1、函数定位:
假如要把C源文件 tools.c 中的函数
int BIN2HEX(int xx)
{
  ...
}
放在CODE MEMORY的0x1000处,先编译该工程,然后打开该工程的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的0x1000处了

2、赋初值的变量定位:
要将某变量定位在一绝对位置且要赋初值,此时用 _at_ 不能完成,则如下操作:
在工程中建立一个新的文件,如InitVars.c,在其中对要处理的变量赋初值(假设是code变
量):
char code myVer = {"COPYRIGHT 2001-11"};
然后将该文件加入工程,编译,打开M51文件,若定义的是code型,则在
* * *   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中写:
char xdata myVer = {"COPYRIGHT 2001-11"};
然后将该文件加入工程,编译,打开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、若有多个变量或函数要进行绝对地址定位,则应按地址从低到高的顺序排列。

 

使用KeilC51软件,可以很方便地将代码或者数据绝对定位到某个地址。
1、代码定位:
方法1:使用伪指令CSEG。比如要将MyFunc1定位到代码区C:0x1000,则新建一个A51文件,添加以下内容:
 PUBLIC  MYFUNC1
 CSEG AT 1000H
MYFUNC1:
 ;其它代码
 RET
 在其它源文件中,就可以调用MyFunc()函数了。需要注意的是,编译器不检测传递参数的数目,仅检测函数是否有返回值。
方法2:使用BL51 Locate选项。比如在main.c中定义了一个MyFunc2函数,并且要将该函数定位到代码区C:0x2000,则从菜单中选择Project->Options for Target 'Target1',在弹出的对话框中选择BL51 Locate页,在下面的code栏中写上?PR?MYFUNC2?MAIN(0x2000)即可。
如果想定位多个函数,也可以使用*通配符。 
2、变量定位:
只有全局变量可以绝对定位,局部变量无法实现绝对定位。
方法1:使用_at_关键字。声明一个全局变量unsigned char data MyBuf1[8] _at_ 0x20;
方法2:使用BL51 Locate选项。比如将main.c中定义的所有data型的全局变量定位到数据区D:0x28开始的空间,则从菜单中
选择Project->Options for Target 'Target1',在弹出的对话框中选择BL51 Locate页,在下面的data栏中写上?DT?MAIN(0x28)即可。
如果是idata,则使用?ID?MAIN(0x28),如果是xdata,则使用?XD?MAIN(0x28),如果是pdata,则使用?PD?MAIN(0x28)
3、堆栈定位:
在STARTUP.A51文件中定义了堆栈区?STACK,其起始地址同样可以在BL51 Locate页中设置,在Stack栏写上?STACK(0x80)

 

还可以通过汇编实现
// my.a51

public my_flash_var

cseg at 0F100H

my_flash_var:
    db 55h

end

然后C声明

// flash.c
extern unsigned char code my_flash_var;

BL51 locate 选项卡中

code range 和 xdata range如果不填写,编译默认将程序中相应代码和变量从空间前面取起
--------------------- 
作者:zyboy2000 
来源:CSDN 
原文:https://blog.csdn.net/zyboy2000/article/details/5731325 
版权声明:本文为博主原创文章,转载请附上博文链接!

  • 3
    点赞
  • 1
    评论
  • 7
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值