讲在前面的话:
博主在学习正点原子这一章节的时候很是懵,感觉摸不到门路,于是在网上找了一些相关的视频讲解,将他们的内容整合了一下,并尽量用易懂的话讲解。(本文基于stm32f103ZET6)
一、预备知识
- CM3 内核支持256个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256 级的可编程中断设置。
- STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分。
- STM32 有84个中断,包括 16 个内核中断和 68 个可屏蔽中断,具有 16 级可编程的中断优先级。
- 我们常用这68 个可屏蔽中断,在 STM32F103 系列上面,又只有 60 个(在 107 系列才有 68 个)。
所以在此我们就只针对 STM32F103 系列这 60 个可屏蔽中断进行介绍。
学过51单片机的我们都知道他只有定时器中断、外部中断、串口中断,一般只有五个基本中断源。
而stm32里面有60个中断,这么多的中断我们如何管理呢?
所以这里就需要NVIC中断优先级分组。
二、NVIC中断优先级分组
NVIC(Nested vectoredinterrupt controller):嵌套向量中断控制器。
那它是如何管理中断的呢?
首先你想想看,你手底下有60号人,如果是你你打算怎么管理?
那当然是分而治之啊!拉拢一帮人给予高地位(官职),让他们帮你遏制另一些人。
换到stm32里面分配地位的就是IP[240]。
IP[240](Interrupt Priority Registers)是一个中断优先级控制的寄存器组。这个寄存器组相当重要!毕竟是分配地位的,STM32 的中断分组就与这个寄存器组有关。
IP 寄存器组由 240 个 8bit 的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断,而本文 STM32 只用到 了其中的前 60 个。
IP[59]~IP[0]分别对应中断 59~0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位,这高4位是给予中断地位的关键。
好,现在这 4 位开始设置官职,它把中断分为不同品级(抢占优先级),同一品级又划分了不同品阶(子优先级)。看官职先看你的品级(抢占优先级在前),品级一样看你的品阶(子优先级在后)。
好了,现在官职倒是定好了,那么如何分配呢?
前面说了高四位是给予官职的关键,所以这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。
STM32 将中断分为 5 个组,组 0~4。该分组的设置是由SCB->AIRCR 寄存器的 bit10~8 来定义的。至于这个寄存器到底如何操作,目前我们先不用关心。
这个响应优先级就是子优先级。
抢占优先级和子优先级有什么区别呢?
前面我们提到过,我们把抢占优先级称为官职的品级,响应优先级称为品阶。
官职品级和品阶的概念不用我多说了吧。
抢占优先级和响应优先级有0~4, 在官职里面对于抢占优先级,我们称它有零品~四品,在官职里面数字越小地位越高,所以有一品大员的称呼。当然实际上是没有零品的,这是我造的,这里0最小,所以它地位最高,零品大员!
对于响应优先级,我们称它有零品阶~四品阶,也是数字越小地位越高。
高优先级的抢占优先级可以打断正在进行的低抢占优先级中断;
(因为我品级高,地位高,可以打断你,先做我的事)
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断;
(咱俩品级一样,虽然你的品阶比我高,但是咱俩的级别一样,你好歹不能直接打断我做事,这不是不给我面子吗?我做完后你才能做事)
(咱们品级一样,级别相同,现在咱俩同时要做一件事,你的品阶高,地位也就比我高了一点,不是我怂,只是客气还是你先做吧,你做完我再做)
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行。
(品级一样,品阶一样,你先接手,那你先做事)
好,现在分析图中的意思。组 0~4意思就是这高四位中有几位可以设置抢占优先级。比如:
组0:这四位全部只能设置响应优先级;
组1:这四位的高一位可以设置抢占优先级,剩下三位可以设置响应优先级。
组2:这四位高两位可以设置抢占优先级,剩下两位可以设置响应优先级。
组4:这四位全部用来设置抢占优先级。
看个例子:
请注意:
一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
三、相关配置函数
3.1 中断优先级分组函数
中断优先级分组函数:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
看其命名NVIC Priority(优先级) Group(组) Config(配置)
转入他的释义看:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
可以看到有配置SCB->AIRCR 寄存器的语句。
这个中断优先级分组函数括号里面可以填上自己想要的中断分组,比如组2
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
分组设置好之后如何设置单个中断的抢占优先级和响应优先级呢?
这其中涉及很多寄存器,先一起看看吧。
3.2相关寄存器
中断设置相关寄存器:
typedef struct
{
__IO uint32_t ISER[8];
uint32_t RESERVED0[24];
__IO uint32_t ICER[8];
uint32_t RSERVED1[24];
__IO uint32_t ISPR[8];
uint32_t RESERVED2[24];
__IO uint32_t ICPR[8];
uint32_t RESERVED3[24];
__IO uint32_t IABR[8];
uint32_t RESERVED4[56];
__IO uint8_t IP[240];
uint32_t RESERVED5[644];
__O uint32_t STIR;
} NVIC_Type;
__IO uint8_t IP[240]; //中断优先级控制的寄存器组
__IO uint32_t ISER[8]; //中断使能寄存器组
__IO uint32_t ICER[8]; //中断失能寄存器组
__IO uint32_t ISPR[8]; //中断挂起寄存器组
__IO uint32_t ICPR[8]; //中断解挂寄存器组
__IO uint32_t IABR[8]; //中断激活标志位寄存器组
先眼熟一下这些寄存器,然后我们再来说怎么设置。
其实在目录二中已经提到过,就是IP[240] (Interrupt Priority Registers)
中断优先级控制的寄存器组:IP[240]
他有240个8位寄存器,每个中断使用一个寄存器来确定优先级。STM32F10x系列一共60个可屏蔽中断,所以只使用IP[59]~IP[0]。IP[59]~IP[0]分别对应中断 59~0,而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。
忘记的小伙伴可以往前翻一下。
在库函数中设置抢占和响应优先级是用这个函数:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
这个函数有一个指针类型成员变量,现在去看看NVIC_InitTypeDef*的释义:
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
这里面有4个成员变量:
NVIC_IRQChannel:定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到 每个中断对应的名字。例如 USART1_IRQn。
NVIC_IRQChannelPreemptionPriority:定义这个中断的抢占优先级别。
NVIC_IRQChannelSubPriority:定义这个中断的响应优先级别。
NVIC_IRQChannelCmd:该中断是否使能。
比如我们要使能串口 1 的中断,同时设置抢占优先级为 1,子优先级位 2,初始化的方法是:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化 NVIC 寄存器
中断使能寄存器组:ISER[8]
ISER[8]: 中断使能寄存器组
它是32位寄存器,每个位控制一个中断的使能。STM32F10x只有60个可屏蔽中断,所以只使用了
其中的ISER[0]和ISER[1]。ISER[0]的bit0~bit31分别对应中断0~31,ISER[1]的bit0~27对应中断32~59。
他的作用是使能中断,哎,这个作用是不是在哪里听过?
再看看他用的函数也是:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
所以它对应的是指针类型成员变量的第四个成员变量使能:
FunctionalState NVIC_IRQChannelCmd
所以配置这个成员变量就相当于配置这个寄存器
对应的还有中断失能寄存器组。
中断失能寄存器组:ICER[8]
ICER[8]:中断失能寄存器组
他的作用是用来失能中断。
他也是32位寄存器,每个位控制一个中断的失能。STM32F10x只有60个可屏蔽中断,所以只使用
了其中的ICER[0]和ICER[1]。ICER[0]的bit0~bit31分别对应中断0~31。ICER[1]的bit0~27对应中断
32~59,配置方法跟ISER一样。也使用函数:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
配置第四个成员变量失能,就是配置这个寄存器。
作用:用来挂起中断
中断解挂控制寄存器组:ICPR[8]
作用:用来解挂中断
中断激活标志位寄存器组:IABR [8]
作用:只读,通过它可以知道当前在执行的中断是哪一个,如果对应位为1,说明该中断正在执行。
3.3总结步骤
先设置中断优先级分组,比如设置为组2:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
注意:整个系统执行过程中,只能设置一次,不要用着用着突然改了
然后针对每个中断,设置对应的抢占优先级和响应优先级,例如:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化 NVIC 寄存器
最后如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。
这个我们先不做探究。文章来源:https://www.toymoban.com/news/detail-835026.html
待补充,待更新...文章来源地址https://www.toymoban.com/news/detail-835026.html
到了这里,关于stm32-NVIC中断优先级管理(以古代官职角度分析)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!