关于STM32的NVIC设置问题(转载、自己见解)
1、SCB->VTOR设置问题(转载):
对程序:
void MY_NVIC_SetVectorTable(u32 NVIC_VectTab, u32 Offset){
SCB->VTOR = NVIC_VectTab|(Offset & (u32)0x1FFFFF80);
//中断向量在RAM中0x20000000,即VTOR第29位为1;中断向量在Flash中0x08000000
}
中SCB->VTOR 相关描述,在《ARM Cortex-CM3 权威指南》第 113 页有如下描述:
为动态重分发中断, CM3 允许向量表重定位——从其它地址处开始定位各异常向量。这些地址对应的区域可以是代码区,但也可以是 RAM 区。在 RAM 区就可以修改向量的入口地址了。为了实现这个功能, NVIC 中有一个寄存器,称为“向量表偏移量寄存器” (在地址 0xE000_ED08 处) ,通过修改它的值就能定位向量表。但必须注意的是:向量表的起始地址是有要求的:必须先求出系统*有多少个向量,再把这个数字向上增大到是 2 的整次幂,而起始地址必须对齐到后者的边界上。例如,如果一共有 32 个中断,共有 32+16(系统异常) =48 个向量,向上增大到 2 的整次幂后值为 64,因此地址地址必须能被 64*4=256 整除,从而合法的起始地址可以是: 0x0,0x100, 0x200 等。
而在《ARM Cortex-CM3 权威指南(英文版) 》 (或《The Definitive Guide to the ARMCortex-CM3》 )中第 123 页 Table7.7 描述的 28:7 位(Table offset value from Code region or RAM region) 。
根据“原子论坛”链接(www.openedv.com/posts/list/392.htm)中“正点原子”对以上内容描述及补充:
向量表偏移量寄存器,也就是 SCB->VTOR.它的第 29 位,用来标识向量表是在 CODE 区还是 RAM 区,从而 0X1,就是最高 3 位不去动,这好理解. 但是低位,根据上面这段话的理解,STM32 自己有 60 个中断,加上 CM3 的 16 个,总共有 76 个中断,扩大到 2 的整次幂,那就是128,然后再乘以 4,得到 512,也就是 0X200.根据这样计算,合法的偏移地址应该是0X0,0X200,0X400,0X600... 因此,在此处应该&0X1FFF FE00.才对. 以上是我的理解.实际上确是&0X1FFF FF80;这点,我也有疑问.
“京剧娃娃”的解答应该是最终解答(这种方法也很重要,归结起来就是对于特定的芯片,有可能相对内核架构对寄存器进行了扩展,实际上应该是&0X1FFF FE0,因为编程中 Offset 偏移量是个相对绝对值(32 位) ,对于 VTOR8:7 位是写入 0 的,所以程序在&0X1FFF FF8 运行正常) 。
我觉得原子大哥的"&0X1FFF FE00"更确切一些。原因如下:在 "ARMv7-M Architecture Application Level Reference Manual(November 2010)" 的 716 页给出的 VTOR 定义为:[31:7]——Bits [31:7] of the vector table address[6:0] ——Reserved.(这里不是[29:7]的原因在 "Cortex-M3 Technical Reference Manual.r2p1" 的 24页"The Vector Table Offset Register located at address 0xE000ED08 has been increased by two bits from 29:7 to 31:7",也就是 r2p1 版本升级扩展了两 位 , 不 过 仍 然 向 后 兼 容 :"One or two of the high-order bits of the TBLOFF field can be implemented as RAZ/WI, reducing the supported address range. For example, if two bits are implemented as RAZ/WI, then TBLOFF[29:7] defines bits [29:7] of the address")。也就是说 ARM 官方 r2p0 的定义:[31:30]保留, [29]作为 TBLBASE 判别标准屏蔽, [6:0]保留,如此一来得到&0X1FFF_FF80;而对于 ST 官方的定制而言, 在"PM0056 Programming manual Rev4"的 134 页 给出的定制为:"[31:30] Reserved, must be kept cleared""TBLOFF[29:9]: Vector table base offset field""Bits 8:0Reserved, must be kept cleared"也就是说 ST 把[8:7]也给保留了,原因就是楼上 kenluo 所说的。这种情况下按 ST 官方的定制::[31:30]保留,[29]作为 TBLBASE 判别标准屏蔽,[8:0]保留,如此一来得到&0X1FFF_FE00,就是原子大哥的解释。
2、抢占优先级和响应优先级设置问题
void MY_NVIC_PriorityGroupConfig(u8 NVIC_Group)
{
u32 temp,temp1;
temp1=(~NVIC_Group)&0x07; //取反码后截取低3位,待送入AIRCR[10:8]
temp1<<=8;
temp=SCB->AIRCR; //读取先前设置
temp&=0X0000F8FF;//清空先前分组
temp|=0X05FA0000; //写入钥匙
temp|=temp1;
SCB->AIRCR=temp; //设置分组
}
//NVIC_PreemptionPriority:抢占优先级
//NVIC_SubPriority :响应优先级
//NVIC_Channel :中断编号
//NVIC_Group :中断分组0~4
//注意优先级不能超过设定的组的范围,否则会出现意想不到的错误
//组划分:
//组0:0位抢占优先级,4位响应优先级 AIRCR[10:8]:111
//组1:1位抢占优先级,3位响应优先级 AIRCR[10:8]:110
//组2:2位抢占优先级,2位响应优先级 AIRCR[10:8]:101
//组3:3位抢占优先级,1位响应优先级 AIRCR[10:8]:100
//组4:4位抢占优先级,0位响应优先级 AIRCR[10:8]:011
//NVIC_SubPriority和NVIC_PreemptionPriority的原则是,数值越小越优先
3、NVIC寄存器(转)
中断向量嵌套控制器是用来管理所有中断和事件的,包括中断的使能和除能,中断的优先级。这个是属于内核的东西,所以ST的参考手册上对它的描述较少,但他又是十分重要的东西,要了解它就要看ARM的《Cortex™-M4 Devices Generic User Guide》。
相关寄存器
译自《Cortex™-M4 Devices Generic UserGuide》,若有错误,请以原文为准。
中断使能寄存器NVIC_ISER[8]
中断使能寄存器共有8个,ISER[0]设置0~31号中断的使能,ISER[1]设置32~63号中断的使能,如此类推。以下以ISER[0]为例:
[31:0] SETENA中断设置使能位。
写:
0 =无影响
1 =使能中断。
读:
0 =中断是禁止的
1=中断已经被使能
如果要使能0号中断,就向该寄存器的0位写1,如果要使能38号中断,就向NVIC_ISER[1]的6位写1,如此类推,至于哪个中断对应哪个中断号,请参见参考手册《RM0090 Reference manual》中的第9章Table 30. Vector table的Position一列。
中断除能寄存器NVIC_ICER[8]
中断除能寄存器共有8个,ICER[0]设置0~31号中断除能,ICER[1]设置32~63号中断的使能,如此类推。以下以ICER[0]为例:
[31:0] SETENA中断设置使能位。
写:
0 =无影响
1 =除能中断。
读:
0 =中断是禁止的
1=中断已经被使能
以下寄存器均为8个,仅以***R[0]为例
中断挂起设置寄存器NVIC_ISPR[8]
[31:0]SETPEND中断挂起设置位。
写:
0 =无影响
1 =改变中断状态为挂起。
读:
0 =中断没有挂起
1 =中断正在等待处理。
解除中断挂起寄存器NVIC_ICPR[8]
[31:0] CLRPEND中断清除挂起位。
写:
0 =无影响
1 =删除中断的挂起状态。
读:
0 =没有挂起的中断
1 =中断正在等待处理。
中断**位寄存器NVIC_IABR[8]
[31:0]中断活跃的标志:
0 =中断不活跃
1 =中断活跃。
如果相应的中断的状态是作为一个活跃的或活跃和正被挂起的,读该位将会读出1。
中断优先级寄存器NVIC_IPR[60]
中断优先级寄存器为60个32位寄存器,st的结构体中用了240个8位的字节数组NVIC->IP[240]来映射,每一个对应一个中断的优先级。
ARM的中断优先级分两种,抢占优先级和响应优先级。
具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。
当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。
中断优先级分组就是把优先级寄存器分割,分开哪几位是响应优先级,哪几位是抢占优先级。至于怎样设置分组,就要看一个不属于NVIC的寄存器了。
应用中断和复位控制寄存器SCB_AIRCR
在这里我们需要看的是[31:16]位和[10:8]位,[31:16]位是识别码,用以保护此寄存器不会被意外修改,[10:8]位就是中断优先级分组的设置位。
[31:16]
写:VECTKEYSTAT
读:VECTKEY
RW注册键:
读为0xFA05
写入时,要写0x05FA到 VECTKEY,否则写入将被忽略。
[10:8]中断优先级
每一个ARM的M4中断优先级设计为可编程的8位,具体到stm32f4就只留给用户4位共16级的可编程优先级,其中低4位已被占用。也就是说stm32f4的优先级分组情况如下表所示:
[10:8] |
分割点 |
抢占优先级位 |
响应优先级位 |
抢占优先级数 |
响应优先级数 |
0b011 |
xxxx |
[7:4] |
none |
16 |
1 |
0b100 |
xxx.y |
[7:5] |
[4] |
8 |
2 |
0b101 |
xx.yy |
[7:6] |
[5:4] |
4 |
4 |
0b110 |
x.yyy |
[7] |
[6:4] |
2 |
8 |
0b111 |
yyyy |
none |
[7:4] |
1 |
16 |
最后一个NVIC的寄存器
软件触发中断寄存器NVIC_STIR
当SCB_CCR的USERSETMPEND位为1时,无特权的用户程序才能写此寄存器。
[31:9]保留。
[8:0] INTID ID号中断触发,
范围0-239。例如,0x03的指定中断IRQ3触发。