KEIL 程序定位

时间:2023-03-09 04:29:33
KEIL 程序定位

  用Keil做51的开发也4年多了,代码量基本上维持在5~10K左右,说大不大,说小也不小,也就是个中等货色。这段期间工作上难得有稍许的空间,潜心研究了一下keil中如何在 CODE中定位C程序的方法。

在汇编中数据段和程序段都比较容易定位,例如程序1:

    cseg    at 0x0000
    ljmp    main
    dseg    at 0x20 

    iseg    at 0xC0 

    xseg    at 0x0010 

    cseg    at 0x1000
main: 

    sjmp    main
    END 

  这段代码就把main定位到0x1000的位置。aa 定位在data(0x20)中,bb定位在idata(0xc0)中,CC定位到xdata(0x0010)当然,cc在不超过256字节的时候还可以用 page方式访问。

  在C语言程序里面定位比较复杂。

  常见的数组定位有如下几个方式:

  1、使用 _at_ ,例如程序2:

#include <reg51.h>
unsigned ] _at_ 0x20;    //在data段中0x20开始的地方定义一个20字节的数组
unsigned ] _at_ 0x80;    //在idata段中0x80开始的地方定义一个20字节的数组
unsigned ] _at_ 0x0000;  //在pdata段中0x00开始的地方定义一个20字节的数组
unsigned ] _at_ 0x0100;  //在xdata段中0x0100开始的地方定义一个20字节的数组

void main()
{
    sysTemp_aa[] = 0x55;
    sysTemp_bb[] = 0x55;
    sysTemp_cc[] = 0x55;
    sysTemp_dd[] = 0x55;

    )
    {
        ;
    }
}

编译后生成的汇编代码如下:

C:0x0000    020015   LJMP     C:0015 
    11: void main() 
12: { 
    13:         sysTemp_aa[0] = 0x55; 
C:0x0003    752055   MOV      sysTemp_aa(0x20),#0x55 
    14:         sysTemp_bb[0] = 0x55; 
C:0x0006    7880     MOV      R0,#sysTemp_bb(0x80) 
C:0x0008    7655     MOV      @R0,#0x55 
    15:         sysTemp_cc[0] = 0x55; 
C:0x000A    7800     MOV      R0,#sysTemp_cc(0x00) 
C:0x000C    7455     MOV      A,#0x55 
C:0x000E    F2         MOVX    @R0,A 
    16:         sysTemp_dd[0] = 0x55; 
17:  
C:0x000F    900100  MOV      DPTR,#sysTemp_dd(0x0100) 
C:0x0012    F0         MOVX    @DPTR,A 
    18:         while(1) 
C:0x0013    80FE     SJMP     C:0013 
C:0x0015    787F     MOV      R0,#0x7F 
C:0x0017    E4        CLR       A 
C:0x0018    F6        MOV      @R0,A 
C:0x0019    D8FD     DJNZ     R0,C:0018 
C:0x001B    758193   MOV      SP(0x81),#0x93 
C:0x001E    020003   LJMP     main(C:0003)

请注意加横线的是汇编代码对应的C程序。

  函数定位

  更深入一 点,如果我想把main函数定位呢?上面的汇编代码是由keil自动定位的一个地址,添加或者删除程序后,main的地址是不确定的,能否在也使用 _at_呢?答案是否定的。要想把程序段绝对定位需要在keil里面采用一定的设置。例如程序3:

#include <reg51.h>
unsigned ] _at_ 0x20;
unsigned ] _at_ 0x80;
unsigned ] _at_ 0x0000;
unsigned ] _at_ 0x0100;

extern void test1();

void main()
{
    sysTemp_aa[] = 0x55;
    sysTemp_bb[] = 0x55;
    sysTemp_cc[] = 0x55;
    sysTemp_dd[] = 0x55;

    )
    {
        test1();
    }
}

void test1()
{
    unsigned char i;

    ;i<;i++)
    {
        ;
    }
}

  我想把main函数定位到0x1000开始的地放,test1定位到0x2000开始的地方,怎么设置呢?点击“project”->”option for target ..."弹出如下窗口:

KEIL 程序定位

选择BL51 MISC,在Misc controls的框中填入code (?PR?MAIN?TEST (0x1000),?PR?TEST1?TEST (0x2000))。这里我解释一下,

code 表示程序段

?PR?各位可参考一下keil的帮助文件

?PR? 表示program 意思是 Executable program code(可执行程序段)

MAIN 和 TEST1 就是你要定位的程序名称

?TEST是main和test1所在的文件名,我这里只是示例一下,命名不是很明确,在实际应用中需要看情况而定。

好,我们编译一下,看看实际的效果。

KEIL 程序定位

再看看test1汇编程序

KEIL 程序定位

o(∩_∩)o...,正如自己所期望的那样,main和test1均定位到了所需的地址。下班了,明天再来更深入的介绍。

  如何定位整个文件,和keil创建工程文件中的group。

  继续昨天讲解的如何定位子程序的话题。当程序变大,子程序很多,我想把整个文件定位到一个固定地址怎么办呢?难不成每一个文件都在BL51 MISC中设置吗?那手脖子都累掉了,有那功夫还不如仔细研究研究keil的帮助文件,看看它里面是如何说明的。我仔细研究了一下keil的帮助文件,发现文件定位的方式有如下3种,且听我一一道来:

  方法1:这是最基本的方法,和昨天定位子程序的方法差不多,示例代码如下:

