嵌套向量中断控制器(NVIC)详解

引言

在平时的工作中,我们经常会遇到这样的情况:有人来找你做一些事情,而且这些事情要比手头的工作更重要。那现在就需要停下手中的工作,先去完成突然到来的这部分工作。这样的情况也类似于图论中的关键路径中,突然在当前事件之前插入了一个新的事件,那我们不得不先去完成之前的那个任务,才能继续完成后面的工作。

上述情况是非常常见的,那在STM32中,我们如果遇到了比当前任务更紧急的事情需要去处理,我们应该怎么办呢?这个时候,我们需要通过中断来完成这样的任务。

在现实生活中,突然出现的任务一定有轻重缓急之分。如果同时出现许多的临时任务,我们一定会首先评估他们谁更需要先被处理掉,然后再依次进行处理。在STM32中,我们也需要这样的功能,多个中断来临的时候,我们需要首先判断这个中断紧不紧急,然后再考虑处理的先后顺序。那我们是由什么来控制这样的顺序的呢?毫无疑问,这就是通过嵌套向量中断控制器(NVIC)完成的。




NVIC简介

CM3内核支持256个中断,包括16个内核中断和250个外部中断,同时具有256级的可编程中断设置。而STM32只使用了一部分。STM32拥有84个中断,包括16个内核中断和68个可屏蔽中断(STM32F107系列才使用了68个可屏蔽中断),还拥有16级可编程的中断优先级。STM32F103系列只使用了60个可屏蔽中断。

STM32中控制中断优先级的寄存器组是IP[240]。是由240个8bit的寄存器组成的。而STM32F103只用了前六十个(0 ~ 59)。在STM32F103系列单片机中,这8bit也没有全部使用,而是只使用了其高四位(4 ~ 7位)。下图是中断分组的分配情况。

优先级分组 抢占优先级 响应优先级 高四位描述
0 0级 0 ~ 15级 0bit用于抢占优先级
4bit用于响应优先级
1 0 ~ 1级 0 ~ 7级 1bit用于抢占优先级
3bit用于响应优先级
2 0 ~ 3级 0 ~ 3级 2bit用于抢占优先级
2bit用于响应优先级
3 0 ~ 7级 0 ~ 1级 3bit用于抢占优先级
1bit用于响应优先级
4 0 ~ 15级 0级 4bit用于抢占优先级
0bit用于响应优先级

响应优先级又称为子优先级。
数值越小,优先级越高。

这里需要关注的是,抢占优先级的级别高于子优先级。高抢占优先级的中断可以在低抢占优先级中断执行的过程中被响应,反之则不行。这就是所谓的中断嵌套。换句话说呢,就是高抢占优先级中断可以抢占低抢占优先级中断的执行。当多个中断同时到来,而且他们的抢占优先级相同,则先进入子优先级更高的中断。

但是,当抢占优先级相同的情况下,高子优先级中断不能打断低子优先级中断,也就是不能实现中断嵌套。这种情况下,高子优先级中断必须先等待低子优先级中断执行完成,在进行自己的中断。




软件部分

配置某个中断优先级的步骤如下:

  • 配置中断优先级分组
  • 配置某一中断的优先级

配置中断优先级分组

这一步可以通过函数 NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup) 来完成。关于优先级分组的内容上文已经做过了讲解。这里提供一个使用范例:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

配置某一个中断的优先级

这一步我们需要用到一个函数 NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) 。里面的结构体的成员变量如下:

typedef struct
{
	uint8_t NVIC_IRQChannel;			//中断名称
	uint8_t NVIC_IRQChannelPreemptionPriority;	//抢占优先级
	uint8_t NVIC_IRQChannelSubPriority;		//响应优先级
	FunctionalState NVIC_IRQChannelCmd;		//中断使能

}NVIC_InitTypeDef;

因此,配置某一中断优先级的步骤如下:

  • 声明上述结构体
  • 配置结构体成员变量的值
  • 利用 NVIC_Init 函数初始化

这里给出一个范例:

NVIC_InitTypeDef NVIC_InitStructure;	//声明结构体

NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;                  //声明中断名称
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;      //设置抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x03;             //设置响应优先级为3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;		          //使能中断

NVIC_Init(&NVIC_InitStructure);			//对上述配置进行初始化

至此,我们就完成了对某一中断优先级的配置,再根据不同的中断配置不同的触发条件等信息,就可以完成在特定条件下的中断。这里不再展开叙述。


关于NVIC和中断嵌套的更多的神奇理解可以参考下面这篇文章的后半部分:STM32 NVIC

我是这耀眼的瞬间,是划过天边的刹那火焰。
原文地址:https://www.cnblogs.com/Rane/p/13603323.html