1. MCU复位后的软件启动过程
1.1 概述
下图是Cortex-M3内核的MCU启动过程:
1.2 STM32的启动模式
根据BOOT0、BOOT1引脚,有3种启动方式:
主闪存存储器:也就是通常使用的FLASH,从0x0800 0000启动
系统存储器
IC封测时,厂商在该处固化了BootLoader(厂商使用后门应该可以修改)
从该处启动后,可采用【ISP】方式烧录代码
SRAM:从此处启动的应用较少
注:前两种模式,都把其基地址映射到0x0000 0000。
1.3 核心的汇编启动文件
不同MCU的启动方式基本大同小异,复位后都是从汇编的启动文件开始
MDK环境的启动文件过于简略:
大部分准备工作都交给C库处理,在【__main】函数中
无法窥视MCU具体的启动流程
基于此,这里使用【STM32CubeIDE】中使用的启动文件,详细描述启动过程。
1.4 STM32复位后执行的第一条指令
这里需要准备的文件:
.s汇编启动文件
.list文件,汇编代码,以FLASH地址为序号,且包含C代码
.map文件,查看SRAM、FLASH的地址分配
.ld链接脚本
1.4.1 复位后的启动机制
这里截取【Cortex ™ -M4 Devices Generic User Guide】文档中的描述:
developer.arm.com/documentation/dui0553/latest/
即复位后,由于FLASH映射到0x0000 0000:
CPU首先从【0x0000 0000】也就是【0x0800 0000】获取栈顶
接着加载【0x0000 0004】也就是【0x0800 0004】处保存的【复位向量】至PC指针中
跳转到【复位中断服务函数】,执行复位中断服务函数中的第一条指令
1.4.2 从例程看第一条执行的指令
1)链接脚本中定义【栈顶地址_estack】:
2)接着在链接脚本的【SECTIONS】命令的开头,把FLASH的起始处分配给中断向量表【isr_vector】:
3)接着在汇编启动文件中,定义全局的中断向量表【g_pfnVectors】,【_estack】【复位中断服务函数】依次放在开头:
4)编译项目,查看【.list文件】:
【isr_vector】放到了FLASH起始处【0x0800 0000】
【Reset_Handler】起始地址为【0x0800 19CC】
未找到【isr_vector】存储的数据,无法观察上电取第一条指令的过程
转而从【HEX文件】下手
5)查看【HEX文件】:
【0x0800 0000】处存储【0x2002 0000】,是【_estack】栈顶地址
【0x0800 0004】存储【0x0800 19CD】,是【Reset_Handler】的地址,其bit0置1,符合要求
6)复位中断处理函数的第一条指令,即设置栈指针【sp = _estack】
1.5 复位中断处理函数
整体的执行内容:
1.5.1 数据从LMA加载到VMA
搬运函数、搬运变量初始值都是一样操作,只是VMA、LMA的值不一样:
1.5.2 .bss清零
沿用相同的指令,这里不做具体讲解,建议查ARM指令集(也可整段代码发给ChatGPT,由它讲解)。
1.5.3 调用SystemInit
字面意思,一般是芯片原厂提供的,对IC进行初步的初始化。
1.5.4 调用__libc_init_array
执行C库的初始化,目前未理解。查看【.list文件】:
部分寄存器压入栈
两次加载函数列表至r4 r5,并逐一执行函数
中间跳转执行了【_init】函数
最后出栈,退出【__libc_init_array】
1.5.5 跳转到main
在复位中断服务函数的末尾,终于跳到我们写的【main】中。
-- END --