当前位置:网站首页>NVIC中断优先级管理

NVIC中断优先级管理

2022-07-07 06:26:00 一只大喵咪1201

作者:一只大喵咪1201
专栏:《STM32学习》
格言:你只管努力,剩下的交给时间!

请添加图片描述

描述

中断可以看作是一个事件。

比如,你正在家里学习,突然来了电话,你只能停下当前的学习去接电话,在接电话的过程中又有人敲门,你只好放下电话去开门,然后再拿起电话继续打电话,当挂掉电话后又继续前面的学习。

在上面的例子中,学习是一直在进行的事件,而打电话是一个中断事件,在打电话过程中有人敲门又是一个中断事件。
STM32中的中断也是这个道理,在执行主程序的过程中会有其他事件打断这个过程,进入到事件中的程序去执行,执行完中断程序后再返回主程序继续执行,这样就是中断。

🦔中断类型

  • CM3内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。
  • STM32F10系列并没有使用CM3内核的全部东西,而是只用了它的一部分。
  • STM32F10系列有84个中断,包括16个内核中断和68个可屏蔽中断,具有16级可编程的中断优先级。
  • STM32F103系列上面,又只有60个可屏蔽中断(在107系列才有68个)。

这里的外部中断和可屏蔽中断都是一个意思,是指不包括内核中断的所有中断。

图
此图便是本喵使用的STM32F103ZET6的60个外部中断。

中断优先级分组

有这么多类型的中断,是怎么管理的呢?STM32通分组的方式来管理这些中断。

该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。

图
通过赋予SCB->AIRCR寄存器中第8位到第10位的值将这些中断类型分为5个组。
不同的分组中,AIRCR寄存器中的第4位到第8位管理优先级的情况不同。
比如常用的第2组,这4位中两位是管理抢占优先级的,两位是管理响应优先级的。按照从左到右的顺序。
这时又有疑惑了,抢占优先级和响应优先级是什么呢?

🦔抢占优先级和响应优先级

抢占优先级:

  • 抢占优先级是指中断的打断优先级,抢占优先级高的中断可以打断正在执行的抢占优先级低的中断。

响应优先级

  • 响应优先级是指中断的响应顺序,响应优先级只有在抢占优先级相同的情况下才有意义。当抢占优先级相同时,俩个中断同时发生,响应优先级高的中断先响应。

俩个优先级中,数字越小表示的优先级越高。
举例说明:

  • 假定设置中断优先级组为2
  • 然后设置
  • 中断3(RTC中断)的抢占优先级为2,响应优先级为1。
  • 中断6(外部中断0)的抢占优先级为3,响应优先级为0。
  • 中断7(外部中断1)的抢占优先级为2,响应优先级为0。

那么的这三个中断的优先级顺序就是

  • 中断7>中断3>中断6

当抢占优先级和响应优先级都相同的时候,哪个中断先发生就先执行哪个中断程序。
注意:
中断分组仅在系统的初始化完成后设置一次。如果在后面的程序中有修改分组就会混乱。
比如当使用的是2组的时候
图
优先级管理位如上图设置,此时的抢占优先级是2,响应优先级是2。
在使用这个分组后再将分组设置成3组的时候,此时抢占优先级是5,响应优先级是0。
如此一来,优先级的管理就混乱了。

🦔中断优先级分组函数

ST官方提供了设置分组的库函数,不用我们自己去挨个设置寄存器的值。

