计划用一个月的时间,系统、细致的梳理一下关于STM32的一些知识点,虽然现在工作上用的并不是STM32平台,但是这些知识还是是比较基础、重要的。随着时间的推移,也比较容易遗忘,所以特地以博客做笔记,记录下来。

在我们使用嵌入式RTOS 时,一定要对Cortex-M3/M4内核的嵌套中断向量控制器NVIC有很深的理解,如果对这块不够理解的话,是很难理解到RTOS中任务切换、挂起与解挂等操作的。

本篇文章总结本人对NVIC的一些理解,结合了网上的一些资料,如有偏颇,望不吝赐教。


1、抢占优先级与响应优先级

  STM32(Cortex-M3)中有两个优先级——抢占优先级和响应优先级,有人也叫做先占优先级和次占优先级。

响应顺序:
1、抢占优先级高的任务能打断抢占优先级低的任务的执行;

通俗解释:假设有两个任务AB,他们的抢占优先级分别是12,当B的中断程序正在执行时,如遇到A的中断,则会打断B的执行,优先执行A的任务。这叫中断嵌套,即:具有高抢占优先级的中断能够在低抢占优先级中断处理过程被响应。

2、抢占优先级相同时,它们之间没有嵌套关系;当有一个中断触发时,如果有一个中断正在处理,那么需要等该中断处理完成之后,才能响应刚触发的中断。

通俗解释:假设有两个任务AB,他们的抢占优先级相同,都为1,现在时刻正在处理B的中断,如果来了一个A的中断请求,那么需要等到B任务中断响应完成之后,才能去响应A的中断;这叫同抢占优先级中断等待,即:具有相同抢占优先级的中断,需要等到当前中断处理函数退出之后,才能响应新的中断请求。

3、抢占优先级相同时,如果同时来了两个(多个)中断请求,那响应优先级高的更先执行。如果响应优先级相同,则根据其在中断表中的顺序,排在前面的优先执行。

通俗解释:还是假设有两个任务AB,他们的抢占优先级相同,都为1A的响应优先级为0,而B的响应优先级为2,那么,如果 AB 的中断同时到来时,优先执行的是任务A,因为它的响应优先级更高;另一种情况:如果他们的响应优先级相同,都为2,那么就得查表,看那个任务在中断向量表中的顺序更靠前,靠前的优先执行。


2、中断优先级分组

部分内容摘自CSDN

既然每个中断源都需要被指定这两种优先级,就需要有相应的寄存器位记录每个中断的优先级;在Cortex-M3中定义了8个比特位用于设置中断源的优先级,这8个比特位可以有8种分配方式,如下:

所有8位用于指定响应优先级
最高1位用于指定抢占式优先级,最低7位用于指定响应优先级
最高2位用于指定抢占式优先级,最低6位用于指定响应优先级
最高3位用于指定抢占式优先级,最低5位用于指定响应优先级
最高4位用于指定抢占式优先级,最低4位用于指定响应优先级
最高5位用于指定抢占式优先级,最低3位用于指定响应优先级
最高6位用于指定抢占式优先级,最低2位用于指定响应优先级
最高7位用于指定抢占式优先级,最低1位用于指定响应优先级

Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此STM32把指定中断优先级的寄存器位减少到4位,这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组

接下来就是指定中断源的优先级,下面以一个简单的例子说明如何指定中断源的抢占式优先级和响应优先级:

// 选择使用优先级分组第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);

详细设置可参照下面表格:

NVIC_PriorityGroup抢占优先级响应优先级描述
NVIC_PriorityGroup_000-15抢占优先级0,响应优先级4
NVIC_PriorityGroup_10-10-7抢占优先级1,响应优先级3
NVIC_PriorityGroup_20-30-3抢占优先级2,响应优先级2
NVIC_PriorityGroup_30-70-1抢占优先级3,响应优先级1
NVIC_PriorityGroup_40-150抢占优先级4,响应优先级0

注意

要注意的几点是:

1)如果你设置的抢占优先级超出了或者不在选定优先级分组范围内,则可能会出现无法预料的后果,即中断紊乱。

如果设置的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果;

