stm32上电完成的操作
- 定义堆栈空间、指针
- 定义并初始化中断向量表
- 初始化中断处理程序
- 初始化内存堆栈空间
指令 | 作用 |
---|---|
EQU | 定义字符常量,相当于C语言的 define |
AREA | 汇编一个新的代码段或数据段 |
SPACE | 分配内存空间 |
PRESERVE8 | 当前文件堆栈需按照 8 字节对其 |
EXPORT | 声明全局属性,可被外部文件使用 |
DCD | 以字为单位分配内存,要求4字节对齐,并初始化这些内存 |
PROC | 定义子程序,与 ENOP 成对使用,表示子程序结束 |
WEAK | 弱定义,如果外部文件声明了一个标号,则优先使用外部文件定义的标号,如果外部文件没有定义,则使用当前位置的标号 |
IMPORT | 声明标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似 |
B | 跳转到一个标号 |
END | 到达文件的末尾,文件结束 |
IF,ELSE,ENDIF | 汇编条件分支语句 |
LDR | 从存储器中加载字到一个寄存器中 |
BL | 跳转到由寄存器/标号给出的地址,并把跳转前的下条指令地址保存到 LR |
BLX | 跳转到由寄存器给出的地址,并根据寄存器的LSE确定处理器的状态,还要把跳转前的下条指令地址保存到LR |
BX | 跳转到由寄存器/标号给出的地址,不用返回 |
上电初始化后的内存布局
1.初始化栈信息
; Amount of memory (in bytes) allocated for Stack
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
- 初始化栈:栈用于保存临时变量如形参、局部变量,根据程序大小制定即可,此处为1KB。
- AREA: 告诉汇编器开辟一个新的代码段或者数据段
- STACK:表示分配的空间为栈
- ALIGN=3:表示栈空间按照2^3字节对齐
- SPACE: 用于分配一个一定大小的内存空间,以字节为单位,这里指定的大小为Stack_size
- __initial_sp:在SPACE之后,表示分配空间的开始地址,对于栈来说,自上向下生长,因此开始地址为高址。
观察编译后生成的map文件内存空间分配无误。
2.初始化堆
与栈空间分配相同的视线过程
Heap_Size EQU 0x00000200
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
初始化堆:堆是为程序员分配的一片空间,为程序员malloc和free使用,此处为512KB。
3.初始化中断向量表
中断向量表中对应每个设备中断的中断处理程序的入口地址(即函数名),通过调用函数名执行中断处理程序(这也是非Linux中断处理程序没有参数和返回值的体现之一),中断向量表的中断处理函数通常都为无执行内容的弱函数,支持程序员员重写。
弱函数的目的是为了防止重复定义的函数在编译器编译阶段报错,优先选择.c文件的函数执行。
(1)创建中断向量表数据段
中断向量表每个中断向量为4B,每个元素为中断处理函数,设备中断向量号为x的中断处理函数通过[__Vectors+vec_nr*4]遍历。
; Vector Table Mapped to Address 0 at Reset
AREA RESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
……
DCD I2C1_EV_IRQHandler ; I2C1 Event
DCD I2C1_ER_IRQHandler ; I2C1 Error
DCD I2C2_EV_IRQHandler ; I2C2 Event
……
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
- 定义名为RESET的数据段,只读;
- __Vectors:中断向量表数组
- __Vectors_End:中断向量表数组结束地址
- __Vectors_Size:中断向量表数组大小,=__Vectors_End-__Vectors
上文为堆栈分配的空间均位于SRAM中,不占用代码空间,从这个数据段开始才是stm32代码空间的起始位置。定义了中断向量表相关的变量;定义并初始化了栈顶位置(__initial_sp)以及15个内核异常处理函数的入口地址,接下来是外部中断。
(2)创建中断向量表部分中断处理程序代码段
(2.1)定义中断代码段
AREA |.text|, CODE, READONLY
(2.2)复位中断处理程序
初始化外设接口、PLL时钟倍频;跳转执行main函数
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit ;初始化嵌入式闪存接口、PLL并更新;系统核心时钟变量
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
(2.3)中断处理程序
真正的中断服务函数需要在外部的.c文件中实现,这里只是占位,是把中断和与之对应中断服务函数绑定了。当开启某个中断后,没有写对应的中断服务函数或者函数名有误,当中断来临时程序还是跳转到启动文件预先写好的中断服务函数中,在这个函数中无限循环。
Default_Handler PROC
;优先查找外部c文件定义的中断处理程序
EXPORT WWDG_IRQHandler [WEAK]
……
EXPORT I2C1_EV_IRQHandler [WEAK]
EXPORT I2C1_ER_IRQHandler [WEAK]
EXPORT I2C2_EV_IRQHandler [WEAK]
EXPORT I2C2_ER_IRQHandler [WEAK]
EXPORT SPI1_IRQHandler [WEAK]
EXPORT SPI2_IRQHandler [WEAK]
EXPORT USART1_IRQHandler [WEAK]
EXPORT USART2_IRQHandler [WEAK]
EXPORT USART3_IRQHandler [WEAK]
……
;汇编文件定义的中断处理程序
WWDG_IRQHandler
I2C1_EV_IRQHandler
I2C1_ER_IRQHandler
I2C2_EV_IRQHandler
I2C2_ER_IRQHandler
SPI1_IRQHandler
SPI2_IRQHandler
USART1_IRQHandler
USART2_IRQHandler
USART3_IRQHandler
……
B . ;B表跳转到一个标号,"."表无限循环
ENDP
ALIGN
4.堆栈初始化
;*******************************************************************************
; User Stack and Heap initialization
;*******************************************************************************
IF :DEF:__MICROLIB
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则将栈顶地址和堆的起始、结束地址export出去,microlib会进行堆栈初始化的操作,若是没有使用,则堆栈初始化时会使用__user_initial_stackheap函数。