在链接的过过程中与编译过程相比其中显著的与编译指令不同的便是 -T xx.ld。在这里 -T xx.ld实际上是调用了一个.ld的文件,那么.ld文件是做什么的呢?这里就比较高深了,在51单片机中我们知道最后在生成代码后51单片机内存中会有如 code、xdata、data的区段,来讲代码中执行部分、变量部分等分区块放置,而.ld就是一种链接器使用的规则性文件,他告诉链接器单片机系统的ROM、RAM的地址和他们的大小等信息,并指示链接器将什么代码保存在什么位置。对于.ld文件它是有一套自己的语法及设置参数的规则的,大家可以不具体作了解,但求看懂其中一部分的信息。/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20010000; /* end of 64K RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0; /* required amount of heap */
_Min_Stack_Size = 0x200; /* required amount of stack */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(4);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(4);
} >RAM
/* MEMORY_bank1 section, code must be located here explicitly */
/* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */
.memory_b1_text :
{
*(.mb1text) /* .mb1text sections (code) */
*(.mb1text*) /* .mb1text* sections (code) */
*(.mb1rodata) /* read-only data (constants) */
*(.mb1rodata*)
} >MEMORY_B1
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
/* Copy the data segment initializers from flash to SRAM */
pulSrc = &_sidata;
for(pulDest = &_sdata; pulDest < &_edata; )
{
*(pulDest++) = *(pulSrc++);
}
/* Zero fill the bss segment. This is done with inline assembly since this
will clear the value of pulDest if it is not kept in a register. */
__asm(" ldr r0, =_sbss\n"
" ldr r1, =_ebss\n"
" mov r2, #0\n"
" .thumb_func\n"
"zero_loop:\n"
" cmp r0, r1\n"
" it lt\n"
" strlt r2, [r0], #4\n"
" blt zero_loop");
/* Setup the microcontroller system. */
SystemInit();
/* Call the application's entry point.*/
main();
}
在启动函数中我们可以清晰地看到,在最后一步中,单片机的程序被转入到了main函数的入口,那么在执行main函数之前,C语言,和内联汇编程序干了什么呢?首先头位置的C语言将终端向量表从ROM头位置,复制到了RAM头位置(即:0x20000000),这里在RAM中的终端向量表时间上没有没我们用到,当然这是因为在M3的内核中,它允许用户在NIVC的寄存器中重新定义终端向量表的位置,我们可以使用NVIC_SetVectorTable(NVIC_VectTab_FLASH,0);
这个函数来将终端向量表设置到到0x20000000位置。该功能实际上是用于方便装有系统的环境中使用,可以加快终端响应的速度,同时可以快速的动态的更改终端处理的程序。当然在我们的应用中并未使用到这一特性,所以此处的复制中断向量表的操作是可以删除的,它在此的作用只是为了防止用户在程序中使用了重定向向量表语句而使得程序跑飞所添加的。因为终端向量是系统最基础稳定性的保证,如果在硬件错误发生等中断发生的情况下单片机无法正确的跳转,会对代码调试和系统稳定运行带来严重的影响。之后紧跟的这几条汇编代码实现的是:全局变量与静态变量的初始化并将其从flash中调入内存,即在C语言运行全局变量与静态变量的初始化操作。在此之后, SystemInit();函数被调用,配置好时钟等参数。最后我们的main函数就可以执行啦~。这便是是我们在这个例程中使用的启动文件,而在keil工程中,这个文件是用汇编代码写成的,但这些文件功能都是一样的,设置终端向量表,初始化全局与静态变量,进入main函数,都是这样的流程。在gcc的环境中我们也可以是用汇编编写这样的文件,我们面前的选择有很多,当然我们没必要自己编写这些链接文件和启动代码,在之后的实际的工程建立中我会告诉大家实际的方法。不过在此之前我们还是要先把基础的内容学好再说。其他的说明在文件中我们看到了**_sidata、_sdata**等变量,这些变量在文件的前面部分被定义为外部:extern unsigned long _sidata; /*!< Start address for the initialization
values of the .data section. */
extern unsigned long _sdata; /*!< Start address for the .data section */
extern unsigned long _edata; /*!< End address for the .data section */
extern unsigned long _sbss; /*!< Start address for the .bss section */
extern unsigned long _ebss; /*!< End address for the .bss section */