中断向量表重定向在芯片IAP(或OTA)升级过程中,是不可避免的问题。
在Cotex-M3/M4内核中有向量表偏移寄存器VTOR
在Cortex-M0内核中没有向量表偏移寄存器,但在Cortex-M0+内核中有
我们先分析有VTOR寄存器的情况,然后再看没有的情况
ARM处理器的启动流程
可以参考基于STM32看Cortex-M内核相关的一些底层知识文章中的“Cortex-M芯片启动详细过程”章节
嵌套中断向量控制器NVIC
在《ARM Cortex-M3与Cortex-M4权威指南》中的4.5.2章节中有如下描述:
当异常发生时,处理器需要确定相应的异常处理入口位置。对于ARM7TDMI等ARM处理器,这一操作由软件实现,**Cortex-M处理器则会从存储器的向量表中自动定位异常处理的入口。**因此,这样也降低了从异常产生到异常处理执行间的延时。
4.5.3章节有如下描述:
当异常事件产生且被处理器内核接受后,相应的异常处理就会执行。要确定异常处理的起始地址,处理器利用了一种向量表机制。向量表为系统存储器内的字数据数组,每个元素都代表一个异常类型的起始地址,如图4.26所示。向量表是可以重定位的,重定位由NVIC中名为向量表偏移寄存器(VTOR)的可编程寄存器控制。复位后,VTOR默认为0,向量表则位于地址0x0处。
通过上面的描述我们可以知道,当中断触发时,处理器会到0x0+VTOR的位置寻找向量表,并跳转到对应的异常处理函数。所以我们的代码中有多个向量表也是可以的,只需要动态改变VTOR寄存器的值即可。
向量表和向量表重定位
详细内容请看《ARM Cortex-M3与Cortex-M4权威指南》7.5章节。
一版来说,起始地址(0x00000000)处应为启动存储器,它可以为Flash存储器或ROM设备,而且在运行时不能对他们进行修改。不过,有些应用可能需要在运行时修改或定义向量表,为了进行这种处理,Cortex-M3和Cortex-M4处理器实现了一种名为向量表重定位的特性。
使用VTOR偏移不在起始位置时需要注意对齐问题:
在使用VTOR时,需要将向量表大小扩展为下一个2的整数次方,且新向量表的基地址必须要对齐到这个数值。
IAP过程中的重定向
正常的执行过程为,启动时先执行Boot程序中的Reset_Handler函数然后经过分散加载,初始化堆栈,初始化C库后跳转到用户main函数执行,其中监测到不许要升级则通过跳转函数(设置主栈指针,将App中Reset_Handler函数的地址作为执行地址)跳转到App程序中的Reset_Handler函数,然后再经过分散加载,初始化堆栈,初始化C库等操作后跳转到用户main函数中执行,在main函数开始位置首先要修改VTOR的值,则后续的中断触发都会跳转到App代码中对应的中断服务函数中。
没有VTOR寄存器的情况
对于STM32F0系列的单片机使用的是Cortex-M0内核,是没有VTOR寄存器的。这种情况可以通过将中断向量表复制到RAM起始位置,然后通过SYSCFG系统配置控制器中的SYSCFG_CFGR1寄存器中的MEM_MODE位控制将SRAM空间映射到0x00000000位置。然后中断触发的时候处理器将会在起始地址寻找中断向量表并确定入口位置。
ST F0参考手册中也讲述了这种重定向方法,这也是STM32F0系列单片机实现IAP升级的方法,需要注意的是:中断向量表一定要复制到SRAM的起始位置才可以,因为映射后也是要到起始位置查找中断向量表。
#if defined(__CC_ARM)
__IO uint32_t VectorTable[48] __attribute__((at(0x20000000)));
#endif
void iap_appInit(void) {
for(uint8_t i = 0; i < 48; ++i) {
VectorTable[i] = *(__IO uint32_t*)(APP_START_ADDR + (i<<2));
}
/* Enable the SYSCFG peripheral clock */
__HAL_RCC_SYSCFG_CLK_ENABLE();
/* Remap SRAM at 0x0000000 */
__HAL_SYSCFG_REMAPMEMORY_SRAM();
}
这里要注意的是IRAM1的配置处要将前面0xC0大小的空间流出来给代码中定义的中断向量表数组,否则编译的时候会报错。