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.h
的IRQn_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,所以优先级中,有一位用来表示抢占优先级,有三位用来表示子优先级。
抢占优先级和子优先级对于中断的关系
高抢占优先级是可以打断正在进行的低抢占优先级中断的。
抢占优先级相同的中断,高子优先级不可以打断低子优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个子优先级高,就先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
NVIC的控制函数
中断编程
中断编程的步骤
- 开启外设的中断
- 定义
NVIC_InitTypeDef
结构体,初始化NVIC配置 - 编写中断服务函数
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教程