【STM32】NVIC中断优先级管理

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

请添加图片描述

NVIC中断优先级管理

  • 🙀描述
    • 🦔中断类型
  • 🙀中断优先级分组
    • 🦔抢占优先级和响应优先级
    • 🦔中断优先级分组函数
  • 🙀中断优先级设置
    • 🦔中断优先级设置寄存器
    • 🦔中断优先级初始化函数
  • 🙀总结

🙀描述

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

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

在上面的例子中,学习是一直在进行的事件,而打电话是一个中断事件,在打电话过程中有人敲门又是一个中断事件。
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 priority4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority0 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);如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部