STM32启动过程
1、MDK 编译生成文件简介
MDK 编译工程,会生成一些中间文件(如**.o**、.axf、.map等),最终生成hex文件
- MDK编译过程文件共11种:
文件类型 | 说明 |
---|---|
.o | 可重定向对象文件,每个**.c**/.s文件编译都会生成一个**.o** 文件 |
.axf | 可执行对象文件,由**.o** 文件链接生成,仿真时需要用到此文件 |
.hex | INTEL Hex格式文件,用于下载到MCU运行,由**.axf**文件转换而来 |
.crf | 交叉引用文件,包含浏览信息(定义、标识符、引用) |
.d | 由 ARMCC/GCC 编译生产的依赖文件,每个.o 文件,都有一个对应的.d 文件 |
.dep | 整个工程的依赖文件 |
.lnp | MDK 生成的链接输入文件,用于命令输入 |
.lst | C 语言或汇编编译器生成的列表文件 |
.htm | 链接生成的列表文件 |
.build_log.htm | 最近一次编译工程时的日志记录文件 |
.map | 链接器生成的列表文件/MAP文件,对分析程序存储占用情况非常有用 |
2、MAP文件浅析
2.1 MAP文件概念和作用
MAP文件是MDK编译代码后产生的,集程序、数据及IO空间的一种映射列表文件。简单说就是包括了:各种**.c**文件、函数、符号等的地址、大小、引用关系等信息
- 作用:分析各**.c文件占用FLASH和RAM**的大小,方便优化代码
2.2 MAP文件组成
组成部分 | 简介 |
---|---|
程序段交叉引用关系 | 描述各文件之间函数调用关系 |
删除映像未使用的程序段 | 描述工程中未用到而被删除的冗余程序段/数据 |
映像符号表 | 描述各符号(程序段/数据)在存储器中的地址、类型、大小等 |
映像内存分布图 | 描述各个程序段在存储器中的地址及占用大小 |
映像组件大小 | 给出整个映像代码(.o)占用空间汇总信息 |
- 映像符号表及映像组件大小中的一些概念:
基础概念 | 简介 |
---|---|
Section | 描述映像文件的代码或数据块,我们简称程序段 |
RO | Read Only 的缩写,包括只读数据(RO data)和代码(RO code)两部分内容,占用 FLASH 空间 |
RW | Read Write 的缩写,包含可读写数据(RW data,有初值,且不为 0),占用 FLASH(存储初值)和 RAM(读写操作) |
ZI | Zero initialized 的缩写,包含初始化为 0 的数据(ZI data),占用 RAM 空间。 |
.text | 相当于 RO code |
.constdata | 相当于 RO data |
.bss | 相当于 ZI data |
.data | 相当于 RW data |
3、STM32启动模式
M3/M4/M7等内核复位后,做的第一件事:
- 从地址0x0000 0000处取出堆栈指针MSP的初始值,即
SP = _initial_sp
- 从地址0x0000 0004处取出程序计数器指针PC的初始值,即
PC = Reset_Handler
以STM32F1系列为例:
在系统复位后,SYSCLK的第 4 个上升沿,BOOT引脚的值将被锁存,内核复位后的起始地址和中断向量表的位置被重映射
BOOT1 | BOOT0 | 启动模式 | 0x0000 0000映射地址 | 0x0000 0004映射地址 |
---|---|---|---|---|
x | 0 | FLASH | 0x0800 0000 | 0x0800 0004 |
0 | 1 | Bootloader | 0x1FFF F000 | 0x1FFF F004 |
1 | 1 | SRAM | 0x2000 0000 | 0x2000 0004 |
- 通过 boot 引脚设置可以将中断向量表定位于 FLASH 区,即起始地址为 0x0800 0000,同时复位后 PC 指针位于 0x0800 0004处
- 通过 boot 引脚设置可以将中断向量表定位于系统存储器内置 Bootloader 区,即起始地址为 0x1FFF F000,同时复位后 PC 指针位于 0x1FFF F004 处
- 通过 boot 引脚设置可以将中断向量表定位于 SRAM 区,即起始地址为 0x2000 0000,同时复位后 PC 指针位于 0x2000 0004 处
4、STM32启动过程
以内部FLASH启动为例:
4.1 启动文件介绍
启动文件主要做了以下工作:
-
初始化堆栈指针
SP = _initial_sp
_initial_sp从地址0x0800 0000获取
-
初始化程序计数器指针
PC = Reset_Handler
Reset_Handler从地址0x0800 0004获取
-
设置堆的大小Heap_Size、栈的大小Stack_Size
-
初始化中断向量表 __Vectors定义
-
调用初始化函数 如:调用 SystemInit 函数(可选)
-
调用标准 C 库中的 __main 函数初始化用户堆栈,最终调用 main 函数
4.2 Reset_Handler函数介绍
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0,=SystemInit
BLX R0
LDR R0,=__main
BX R0
ENDP
这是一段汇编代码
-
EXPORT Reset_Handler [WEAK]
:Reset_Handler函数可以在外部调用;若此函数在别处重新定义,则此处函数定义失效 -
IMPORT __main
、IMPORT SystemInit
:声明**__main函数、SystemInit函数来自外部文件,类似于 C 中的extern** -
LDR R0,=SystemInit BLX R0
取SystemInit函数首地址到R0寄存器中,然后跳转到R0寄存器对应的地址,即调用SystemInit函数
-
LDR R0,=__main BX R0
取**__main函数首地址到R0寄存器中,然后跳转到R0寄存器对应的地址,即==调用__main**函数==
4.3 堆栈简介
内存 | 作用 |
---|---|
栈(Stack) | 编译器自动分配和释放,存放函数参数、局部变量等 |
堆(Heap) | 人为手动分配和释放,如:malloc、calloc、realloc等 |
Stack_Size EQU 0x0000 0400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
-
栈的定义
-
Stack_Size EQU 0x0000 0400
:给常量0x0000 0400取个别名,常量的别名是 Stack_Size,类似于 C 中的define -
AREA STACK, NOINIT, READWRITE, ALIGN=3
:汇编一个新的代码段或者数据段,STACK为段名,可以任意命名
NOINIT 表示不初始化
READWRITE 表示可读可写
ALIGN=3表示按照2的3次幂对齐,即 8 字节对齐
-
Stack_Mem SPACE Stack_Size
:分配大小为 Stack_Size 字节连续的存储单元给栈空间 -
__initial_sp
:紧挨着 SPACE 放置,表示栈的结束地址,栈是从高往低生长,所以结束地址就是栈顶地址
-
Heap_Size EQU 0x0000 0200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
PRESERVE8
THUMB
-
堆的定义
-
Heap_Size EQU 0x0000 0200
:给常量0x0000 0200取个别名,常量的别名是 Stack_Size,类似于 C 中的define -
AREA STACK, NOINIT, READWRITE, ALIGN=3
与栈的意义相同 -
Heap_Mem SPACE Heap_Size
:分配大小为 Heap_Size 字节连续的存储单元给堆空间 -
__heap_base __heap_limit
__heap_base表示堆的起始地址,__heap_limit 表示堆的结束地址。堆和栈的生长方向相反的,堆是由低向高生长,而栈是从高往低生长
-
PRESERVE8
:指示编译器按照 8 字节对齐 -
THUMB
:指示编译器之后的指令为 THUMB 指令
-