NVIC(Nested vectored interrupt controller) 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以说 STM32 的 NVIC 是 Cortex-M3 的 NVIC 的一个子集

CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256级的可编程中断设置。但 STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分。STM32 有 84 个中断,包括 16 个内核中断和 68 个可屏蔽中断,具有 16 级可编程的中断优先级。

而我们常用的就是这 68 个可屏蔽中断,但是 STM32 的 68 个可屏蔽中断,在 STM32F103 系列上面,又只有 60 个(在 107 系列才有 68 个)。

中断分组

STM32 的中断分组:STM32 将中断分为 5 个组,组 0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。
image.png
通过这个表,我们就可以清楚的看到组 0~4 对应的配置关系,例如组设置为 3,那么此时所有的 60 个中断,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级,低 1 位是响应优先级。每个中断,你可以设置抢占优先级为 0~7,响应优先级为 1 或 0。

中断优先级

抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。这里需要注意两点:

  • 第一,如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
  • 第二,高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。而抢占优先级相同的中断,高优先级的响应优先级不可以打断低响应优先级的中断。

    HAL 库实现中断分组设置以及中断优先级管理

    NVIC 中断管理相关函数主要在 HAL 库关键文件 stm32f1xx_hal_cortex.c 中定义。

    中断优先级分组设置函数 HAL_NVIC_SetPriorityGrouping

    函数原型及实现:

    ```c void HAL_NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { / Check the parameters / assert_param(IS_NVIC_PRIORITY_GROUP(PriorityGroup));

    / Set the PRIGROUP[10:8] bits according to the PriorityGroup parameter value / NVIC_SetPriorityGrouping(PriorityGroup); }

  1. 其中 `IS_NVIC_PRIORITY_GROUP` 定义的参数范围如下,和上文的中断分组对应:
  2. ```c
  3. #define IS_NVIC_PRIORITY_GROUP(GROUP)
  4. (((GROUP) == NVIC_PriorityGroup_0) ||\
  5. ((GROUP) == NVIC_PriorityGroup_1) || \
  6. ((GROUP) == NVIC_PriorityGroup_2) || \
  7. ((GROUP) == NVIC_PriorityGroup_3) || \
  8. ((GROUP) == NVIC_PriorityGroup_4))

这个函数是通过调用函数 NVIC_SetPriorityGrouping 来进行中断优先级分组设置,其中NVIC_SetPriorityGrouping 是在文件 core_cm3.h 头文件中定义的,这个函数主要作用是通过设置 SCB->AIRCR 寄存器的值来设置中断优先级分组。

函数使用

这个函数的作用是对中断的优先级进行分组,这个函数在系统中只需要被调用一次,一旦分组确定就最好不要更改,否则容易造成程序分组混乱。

此函数在 HAL 库初始化函数 HAL_Init 中被调用HAL_Init 函数非常重要,其作用主要是对中断优先级分组,FLASH 以及硬件层进行初始化,在系统主函数 main 开头部分,都会首先调用 HAL_Init 函数进行一些初始化操作。
所以,我们要进行中断优先级分组设置,只需要修改 HAL_Init 函数内部 HAL_NVIC_SetPriorityGrouping 的参数即可。

中断优先级设置相关函数

官方 HAL 库文件 stm32f1xx_hal_cortex.c 中定义了三个单个中断优先级设置、使能相关函数如下:
void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
void HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
void HAL_NVIC_DisableIRQ(IRQn_Type IRQn);

第一个函数 HAL_NVIC_SetPriority 是用来设置单个中断的抢占优先级和响应优先级的值。
第二个函数 HAL_NVIC_EnableIRQ 是用来使能某个中断通道。
第三个函数 HAL_NVIC_DisableIRQ 是用来清除某个中断使能的,也就是中断失能。

中断优先级设置函数 HAL_NVIC_SetPriority 函数原型及使用

void HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority)

  • 第 1 个参数 IRQn 是中断号,由 IRQn_Type 定义的枚举类型;
  • 第 2 个参数 PreemptPriority 用于设置抢占优先级, 范围 0 - 15;
  • 第 3 个参数 SubPriority 用于设置子优先级,范围 0 -15。

此函数的调用比较简单,比如设置 ADC 中断的抢占优先级是 1, 子优先级是 0, 那么此函数的设置就是:
HAL_NVIC_SetPriority(ADC_IRQn, 1, 0)

中断使能函数 HAL_NVIC_EnableIRQ 函数原型及使用

void HAL_NVIC_EnableIRQ(IRQn_Type IRQn)

  • 参数 IRQn 是中断号,由 IRQn_Type 定义的枚举类型

注意事项:
调用此函数前要先调用优先级分组设置函数 NVIC_PriorityGroupConfig,而这个函数会在 HAL_Init 里面被调用。 函数基本都会在 main 函数里面优先被调用,所以保证 HAL_Init 优先被调用即可。

使用举例:
此函数的调用比较省事,比如使能 ADC 中断, 那么此函数的设置就是:
HAL_NVIC_EnableIRQ (ADC_IRQn)。

中断优先级设置的步骤

  1. 系统运行开始的时候在 HAL_Init 中设置中断分组。确定组号,也就是确定抢占优先级和响应优先级的分配位数。设置函数为 HAL_NVIC_PriorityGroupConfig
  2. 设置单个中断的中断优先级别和使能相应中断通道,使用到的函数函数主要为函数 HAL_NVIC_SetPriority 和函数 HAL_NVIC_EnableIRQ

    MDK 中查看实际优先级配置

    语雀内容