stm32-NVIC中断优先级管理(以古代官职角度分析)

讲在前面的话:

博主在学习正点原子这一章节的时候很是懵,感觉摸不到门路,于是在网上找了一些相关的视频讲解,将他们的内容整合了一下,并尽量用易懂的话讲解。(本文基于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(RTC中断)的抢占优先级为2(高两位设置为1 0),响应优先级为1(低两位设置为0 1);
中断6(外部中断0)的抢占优先级为3(高两位设置为1 1),响应优先级为0(低两位设置为0 0);
中断7(外部中断1)的抢占优先级为2(高两位设置为1 0),响应优先级为0(低两位设置为0  0)。
结合前面所学 ,这3个中断的优先级顺序为:中断7>中断3>中断6。

请注意: 

一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组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);

配置第四个成员变量失能,就是配置这个寄存器。 

下面几个寄存器用的少,混个眼熟先:
中断挂起控制寄存器组:ISPR[8]

作用:用来挂起中断 

 中断解挂控制寄存器组: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 寄存器

最后如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。

这个我们先不做探究。

待补充,待更新...

NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)是ARM Cortex-M系列微控制器中常用的一种中断控制器。在NVIC中,有16个不同的中断优先级,它们被分为若干个组。分组的目的是为了方便中断的管理和响应。 在ARM Cortex-M系列中,中断优先级是由8位的寄存器来控制的,最高优先级为0,最低优先级为255。NVIC根据中断优先级寄存器的值来确定哪个中断应该被处理。NVIC支持若干种分组方式,包括: 1. 分组0:将所有中断分为一组,优先级值越小的中断优先级越高。这种方式不允许优先级抢占,因此可能会导致低优先级的中断被长时间阻塞。 2. 分组1:将中断分为两组,第一组包含最高优先级的中断,第二组包含剩余的中断。这种方式允许抢占,但是可能会导致优先级反转问题。 3. 分组2:将中断分为若干组,每组之间的优先级差为相邻的2的整数次幂。例如,当分为4组时,第一组包含优先级为0到3的中断,第二组包含优先级为4到7的中断,以此类推。这种方式既允许抢占,又避免了优先级反转问题。 4. 分组3:将中断分为若干组,每组只包含一个中断。这种方式也允许抢占,同时也避免了优先级反转问题,但是需要消耗更多的中断向量表空间。 选择合适的分组方式应该根据具体的应用场景和中断处理要求来决定。常用的分组方式是分组2。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值