Keil C51中变量和函数的绝对地址定位问题

发布者:技术掌门最新更新时间:2016-12-14 来源: eefocus关键字:Keil  C51  变量  函数  绝对地址  定位问题 手机看文章 扫描二维码
随时随地手机看文章

1、变量绝对地址定位


1) 在定义变量时使用 _at_ 关键字加上地址就可。


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 中的函数的方法与此类似, 但是相应的参数传递可能要另寻方法.


 


  段的定义 


  RSEG是段选择指令,要想明白它的意思就要了解段的意思。


  段是程序代码或数据对象的存储单位。程序代码放到代码段,数据对象放到数据段。段分两种,一是绝对段,一是再定位段。绝对段在汇编语言中指定,在用L51联接的时候,地址不会改变。用于如访问一个固定存储器的i/o,或提供中断向量的入口地址。而再定位段的地址是浮动的,它的地址有L51对程序模块连接时决定,C51对源程序编译所产生的段都是再定位段,它都有段名和存储类型。绝对段没有段名。


说了这么多,大家可能还是不明白段是什么意思。别急,接着往下看。


例如,你写用C写了一个函数 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 段名


它用来选择一个在前面已经定义过的再定位段作为当前段。用法就像我们上面的例子,先申明了一个函数段,后面写这个函数段。


  2、绝对段选择指令


复制代码

CSEG [AT 绝对地址表达式]  //绝对代码段

DSEG [AT 绝对地址表达式]  //内部绝对数据段

XSEG [AT 绝对地址表达式]  //外部绝对数据段

ISEG [AT 绝对地址表达式]  //内部间接寻址绝对数据段

BSEG [AT 绝对地址表达式]  //绝对位寻址段 

复制代码

它们的用法,举一个例子:


例如我们写串口中断程序,起始地址是0x23.就这样写


CSEG AT 0X23

LJMP serialISR

RSEG ?PR?serialISR?TEST

. serialISR:  

  汇编函数使用同一个工程C文件中的变量,例如ICFLAG在C文件中定义,则汇编文件中定义方式为


EXTERN    ICFLAG    ;定义外部变量 

 


  定义函数,例如


复制代码

CARDATR:

    ...........

    RET


GLOBAL    CARDATR 

复制代码

 在同一个工程文件下调用汇编中的函数CARDATR,则应该定义函数


extern void CARDATR(void); 

 


C18指定数据绝对地址


例如:


#pragma udata overlay RECBUFS =0x190 //200

UINT8 NUMBER;

UINT8 REC_BUF[31];

#pragma udata

 


关键字:Keil  C51  变量  函数  绝对地址  定位问题 引用地址:Keil C51中变量和函数的绝对地址定位问题

上一篇:KEIL C51中的_at_关键字
下一篇:keil中使用_at_绝对地址定位

推荐阅读最新更新时间:2024-03-16 15:25