2)抢占式优先级别相同的中断源之间没有嵌套关系;

3)如果某个中断源被指定为某个抢占优先级别,没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。


摘自一位大佬的理解(有删改):

STM32有43个channel的settable的中断源;AIRC(Application Interrupt and Reset Register)寄存器中有用于指定优先级的4 bits。这4个bits用于分配抢占优先级和响应优先级,在STM32的固件库中定义如下

/* Preemption Priority Group -------------------------------------------------*/
#define NVIC_PriorityGroup_0          ((u32)0x700) /* 0 bits for pre-emption priority
                                                      4 bits for subpriority */
#define NVIC_PriorityGroup_1          ((u32)0x600) /* 1 bits for pre-emption priority
                                                      3 bits for subpriority */
#define NVIC_PriorityGroup_2          ((u32)0x500) /* 2 bits for pre-emption priority
                                                      2 bits for subpriority */
#define NVIC_PriorityGroup_3          ((u32)0x400) /* 3 bits for pre-emption priority
                                                      1 bits for subpriority */
#define NVIC_PriorityGroup_4          ((u32)0x300) /* 4 bits for pre-emption priority
                                                      0 bits for subpriority */

形象化的理解是:
背景:
你是上帝,你造了43个人(43个中断源),这么多人要分社会阶级和社会阶层了;

“阶级”的词性比较重,所以:抢占优先级->阶级;

"阶层"比较中性,所以:响应优先级->阶层;

每个阶级(抢占优先级)内部,都有一些阶层(响应优先级);

如果按照NVIC_PriorityGroup_4这么分,就分为了16个阶级(1个阶级就是1个抢占优先级),0个阶层;高阶级的人,可以打断低阶级的正在做事的人(嵌套),最多可以完成1个中断和15级嵌套。
每个阶级(每个抢占优先级)当中,由你来指定这43人中,谁进入该阶级;

情景1:假设有一个人叫EXTI0_IRQChannel,你指定他进入“阶级8”,则
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 8; // 指定抢占式优先级别为8,可取0-15

另外,在同一阶级内部,一个人在做事的时候,另外一个人不能打断他;(抢占优先级别相同的中断源之间没有嵌套关系)
还有,如果他们两个同时想做事,因为没有阶层,那么就根据中断向量表中的物理排序,让排名靠前的人去做;

情景2:又有1个人叫做SPI1_IRQChannel,设定如下
NVIC_InitStructure.NVIC_IRQChannel = SPI1_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 指定抢占式优先级别,可取0-15

SPI1_IRQChannel的阶级比前者更高,所以EXTI0_IRQChannel做事的时候可以打断(嵌套)。

如果按照NVIC_PriorityGroup_3这么分,就分为了8个阶级(1个阶级是1个抢占优先级),每个阶级内有2个阶层(响应优先级);高阶级的人,可以打断低阶级的正在做事的人(嵌套),最多可以完成1个中断和7级嵌套。每个阶级(每个抢占优先级),你来指定这43人中,谁进入该阶级;

情景3:有一个人叫EXTI0_IRQChannel,你指定他进入“阶级3”,则:
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 指定抢占式优先级别,可取0-7
还需要指定他的阶层:
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 指定响应优先级别,可取0-1

另有1个人叫EXTI9_5_IRQChannel,他的阶级和阶层设定如下
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; // 指定抢占式优先级别,可取0-7
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 指定响应优先级别

那么这两个人是同一阶级的兄弟(就叫无产阶级吧2333),一个人在做事的时候,另外一个人不能打断他;(抢占优先级别相同的中断源之间没有嵌套关系)
如果他们两个同时想做事,因为前者的阶层高,所以前者优先。

情景4:还有一个人叫USART1_IRQChannel,他的阶级和阶层设定如下
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 指定抢占式优先级别,可取0-7
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 指定响应优先级别

情景3相比,USART1_IRQChannel的优先级最高,当前面两个人做事的时候,USART1_IRQChannel都可以打断(嵌套)前面两个人做事。

其他的类推。

最后修改:2019 年 09 月 21 日
您的支持就是我持续更新的动力!