STM32中断向量表的个人理解
1 中断向量表存储位置
在研究 stm32 的启动文件中,有一个部分是设置中断向量表,那么中断向量表是写在什么地方呢?通常写在flash的起始位置,向量表的排列顺序和内容遵循ARM Cortex-M内核的规定。
- 栈顶指针 MSP 的值会根据程序数据段的大小,加上定义的堆空间的大小与定义的栈空间的大小,最终加起来才会得到栈顶指针的值的,也就是说栈顶指针所指向的地址还和具体的程序定义了多少全局数据有关。栈顶指针的值会保存在启动文件定义 __initial_sp 这个标号中,这个标号具体的数值在程序编译后才会被确定。
- Reset_Handler 复位异常的入口函数,系统上电后,程序运行的第一条指令就是从这里开始运行的。
- 异常向量表:这里面存放了系统异常(上面的 Reset_Handler 也属于系统异常的一种)和外部中断的入口地址,当程序发生异常或中断时,硬件会强制 CPU 跳转到相应的入口执行中断服务函数。
2 STM32上电流程
我们利用 MDK 设置 flash 的地址后,编译器就会将我们启动文件的中断向量表写入到 flash 的起始位置。
启动文件种建立中断向量表如下所示:
1. __Vectors DCD __initial_sp ; Top of Stack
2. DCD Reset_Handler ; Reset Handler
3. DCD NMI_Handler ; NMI Handler
4. DCD HardFault_Handler ; Hard Fault Handler
5.
6. //中间部分省略未写
7.
7. DCD 0 ; Reserved
8. DCD WAKEUP_PIN_IRQHandler ; Interrupt for all 6 wake-up pins
9.
11.
10. __Vectors_End
13.
11. __Vectors_Size EQU __Vectors_End - __Vectors
当我们把程序下载到 flash 中,中断向量表就被放入 flash 的起始位置 0x08000000 ,硬件复位之后,CPU 内的时序逻辑电路首先完成如下两个工作:
- 将 0x08000000 位置存放的堆栈栈顶地址存放到 SP 中(MSP)。
- 将 0x08000004 位置存放的向量地址装入 PC 程序计数器。
CPU 从 PC 寄存器指向的物理地址取出第 1 条指令开始执行程序,也就是开始执行复位中断服务程序 Reset_Handler。
复位中断服务程序会调用SystemInit()函数来配置系统时钟、配置FMC总线上的外部SRAM/SDRAM,然后跳转到 C 库中__main 函数。由 C 库中的__main 函数完成用户程序的初始化工作(比如:变量赋初值等),最后由__main 函数调用用户写的 main()函数开始执行 C 程序。
对于启动文件来说 CPU 完成上面两个工作后就会跳到就会跳到 Reset_Handler 处执行复位中断服务程序,对于启动文件里的堆栈操作只是给编译器去看的,并不是在CPU中执行。
3 补充
reset_handler 的实际地址是多少?它下面的一堆例如Nmi_handler地址又是多少呢?发生中断是怎么跑到这个地址的呢?下面挨个讲解。
1、我们可以通过反向来得知这些入口地址,查看工程下的map文件就可以看到了,这个地址跟keil里面设置的target->flash起始地址息息相关,实际上我们不太需要关心,让编译器分配,中断向量表放的就是他们的地址。
2、Cortex-M3内核则是固定了中断向量表的位置顺序而起始地址是可变化的。
3、进到C语言后会先配置NVIC,NVIC_SetVectorTable()里面可以配置中断向量表的起始地址和偏移,主要是告诉CPU该向量表是位于Flash还是Ram,偏移是多少。例如设置为位于Flash内,偏移就是烧入的程序地址,可在Keil target中设置。这样CPU就知道入口地址了。
4、发生中断后,CPU找到中断向量表地址,然后根据偏移(对号入座)再找到中断地址,这样就跳过去了。