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 来定义的。
通过这个表,我们就可以清楚的看到组 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); }
其中 `IS_NVIC_PRIORITY_GROUP` 定义的参数范围如下,和上文的中断分组对应:
```c
#define IS_NVIC_PRIORITY_GROUP(GROUP)
(((GROUP) == NVIC_PriorityGroup_0) ||\
((GROUP) == NVIC_PriorityGroup_1) || \
((GROUP) == NVIC_PriorityGroup_2) || \
((GROUP) == NVIC_PriorityGroup_3) || \
((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)。
中断优先级设置的步骤
- 系统运行开始的时候在
HAL_Init
中设置中断分组。确定组号,也就是确定抢占优先级和响应优先级的分配位数。设置函数为HAL_NVIC_PriorityGroupConfig
。 - 设置单个中断的中断优先级别和使能相应中断通道,使用到的函数函数主要为函数
HAL_NVIC_SetPriority
和函数HAL_NVIC_EnableIRQ
。MDK 中查看实际优先级配置
语雀内容