【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引脚决定了这个寄存器的复位值,但是软件也可以再次修改。

相关推荐
1101 11014 小时前
STM32-笔记36-ADC(模拟/数字转换器)
笔记·stm32·嵌入式硬件
youcans_4 小时前
【动手学电机驱动】STM32-MBD(3)Simulink 状态机模型的部署
stm32·单片机·嵌入式硬件·matlab·代码生成
end_SJ4 小时前
初学STM32 --- USMART
stm32·单片机·嵌入式硬件
我想学LINUX6 小时前
【stm32+K210项目】基于K210与STM32协同工作的智能垃圾分类系统设计与实现(完整工程资料源码)
stm32·单片机·嵌入式硬件·毕业设计·课程设计·项目
JoneMaster6 小时前
[读书日志]8051软核处理器设计实战(基于FPGA)第五篇:8051软核处理器主体框架搭建(verilog)
嵌入式硬件·fpga开发·硬件架构
end_SJ7 小时前
初学stm32 --- RTC实时时钟
stm32·嵌入式硬件·实时音视频
森旺电子7 小时前
STM32+WIFI获取网络时间+8位数码管显示+0.96OLED显
stm32·单片机·嵌入式硬件
厉昱辰7 小时前
入手51单片机的学习路径
嵌入式硬件·学习·51单片机
析木不会编程8 小时前
STM32的LED点亮教程:使用HAL库与Proteus仿真
stm32·嵌入式硬件·proteus
Zevalin爱灰灰10 小时前
FreeRTOS从入门到精通 第七章(FreeRTOS列表和列表项)
stm32·操作系统·freertos