一,一般启动流程
嵌入式开发与前端开发不一样,嵌入式开发者不仅要关注用户层代码编写逻辑,还要关注硬件层对代码执行流程。嵌入式设备在上电复位后,最先执行的不是我们编写的应用层代码接口main,他必须先对硬件层接口寄存器做一系列初始化动作,否则应用层脱离了硬件层将全部瘫痪。同样在写boot loader或者IAP启动的时候,我们同样要知道系统运行流程,以便于修改栈顶寄存器内容和中断向量表寄存器内容,来达到控制系统运行哪段程序。直白来讲就是在FLASH片上存放多个APP代码,且指定一个最先运行的APP,最先运行的APP就是IAP程序了,在IAP程序中,根据某些条件(是否检索到升级文件)来指定接下来运行的APP。
1,通过单步调试来观察系统运行流程
我们以前在Keil上做单步调试时候发现,一点击Debug程序立马进入main函数里面,以为main就是整个系统的入口,实际上不是的。因为我们在设置选项卡里面设置调试跳转选项。如下,将跳转至main选项给关闭掉才能跟踪到系统最开始运行流程。
2,开始调试
点击调试后发现,程序跳转到startup_stm32f407xx.s文件内,内容如下:
Stack_Size EQU 0x00000400
AREASTACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
本段内容开辟了一块栈空间,大小为0x400,且不做初始化,可读可写,8字节内存对其方式存储和寻址,且定义了栈顶地址为:__initial_sp
。
Heap_Size EQU 0x00000200
AREAHEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_MemSPACE Heap_Size
__heap_limit
本段内容开辟了一块堆空间,大小为0x200,且不做初始化,可读可写,8字节内存对其方式存储和寻址,且定义了堆底地址为:顶地址为:__heap_base
;堆顶地址为:__heap_limit
。
注意了,我们说的顶地址实际上就是堆栈区域最大地址,也就是它的深度,在堆栈地址通多顶地址然后自减来实现寻址。
PRESERVE8 ;8字节对其
THUMB ;THUMB指令集
; Vector Table Mapped to Address 0 at Reset
AREARESET, DATA, READONLY
EXPORT __Vectors
EXPORT __Vectors_End
EXPORT __Vectors_Size
本段定义了一块数据区只读内容,且底部指针地址为:__Vectors
;顶部指针地址为:__Vectors_End
;大小为:__Vectors_Size
这段内容是干什么的呢,下面会对它内容填充。
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
......
DCD FPU_IRQHandler; FPU
__Vectors_End
__Vectors_Size EQU __Vectors_End - __Vectors
在上上一段代码定义了一块**__Vectors_Size
大小的区域,本段则是具体将这段区域进行填充,首地址存放:__initial_sp
** 栈顶指针,其次是:Reset_Handler
接下来就是其他必要的中断程序。这个区域很明显就是我们程序中至关重要的向量中断表。
__initial_sp
就是系统在识别BOOT0和BOOT1后由0X00000000跳往过来的地方,也可以看作是整个程序的起点。
Reset_Handler
则是复位中断程序的地址,和上一个起点保持4字节的位移,这是因为所有中断向量地址占用空间只是4字节(4*8=32),本芯片也是32位处理器。
AREA|.text|, CODE, READONLY
; Reset handler
Reset_HandlerPROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
定义reset的复位中断程序,也就是程序在执行到复位后,
EXPORT Reset_Handler
表示这个函数可以供外部C调用
IMPORT SystemInit
表示外部定义的C函数可以传递到汇编内部调用,为下面调用做好铺垫
IMPORT __main
表示外部定义的C函数可以传递到汇编内部调用,为下面调用做好铺垫
;memcpy the statck point to MSP
;LDR R0,=__initial_sp
;MSR MSP, R0
这个实际上是整个程序的第一步,也就是如上debug调试最开始进入的地方,上面介绍那么多全都是初始化。
1,把 __initial_sp
指针地址拷贝给 R0
,也就是把上面定义向量表顶部指针地址取出来。
2,将这个地址赋值给栈顶指针MSP寄存器,也就是程序最开始执行地方(主栈指针),说白了就是寄存器映射吧。
;check the boost space correspands to application
;LDR R0, = 0X0000004
;LDR R1, [R0]
;LSRS R1, R1, #24
;LDR R2,=0XIF
;CMP R1,R2
3,向下位移4字节(就到了set_handle复位中断函数)
将R0赋值给R1
将R1向右位移24字节得出最高位的80或者1f
比较这个R1和R2(0x1f)是否相等,意思是判断系统从flash启动还是0x1f处的ISP启动
4,执行复位中断函数,上面已经定义了,主要是申明下将systeminit和_main定义为IMPORT
;BEN application
5,如果不是从0x1f处的ISP启动,则跳转到Aplication位置
;Application ROM
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
6,开始执行systeminit(完成芯片正常运行所需的必备功能项的配置),这个配置内容同样非常重要,但不是本节介绍重点。
7,开始执行MDK内部封装的_main接口,主要是对:完成全局/静态变量的初始化工作(从flash到SRAM的加载)/初始化堆栈/库函数的初始化/程序的跳转,进入main()函数
; Dummy Exception Handlers (infinite loops which can be modified)
NMI_Handler PROC
EXPORT NMI_Handler[WEAK]
B .
ENDP
HardFault_Handler\
PROC
EXPORT HardFault_Handler [WEAK]
B .
ENDP
…
Default_Handler PROC
EXPORT WWDG_IRQHandler [WEAK]
EXPORT PVD_IRQHandler [WEAK]
......
EXPORT FPU_IRQHandler [WEAK]
WWDG_IRQHandler
PVD_IRQHandler
......
FPU_IRQHandler
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
LDR R1, =(Stack_Mem + Stack_Size)
LDR R2, = (Heap_Mem + Heap_Size)
LDR R3, = Stack_Mem
BX LR
ALIGN
ENDIF
END
上面的 __user_initial_stackheap
是在系统的 _main
中自动执行,初始化栈空间和位移
;******************* © COPYRIGHT 2011 STMicroelectronics END OF FILE
二,启动模式简介
上面再讲启动流程,实是在默认flash启动用户APP程序是从0x08000000开始)基础上描述的,实际上STM32支持多种启动模式,这里进行简单介绍:
1,BOOT0和BOOT1
系统有几种启动方式,但是系统在上电前就必须需要知道从哪儿启动,实际上STM32的启动模式由芯片的启动引脚BOOT0和BOOT1决定。MCU在上电时会读取BOOT0和BOOT1引脚的电平状态并锁存,系统根据电平状态来从选择启动方式(注意:这里说的启动是指正常的上电,当我们采用jlink/jtag进行程序烧写或调试时,MCU会会忽略引脚状态)。下面是官方使用指南的说明:
三种启动模式对应的物理存储介质均是芯片内置的,它们是:
a.主闪存存储器 = 芯片内置的Flash。0X80
b.系统存储器 = 芯片内部一块特定的区域,芯片出厂时在这个区域预置了一段Bootloader,就是通常说的ISP程序。这个区域的内容在芯片出厂后用户无法进行读写操作。0X1F
c.SRAM = 芯片内置的RAM区,具有掉电丢失性。0X20
原文链接:https://blog.csdn.net/ye1223/article/details/79812958
本节重点讨论FLASH启动模式,在设计工程,如不是极其特殊需求,是不会用到其他两种的,讨论FLASH启动下,IAP与APP各自start文件如何编写。关于IAP程序大家不用字节写了,直接Fork在GitHub上star最多的即可。
2,FLASH启动模式下的IAP程序的start文件启动流程设计
3,FLSHA启动模式下的APP程序start文件启动流程设计
我先去取个快递吃个饭,有时间回来再更新下,尽量今晚写完吧。