最新动态

工业物联网通讯专家!
认真做好每一件事,设计简单、可靠、价格合理的产品!

gcc环境下定义变量到固定地址的flash区的方法

先转载一段内容,作者:myorange  http://www.ourdev.cn/bbs/bbs_content.jsp?bbs_sn=3237920&bbs_id=9999

前段时间有如题的问题,在论坛找了不少贴子看,有些启发和参考价值,但是没找到个能看了就可以用的答案(可 能有但我没找到),经过2个晚上的研读(《GNU-ld链接脚本浅析》:http://blog.chinaunix.net/u/13991 /showart_177822.html ) 和实践,终于自己把问题解决掉. 现在整理下,贴在这里. 


可能大家都熟悉 "char  a[] PROGMEM={1,2,3,4}; "的定义方式,但是这种方式定义出来的变量地址 
是由编译器和连接器分配的,不能确定. 那有什么办法没呢? 答案是有. 
  
在网上搜索到2种方法: 
1. 在makefile 中定义section,如下: 
LDFLAGS = -Wl,--section-start=.mydatasection=0x001000,-Map=$(TARGET).map,--cref  

然后引用 
const unsigned char data[] __attribute__((section(".mydatasection")))={   
0x10,0x20,0x30   
};   

2. 在连接脚本里修改设置.  

在实验方法1 
在makefile 中设置 
LDFLAGS += -Wl,--section-start=.mydatasection=0x001500 
在main.c中引用 
const unsigned char mydata[4] __attribute((section(".mydatasection"))) = {'9','1','2','3'}; 

但是要注意,mydatasection 的定位地址,要是其它段没有用到的才可以,否则会报错,有地址重叠.


    
实验方法2(修改连接脚本),winavr20071221 版本,默认用的是avr4.x 脚本. 
先是研读了下)《GNU-ld链接脚本浅析》:http://blog.chinaunix.net/u/13991/showart_177822.html  
(惭愧啊,现在也还是没读太明白). 

  我修改后的脚本内容部分: 
   
   .start :     /*原来这里是.text,我改成.start*/ 
  { 
    *(.vectors) 
    KEEP(*(.vectors)) 
    /* For data that needs to reside in the lower 64k of progmem.  */ 
    *(.progmem.gcc*) 
    *(.progmem*) /*呵呵,常用到的 "PROGMEM" 是在这里定义的啊 */ 
    . = ALIGN(2); 
     __trampolines_start = . ; 
    /* The jump trampolines for the 16-bit limited relocs will reside here.  */ 
    *(.trampolines) 
    *(.trampolines*) 
     __trampolines_end = . ; 
    /* For future tablejump instruction arrays for 3 byte pc devices. 
       We don't relax jump/call instructions within these sections.  */ 
    *(.jumptables) 
    *(.jumptables*) 
    /* For code that needs to reside in the lower 128k progmem.  */ 
    *(.lowtext) 
    *(.lowtext*) 
     __ctors_start = . ; 
     *(.ctors) 
     __ctors_end = . ; 
     __dtors_start = . ; 
     *(.dtors) 
     __dtors_end = . ; 
    KEEP(SORT(*)(.ctors)) 
    KEEP(SORT(*)(.dtors)) 
    /* From this point on, we don't bother about wether the insns are 
       below or above the 16 bits boundary.  */ 
    *(.init0)  /* Start here after reset.  */ 
    KEEP (*(.init0)) 
    *(.init1) 
    KEEP (*(.init1)) 
    *(.init2)  /* Clear __zero_reg__, set up stack pointer.  */ 
    KEEP (*(.init2)) 
    *(.init3) 
    KEEP (*(.init3)) 
    *(.init4)  /* Initialize data and BSS.  */ 
    KEEP (*(.init4)) 
    *(.init5) 
    KEEP (*(.init5)) 
    *(.init6)  /* C++ constructors.  */ 
    KEEP (*(.init6)) 
    *(.init7) 
    KEEP (*(.init7)) 
    *(.init8) 
    KEEP (*(.init8)) 
    *(.init9)  /* Call main().  */ 
    KEEP (*(.init9)) 
  }    
  
  .first_data 0x100 : AT(0x100)  /*这里开始定义第一个自己section,注意要 VMA 和LMA 都要设置,且 
                                                                    相同,否则编译会有错,可能是因为不支持VMA与LMA不同地址*/ 
  { 
    . = ALIGN(2); 
         *(.first_data) 
    *(.first_data*)                        /*注意这行哦,*(.first_data*) 表示用改section 定义的数据放在这里*/ 
  } 
      

  .second_data 0x180 : AT(0x180 ) /*这里第2个*/ 
  { 
  . = ALIGN(2); 
         *(.second_data)     
         *(.second_data*) 
  } 
   
  .text 0x200 :  AT(0x200)   /*这里第3个*/ 
  { 
         
    *(.text) 
    . = ALIGN(2); 
    *(.text.*) 
    . = ALIGN(2); 
    *(.fini9)  /* _exit() starts here.  */ 
    KEEP (*(.fini9)) 
    *(.fini8) 
    KEEP (*(.fini8)) 
    *(.fini7) 
    KEEP (*(.fini7)) 
    *(.fini6)  /* C++ destructors.  */ 
    KEEP (*(.fini6)) 
    *(.fini5) 
    KEEP (*(.fini5)) 
    *(.fini4) 
    KEEP (*(.fini4)) 
    *(.fini3) 
    KEEP (*(.fini3)) 
    *(.fini2) 
    KEEP (*(.fini2)) 
    *(.fini1) 
    KEEP (*(.fini1)) 
    *(.fini0)  /* Infinite loop after program termination.  */ 
    KEEP (*(.fini0)) 
     _etext = . ; 
  }  > text 
  
 .third_data 0x1400 : AT(0x1400)  /*这里第4个*/ 
  { 
        . = ALIGN(2); 
    *(.third_data) 
    *(.third_data*) 
  } 
   
  .data          : AT (ADDR (.third_data) + SIZEOF (.third_data)) 
  { 
     PROVIDE (__data_start = .) ; 
    *(.data) 
    *(.data*) 
    *(.rodata)  /* We need to include .rodata here if gcc is used */ 
    *(.rodata*) /* with -fdata-sections.  */ 
    *(.gnu.linkonce.d*) 
    . = ALIGN(2); 
     _edata = . ; 
     PROVIDE (__data_end = .) ; 
  }  > data 

