STM32-启动文件
- 简介
- 启动文件
- 栈空间开辟
- 堆空间开辟
- 中断向量表定义
- 复位程序
- 系统启动流程
简介
STM32 启动文件由 ST 官方提供,由汇编编写,是系统上电复位后执行的第一个程序。
启动文件主要做的工作。
1.初始化堆栈指针 SP = _initial_sp
2.初始化程序计数器指针 PC = Reset_Handler
3.设置堆和栈的大小
4.初始化中断向量表
5.配置外部 SRAM 作为数据存储器(可选)
6.配置系统时钟,通过调用 SystemInit 函数(可选)
7.调用 C 库中的 _main 函数初始化用户堆栈,最终调用 main 函数
启动文件
栈空间开辟
33 行EQU: 宏定义的伪指令,给数字常量取一个符号名,类似与C中的define。定义栈的大小为0x0000 0400字节,即1024B(1KB),常量的符号为Stack_Size。
35 行 AREA 汇编一个新的代码段或者数据段。段名为STACK;NOINIT为不初始化;READWRITE为可读可写;ALIGN=3.表示按照2^3对齐,即8字节对齐。
36 行 SPACE 分配内存指令,分配大小为 Stack_Size 字节连续的存储单元给栈空间。
37 行__initial_sp 紧挨着 SPACE 放置,表示栈的结束地址,栈是从高往低生长,所以结束地址就是栈顶地址。
栈主要用于存放局部变量,函数形参等,属于编译器自动分配和释放的内存,栈的大小不能超过内部 SRAM 的大小。如果工程比较大,定义的局部变量比较多时,可以在这修改栈的空间大小。
堆空间开辟
这段代码和上面类似,定义Heap_Size为512个字节,段名为HEAP,不初始化,可读可写,8字节对齐。
__heap_base表示起始地址,__heap_limit表示结束地址。堆和栈的生长方向相反,堆是由低向高生长,而栈是从高往低生长。
堆主要由于动态内存的分配,由程序员分配和释放,如malloc(),calloc()。
51行和 52行这两句代码分别是指示编译器按照8字节对齐,以及指示编译器之后的指令为THUMB指令。
中断向量表定义
定义一个数据段,名字为RESET,READONLY表示只读。EXPORT表示一个符号具有全局属性,可以被外部的文件使用。
当内核响应了一个发送的异常后,对应的异常服务例程(ESR)就会执行,为了决定ESR的入口地址,内核使用了向量表查表机制。向量表其实是一个WORD(32位)数组,每个下标对应一种异常,该下标元素的值则是该 ESR 的入口地址。
__Vectors 为向量表起始地址, __Vectors_End 为向量表结束地址,__Vectors_Size 为向量表大小,__Vectors_Size = __Vectors_End - __Vectors。
DCD:分配一个或者多个以字为单位的内存,以四字节对齐,并要求初始化这些内存。
中断向量表被放置在代码段的最前面。当我们的程序在FLASH运行时,那么向量表的起始地址是:0x0800 0000。DCD:以四字节对齐分配内存,也就是下个地址是0x0800 0004,存放的是Reset_Handler中断函数入口地址。
从代码上看,向量表中存放的都是中断服务函数的函数名,所以 C 语言中的函数名对芯片来说实际上就是一个地址。
复位程序
125行 定义一个段名为.text的只读代码段,在CODE区。
程序从129行开始,130行声明一个复位中断向量Reset_Handler为全局属性,外部文件可以调用复位中断服务。WEAK:表示弱定义,如果外部文件也定义了该标号则首先引用外部定义的标号,如果外部没有声明则使用这个。
131行和132行 IMPORT表示该标号来自外部文件,__main和SystemInit均来自外部文件。
133行 LDR表示从存储器中加载字到一个存储器中。SystemInit是一个标准的库函数,在system_stm32f1xx.c文件中定义,主要作用是配置系统时钟,还有初始化FSMC/FMC总线上外挂的SRAM(可选),前面说配置外部SRAM作为数据存储器(可选)就是这个。
134行 BLX表示跳转到由寄存器给出的地址,并根据寄存器的LSE确定处理器的状态,还要把跳转前的下条指令地址保存到LR。
135行把__main的地址给R0__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈和变量等,最终调用 main 函数去到 C 的世界。这就是为什么我们写的程序都有一个 main 函数的原因,如果不调用__main,那么程序最终就不会调用我们 C 文件里面的main,也就无法正常运行。
136行 BX 表示跳转到由寄存器/标号给出的地址,不用返回。这里表示切换到__main地址,最终调用 main 函数,不返回,进入 C 的世界。
137 行 ENDP 表示子程序结束。LDR、BLX、BX 是内核的指令,可在《CM3 权威指南 CnR2》第四章-指令集里面查
询到。
系统启动流程
在以前A7/A9内核的控制器在复位后,CPU会从存储空间的绝对地址0x0000 0000取出第一条指令执行复位中断服务程序的方式启动,即固定了复位后的起始地址为0x0000 0000同时中断向量表的位置也是固定的。而Cortex-M3内核复位后的起始地址和中断向量表的位置可以被重映射。重映射的方式为,boot引脚的选择。
1.将中断向量表定位在SRAM区,即起始地址为0x2000 0000,同时复位后PC指针位于0x2000 0000处。
2.将中断向量表定位在FLASH区,即起始地址为0x8000 0000,同时复位后PC指针位于0x2000 0000处。
3.将中断向量表定位于内置 Bootloader 区。
Cortex-M3 内核规定,起始地址必须存放堆顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在 Cortex-M3 内核复位后,会自动从起始地址的下一个 32 位空间取出复位中断入口向量,跳转执行复位中断服务程序。
以将代码下载到内部FLASH为例,即代码从0x0800 0000开始被执行。
当产生并立刻复位状态后,CM3内核做的第一件事就是读取下列两个32位整数的值:
(1)从地址 0x0800 0000 处取出堆栈指针 MSP 的初始值,该值就是栈顶地址。
(2)从地址 0x0800 0004 处取出程序计数器指针 PC 的初始值,该值指向复位后执行的第一条指令。