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