【NVIC】NVIC讲解
1、中断
中断是指程序在正常运行过程中,CPU突然接收到中断请求时,执行下面两个操作:
1)CPU执行“断点现场保护”和“定位中断服务程序地址”两个步骤,即暂停执行当前程序并跳转到中断程序。
2)完成中断服务程序后,CPU会执行“断点现场恢复”和“返回原来主程序”
2、中断类型
STM32F429在内核中搭载了一个中断响应系统 ,支持为数众多的系统中断和外部中断。其中系统异常有10个,外部中断有91个。除了个别中断的优先级被定死外,其他异常的优先级都是可编程。不同MCU的中断可在如下位置查询到:
1)在文件stm32f4xx.h头文件中,在IRQn_Type这个结构体里面包含了F4系列全部的中断类型。
2)在《参考手册》中“中断和事件”章节中也可以查看到。
3、NVIC
NVIC是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合, 是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M4内核里面的NVIC进行裁剪,把不需要的部分去掉, 所以说STM32的NVIC是Cortex-M4的NVIC的一个子集。
3.1、NVIC寄存器简介
NVIC的寄存器如下所示:
在配置中断的时候我们一般只用ISER(中断使能寄存器)、ICER(中断清除寄存器)和IPR(中断优先级寄存器)。
3.2、NVIC中断配置固件库
只要是遵循CMSIS协议的不同Cortex-M处理器,都可以使用下面的函数对NVIC进行配置:
注意:
1、上述库函数中使能、使能、清除中断挂起位的函数只能针对大于0的外部中断,内核外设的中断使能、使能由对应外设自己控制。
2、使用NVIC_SetPriority()对外部中断设置中断优先级时,配置的是NVIC、对系统异常设置中断优先级时,配置的内核外设SCB的SHPRx(x=1.2.3)外设。
SPRH1-SPRH3是一个32位的寄存器,但是只能通过字节访问,每8个字段控制着一个内核外设的中断优先级的配置。在STM32F429中, 只有位7:4这高四位有效,低四位没有用到,所以内核外设的中断优先级可编程为:0~15,只有16个可编程优先级,数值越小,优先级越高。 如果软件优先级配置相同,那就根据他们在中断向量表里面的位置编号来决定优先级大小,编号越小,优先级越高。
如果要修改内核外设的优先级,只需要修改下面三个寄存器对应的某个字段即可。
3.3、中断优先级
当两个中断同时到来的时候,中断优先级的高低影响对应中断服务函数的先后执行顺序。高优先级有两种体现:
1)两个中断同时到来时先响应优先级教高的那个中断请求,再响应优先级低的那个,如下图:
2)当优先级低的中断服务程序在运行时,优先级高的中断也可以打断优先级低的中断服务程序,而优先级低的中断服务程序只有等到优先级高的中断服务程序运行完成后才能继续运行,如下所示:
优先级定义
NVIC_IPRx(Cortex-M4中x=0…59)中断优先级寄存器用来配置外部中断的的优先级,每个中断请求优先级IP[N](N=0…239)占8bit,所以一共支持240中断(F429只用到了一部分),如下所示:
理论上每个中断可配置的优先级为0~255,数值越小,优先级越高,但是大多数CM4芯片都会精简设计,以至于支持的优先级数减少,实际中只用到了bit[7:4]高四位用来表示优先级。bit[3:0]低四位值为0,且不能写入。
用于表达优先级的这4bit,又被分组为抢占优先级+子优先级,在有多个中断同时到来时会有如下表现:
1)先比较抢占优先级,抢占优先级高的比抢占优先级低的优先得到执行。
2)抢占优先级相同,比较子优先级。
3)抢占优先级和子优先级都相同时,就比较硬件中断编号,编号越小,优先级越高。
优先级分组
优先级的分组有内核外设SCB的应用程序中断及复位控制寄存器AIRCR的PRIGROUP[10:8]位决定,具体分组如下所示:
设置优先级分组可调用下面两个函数:
1)调用core_cm4.h文件中的NVIC_SetPriorityGrouping()实现
2)如果有misc.h和misc.c文件,可调用库函数NVIC_PriorityGroupConfig()实现。
4、中断编程
以标准库为例,在配置每个中断时一般按照如下操作进行:
1)使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。
2)初始化NVIC_InitTypeDef结构体,配置中断优先级分组,设置中断源、抢占优先级和子优先级、使能中断请求。
3)编写中断服务函数。在启动文件startup_stm32f429_439xx.s中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。 实际的中断服务函数都需要我们重新编写,中断服务函数我们统一写在stm32f4xx_it.c这个库文件中。
关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口, 直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。
//假设外设已完成配置
//使能串口接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
//配置中断优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//NVIC结构体配置
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
//NVIC初始化
NVIC_Init(&NVIC_InitStructure);
/*
在stm32f4xx_it.c这个库文件编写中断服务函数
*/
void USART1_IRQHandler(void)
{
if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)//监测到接收中断
{
USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除对应标志位
//用户根据需要填写自定义代码
}
}
注意:中断优先级配置函数NVIC_PriorityGroupConfig()在整个程序中只需要设置一次。当设置好了中断优先级分组,其他外设对应的中断向量的中断优先级会基于目前设置分组来解读。所以NVIC_PriorityGroupConfig()适合放在main()中。
作者:halo_frrrank