第七章 启动文件详解

7.1启动文件介绍

1、初始化堆栈指针 SP=_initial_sp

2、初始化PC指针 =Rest_Handler

3、初始化中断向量表

4、配置系统时钟

5、调用C库函数_main初始化用户堆栈,调用main函数

7.2 ARM汇编指令

EQU:给数字常量去一个富豪们,相当于C语言的define

AREA:汇编一个新的代码段或者数据段

SPACE:分配内存空间

PRE-SERVE8:当前文件堆栈按照8位字节对齐

EX-PORT:声明一个符号具有全局属性,可被外部的文件使用

DCD:字节单位分配,要求4字节对齐,要去初始化这些内存

PROC:定义子程序,与ENDP成对使用,表示子程序结束

WEAK:弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义也不出错。要注意的是:这个不是 ARM 的指令,是编译器的,这里放在一起只是为了方便。

IM-PORT:跳转到一个符号

ALIGN:编译器对指令或者数据的存放地址进行对齐,一般需要跟一个立即数,缺省表示 4 字节对齐要注意的是:这个不是 ARM 的指令,是编译器的,这里放在一起只是为了方便。

END:到达文件的末尾,文件结束

IF,ELSE,ENDIF:汇编条件分子语句,跟c语言的if else如此

7.3 启动文件代码讲解

7.3.1 Stack栈

1
2
3
4
5
Stack_Size     EQU 0x00000400 //开辟栈的大小为0X00000400(1KB)
AREA STACK, NOINIT, READWRITE, ALIGN=3
//名字为STACK 可读写 8(2^3)
Stack_Mem SPACE Stack_Size //分配内存空间,单位 字节,这里指定大小为Strack_Size
__initial_sp //栈的结束地址,由高向低生长

栈的作用是用于局部变量,函数调用,函数形参等开销,栈的大小不能超过内部SRAM的大小,超过要修改栈的大小。

7.3.2 Heap堆

1
2
3
4
5
6
Heap\_Size EQU 0x00000200    //开辟堆大小0X00000200(512字节)
AREA HEAP, NOINIT, READWRITE, ALIGN=3
//名字 不初始化,可读写,8(2^3)
\_\_heap\_base //堆的起始地址
Heap_Mem SPACE Heap_Size
__heap_limit //堆的结束地址 堆由低到高生长和栈相反

堆用来动态内存的分配,像malloc()函数申请的内存就在堆上面

7.3.3 向量表

1
2
3
4
5
AREA RESET, DATA, READONLY
//定义数据段为RESET 可读,生命三个标号具有全局属性,可供外部文件调用
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size

__Vectors 为向量表起始地址,__Vectors_End 为向量表结束地址,两个相减即可算出向量表大小

7.3.4 复位程序

1
2
3
4
5
6
7
8
9
10
11
12
AREA |.text|, CODE, READONLY
//定义一个名称为.text 的代码段,可读。
.text
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP

复位子程序是系统上电后第一个执行的程序,调用 SystemInit 函数初始化系统时钟,然后调用 C库函数 _mian,最终调用 main 函数去到 C 的世界。

SystemInit() 是一个标准的库函数,在 system_stm32f103xe.c 这个库文件中定义。主要作用是配置系统时钟,这里调用这个函数之后,单片机的系统时钟配被配置为 72M。

__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈。

7.3.5 中断服务程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NMI_Handler PROC ; 系统异常
EXPORT NMI_Handler [WEAK]
B .
ENDP
; 限于篇幅,中间代码省略
SysTick_Handler PROC
EXPORT SysTick_Handler [WEAK]
B .
ENDP
Default_Handler PROC ; 外部中断
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
EXPORT TAMP_STAMP_IRQHandler [WEAK]
; 限于篇幅,中间代码省略
LTDC_IRQHandler
LTDC_ER_IRQHandler
DMA2D_IRQHandler
B .
ENDP

B:跳转到一个标号。这里跳转到一个‘.’,即表示无线循环。

7.3.6 用户堆栈初始化

ALIGN:对指令或者数据存放的地址进行对齐,后面会跟一个立即数。缺省表示 4 字节对齐。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
; 用户栈和堆初始化, 由 C 库函数_main 来完成
IF : DEF :__MICROLIB ; 这个宏在 KEIL 里面开启
EXPORT __initial_sp
EXPORT __heap_base
EXPORT __heap_limit
ELSE
IMPORT __use_two_region_memory ; 这个函数由用户自己实现
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END

首先判断是否定义了 __MICROLIB,如果定义了这个宏则赋予标号 __initial_sp(栈顶地址)、__heap_base(堆起始地址)、__heap_limit(堆结束地址)全局属性,可供外部文件调用。