文件中引用: 
const unsigned char buff2[4] __attribute((section(".first_data"))) = {'9','1','2','3'}; 
const unsigned char buff3[4] __attribute((section(".first_data"))) = {'9','1','2','3'}; 



看编译后的map 
first_data     0x00000100        0x8 load address 0x00000100 
                0x00000100                . = ALIGN (0x2) 
 *(.first_data) 
 .first_data    0x00000100        0x8 main.o 
                0x00000104                buff3 
                0x00000100                buff2 
 *(.first_data*) 

.second_data    0x00000180        0x0 load address 0x00000180 
                0x00000180                . = ALIGN (0x2) 
 *(.second_data) 
 *(.second_data*)     
  
third_data     0x00001400        0x0 load address 0x00001400 
                0x00001400                . = ALIGN (0x2) 
 *(.third_data) 
 *(.third_data*) 

.data           0x00800100        0x0 load address 0x00001400 
                0x00800100                PROVIDE (__data_start, .) 



读取函数还是跟  用 PROGMEM 定义的一样, PROGMEM,只是告诉编译器或连接器把该变量放到什么地方去. 

另外,该ld脚本是经过修改,可能就适合这个特定项目,可以在makefile 中指定该项目的特定的脚本,而不是用默认的 
方法:  LDFLAGS += -Wl,-T ./avr4.x 


呵呵,这样就能做到开辟一个指定地址空间的flash,用来存储特定的数据,而不必担心被其它text 或data  

占用掉. 

 

自我体会:

在LPC1111编程时的应用, LPCexpress GCC 指定变量到flash地址。

 

在Project->Properties->setting->Miscellaneous 添加一条记录: --section-start=.mydatasection=0x001800


static volatile unsigned char data[] __attribute__((section(".mydatasection")))={ 'a','b','c','d','e','f','g','g'

};

 

void main(void) {

    x= data[0];

}


测试效果:

GCC 指定位置

测试效果:



上一篇: JS获取当前时间戳的方法-JavaScript 获取当前时间戳
下一篇: GPRS DTU 方案详解 GPRS TCP to 232透明传输模块

发表评论