STM32之USART库函数USART_SendData的bug
1.最近在调试ATM32F103CB时发现,一串数据的最后一个字节总是发送不出去,用的是RS485收发; 2.代码如下: void uartReturn(unsigned char childBoardAddr) { uchar temp = 0; //must have temp += 0xAB; temp += childBoardAddr; temp += 0x30; temp += 0x01; temp += childBoardAddr; RS485_TX_EN; //enable rs485 tx sendByte(0xAB); sendByte(childBoardAddr); se
[单片机]
KEIL 5下载程序到STM32F103ZET6芯片时的相关设置
1、点击箭头所指的魔术棒打开项目设置,(也可在Project下打开该魔术棒) 2、Device选项卡下选择项目所使用的芯片,右侧是与所选芯片的相关信息(我用的是STM32F103ZET6选择STM32F103ZE即可) 3、Target选项卡下晶振与软件仿真有关,我们是下载到硬件然后调试可以不用设置,下面有ROM和RAM的起始地址和大小,指的都是实际硬件的ROM和RAM的起始地址和大小,默认即可 4、Output选项卡如下,打开Debug调试信息和阅读代码时函数跳转 5、Listing选项卡下将红框中的全部选上,目的是在最后程序链接时记录详细信息(在project.map文件中),英语不太好
[单片机]
keil51下使用sprintf问题
测试环境:keil c51 + STC89C52 说明: 1.keil的不定参数只有15个字节 也就是说sizeof(...) 加起来总共不能超过15字节,否则会出错 2.当不定参数中有常数时,你也会得不到你想要的结果,字符串除外 例: unsigned char count_sec=2, count_min=3, count_hour=4; xdata char buf = ; sprintf(buf, %2d:%2d:%2d , count_hour, count_min, count_sec); 则转换完的结果是不正确的。 解决方式1: unsigned char count_sec=2, count_min=3, cou
[单片机]
keil c51中C程序的启动过程
汇编是从org 0000h开始启动,那么keil c51是如何启动main()函数的?keil c51有一个启动程序startup.a51,它总是和c程序一起编译和链接。下面看看它和main()函数是如何编译的; //主函数如下; void main(void) { while (1) 这是个无条件空循环。 { } } 把上面的main()函数编译后的汇编程序和反汇编代码整理后对照如下; ?C_C51STARTUP SEGMENT CODE ?PR?main?TESTMAIN SEGMENT CODE ?STACK SEGMENT IDATA RSEG ?STACK
[单片机]
C51单片机 蜂鸣器两只蝴蝶代码
#include // 这是 单片机音乐代码生成器 生成的代码 #define uchar unsigned char sbit beepIO=P1^5; // 输出 为 P1.5 可以修改 成 其它 IO 口 uchar m,n; uchar code T ={{0,0}, {0xF8,0x8B},{0xF8,0xF2},{0xF9,0x5B},{0xF9,0xB7},{0xFA,0x14},{0xFA,0x66},{0xFA,0xB9},{0xFB,0x03},{0xFB,0x4A},{0xFB,0x8F},{0xFB,0xCF},{0xFC,0x0B}, {0xFC,0x43},{0xFC,0x
[单片机]
PID调节C51程序(2)
这是从网上找来的一个比较典型的PID处理程序,在使用单片机作为控制cpu时,请稍作简化,具体的PID参数必须由具体对象通过实验确定。由于单片机的处理速度和ram资源的限制,一般不采用浮点数运算,而将所有参数全部用整数,运算到最后再除以一个2的N次方数据(相当于移位),作类似定点数运算,可大大提高运算速度,根据控制精度的不同要求,当精度要求很高时,注意保留移位引起的“余数”,做好余数补偿。这个程序只是一般常用pid算法的基本架构,没有包含输入输出处理部分。 =========================================================================================
[单片机]
stm32 外部中断嵌套[操作寄存器+库函数]
stm32共有19个外部中断: 线0~15:对应外部I/O口的输入中断 线16:连接到PVD输出。PVD(Programmable Votage Detector),即可编程电压监测器。作用是监视供电电压,在供电电压下降到给定的阀值以下时,产生一个中断,通知软件做紧急处理。当供电电压又恢复到给定的阀值以上时,也会产生一个中断,通知软件供电恢复。 线17:连接到RTC实时时钟产生闹钟事件。 线18:连接到USB唤醒事件 在 stm32 NVIC中断 和 stm32 USART串口通信 中已经介绍过stm32的中断和串口输出使用方法,本文运用外部中断嵌套,通过串口发送相应信息,验证外部中断嵌套。 按下PA0(按
[单片机]
stm32 外部中断嵌套[操作寄存器+库<font color='red'>函数</font>]
如何创建51单片机KEIL工程步骤
1. 什么是Keil软件 Keil软件是一款专为嵌入式系统开发而设计的集成开发环境(IDE),常用于51、STM32单片机软件开发的编程软件。Keil编程软件不仅支持多种工具和扩展程序,还能够处理多种文件格式和数据交换方式;可支持单片机程序的在线仿真,或通过仿真器进行实物仿真,大大方便开发人员的工作。 其中,我们常用的Keil软件有两种,分别为Keil C51以及Keil MDK,前者主要用于51单片机等8051内核单片机的开发工具(不支持32位单片机),Keil MDK主要用于ARM架构单片机的开发工具,主要用于STM32单片机等32单片机。所以在使用Keil软件时需要主要这一点。 2.如何创建51单片机KEIL工程步骤
[单片机]
如何创建51单片机<font color='red'>KEIL</font>工程步骤
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved