在面试的时候,我遇到一个提问,单片机上电之后都做了些什么?
那么STM32上电之后都做了些啥呐?
可以配合起来看: 单片机启动文件分析
三种启动模式
简而言之就是根据启动模式决定0x0000 0000重映射到下图哪个地址,那么当单片机开始执行的时候就从哪里开始执行(还是从0x0000 0000开始,只不过这个时候的0x0000 0000代表的实际位置是以下几个之一了。)
三种启动模式:
下好程序后,根据重启芯片时,SYSCLK的第4个上升沿,BOOT引脚被锁存的值,确定启动模式。
1.从主闪存存储器启动,将主Flash地址0x08000000重映射到0x00000000,这样代码启动之后就相当于从0x08000000开始。主闪存存储器是STM32内置的Flash,作为芯片内置的Flash,是正常的工作模式。一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序,这是最常用的启动方式。
2.从系统存储器启动。首先控制BOOT0、BOOT1管脚,复位后,STM32与上述两种方式类似,从系统存储器地址0x1FFF F000开始执行代码。系统存储器是芯片内部一块特定的区域,芯片出厂时在这个区域预置了一段Bootloader,就是通常说的ISP程序。这个区域的内容在芯片出厂后没有人能够修改或擦除,即它是一个ROM区。启动的程序功能由厂家设置。系统存储器存储的其实就是STM32自带的bootloader代码。也是ISP烧录,串口下载程序的启动方式。
3.从内置SRAM启动,将SRAM地址0x20000000映射到0x00000000,这样代码启动之后就相当于从0x20000000开始。内置SRAM,也就是STM32的内存,既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。假如我只修改了代码中一个小小的地方,然后就需要重新擦除整个Flash,比较的费时,可以考虑从这个模式启动代码,用于快速的程序调试,等程序调试完成后,在将程序下载到SRAM中。
重映射
这部分呢见链接: 存储映射_重映射
举个例子,简单来说就是,在arm中访问一个地址是通过地址解释器进行的,0x0000 0000重映射到0x0800 0000,那么在重映射之后,访问0x0000 0000实际上是访问0x0800 0000.
不知道我这么理解是否有错,欢迎指正。
【不是问题的问题】: 为什么STM32的Flash地址要设置到0x08000000
从0x0000 0000开始
简述启动过程以及启动过程中ZI RW RO处理
以主Flash为目的的重映射之后,STM32F407ZGT6的启动过程,采用Cortex-M4、Cortex-M3内核的芯片基本都是这个过程:
1.(标号1)上电后首先从0x00000000(映射到0x08000000,这里只考虑从内部Flash启动)处获得中断向量表。
2.然后在运行用户代码之前会在(标号2)处有一段引导代码负责把存在Flash中的初始化变量的值Copy到SRAM中对应的变量位置(标号3),之后把ZI区域全部清零(标号4)。
3.之后才正式开始运行用户代码(标号5)。
从0x8000 0000开始,直到用户main()
一般而言,系统上电后第一个执行的是由汇编所编写的启动文件,这部分与上图都是从0x8000 0000开始,直到用户main(),但是侧重点不同。
总的来说是这么一个流程:
(1)、初始化堆栈指针SP=_initial_sp
(2)、初始化PC指针,令其=Reset_Handler
(3)、初始化中断向量表 Reset_Handler处于中断向量表第二位
(4)、配置系统时钟 由Reset_Handler中的systeminit函数处理
(5)、调用C库函数_main初始化用户堆栈,从而最终调用main函数进入C的世界
1. 取栈顶地址存入MSP
1.从0x0800 0000读取栈顶地址,并将该地址存入MSP中。
我的理解是,栈顶地址是程序运行的上界,栈顶地址的值为0x2000 xxxx,工程所生成bin文件的前两个字节即为栈顶地址。从0x2000 0000到0x2000 xxxx即为程序所运行的范围,该段内存分布为:RW段、ZI段:其中RW段为可读写的非0数据段,ZI段包括了0数据段、堆区、栈区。
这边可以看一下:stm32堆栈的认知
2. 跳转至复位中断(Reset_Handler(void))
从0x0800 0004读取中断向量表的首地址(即复位中断入口地址),装入PC程序计数器,跳转执行
3. 系统时钟设置(在复位中断程序内被调用)
这篇帖讲的很详细: STM32启动过程解读与跟踪验证
这里摘取了一些主要内容
指令 LDR R0,=SystemInit
将函数SystemInit的入口地址传给R0,从反汇编窗口可以看到指令被写成LDR r0,[pc,#36],表明SystemInit的入口地址在存放在PC指针偏移36处,即0x08000420(+0x4)+0x24=0x08000448(因为CM3内部使用了指令流水线,读PC时返回的值是当前指令的地址+4《Cortex™-M3权威指南》)
下一条指令在0x08000422处,BLX R0 将R0的值传给PC(必须保证加载到PC的数值是奇数(即LSB=1),传给PC后,PC的LSB读回0),调用SystemInit函数
。这个函数里面开启了外部晶振
,设置了PLL
,除能了所有中断
,设置了时钟为32MHz
,并且重定位中断向量表
在0x08000000处(这句在Flash启动时可以不需要,因为能从0x00000000映射到0x08000000)。
从该函数返回后,用同样的方式调用C/C++标准实时库的__main函数,进行了一些处理(如用户堆栈初始化等,具体可以跟踪汇编代码,这部分网上已有过分析,不再赘述)后,调用main函数,进入C语言环境
。