最近在项目中处理多个中断事件的时候遇到了一些问题,后来发现是因为自己没有对 NVIC 的知识理解透彻,导致配置 NVIC 的时候出现了问题,在网上学习的过程中发现不少文章都是介绍NVIC的配置,并没有深入介绍NVIC。这里就自己对于NVIC学习后的理解做一个分析总结。
NVIC,全称为Nest Vector Interrupt Controller, 直译过来为嵌套中断向量控制器,用于中断嵌套的管理,降低中断延迟时间并且能更加高效处理后续中断。简单说来就是给不同的中断源分配不同的中断优先级,优先级越高,『权利』越大。STM32优先级由分为先占优先级(主 优先级,抢占优先级)和从优先级。先占优先级高的中断可以打断先占优先级较低的中断执行过程,即中断嵌套。先占优先级相同则相互不能嵌套,但是当同一时刻两个不同的中断源发生请求时,CPU会先处理从优先级高的中断事件。
举个例子
1:在学校里面,你正在帮班长(中断源1)收同学作业(中断请求),这时候语文老师(中断元2)让你擦黑板(中断请求),你必须停下收作业这一事件立马执行擦黑板这一指令,因为语文老师的『权利』更大。在这个比喻当中,其实你就是CPU,收作业和擦黑板都是优先级不同的中断源(班长和语文老师)发出的中断请求,高优先级的中断可以打断正在执行的低优先级中断事件,称之为中断嵌套。
2:你正在帮你女朋友辅导作业的过程中突然你死党也让你去帮他辅导作业,你是一个有原则的人(先占优先级相同),所以你会处理完手头的事 即帮你女朋友把问题解决了再去帮你死党辅导。但是如果你正在休息的时候你女朋友和你死党同时让你帮他们辅导作业(不可以同时进行),女朋友更重要(女朋友从优先级高于死党从优先级),你当然是先帮你女朋友解决问题,完了之后再去帮你死党啦。
下面来看NVIC配置的实例
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置中断优先级分组1
/* Enable the RTC Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //中断源为RTC中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //设置先占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//设置从优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断使能
NVIC_Init(&NVIC_InitStructure);
}
STM32中指定优先级的寄存器为4位,其定义如下:
第0组:先占优先级只能设为0,所有4位用于指定从优先级(0~15),
第1组:最高1位用于指定先占式优先级(0~1),最低3位用于指定从优先级(0~7)
第2组:最高2位用于指定先占式优先级(0~3),最低2位用于指定从优先级(0~3)
第3组:最高3位用于指定先占式优先级(0~7),最低1位用于指定从优先级(0~1)
第4组:所有4位用于指定先占优先级(0~15),从优先级只能设为0。
我们再来分析NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1)函数,其入口参数可以为:
NVIC_PriorityGroup_0 选择第0组
NVIC_PriorityGroup_1 选择第1组
NVIC_PriorityGroup_2 选择第2组
NVIC_PriorityGroup_3 选择第3组
NVIC_PriorityGroup_4 选择第4组
下面举例说明 NVIC 不同优先级对应着不同的情况。
一个中断源为EXTI0,配置其优先级如下
Void EXTI0_ NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置中断优先级分组1
/* Enable the RTC Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChann; //中断源为RTC中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级可为0或1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//从优先级可为0-7
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断使能
NVIC_Init(&NVIC_InitStructure);
}
Void RTC_ NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组2
/* Enable the RTC Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; //中断源为RTC中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级可为0-3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//从优先级可为0和1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断使能
NVIC_Init(&NVIC_InitStructure);
}
Void TIM3_ NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组2
/* Enable the RTC Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //中断源为TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; //先占优先级可为0-3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//从优先级可为0和1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//中断使能
NVIC_Init(&NVIC_InitStructure);
}