STM32学习笔记1——启动
本文最后更新于:2024年10月15日 晚上
0x00 Reference
Cortex-M3/M4芯片启动流程概括_cortex-m4 mcu的启动与初始化过程-CSDN博客
01.一探究竟STM32的三种启动模式_stm32启动模式-CSDN博客
0x01 加载中断向量表
stm32基于ARM cortex-m3 内核设计,所以先来看cm3内核的启动流程
cm3内核在上电 or 复位之后会先做两件事
- 去0x00000000的地址读取MSP寄存器的初始值(MSP就是栈顶指针,这里就是将栈的初始地址赋给MSP,让内核找到栈的位置,在之后的启动流程中可能遇到)
- 去0x00000004的地址读取PC寄存器的初始值——Reset中断处理函数的地址(PC寄存器就是指向当前程序的地址,CPU就是不停地从PC中取址然后执行)
在PC获取到初始值之后,CPU就会跳转到PC所指的Reset中断服务函数开始执行程序
OK,这里有一个问题为什么0x00000000的地址一定是MSP的初始值,0x00000004的地址一定是PC寄存器的初始值呢?这是因为CM3内核规定了中断向量表的结构,并且规定了向量表存放的地址一定是0x00000000。可以看到向量表的前两位分别是MSP的初始值和复位中断处理函数的地址
另外一个问题是,为什么在STM32上刷写程序是写入到0x08000000,而不是0x00000000呢?这是因为ST公司在设置STM32时为了灵活性,设计了一个硬件电路,这个电路会根据BOOT0和BOOT1的选择不同而将不同的地址映射到0x00000000
打个比方,当boot0:boot1 = 0:0 时,CPU上电去0x00000000的地址读取中断向量表,而该地址已经被硬件电路重新映射到了0x08000000了,对于CPU来说,它认为它读取的地址是0x00000000,而它实际读取的内容是来自0x08000000的flash中的数据
STM32 的三种启动方式
- 第一种方式(boot0 = 0):Flash memory启动方式
启动地址:0x08000000 是STM32内置的Flash,一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。基本上都是采用这种模式。 - 第二种方式(boot0 = 1;boot1 = 0):System memory启动方式
启动地址:0x1FFF0000从系统存储器启动,这种模式启动的程序功能是由厂家设置的。一般来说,这种启动方式用的比较少。系统存储器是芯片内部一块特定的区域,STM32在出厂时,由ST在这个区域内部预置了一段BootLoader, 也就是我们常说的ISP程序, 这是一块ROM,出厂后无法修改。一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的BootLoader 中,提供了串口下载程序的固件,可以通过这个BootLoader将程序下载到系统的Flash中。 - 第三种方式(boot0 = 1;boot1 = 1):SRAM启动方式。
启动地址:0x20000000 内置SRAM,既然是SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试。假如我只修改了代码中一个小小的 地方,然后就需要重新擦除整个Flash,比较的费时,可以考虑从这个模式启动代码(也就是STM32的内存中),用于快速的程序调试,等程序调试完成后,在将程序下载到SRAM中。
0x02 Reset 中断服务函数
前面说到cm3复位后会跳转到Reset中断服务函数,那这个函数在哪呢?
在写stm32的程序时,会有一个startup_xxxx.s的启动文件,使用stm32cubeMX会自动根据编译器的不同生成不同的启动文件。
为什么不同编译器的启动文件不同呢?
首先,启动文件使用的是汇编语言,虽然针对同一个指令集,理论上来说其汇编语言也是相同的,但是由于其汇编器的不同,因此在汇编语言的细节上也有差异,针对GNU的编译器的启动文件在ARMCC编译器可能就编译不了了,所以针对不同的编译器所使用的启动文件也就不同了
1 |
|
这是一个使用GCC编译器的Reset_Handler,由STM32cubeMX自动生成,主要做了如下的事情
- 调用
SystemInit
来初始化系统。 - 将初始化数据从Flash复制到RAM。
- 清零BSS段(未初始化数据段)。
- 调用
__libc_init_array
来执行C库的构造函数。 - 调用
main
函数,这是C程序开始执行的地方。
这里有两个值得注意的地方,首先是SystemInit,如果定义了DATA_IN_ExtSRAM,则会初始化外部SRAM,启动FSMC控制器;而如果定义了USER_VECT_TAB_ADDRESS,则会重定向 向量表
1
2
3
4
5
6
7
8
9
10
11
12
13
void SystemInit (void)
{
#if defined(STM32F100xE) || defined(STM32F101xE) || defined(STM32F101xG) || defined(STM32F103xE) || defined(STM32F103xG)
#ifdef DATA_IN_ExtSRAM
SystemInit_ExtMemCtl();
#endif /* DATA_IN_ExtSRAM */
#endif
/* Configure the Vector Table location -------------------------------------*/
#if defined(USER_VECT_TAB_ADDRESS)
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#endif /* USER_VECT_TAB_ADDRESS */
}另外一个值得注意的是,使用GNU工具链,需要调用
__libc_init_array
来初始化C库(newlib),而使用ARMCC编译器生成的启动文件中却没有这一步。
在执行完Reset_Handler后,程序就会跳转到main函数开始执行用户程序了