一:综述
STM32 目前支持的中断共为 84 个(16 个内核+68 个外部), 16 级可编程中断优先级 的设置(仅使用中断优先级设置 8bit 中的高 4 位)和16个抢占优先级(因为抢占优先级最多可以有四位数)。
二:优先级判断
STM32(Cortex-M3)中有两个优先级的概念——抢占式优先级和响应优先级,有人把响应优先级称作‘亚优先级’或‘副优先级’,每个中断源都需要被指定这两种优先级。
具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套低抢占式优先级的中断。
当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。
三:优先级分组
既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位在NVIC应用中断与复位控制寄丛器(AIRCR)的中断优先级分组域中,可以有8种分配方式,如下:
所有8位用于指定响应优先级
最高1位用于指定抢占式优先级,最低7位用于指定响应优先级
最高2位用于指定抢占式优先级,最低6位用于指定响应优先级
最高3位用于指定抢占式优先级,最低5位用于指定响应优先级
最高4位用于指定抢占式优先级,最低4位用于指定响应优先级
最高5位用于指定抢占式优先级,最低3位用于指定响应优先级
最高6位用于指定抢占式优先级,最低2位用于指定响应优先级
最高7位用于指定抢占式优先级,最低1位用于指定响应优先级
这就是优先级分组的概念。
Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位(AIRCR高四位),这4个寄存器位的分组方式如下:
第0组:所有4位用于指定响应优先级
第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
第4组:所有4位用于指定抢占式优先级
可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:
NVIC_PriorityGroup_0 => 选择第0组
NVIC_PriorityGroup_1 => 选择第1组
NVIC_PriorityGroup_2 => 选择第2组
NVIC_PriorityGroup_3 => 选择第3组
NVIC_PriorityGroup_4 => 选择第4组
中断优先级分组是为了给抢占式优先级和响应优先级在中断优先级寄丛器的高四位分配各个优先级数字所占的位数。在一个程序中只能设定一次。
四:中断源的优先级
接下来就是指定中断源的优先级,中断源优先级是在中断优先级寄存器中设置的,只能设置及高四位,必须根据中断优先级分组中设置好的位数来在该寄存器中设置相应的数值。假如你选择中断优先级分组的第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级,那么抢占式优先级就有000-111共八种数据选择,也就是有八个中断嵌套,而响应优先级中有0和1两种,总共有8*2=16种优先级。
中断源优先级具体的设置了该中断源的优先级别
在一个程序中可以设定多个(最多16个)优先级,每个中断源只能设定的一个。
每写一个关于中断优先级的程序必须包含下列两个函数:
(1)void NVIC_PriorityGroupConfig(u32 NVIC_PriorityGroup)中断分组设置
(2)void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)中断优先级设置
具体设置:
可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的参数有下列5种:
NVIC_PriorityGroup_0 => 选择第0组
NVIC_PriorityGroup_1 => 选择第1组
NVIC_PriorityGroup_2 => 选择第2组
NVIC_PriorityGroup_3 => 选择第3组
NVIC_PriorityGroup_4 => 选择第4组
五:举例
接下来就是指定中断源的优先级,下面以一个简单的例子说明如何指定中断源的抢占式优先级和响应优先级:
// 选择使用优先级分组第1组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// 使能EXTI0中断
NVIC_InitStructure.NVIC_IRQChannel =EXTI0_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //指定抢占式优先级别1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //指定响应优先级别0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能EXTI9_5中断
NVIC_InitStructure.NVIC_IRQChannel =EXTI9_5_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //指定抢占式优先级别0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定响应优先级别1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
六:注意事项
1)如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;
2)抢占式优先级别相同的中断源之间没有嵌套关系;
3)如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。
七:开关总中断
在STM32/Cortex-M3中是通过改变CPU的当前优先级来允许或禁止中断。
PRIMASK位:只允许NMI和hardfault异常,其他中断/异常都被屏蔽(当前CPU优先级=0)。
FAULTMASK位:只允许NMI,其他所有中断/异常都被屏蔽(当前CPU优先级=-1)。
在STM32固件库中(stm32f10x_nvic.c和stm32f10x_nvic.h)定义了四个函数操作PRIMASK位和FAULTMASK位,改变CPU的当前优先级,从而达到控制所有中断的目的。
下面两个函数等效于关闭总中断:
void NVIC_SETPRIMASK(void);
void NVIC_SETFAULTMASK(void);
下面两个函数等效于开放总中断:
void NVIC_RESETPRIMASK(void);
void NVIC_RESETFAULTMASK(void);
上面两组函数要成对使用,不能交叉使用。
例如:
第一种方法:
NVIC_SETPRIMASK(); //关闭总中断
NVIC_RESETPRIMASK(); //开放总中断
第二种方法:
NVIC_SETFAULTMASK(); //关闭总中断
NVIC_RESETFAULTMASK(); //开放总中断
常常使用
NVIC_SETPRIMASK(); // Disable Interrupts
NVIC_RESETPRIMASK(); // EnableInterrupts
补充 可以用
#define CLI() __set_PRIMASK(1)
#define SEI() __set_PRIMASK(0)
stm32优先级
1.优先级等级:
STM32用户能分配的优先级有16级,也就是用优先级寄存器NVIC->IP[x]的高四位来表示莫个中断的优先级。
2.优先级组:
在STM32中将一个中断的优先级分为:抢占优先级和子优先级。
在进行优先级判断的时候先是比较抢占优先级然后比较子优先级。
在固件库中用变量分别表示抢占优先级和子优先级:
NVIC_InitTypeDef.NVIC_IRQChannelPreemptionPriority;(抢占优先级)
NVIC_InitTypeDef.NVIC_IRQChannelSubPriority;(子优先级)
优先级组就是对抢占优先级和子优先级进行的分界:
例如设置优先级组为0x05,那么表示的是莫个中断的优先级从第5位开始为界限,高两位[6:7]是抢占优先
级。第两位是[5:4]表示的是子优先级。
举例说明:
//调用优先级组设置函数,设置优先级是0x05.
.....
NVIC_SetPriorityGrouping(5);
.....
//这里说明了一个优先级寄存器的(NVIC->IP[x])的7,6位表示的是抢占优先级。5,4表示的是优先级。
......
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
.......
//这里进行优先级的设置:通过上面的设置可以知道抢占优先级和子优先级的范围都是3~0;
//那么这样就设置好了一个中断的优先级
//====================================================
//
STM32 中断与嵌套NVIC 快速入门。
// netjob 2008-8-1
//====================================================
我也是靠看这本书才弄懂的:
Cortex-M3 权威指南
Joseph Yiu 著
宋岩 译
其实很简单。
//CM3 有 最多240个中断(通常外部中断写作IRQs),就是 软件上说的IRQ_CHANAELx(中断通道号x)
每个中断有自己的可编程的中断优先级【 有唯一对应的 中断优先级寄存器 】。
由于CM3支持 硬件中断嵌套,所以可以有 256 级的可编程优先级和 256级中断嵌套【书上称:抢占(preempt)优先级】
所以大家可以设:
IRQ CHANAEL 0 通道 = 2 中断优先级
WWDG 窗口定时器中断
IRQ CHANAEL 1 通道 = 0 中断优先级
PVD
联到EXTI的电源电压检测(PVD)中断
IRQ CHANAEL 3 通道 = 255 中断优先级
RTC 实时时钟(RTC)全局中断
IRQ CHANAEL 6 通道 = 10 中断优先级
EXTI0 EXTI线0中断.....
IRQ CHANAEL 239 通道 = (0
这个实在是太恐怖了! 是的,其实CM3 并没有这样做。
实在的芯片例如STM32等就只有设计来可用才64级可编程优先级和8级中断嵌套。
对 64级中断就是说:( INT0 到INT63)这个大家比较好理解,其它的64···239就不用了。
IRQ CHANAEL 0 。。。 IRQ CHANAEL 63
而8级中断嵌套这又是何解呢? 是这样的,上面说 一个 【中断】对应一个【中断优先级寄存器】,而这个寄存器是 8 位的。 当然就是256级了。而现在就用了 它其中的 BIT7,IT6,BIT5
三位来表示,而且是MSB对齐的。 用了3 个位来表达优先级(MSB 对齐的我们能够使用的8个优先级为:0x00(最高),0x20,0x40,0x60,0x80,0xA0,0xC0以及0xE0。)
这样我们在【中断优先级寄存器】就不能按理论的填0到255之间的数了,而只能填0x00(最高),0x20,0x40,0x60,0x80,0xA0,0xC0以及0xE0。)
所以大家可以设:
IRQ CHANAEL 0 通道 = 0x20 中断优先级
WWDG 窗口定时器中断
IRQ CHANAEL 1 通道 = 0x40 中断优先级
PVD
联到EXTI的电源电压检测(PVD)中断
IRQ CHANAEL 3 通道 = 0x20 中断优先级
RTC 实时时钟(RTC)全局中断
IRQ CHANAEL 6 通道 = 0xA0 中断优先级
EXTI0 EXTI线0中断
.....
IRQ CHANAEL 63 通道 =【0x00(最高),0x20,0x40,0x60,0x80,0xA0,0xC0
以及0xE0。)】
大家注意到了,上面通道0和通道3 的优先级都是0X20, 这怎么办?
//
如果优先级完全相同的多个异常同时悬起,则先响应异常编号最小的那一个。如IRQ#0会比IRQ #3先得到响应,而且文中还讲了【优先级分组】,这又是什么回事?其实我回头看来,这个【优先级分组】和【抢占优先级】【亚优先级】都毫无意义的。
如果当时用 256级即是把【中断优先级寄存器】的8位都全用上,就没这个必要了。还什么优先级分组呢!
就是因为厂家现在【偷工减料】,才搞出这个明堂来的。
是这样的,在应用程序中断及复位控制寄存器(AIRCR) 中的10:8位【3位】是表示【优先级分组】它作用主要是用于对【中断优先级寄存器】『我们现在中用了BIT7,BIT6,BIT5三位』的功能的说明。
有一个表,在《Cortex-M3 权威指南》的110页,例如我们把AIRCR的10:8位设为【5】,查表可得【抢占优先级】=【7:6】,【亚优先级】=【5:0】,对于【中断优先级寄存器】只用了BIT7,6,5,因此我们可以看作是【7:6】,【5】。那4-0 可以不管。
现在我们的 IRQ0=0X20, IRQ3=0X20, 也就是 【0 0 1 0】『bit7=0,bit6=0,bit5=1,bit4=0』因为大家(IRQ0/IRQ3)的【抢占优先级】=【7:6】都是0,说明它们的中断相应级别是一样的。再继续判断它们哪个更优先的责任就要看【5】,结果连【5】都是一样的!
那就按默认:
// 如果优先级完全相同的多个异常同时悬起,则先响应异常编号最小的那一个。如IRQ#0会比IRQ #3先得到响应,由于CM3没有进中断【关全局中断相应】这事,只要是中断通道打开了,就会存在通道间的嵌套,即是会发生【抢占】的情况了。
上面就简短的说明,如果要详细理解,可以看《Cortex-M3权威指南》。有任何理解不当,请各位多多指教!
补充注意:
“2)抢占式优先级别相同的中断源之间没有嵌套关系;”
所以大家可以设:
IRQ CHANAEL 0 通道 = 0x20 中断优先级 WWDG 窗口定时器中断
IRQ CHANAEL 1 通道 = 0x40 中断优先级 PVD
联到EXTI的电源电压检测(PVD)中断
IRQ CHANAEL 3 通道 = 0x20 中断优先级 RTC 实时时钟(RTC)全局中断
IRQ CHANAEL 6 通道 = 0xA0 中断优先级 EXTI0EXTI线0中断
这样 0 通道和3 通道就不会有嵌套情况,而是0 通道按默认比3 通道优先高些。而0 通道与1通道就会有嵌套情况。
芯片复位后,默认的优先级分组 是 0, 就是 【7:1】表示抢占式优先级,【0】表示亚优先级, 这样对于MSB对齐的 8个优先级为:0x00(最高),0x20,0x40,0x60,0x80,0xA0,0xC0以及0xE0。)
使用就很方便了,建议大家就用默认的默认的优先级分组是0,也就是复位后的值,哈哈!
例如下面的两个按键,都使用外中断方式,使用了PD.3,和PD.4两个引脚。这两个中断的优先级都是0X20,按默认的优先级分组,它们之间不会发生中断嵌套。
STM32_Nvic_Regs->Priority[9].all=0x20; // 中断的优先级是0X20
STM32_Nvic_Regs->Enable[0].bit.INT9=1; // 开INT9 中断IRQ9
STM32_Nvic_Regs->Priority[10].all=0x20; // 中断的优先级是0X20
STM32_Nvic_Regs->Enable[0].bit.INT10=1; // 开INT10 中断IRQ10