一、STM32三种启动方式
常规模式(主闪存存储器):
- 这是最常见的启动模式。
- 在此模式下,处理器会执行复位向量表中的复位地址,从而启动芯片。
- 芯片会执行各种初始化操作,包括时钟初始化、外设初始化等,然后跳转到用户定义的启动代码。
- 基地址:0x0800000
Bootloader模式(系统存储器):
- 这是一种特殊的启动模式,通常用于更新固件或通过外部接口(如UART或USB)加载新的程序。
- 在此模式下,处理器会将启动地址设置为Bootloader的起始地址,而不是复位向量表中的复位地址。
- Bootloader负责检查外部接口是否有新的程序,如果有,则加载并启动新程序;否则,它会跳转到复位向量表中的复位地址。
- 基地址:0x1FFFF000
系统内存模式(内置SRAM):
- 这种模式通常用于恢复或修复芯片中的固件。
- 在此模式下,处理器会将启动地址设置为系统内存中的特定地址,而不是复位向量表中的复位地址。
- 系统内存包含一个特殊的Bootloader,它可以用于加载新的固件或执行其他恢复操作。
- 基地址:0x20000000
这三种启动模式为STM32芯片提供了灵活性和可靠性,使其适用于各种不同的应用场景。开发人员可以根据实际需求选择合适的启动模式来确保系统的正常运行和维护。启动时都会自动把基地址重映射到0x00000000,执行RESET中断。
二、常规启动模式分析
Stack_Size EQU 0x00000400 ; 定义栈的大小为1024字节(0x400字节)
AREA STACK, NOINIT, READWRITE, ALIGN=3 ; 定义栈区域,不进行初始化,可读可写,字节对齐
Stack_Mem SPACE Stack_Size ; 分配栈的空间
__initial_sp ; 栈顶指针,初始堆栈指针
; <h> Heap Configuration
; <o> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
Heap_Size EQU 0x00000200 ; 定义堆的大小为512字节(0x200字节)
AREA HEAP, NOINIT, READWRITE, ALIGN=3 ; 定义堆区域,不进行初始化,可读可写,字节对齐
__heap_base ; 堆的起始地址
Heap_Mem SPACE Heap_Size ; 分配堆的空间
__heap_limit ; 堆的结束地址
PRESERVE8
THUMB
; 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 NMI_Handler ; NMI Handler NMI处理函数
DCD HardFault_Handler ; Hard Fault Handler 硬件故障处理函数
DCD MemManage_Handler ; MPU Fault Handler 内存管理单元故障处理函数
DCD BusFault_Handler ; Bus Fault Handler 总线故障处理函数
DCD UsageFault_Handler ; Usage Fault Handler 使用错误处理函数
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler 服务调用处理函数
DCD DebugMon_Handler ; Debug Monitor Handler 调试监视器处理函数
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler 挂起请求处理函数
DCD SysTick_Handler ; SysTick Handler 系统滴答定时器处理函数
; External Interrupts 外部中断处理函数
DCD WWDG_IRQHandler ; Window Watchdog
DCD PVD_IRQHandler ; PVD through EXTI Line detect
...
(其他外部中断处理函数)
...
__Vectors_End ; 向量表结束标志
__Vectors_Size EQU __Vectors_End - __Vectors ; 向量表大小
AREA |.text|, CODE, READONLY ; 定义代码区域,只读
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK] ; 导出复位处理函数,标记为弱符号
IMPORT __main
IMPORT SystemInit
LDR R0, =SystemInit ; 调用系统初始化函数
BLX R0
LDR R0, =__main ; 跳转到主函数
BX R0
ENDP
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler [WEAK] ; 导出NMI处理函数,标记为弱符号
B . ; 无限循环
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK] ; 导出硬件故障处理函数,标记为弱符号
B . ; 无限循环
ENDP
MemManage_Handler\
PROC
EXPORT MemManage_Handler [WEAK] ; 导出内存管理单元故障处理函数,标记为弱符号
B . ; 无限循环
ENDP
...
(其他异常处理函数)
...
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK] ; 导出默认的外部中断处理函数,标记为弱符号
EXPORT PVD_IRQHandler [WEAK]
...
(其他外部中断处理函数)
...
B . ; 无限循环
ENDP
ALIGN
;*******************************************************************************
; 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 ; 将堆的起始地址加载到R0寄存器
LDR R1, =(Stack_Mem + Stack_Size) ; 将栈的起始地址加上栈的大小加载到R1寄存器
LDR R2, = (Heap_Mem + Heap_Size) ; 将堆的起始地址加上堆的大小加载到R2寄存器
LDR R3, = Stack_Mem ; 将栈的起始地址加载到R3寄存器
BX LR ; 返回
ALIGN
ENDIF
END
这段启动代码实现了STM32的启动流程,
栈和堆的初始化:
- 定义了栈的大小和堆的大小,并分配了相应的空间。
- 在向量表中定义了栈顶指针
__initial_sp
、堆的起始地址__heap_base
和结束地址__heap_limit
。
向量表的定义:
- 向量表是在芯片复位时会被加载到内存地址0处,用于存放异常处理函数的入口地址。
- 向量表中定义了复位、NMI、硬件故障、内存管理单元故障、总线故障、使用错误等异常处理函数的入口地址。
- 向量表还包含了外部中断处理函数的入口地址。
Reset_Handler复位处理函数:
Reset_Handler
是复位时的处理函数,在芯片复位时会被调用。- 该函数首先调用
SystemInit
完成系统的初始化工作,然后跳转到__main
函数继续执行用户的主程序。
异常处理函数的定义:
- 定义了各种异常处理函数的入口地址,如 NMI、硬件故障、内存管理单元故障、总线故障等。
- 这些异常处理函数是无限循环的,表示当对应的异常发生时,系统会停留在该函数处进行处理。
用户栈和堆的初始化:
- 如果使用了标准库函数
__MICROLIB
,则导出了栈和堆的初始化函数__user_initial_stackheap
。 - 如果未使用标准库函数,则需要手动定义栈和堆的初始化函数,并在其中设置栈的起始地址、堆的起始地址和结束地址。
- 如果使用了标准库函数
STM32的启动流程比较简单,从这个流程可以学习到通用的的ARM系统的启动模式。