今天在看代码的时候,看到有这么几行:
//配置向量表
#ifdef VECT_TAB_RAM //向量表位于SRAM区
MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else //向量表位于CODE(FLASH)区
MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
其函数实现如下:
//设置向量表偏移地址
//NVIC_VectTab:基址
//Offset:偏移量
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset)
{
SCB->VTOR = NVIC_VectTab | (Offset & (u32)0x1FFFFF80); //设置NVIC的向量表偏移寄存器
//用于标识向量表是在CODE区还是在RAM区
}
这里的疑问是:为什么要&上0x1FFFFF80?将1FFFFF80H=0001 1111 1111 1111 1111 1111 1000 0000B,即将bit[31:29]、bit[6:0]清零,而保留bit[28:7]位。可是为什么要这样呢?
带着这个疑问,查阅Cortex-M3编程手册(PM0056 PAGE 133),找到SCB_VTOR寄存器(向量表偏移寄存器)。这个寄存器只有bit[29:9]有用,其余Reserved。
bit[29:9]:TBLOFF[29:9]:向量表基本偏移字段。其中bit29决定了向量表位于Code区还是SRAM区。所以真正起到作用的是bit[28:9]。将bit[28:9]置一,其余位置零,得到32位2进制数0001 1111 1111 1111 1111 1110 0000 0000B=1FFFFE00H。所以我的理解是,对于stm32103,代码里的0x1FFFFF80替换为0x1FFFFE00也是可以的。
那这里又出来一个问题,为什么官方代码里使用了0x1FFFFF80,而不是0x1FFFFE00呢?
原来,这和向量表的起始地址有关。在Cortex-M3权威指南page113有这么一段话:向量表的起始地址是有要求的:必须先求出系统中共有多少个向量,再把这个数字向上增大到是 2 的整次幂,而起始地址必须对齐到后者的边界上。例如,如果一共有 32 个中断,则共有 32+16(系统异常) =48 个向量,向上增大到 2 的整次幂后值为 64,因此地址地址必须能被 64*4=256 整除,从而合法的起始地址可以是: 0x0, 0x100, 0x200 等。
对应到stm32f103,一共有16个内核中断+60个中断=76个中断,向上增大到2的整次幂后为128,因此向量表的起始地址必须能被128*4=512整除,从而合法的起始地址可以是0x0,0x200,0x400,0x800等。除去0x0不谈,最小的合法地址是0x200,其bit9=1,后面全是0,&上0x1FFFFE00是完全没有问题的,不会造成地址丢失。
但是,要想到极端一些的情况,比如一个芯片,它用了16个内核中断+15个中断=31个中断,向上增大到2的整次幂后为32,因此向量表起始地址必须能被32*4=128整除,从而合法的起始地址可以是0x0,0x80,0x100,0x200等。同样的,除去0x0不谈,最小的合法地址是0x80,其bit7=1,后面全是0,这时为了保证bit[28:7]地址不丢失,就不能用0x1FFFFE00相与,只能用0x1FFFFF80相与。
到这里,官方代码为什么使用了0x1FFFFF80,而不是0x1FFFFE00的疑问就顺理成章的解决了:为了代码更好的兼容性,能够兼顾到低配的stm32系列。