Stm32中的NVIC

总结stm32异常与中断

谁在管理这stm32中的中断

NVIC(内嵌中断向量管理器),它管理着stm32中的所有中断。控制着整个芯片的功能,其实内核上的一个外设。但是各个芯片厂商在设计自己的芯片的时候会对Cortex内核中的NVIC进行裁减,所以一般来说我们使用的芯片中的NVIC都是源内核NVIC的子集。

我们知道,在 Stm32 中还有一个中断管理外设——EXIT,所以它们之间的关系是:

NVIC 管理着 Stm32 中所有的中断,包括系统异常和外设中断;
EXIT 管理着 Stm32 芯片的外部中断,包括 GPIO、USB、I2C、SPI、USART、ADC、TIM、WWDG、RTC、SDIO、LCD、CAN、EXTI、DMA、TSC、IWDG、FSMC、HASH、CRYP、PWR、DAC、CEC 等。

所以 EXIT 实际上是 NVIC 的下属。

NVIC 寄存器介绍

优先级的定义

下图是stm32f10x的优先级分配寄存器的位。stm32使用了4bit进行分配优先级。

stm32中的系统异常和中断都定义在文件stm32f10x.hIRQn_Type枚举中。

其中系统异常有8个(如果把Reset和HardFault也算上的话就是10个)

中断数是60个。

stm32中中断的特点:

stm32中的中断和异常没有区别,只是系统的中断称之为异常,外设的中断称之为中断

中断悬起:当一个中断无法被马上反应的话,称之为中断悬起

数字越小优先级越高

有 3 个系统异常:复位,NMI 以及硬 fault,它们有固定的优先级,并且它们的优先级号是负数,从而高于所有其它异常。所有其它异常的优先级则都是可编程的,但不能被编程为负数。

优先级的分类:在stm32中,优先级包含了主优先级子优先级
stm32中判断一个中断的优先级的顺序是:主优先级->子优先级->硬件中断编号(这个就是在stmf10x.h文件中的IRQn_Type的编号。


优先级的分组

抢占优先级:抢占优先级相同时,才会比较响应优先级
响应优先级:抢占优先级相等时,才会比较相应优先级
自然优先级:中断向量表中的优先级,如果抢占优先级和相应优先级都是一样的话就看自然优先级。

优先级数字越小表示这个优先级越高

抢占优先级就是主优先级

在stm32中,优先级一共有5个优先级分组:

优先级分组

也就是说如果配置为分组0,则相当于没有主优先级。只有子优先级,优先级的所有位都表示为子优先级。

例如,当优先级寄存器中的前四bit是,1011时,设置优先级分组为2。所以这时,主优先级为0b10,而子优先级为0b11。高位表示的是主优先级,低位表示的是子优先级。

注意:如果优先级分组为0,则抢占优先级就不存在,优先级就全部由子优先级控制,所以如果优先级为4,则子优先级就不存在。

由优先级的分组可以知道,如果分组号越小,则主优先级数目越少。实际上就是降低了其优先级。

内核外设的优先级如何和片上外设的优先级比较

由于我们知道看,内核外设是没有主优先级和子优先级的区别的。那么如何比较内核外设和片上外设的优先级:

在STM32F103中, 只有位7:4这高四位有效,低四位没有用到,所以内核外设的中断优先级可编程为:0~15,只有16个可编程优先级,数值越小,优先级越高。

不同于片上外设具有的抢占优先级和子优先级,内核外设只有一个优先级,但是优先级的分组是对于所有的外设都有效的。例如:

SysTick设置优先级组为组1,所以优先级中,有一位用来表示抢占优先级,有三位用来表示子优先级。

抢占优先级和子优先级对于中断的关系

  1. 高抢占优先级是可以打断正在进行的低抢占优先级中断的。

  2. 抢占优先级相同的中断,高子优先级不可以打断低子优先级的中断。

  3. 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个子优先级高,就先执行。

  4. 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;


NVIC的控制函数

库函数中的NVIC控制函数

中断编程

中断编程的步骤

  1. 开启外设的中断
  2. 定义NVIC_InitTypeDef结构体,初始化NVIC配置
  3. 编写中断服务函数

NVIC_InitTypeDef结构体解释

NVIC_InitTypeDef该结构体定义在misc.h文件中,具体的成员如下:

1
2
3
4
5
6
typedef struct {
uint8_t NVIC_IRQChannel; // 中断源
uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级
uint8_t NVIC_IRQChannelSubPriority; // 子优先级
FunctionalState NVIC_IRQChannelCmd; // 中断使能或者失能
} NVIC_InitTypeDef;

NVIC_IRQChannel:配置中断源,这个源就是在stmf10x.h文件中的IRQn_Type

NVIC_IRQChannelPreemptionPriority:具体由优先级的分组决定

NVIC_IRQChannelSubPriority:具体由优先级的分组决定

NVIC_IRQChannelCmd:开启中断或者是关闭中断

编写中断服务函数

在启动文件startup_stm32f10x_hd.s中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。 实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在stm32f10x_it.c这个库文件中。

关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口, 直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。

HAL库函数的使用

需要注意,如果使用 STM32CubeMX 或者 HAL库函数,则优先级的分组已经分好了,并且在整个工程中只需要一个json文件配置优先级即可。也就是说我们不需要动这个东西。

特别注意:使用 NVIC 的时候是不需要设置时钟的。

参考资料

《CM3权威指南(中文本)》

野火stm32教程


Stm32中的NVIC
https://ysc2.github.io/ysc2.github.io/2023/11/17/Stm32中的NVIC/
作者
Ysc
发布于
2023年11月17日
许可协议