6.1 NVIC简介
6.1.1 什么是中断
举个例子,中断就相当于你现在要去烧开水,在你烧水的过程中,你突然要去吃饭。这个过程就叫做中断,其实说白了中断就是做某件事中停一下去做 其他的事情。
6.1.2 嵌套向量中断控制器
( Nested Vectored Interrupt Controller )
NVIC和M4内核可以说是紧密相连的。不仅仅支持向量中断,而且对自动保存和处理器状态恢复,优先级等都有很大的管理,这里推荐你们如果有兴趣的话可以看一下,清华大学出版社出版的《ARM Cortex-M3与RM Cortex-M4权威指南》。当然了呀。这点知道就行了,反正又不是上大学,考试啥的,主要是代码,待会给你们好好屡屡。下面来介绍一下:
1)、
CM4内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。
2)、
STM32F4并没有使用CM4内核的全部中断定义,而是只用了它的一部分。-STM32F42xx/STM32F43xx则总共有96个中断
3)、
STM32F42xx/STM32F43xx的96个中断里面,包括10个内核中断和86个可屏蔽中断,具有16级可编程的中断优先级,而我们常用的就是这86个可屏蔽中断。
6.1.2 中断类型
STM32F4系列中系统异常有 10 个和91个外部中断,具有可编程的中断优先级有16个。除了个别异常的优先级被定死外,其它异常的优先级(用4位表示中断的优先级)都是可编程的。有关具体的系统异常和外部中断可在标准库文件 stm32f4xx.h 这个头文件看到,在 IRQn_Type 这个结构体里面包含了 这个系列全部的异常声明。大家可以去看看。
1-10个异常中断
从上面这个图中可以看到,Reset、NMI、HardFault异常的优先级是固定不变的,但是剩余的优先级都是可以更改的。
有一点大家需要知道的是,STM32中断优先级只使用了M4内核的高四位,低四位是保留的这点大家要清楚。
2、外设可屏蔽中断
这个图具体的大家可以看一下数据手册。
6.1.3 NVIC中断管理方法
1、中断优先级分组
STM32系列的单片机有优先级管理这个概念,咋说呢,举个例子来说吧,比如从1到10,数字越大代表的优先级越低,那么加入我的数字小,你的数字大,就说明有吃的喝的先给我,至于你等着,等我吃饱喝足在赏赐给你点,如果中间我不愿意让你吃了,再给你抢回来(谁让我级别比你高呢,其实就和平时的阶级一个概念)(可能不太恰当,但就是这个意思吧)。这就是优先级。
其实吧,STM32又加了一个,一个是抢占优先级、一个是响应优先级。抢占优先级救赎主优先级,响应优先级就是次优先级也可以叫做子优先级。都是先执行抢占优先级在执行响应优先级。就是我上面说的那个意思。
抢占优先级的概念等同于51单片机中的中断。假设有两中断先后触发,已经在执行的中断先占优先级如果没有后触发的中断先占优先级更高,就会先处理先占优先级高的中断。也就是说又有较高的先占优先级的中断可以打断先占优先级较低的中断。这是实现中断嵌套的基础。
响应(子)优先级只在同一抢占优先级的中断同时触发时起作用,先占优先级相同,则优先执行响应(子)优先级较高的中断。响应(子)优先级不会造成中断嵌套。如果中断的两个优先级都一致,则优先执行位于中断向量表中位置较高的中断(也就是标号越小,优先级越高)。
如下图:优先级:
分组配置是在寄存器SCB_AIRCR(Application interrupt and reset control register:应用中断和复位控制寄存器)中配置:
2、中断优先级管理方法
这个在上面已经说过了这里进行了一个总结:
1)抢占优先级较高的中断是可以打断正在进行的抢占优先级较低的中断;
2)抢占优先级相同的中断,响应优先级高的不可以打断响应优先级低的中断;
3)抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行;
4)如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行。
5)如果两个中断的抢占优先级和响应优先级都是一样的话,且同时请求,则根据中断表中的排位顺序决定。
所以就可以得到下面这个图:
在这里在举个例子吧。
在这里特别说明一点:
6.2 常用库函数
1、中断优先级分组:
这里操作的是寄存器:SCB_ AIRCR[10:8]位段
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
具体内部函数是这样子的:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup){ assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup)); SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;}
NVIC_PriorityGroup : 组用的屏蔽字。定义如下:#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 抢占优先级的位 4 响应优先级位 */#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 抢占优先级的位 3 响应优先级 */#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 抢占优先级的位 2 响应优先级 */#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 抢占优先级的位 1 响应优先级 */#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 抢占优先级的位 0 响应优先级 */例如:中断优先级寄存器定义优先级高3位,抢占优先级占1位,响应优先级占3位。NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1)这时候中断优先级可以是0或者1,响应优先级可以是0~7;
2、中断优先级设置
中断优先级设置以及使能函数用于配置中断向量表中的一个抢占优先级和响应优先级。
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
这是一NVIC个结构体指针的初始化,具体的寄存器定义和初始化定义如下:
typedef struct{ __IO uint32_t ISER[8]; uint32_t RESERVED0[24]; __IO uint32_t ICER[8]; uint32_t RSERVED1[24]; __IO uint32_t ISPR[8]; uint32_t RESERVED2[24]; __IO uint32_t ICPR[8]; uint32_t RESERVED3[24]; __IO uint32_t IABR[8]; uint32_t RESERVED4[56]; __IO uint8_t IP[240]; uint32_t RESERVED5[644]; __O uint32_t STIR; } NVIC_Type;
涉及到的寄存器:这里看看就行,咱就是来操作库函数的,所以知道就行
__IO uint8_t IP[240]; //中断优先级控制的寄存器组__IO uint32_t ISER[8]; //中断使能寄存器组__IO uint32_t ICER[8]; //中断失能寄存器组__IO uint32_t ISPR[8]; //中断挂起寄存器组__IO uint32_t ICPR[8]; //中断解挂寄存器组__IO uint32_t IABR[8]; //中断激活标志位寄存器组typedef struct{ uint8_t NVIC_IRQChannel; //设置中断通道 uint8_t NVIC_IRQChannelPreemptionPriority;//设置抢占优先级 uint8_t NVIC_IRQChannelSubPriority; //设置响应优先级 FunctionalState NVIC_IRQChannelCmd; //使能/禁止} NVIC_InitTypeDef;
举个例子:
比如我设置串口一的中断,抢占优先级为1,响应优先级是2,那就应该这样设置:
NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;// 37 串口1中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;// 抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//响应优先级为2NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器
6.3 举个小栗子
1-中断优先级设置步骤
1)系统运行后先设置中断优先级分组。调用函数:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
整个系统执行过程中,只设置一次中断分组。这个要知道。
2)针对每个中断,设置对应的抢占优先级和响应优先级:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
3)如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。用的不多!
一般情况下片上外设中断管理只需要第1步和第2步!
2-小栗子
1-编写NVIC中断初始化程序实现如下功能:
1、设置中断优先级组为1组
2、设置外部中断1的抢占优先级为0,响应优先级为2
3、设置定时器1的溢出更新中断的抢占优先级为1,响应优先级为4
4、设置USART1的抢占优先级为1,响应优先级为5
并说明当同时出现以上3个中断请求时,中断服务程序执行的顺序?