在网上搜集了一些资料,整理了一下,自己一点点积累,对NVIC有了肤浅的了解。
一、STM32 (Cortex-M3) 中的优先级概念
STM32(Cortex-M3)中有两个优先级的概念:抢占式优先级和副优先级,也把响应优先级称作“亚优先级”或“响应优先级”,每个中断源都需要被指定这两种优先级。(数字小的优先级高)
1. 何为抢占式优先级(pre-emption priority)
抢占式优先级的中断事件会打断当前的主程序/中断程序运行—抢断式优先响应(后者俗称中断嵌套)。
2. 何为副优先级(subpriority)
在抢占式优先级相同的情况下,高的副优先级的中断优先被响应;
在抢占式优先级相同的情况下,如果有低副优先级中断正在执行, 高副优先级的中断要等待已被响应的低副优先级中断执行结束后才能得到响应——非抢断式响应不能嵌套。
二、Cortex-M3中对中断优先级的定义(先看下面标题三的内容)
既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:
所有8位用于指定响应优先级
最高1位用于指定抢占式优先级,最低7位用于指定响应优先级
最高2位用于指定抢占式优先级,最低6位用于指定响应优先级
最高3位用于指定抢占式优先级,最低5位用于指定响应优先级
最高4位用于指定抢占式优先级,最低4位用于指定响应优先级
最高5位用于指定抢占式优先级,最低3位用于指定响应优先级
最高6位用于指定抢占式优先级,最低2位用于指定响应优先级
最高7位用于指定抢占式优先级,最低1位用于指定响应优先级
这就是优先级分组的概念。
三、stm32中对中断优先级的定义
Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这4个寄存器位的分组方式如下:
第0组:所有4位用于指定副优先级(可以设置16级)
第1组:最高1位用于指定抢占式优先级(2级),最低3位用于指定副优先级(8级)
第2组:最高2位用于指定抢占式优先级(4级),最低2位用于指定副优先级(4级)
第3组:最高3位用于指定抢占式优先级(8级),最低1位用于指定副优先级(2级)
第4组:所有4位用于指定抢占式优先级(16级)
四、举例
接下来就是指定中断源的优先级,下面以一个简单的例子说明如何指定中断源的抢占式优先级和副优先级:
// 选择使用优先级分组第1组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// 使能EXTI0中断
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 指定抢占式优先级别1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定副优先级别0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
要注意的几点是:
1、在一个系统中,通常只使用上面5种分配情况的一种。
2、 如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果。
3、 如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。
五、补充:开关总中断
在STM32/Cortex-M3中是通过改变CPU的当前优先级来允许或禁止中断。
PRIMASK位:只允许NMI和hard fault异常,其他中断/异常都被屏蔽(当前CPU优先级=0)。
FAULTMASK位:只允许NMI,其他所有中断/异常都被屏蔽(当前CPU优先级=-1)。
在STM32固件库中(stm32f10x_nvic.c和stm32f10x_nvic.h) 定义了四个函数操作PRIMASK位和FAULTMASK位,改变CPU的当前优先级,从而达到控制所有中断的目的。
例如:
第一种方法:
NVIC_SETPRIMASK(); //关闭总中断
NVIC_RESETPRIMASK();//开放总中断
第二种方法:
NVIC_SETFAULTMASK(); //关闭总中断
NVIC_RESETFAULTMASK();//开放总中断
常常使用:
NVIC_SETPRIMASK(); // Disable Interrupts
NVIC_RESETPRIMASK(); // Enable Interrupts
可以用:
#define CLI() __set_PRIMASK(1)
#define SEI() __set_PRIMASK(0)
来实现开关总中断的功能。