void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
    
  /* Check the parameters */
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
  
  /* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
  SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

这是中断优先级分组的函数,他在ST官方固件库的misc.c源文件中。
通过函数的定义我们可以看到,它的实质就是在配置AIRCR寄存器。

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

以上5个宏定义就是中断优先级分组函数的入口参数,需要分成哪一组就将哪一组对应的参数传入函数中。

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_3);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);

如此便将这60个外部中断分好了组。

中断优先级设置

虽然分好了组,方便管理了,但是具体是哪个中断发生,它的优先级是多少还需要进行具体的设置。

🦔中断优先级设置寄存器

typedef struct
{
    
  __IO uint32_t ISER[8];                      /*!< Offset: 0x000 Interrupt Set Enable Register */
       uint32_t RESERVED0[24];                                   
  __IO uint32_t ICER[8];                      /*!< Offset: 0x080 Interrupt Clear Enable Register */
       uint32_t RSERVED1[24];                                    
  __IO uint32_t ISPR[8];                      /*!< Offset: 0x100 Interrupt Set Pending Register */
       uint32_t RESERVED2[24];                                   
  __IO uint32_t ICPR[8];                      /*!< Offset: 0x180 Interrupt Clear Pending Register */
       uint32_t RESERVED3[24];                                   
  __IO uint32_t IABR[8];                      /*!< Offset: 0x200 Interrupt Active bit Register */
       uint32_t RESERVED4[56];                                   
  __IO uint8_t  IP[240];                      /*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */
       uint32_t RESERVED5[644];                                  
  __O  uint32_t STIR;                         /*!< Offset: 0xE00 Software Trigger Interrupt Register */
}  NVIC_Type; 

在ST官方的固件库中的misc.h中定义了这样一个结构体,结构体的寄存器就是用来设置中断的优先级的。

  • ISER[8]:ISER 全称是:Interrupt Set-Enable Registers,这是一个中断使能寄存器组。STM32F103 的可屏蔽中断只有 60 个,所以对我们来说,有用的就是两个(ISER[0]和 ISER[1]),总共可以表示 64 个中断。而 STM32F103 只用了其中的前 60 位。ISER[0]的 bit 0到31 对应中断0到31。ISER[1]的 bit 0到27 对应中断 32~59;这样总共 60 个中断就分别对应上了。你要使能某个中断,必须设置相应的 ISER 位为 1。
  • ICER[8]:全称是:Interrupt Clear-Enable Registers,是一个中断除能寄存器组。该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的。这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。
  • ISPR[8]:全称是:Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的。
  • ICPR[8]:全称是:Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断接挂。写 0 无效。
  • IABR[8]:全称是:Interrupt Active Bit Registers,是一个中断激活标志位寄存器组。对应位所代表的中断和 ISER 一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。
  • IP[240]:全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。这个寄存器组相当重要STM32 的中断分组与这个寄存器组密切相关。STM32F103只用到了其中的前 60 个。IP[59]到IP[0]分别对应中断 59~0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。这 4 位,又分为抢占优先级和响应优先级。抢占优先级在前,响应优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。

在ST官方的固件库的core_cm3.h中有几个库函数

static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn);
static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn);

分别的作用是挂起某中断,读某中断状态,解除挂起的某中断。

🦔中断优先级初始化函数

ST官方同样提供了中断优先级的设置函数,不用我们去挨个设置寄存器。

typedef struct
{
    
  uint8_t NVIC_IRQChannel; //设置中断通道
  uint8_t NVIC_IRQChannelPreemptionPriority;//设置抢占优先级
  uint8_t NVIC_IRQChannelSubPriority; //设置响应优先级
  FunctionalState NVIC_IRQChannelCmd; //使能/使能
} NVIC_InitTypeDef;

在misc.h中定义了这样一个结构体,成员变量分别代表着

  • 中断通道,也就是哪个类型的中断。
  • 抢占优先级。
  • 响应优先级。
  • 中断使能。

而使用这个结构体和GPIO的使用方法类似

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寄存器

以上便完成了中断优先级的初始化。

总结

在使用中断的时候

  1. 系统运行后先设置中断优先级分组。
    调用函数:void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);整个系统执行过程中,只设置一次中断分组。

  2. 针对每个中断,设置对应的抢占优先级和响应优先级。
    调用函数:void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。

原网站

版权声明
本文为[一只大喵咪1201]所创,转载请带上原文链接,感谢
https://blog.csdn.net/weixin_63726869/article/details/125248703