【STM32】stm32启动流程

STM32的内存架构

在《ARM Cortex-M3权威指南》中有关M3的存储器映射,因为Cortex-M3是32位的单片机,因此其PC指针可以指向2^32=4G的地址空间,也就是图中的 0x00000000到0xFFFFFFFF的区间,也就是将程序存储器、数据存储器、寄存器和输入输出端口被组织在同一个4GB的线性地址空间内,数据字节以小端格式存放在存储器中。不同的型号Flash 和 SRAM 的地址空间不同,起始地址都是一样的。

当STM32微控制器复位(无论是上电复位、软件复位还是外部复位)时,它会从内存映射中的一个特定地址开始执行代码。这个地址通常是0x00000000,这是ARM架构中程序计数器(PC)的起始地。0x00000000地址是STM32微控制器中的一个虚拟地址,它被用作代码执行的起始点。在复位后,微控制器会自动将这个地址指向内置Flash的起始位置,使得程序可以从Flash中的第一个字节开始执行。

Cortex-M3 内核规定,起始地址必须存放栈顶指针,而第二个地址则必须存放复位中断入口向量地址,这样在 Cortex-M3 内核复位后,会自动从 起始地址的下一个 32 位空间 取出复位中断入口向量,跳转执行复位中断服务程序。Cortex-M3 内核固定了中断向量表的位置, 但是起始地址是可变化的。

STM32的三种启动模式

STM32有三种启动模式:FLASH启动、SRAM启动和系统存储器启动,通常三种启动方式由外部引脚boot0和boot1的电平决定。每个系列boot0和boot1电平对应的意义可能不同,下表是STM32F4xx系列的启动方式选择

1) Flash启动,将Flash地址0x0800 0000映射到0x0000 0000,这样启动以后程序相当于是从0x0800 0000开始的,这是我们最常用的模式;

2) SRAM启动,将SRAM地址0x2000 0000映射到0x0000 0000,这样启动以后程序相当于是从0x2000 0000开始的;

3) 系统存储器启动,将系统存储器地址0x1FFF F000映射到0x0000 0000,这样启动以后就相当于从0x1FFF F000开始执行的,值得注意的是这个系统存储器里面存储的其实是STM32自带的Bootloader代码,其实是一个官方的IAP,它提供了可以通过UART1接口将用户的代码下载到Flash中的功能,下载完以后再切换到从Flash中启动就可以正常运行了。

我们一般使用第一种方式启动。当STM32上电时,无论哪种启动模式,程序都将会从地址0x0000 0000开始执行,三种启动模式只是将各自存储空间映射到地址0x0000 0000。

对于CPU来说它是永远从0x00000000地址去加载执行程序的,然后单片机会通过Boot管脚的配置去将Main FLASH(0x0800 0000)重映射或者芯片出厂自带的Bootloader(0x1FFF 0000)重映射,故而代码是下载到 0x80000000 往后的存储空间中,却说运行又是从 0x00000000地址运行的。

STM32启动流程

有了启动模式,我们就可以按要求写程序。其中关键的就是启动文件,启动文件由汇编编写,是系统上电复位后第一个执行的程序,这里就不做详细的解读了。文件中主要做了以下工作:(若STM32从0x8000 0000开始重映射到0x00000000地址)

  1. 初始化堆栈指针SP=_initial_sp

    1)从0x0800 0000读取栈顶地址,并将该地址存入MSP中。

    2)栈顶地址的值为0x2000 xxxx,工程所生成bin文件的前两个字节即为栈顶地址。

    3)从0x2000 0000到0x2000 xxxx即为程序所运行的范围,该段内存分布为:RW段、ZI段:其中RW段为可读写的非0数据段,ZI段包括了0数据段、堆区、栈区。

  2. 初始化PC 指针=Reset_Handler

    1)从0x0800 0004读取中断向量表的首地址(即复位中断入口地址),装入PC程序计数器,跳转执行。

  3. 配置系统时钟

    1)进行系统时钟的初始化,该函数内含VTOR寄存器设置,即中断向量偏移设置:SCB->VTOR = FLASH_BASE|VECT_TAB_OFFSET。产品IAP由bootloader跳转app程序时,需要设置中断向量偏移

  4. 调用C 库函数_main 初始化用户堆栈,从而最终调用main 函数去到C 的世界

