从裸机到嵌入式Linux——STM32芯片是怎么启动的
- 如何定义“启动”
- 启动阶段分析
- 1.寄存器清零
- 2.Boot0/Boot1 启动方式选择
- 3. 加载SP寄存器值
- 4. 加载PC寄存器值
- 5. 跳转到reset_Handler
- 5.1 跳转到system_init
- 5.2 跳转到__main
- 5.2.1 初始化向量表
- 5.2.2 C库代码和分散加载
- 5.2.3 C语言环境初始化
- 6.跳转到main
如何定义“启动”
本节内容依然是为从裸机到嵌入式Linux做前期知识储备。主要是探究STM32芯片的启动过程,就像我们写一个C程序总是从main函数开始到return 0结束一样,我们首先明确本节所谓“芯片启动“的开始节点和终止节点:
开始节点:按下电源/复位键。
终止节点:程序跳转到main函数。
从main函数之后就是我们熟悉的内容了,因此我们将程序跳转到main函数这条指令作为芯片启动的终点。
启动阶段分析
接下来我们看一下芯片的启动共有哪些阶段,我用一张图进行表示。
1.寄存器清零
上电之后,CPU内核中所有的寄存器全部被初始化成0。
2.Boot0/Boot1 启动方式选择
时钟的第4个周期进行Boot启动方式的确认,将Boot的结果保存到一个寄存器中,这个寄存器中的值能够影响最终的地址有映射,也正是这个寄存器的值,让STM32的程序能够从0x08000000启动。
3. 加载SP寄存器值
从0x0000_0000 处加载 SP 寄存器的值,SP这个寄存器里面一直存放栈顶指针吗?那这个效率有点低哦,如果不是,又该是什么样的处理方式呢?
4. 加载PC寄存器值
从 0x0000_0004 处加载PC指针寄存器的值。
4 之后,5 之前都是硬件强行拉的?ARM启动过程分析?
5. 跳转到reset_Handler
0x0000_0004 中的内容是待跳转的地址,这个地址指向Reset_Handler
5.1 跳转到system_init
Reset_Handle 中第一个函数就是System_init,用于初始化时钟。
5.2 跳转到__main
5.2.1 初始化向量表
5.2.2 C库代码和分散加载
5.2.3 C语言环境初始化
6.跳转到main
这里要注意两个特殊寄存器 SP(栈指针寄存器) 和 PC(程序技术器),因此上电和复位之后,这两个寄存器中的值为0x0000_0000 (这是ARMCPU内核决定的,ST无权更改)。因此为了能让程序正常的跑起来,必须从0x0000_0000开始放指令。
0x0000_0000 放的是SP的内容,然后是0x0000_0004 放PC指针。