引用网上的问题:
"单片机上电初始化后,RAM存储初始化全局变量,这些全局变量是从调试器烧进去的S19文件中获取的吗?" 这是个很好的问题, 全局变量和静态变量的初始化值是保存在Flash中的Const段里的 ,新建一个工程的时候默认有个startup的汇编程序文件,它负责将const段中的初始值付给这些全部变量。这些事情是发生在你的main函数之前的。
这个问题其实简单讲就是:单片机程序下载好之后,所有的数据都存放在flash中,那么map文件中的.data段和.bss段怎么在上电之后放到RAM中呢?
mcu启动后,程序运行需要的data段、bss段在mcu startup阶段拷贝到ram中。其实就是在启动文件中添加一段拷贝代码,将flash中存放的.data段和.bss段拷贝到RAM,以下是以startup_stm32f103xe.s 为例:
Reset_Handler: //在复位函数中包含拷贝操作
/* Copy the data segment initializers from flash to SRAM */
ldr r0, =_sdata //data在RAM的起始地址
ldr r1, =_edata //data在RAM的结束地址
ldr r2, =_sidata //data段的FLASH存储地址
movs r3,
b LoopCopyDataInit // 跳转到子函数
CopyDataInit: // copy代码
ldr r4, [r2, r3] //将flash中偏移地址是r3的数据拷贝到R4
str r4, [r0, r3] //将R4的数据放到RAM上去
adds r3, r3,
LoopCopyDataInit: // 循环拷贝函数
adds r4, r0, r3 //R4的地址是搬到ram的地址
cmp r4, r1 // 比较是否到结束地址,到结束地址的话就表示数据搬完了
bcc CopyDataInit //没有结束的话跳转到copy代码
/* Zero fill the bss segment. */
ldr r2, =_sbss // bss的起始地址
ldr r4, =_ebss //bss的结束地址
movs r3,
b LoopFillZerobss //跳转到子函数
FillZerobss:
str r3, [r2] //将R2地址初始化为0
adds r2, r2,
LoopFillZerobss: //循环函数
cmp r2, r4 //判断地址是否到结束地址
bcc FillZerobss //没有结束的话跳转到FillZerobss
Keil中是没有这段代码的,应该是放在了KEIl的STM支持包中,如果是自己使用ARM-GCC构建STM32的编译环境,则启动文件中需要加上这段代码。
上述代码每行有添加了注释,说明了功能。有嵌入式er可能会问:这其中的地址标记符是在哪里定义的?像_sdata,_edata,_sidata这些,这些是在链接脚本中的指定好的,此启动代码对应的链接脚本文件中的定义如下:
/* 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