所以,按照Cortex-M3 内核规定,我们在Flash中存储的第一个4字节就应该是堆栈指针SP的值,第二个4字节存储的就是PC 指针,那实际情况是这样吗?我们可以查看hex文件,查看map文件对照一下。其实,如果继续对照hex文件,我们会发现后面的4字节都是中断服务函数,这就是我们所说的中断向量表。我们再验证一件事,那就是从0x8000 0000开始重映射到0x00000000地址,我们可以使用工具看看这两个地址的内容:

所以,映射的内容其实是整个中断向量表。

中断向量表偏移地址

上图是中断向量表的定义,也就是我们在Flash中烧录的文件内容,第一个4字节的内容是栈顶指针,所以在上图中保留了,后续的内容都是中断服务程序的地址。

VTOR是arm内核的一个寄存器,叫做中断向量偏移量寄存器。当系统上电启动的时候CPU会从先找到中断向量表的位置(其实就是从0x00000000启动的),然后从表中找到复位中断Reset_Handler,而main函数的执行实际上就是在复位中断函数中的。

当来一个中断时,cpu就会从0x0000000+VTOR位置找到中断向量表,然后再查表跳转到对应的中断服务函数去执行。所以你代码里面存在多份中断向量表是可以的,只需要动态改变VTOR的值就可以了。所以,如果有bootloader程序和application程序时,这就是为什么在application程序中需要设置VTOR的原因。

但是,有一个特殊情况,stm32F0系列所采用M0内核的芯片就没有VTOR。这就意味着stm32F0系列没有办法使用这种简单的修改VTOR的方式重新定位APP的中断向量表。那肯定还有其他思路,就是稍微麻烦一点。那就是把中断向量表放在RAM中去,并且针对F0没有VTOR的还必须把中断向量表拷贝到RAM的起始地址。为什么这样做,因为前面我们提到过CPU默认会认为0x00000000处放的就是向量表。那么我们换种思路可以把RAM的地址映射到0x00000000处。这个通过配置SYSCFG寄存器的MEM_MODE就可以把RAM地址映射到0x00000000处。这时候再进入到APP以后首要任务就是把APP的中断向量表拷贝到RAM的起始地址,并且修改MEM_MODE位。

AM地址映射到0x00000000处。这时候再进入到APP以后首要任务就是把APP的中断向量表拷贝到RAM的起始地址,并且修改MEM_MODE位。

MEM_MODE的复位值和复位时 BOOT 引脚的设置相同,也就是BOOT引脚决定了这个寄存器的复位值,但是软件也可以再次修改。

相关推荐
llilian_167 小时前
总线授时卡 CPCI总线授时卡的工作原理及应用场景介绍 CPCI总线校时卡
运维·单片机·其他·自动化
禾仔仔8 小时前
USB MSC从理论到实践(模拟U盘为例)——从零开始学习USB2.0协议(六)
嵌入式硬件·mcu·计算机外设
The Electronic Cat10 小时前
树莓派使用串口启动死机
单片机·嵌入式硬件·树莓派
先知后行。12 小时前
常见元器件
单片机·嵌入式硬件
恒锐丰小吕13 小时前
屹晶微 EG2302 600V耐压、低压启动、带SD关断功能的高性价比半桥栅极驱动器技术解析
嵌入式硬件·硬件工程
Dillon Dong13 小时前
按位或(|=)的核心魔力:用宏定义优雅管理嵌入式故障字
c语言·stm32
Free丶Chan14 小时前
dsPIC系列-1:dsPIC33点灯 [I/O、RCC、定时器]
单片机·嵌入式硬件
v先v关v住v获v取15 小时前
塔式立体车库5张cad+设计说明书+三维图
科技·单片机·51单片机
恒锐丰小吕15 小时前
屹晶微 EG2106D 600V耐压、半桥MOS/IGBT驱动芯片技术解析
嵌入式硬件·硬件工程