//test.c
#include <reg51.h>
unsigned ] _at_ 0x20;
unsigned ] _at_ 0x80;
unsigned ] _at_ 0x0000;
unsigned ] _at_ 0x0100;

extern void test1();
extern void test2();

void main()
{
    sysTemp_aa[] = 0x55;
    sysTemp_bb[] = 0x55;
    sysTemp_cc[] = 0x55;
    sysTemp_dd[] = 0x55;

    )
    {
        test1();
        test2();
    }
}

void test1()
{
    unsigned char i;

    ;i<;i++)
    {
        ;
    }
} 

void test2()
{
    unsigned char i;

    ;i<;i++)
    {
        ;
    }
} 

  现在我想把整个test.c的程序定位到0x1200开始的地方,还使用昨天的那个方法,点击“project”->”option for target ..."弹出如下窗口:

KEIL 程序定位

  在Misc controls中填入code(?PR?*?TEST (0x1200)),什么意思呢,就是test文件中所有的程序就定位到0x1200,呵呵,和昨天子程序定位相似吧,用一个*字符代替了所有的子程序名称。好看一下实际的结果。

KEIL 程序定位

  MAIN函数已经定位到了0x1200了,再看下面两个调用的函数test1和test2分别定位到了0x1218和0x121f,完全符合要求。如果想定位其他文件怎么办?假设现在有一个test2.c文件我想把test2定位到0x2200开始的地方,只要稍微修改一下 code(?PR?*?TEST (0x1200),?PR?*?TEST2(0x2200)), 各位看官是否注意到那个逗号',',用‘,’把各个文件隔开。实际效果请自行实践,我就不再 一一列举。

  方法2:使用USERCLASS。USERCLASS官方解释是The USERCLASS directive assigns a user defined class name to a compiler generated segment. By default, the C51 Compiler uses the basic class name for segment definitions.呵呵我的E文不是很好,只能看懂个大概,各位自己理解吧,想了解更多请参考keil的帮助文件。

  不在废话了,进入正题。首先定义USRCLASS,不过第一步要设置一下编译器,点击“project”->”option for target ..."弹出如下窗口:勾选LX51和AX51,其实AX51不是必须的,本帖只是讲C的定位,汇编程序不谈。

KEIL 程序定位

  选择好了以后选择“LX51 Locate",如图:

KEIL 程序定位

  在"User classes"中填写" CODE_TEST (C:0x1500 - C:0x2000)",如果你对程序的大小不确定那么只要写一个" CODE_TEST (C:0x1500 )"就可以,其他的交给KEIL自动分配。点击”确定“回到程序编辑区,在程序添加如下代码:

#pragma userclass (code = test)  //code的含义应该不用我说吧

  使用#pragma参数,在文件头部定位整个文件,刚刚填写的(CODE_TEST ...)到了程序中就需要写 code = test,为什么这么写,我也不知道keil的规定。总结了一下,比如说我再定一个userClass,名称为 CODE_TEST1(C:0x2000),那么到了程序中我就要这么写:

#pragma userclass (code = test1) 

  引申一下,同理可以使用userclass来限制一个文件使用的RAM区域,稍许修改一下那个CODE,怎么改各位参照KEIL帮助文档自己思考。实际修改后的源码:

//test.c
#include <reg51.h>
#pragma userclass (code = test)
unsigned ] _at_ 0x20;
unsigned ] _at_ 0x80;
unsigned ] _at_ 0x0000;
unsigned ] _at_ 0x0100;

extern void test1();
extern void test2();

void main()
{
    sysTemp_aa[] = 0x55;
    sysTemp_bb[] = 0x55;
    sysTemp_cc[] = 0x55;
    sysTemp_dd[] = 0x55;

    )
    {
        test1();
        test2();
    }
}

void test1()
{
    unsigned char i;

    ;i<;i++)
    {
        ;
    }
} 

void test2()
{
    unsigned char i;

    ;i<;i++)
    {
        ;
    }
} 

看看实际的效果:

KEIL 程序定位

main、test1和test2都已经定位到了1500开始的地方。

方法3:和方法2差不多,也是使用userclass不过方法稍许有点差别,同样定义一个userclass

KEIL 程序定位

回到程序编辑区,右键test.c选择”Option for file test.c",弹出如下窗口:

KEIL 程序定位

  在Misc Controls中填写:userclass (code = test),确定后回到程序编辑区。此时test.c文件的图标会有一个变化,如图:

KEIL 程序定位

编译源程序:

//test.c
#include <reg51.h>
unsigned ] _at_ 0x20;
unsigned ] _at_ 0x80;
unsigned ] _at_ 0x0000;
unsigned ] _at_ 0x0100;

extern void test1();
extern void test2();

void main()
{
    sysTemp_aa[] = 0x55;
    sysTemp_bb[] = 0x55;
    sysTemp_cc[] = 0x55;
    sysTemp_dd[] = 0x55;

    )
    {
        test1();
        test2();
    }
}

void test1()
{
    unsigned char i;

    ;i<;i++)
    {
        ;
    }
} 

void test2()
{
    unsigned char i;

    ;i<;i++)
    {
        ;
    }
} 

调试一下,看看结果是否和方法二相同?结果我就不展示了。

篇幅太长,GROUP定位的下一篇再介绍吧!

GROUP定位的方法和文件定位的方法3类似,详情各位请等我整理上来。