本章参考资料《 STM32F4xx 中文参考手册》第十章-中断和事件:表 46.
STM32F42xxx 和 STM32F43xxx 的向量表; MDK 中的帮助手册—ARM Development Tools:
用来查询 ARM 的汇编指令和编译器相关的指令。
启动文件由汇编编写,是系统上电复位后第一个执行的程序。主要做了以下工作:
1、 初始化堆栈指针 SP=_initial_sp
2、 初始化 PC 指针=Reset_Handler
3、 初始化中断向量表
4、 配置系统时钟
5、 调用 C 库函数_main 初始化用户堆栈,从而最终调用 main 函数去到 C 的世界
在讲解启动代码的时候,会涉及到 ARM 的汇编指令和 Cortex 内核的指令,有关
Cortex 内核的指令我们可以参考《 CM3 权威指南 CnR2》第四章:指令集。剩下的 ARM 的
汇编指令我们可以在 MDK->Help->Uvision Help 中搜索到,以 EQU 为例,检索如下:
这里将回到最初的前面几篇文章的问题,为什么需要手动编写一个SystemInit() 函数让编译器不报错。
WEAK:表示弱定义,如果外部文件优先定义了该标号则首先引用该标号,如果外部
文件没有声明也不会出错。这里表示复位子程序可以由用户在其他文件重新实现,这里并
不是唯一的。
IMPORT:表示该标号来自外部文件,跟 C 语言中的 EXTERN 关键字类似。这里表
示 SystemInit 和__main 这两个函数均来自外部的文件。
SystemInit()是一个标准的库函数,在 system_stm32f4xx.c 这个库文件总定义。主要作
用是配置系统时钟,这里调用这个函数之后, F429 的系统时钟配被配置为 180M。
__main 是一个标准的 C 库函数,主要作用是初始化用户堆栈,最终调用 main 函数去
到 C 的世界。这就是为什么我们写的程序都有一个 main 函数的原因。如果我们在这里不
调用__main,那么程序最终就不会调用我们 C 文件里面的 main,如果是调皮的用户就可以
修改主函数的名称,然后在这里面 IMPORT 你写的主函数名称即可。
你可能会说,_main函数我们也没有写啊,为什么不报错,这个_main函数是MDK编译器自动给我生成的,不用用户操心。
在启动文件里面已经帮我们写好所有中断的中断服务函数,跟我们平时写的中断服务
函数不一样的就是这些函数都是空的,真正的中断复服务程序需要我们在外部的 C 文件里
面重新实现,这里只是提前占了一个位置而已。
如果我们在使用某个外设的时候,开启了某个中断,但是又忘记编写配套的中断服务
程序或者函数名写错,那当中断来临的时,程序就会跳转到启动文件预先写好的空的中断
服务程序中,并且在这个空函数中无线循环,即程序就死在这里。
下面这段话引用自《 CM3 权威指南 CnR2》 3.8—复位序列,CM4 的复位序列跟 CM3 一样。 —秉火
注。
在离开复位状态后, CM3 做的第一件事就是读取下列两个 32 位整数的值:
1、 从地址 0x0000,0000 处取出 MSP 的初始值。
2、 从地址 0x0000,0004 处取出 PC 的初始值——这个值是复位向量, LSB 必须是
1。 然后从这个值所对应的地址处取指。