点击上方「嵌入式大杂烩」,选择「置顶公众号」第一时间阅读编程笔记!
原文:https://www.cnblogs.com/amanlikethis/p/3719529.html
一、概述
1、说明
每一款芯片的启动文件都值得去研究,因为它可是你的程序跑的最初一段路,不可以不知道。通过了解启动文件,我们可以体会到处理器的架构、指令集、中断向量安排等内容,是非常值得玩味的。
STM32作为一款高端 Cortex-M3
系列单片机,有必要了解它的启动文件。打好基础,为以后优化程序,写出高质量的代码最准备。
本文以一个实际测试代码--START_TEST为例进行阐述。
2、整体过程概括
STM32整个启动过程是指从上电开始,一直到运行到 main函数
之间的这段过程,步骤为(以使用微库为例):
①上电后硬件设置SP、PC
②设置系统时钟
③软件设置SP
④加载.data、.bss,并初始化栈区
⑤跳转到C文件的main函数
3、整个启动过程涉及的代码
启动过程涉及的文件不仅包含 startup_stm32f10x_hd.s
,还涉及到了MDK自带的连接库文件 entry.o、entry2.o、entry5.o、entry7.o
等(从生成的 map
文件可以看出来)。
二、程序在Flash上的存储结构
在真正讲解启动过程之前,先要讲解程序下载到 Flash
上的结构和程序运行时(执行到main函数)时的SRAM数据结构。程序在用户Flash上的结构如下图所示。下图是通过阅读hex文件和在MDK下调试综合提炼出来的。
MSP初始值 编译器生成,主堆栈的初始值
异常向量表 不多说
外部中断向量表 不多说
代码段 存放代码
初始化数据段 .data
未初始化数据段 .bss
加载数据段和初始化栈的参数
加载数据段和初始化栈的参数分别有4个,这里只讲解加载数据段的参数,至于初始化栈的参数类似。
0x0800 033c Flash上的数据段(初始化数据段和未初始化数据段)起始地址
0x2000 0000 加载到SRAM上的目的地址
0x0000 000c 数据段的总大小
0x0800 02f4 调用函数_scatterload_copy
需要说明的是初始化栈的函数-- 0x08000304
与加载数据段的函数不一样,为 _scatterload_zeroinit
,它的目的就是将栈空间清零。
三、数据在SRAM上的结构
程序运行时(执行到main函数)时的SRAM数据结构
四、详细过程分析
有了以上的基础,现在详细分析启动过程。
1、上电后硬件设置SP、PC
刚上电复位后,硬件会自动根据向量表偏移地址找到向量表,向量表偏移地址的定义如下:
调试现象如下:
看看我们的向量表内容(通过J-Flash打开hex文件)
硬件这时自动从0x0800 0000位置处读取数据赋给栈指针SP,然后自动从0x0800 0004位置处读取数据赋给PC,完成复位,结果为:
SP = 0x0200 0810
PC = 0x0800 0145
2、设置系统时钟
上一步中令 PC=0x08000145
的地址没有对齐,硬件自动对齐到 0x08000144
,执行 SystemInit
函数初始化系统时钟。
3、软件设置SP
LDR R0,=__main
BX R0
执行上两条之类,跳转到 __main
程序段运行,注意不是main函数, ___main
的地址是0x0800 0130。
可以看到指令LDR.W sp,[pc,#12],结果SP=0x2000 0810。
4、加载.data、.bss,并初始化栈区
BL.W __scatterload_rt2
进入 __scatterload_rt2
代码段。
__scatterload_rt2:
0x08000168 4C06 LDR r4,[pc,#24] ; @0x08000184
0x0800016A 4D07 LDR r5,[pc,#28] ; @0x08000188
0x0800016C E006 B 0x0800017C
0x0800016E 68E0 LDR r0,[r4,#0x0C]
0x08000