目录
1.什么是 NVIC?
NVIC是一种中断控制器,主要用于处理 ARM Cortex-M 微控制器的中断管理。NVIC 负责处理中断优先级、中断向量表以及中断使能和禁止等任务。
中断向量表: NVIC 维护了中断向量表,其中包含了中断服务程序的入口地址。当中断发生时,CPU会根据中断号(或中断向量)在中断向量表中查找相应的中断服务程序的入口地址。
中断优先级: NVIC 允许为每个中断配置优先级。这些优先级决定了中断在系统中的响应顺序。Cortex-M架构中,中断优先级通常是基于位宽的,较低位的值表示较高的优先级。
嵌套中断: NVIC 支持嵌套中断,即在处理某个中断时,如果发生更高优先级的中断请求,系统可以中断当前中断服务程序,处理更高优先级的中断,然后返回继续处理低优先级的中断。这种特性对于实时系统和复杂任务处理非常有用。
中断使能和禁止: NVIC 允许对每个中断进行使能和禁止。这使得可以在运行时动态地配置系统中的中断。
中断状态: NVIC 提供了一些寄存器用于查询和管理中断状态,例如检查某个中断是否处于挂起状态。
中断控制寄存器: NVIC 通过一系列寄存器(如 NVIC_ISER、NVIC_ICER、NVIC_ISPR、NVIC_ICPR)提供中断的使能、禁止、挂起和清除等功能。
2.NVIC寄存器
typedef struct
{
__IOM uint32_t ISER[8U]; /* 中断使能寄存器 */
uint32_t RESERVED0[24U];
__IOM uint32_t ICER[8U]; /* 中断清除使能寄存器 */
uint32_t RSERVED1[24U];
__IOM uint32_t ISPR[8U]; /* 中断使能挂起寄存器 */
uint32_t RESERVED2[24U];
__IOM uint32_t ICPR[8U]; /* 中断解挂寄存器 */
uint32_t RESERVED3[24U];
__IOM uint32_t IABR[8U]; /* 中断有效位寄存器 */
uint32_t RESERVED4[56U];
__IOM uint8_t IP[240U]; /* 中断优先级寄存器(8Bit 位宽) */
uint32_t RESERVED5[644U];
__OM uint32_t STIR; /* 中断触发中断寄存器 */
} NVIC_Type;
STM32F407 的中断在这些寄存器的控制下有序的执行的。只有了解这些中断寄存器,才能
方便的使用 STM32F407 的中断。下面重点介绍这几个寄存器:
1.ISER[8](Interrupt Set Enable Register):中断使能寄存器
2.ICER[8](Interrupt Clear Enable Registers):中断除能寄存器
3.ISPR[8](Interrupt Set Pending Registers):中断使能挂起控制寄存器,通过置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是⽆效的。
4.ICPR[8](Interrupt Clear Pending Registers):中断解除挂起控制寄存器
5.IABR[8](Interrupt Active Bit Registers):是⼀个中断激活标志位寄存器组。对应位所代表的中断和 ISER ⼀样,如果为 1,则表示该位所对应的中断正在被执行。这是⼀个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。
6.IP [240](Interrupt Priority Registers):中断优先级控制寄存器组。由240个8bit的寄存器组成,每个可屏蔽中断占用8bit。但我们只用IP[81] - IP[0]这82个。并且,每个中断通道占用的8bit并没有全部使用,而是只用了高4位。
3.中断优先级
STM32 中的中断优先级可以分为:抢占式优先级和响应优先级,每个中断源都需要被指定这两种优先级。抢占式优先级和响应优先级的区别:
抢占优先级:抢占优先级高的中断可以打断正在执行的抢占优先级低的中断。
响应优先级:抢占优先级相同,响应优先级高的中断不能打断响应优先级低的中断。
还有一种情况就是当两个或者多个中断的抢占式优先级和响应优先级相同时,那么就遵循
自然优先级,看中断向量表的中断排序,数值越小,优先级越高。
在 NVIC 中由寄存器 NVIC_IPR0-NVIC_IPR59 共 60 个寄存器控制中断优先级,每个寄存器的每 8 位又分为一组,可以分 4 组,所以就有了 240 组宽度为 8bit 的中断优先级控制寄存器,
原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。但是实际上 M3 /M4
/M7 芯片为了精简设计,只使用了高四位[7:4],低四位取零,这样以至于最多只有 16 级中断嵌
套,即 2^4=16。
抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。 抢占优先级和响应优先级相同的,按中断号(中断向量表)先后执行。
4.NVIC的配置
设置中断分组
配置某一个中断的优先级
函数名称:void 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_IRQChannel
: 表示要配置的中断通道(或中断号)。对于 ARM Cortex-M 微控制器,中断通道的编号是硬件定义的,代表不同的中断源。例如,USART1 的中断通道号是USART1_IRQn
。
NVIC_IRQChannelPreemptionPriority
: 表示中断的抢占优先级。中断的抢占优先级决定了在同一时间发生多个中断时,哪个中断可以立即被处理,而哪些需要等待。这是一个 0 到 N-1(N 是抢占优先级位数)的值,值越小表示优先级越高。
NVIC_IRQChannelSubPriority
: 表示中断的子优先级。在同一抢占优先级组内,子优先级决定了不同中断的响应顺序。这是一个 0 到 N-1(N 是子优先级位数)的值,值越小表示优先级越高。
NVIC_IRQChannelCmd
: 表示中断通道的使能状态。可以为ENABLE
或DISABLE
,分别表示使能或禁止相应的中断通道。
5.什么是EXTI
EXTI
是外部中断(External Interrupt)控制器的缩写,用于处理与外部事件(通常是硬件引脚的状态变化)相关的中断。在 STM32 微控制器中,EXTI
模块与 GPIO
模块一起使用,以便在特定事件(如边沿触发、电平触发等)发生时触发相应的中断。
初始化函数:void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);EXTI外设的时钟默认是开启的
typedef struct
{
uint32_t EXTI_Line;
EXTIMode_TypeDef EXTI_Mode;
EXTITrigger_TypeDef EXTI_Trigger;
FunctionalState EXTI_LineCmd;
}EXTI_InitTypeDef;
EXTI_Line
: 指定要启用或禁用的外部中断线。这个参数可以是EXTI_Lines
枚举类型中的任何组合值,表示一个或多个中断线。例如,EXTI_Line0
表示外部中断线0。EXTI_Mode
: 指定外部中断线的工作模式。这个参数可以是EXTIMode_TypeDef
枚举类型的值,表示中断线的触发模式。例如,EXTI_Mode_Interrupt
表示中断模式。EXTI_Trigger
: 指定外部中断线的触发方式,即触发信号的边沿。这个参数可以是EXTITrigger_TypeDef
枚举类型的值,表示中断线触发的方式,如上升沿、下降沿等。EXTI_LineCmd
: 指定所选外部中断线的新状态。这个参数可以是FunctionalState
枚举类型的值,表示中断线的使能或禁止状态,可以是ENABLE
或DISABLE
。
6.EXTI和NVIC之间的关系
EXTI
模块配置中断线:EXTI
负责配置外部中断线,将外部事件(通常是引脚状态的变化)连接到中断系统。它定义了中断线的触发条件、使能状态等。EXTI
允许你设置哪些引脚上的事件会触发中断,以及中断是由上升沿、下降沿还是上升和下降沿都触发。配置好EXTI
后,当与之相关联的外部事件发生时,EXTI
会产生中断请求。
NVIC
确定中断优先级: 一旦EXTI
产生中断请求,中断请求会传递给NVIC
。NVIC
负责管理所有中断的优先级。在STM32中,中断的优先级是按组管理的,NVIC
允许配置每个中断的优先级。这就是为什么在使用外部中断时,需要通过NVIC
设置中断的优先级。中断处理函数: 在
NVIC
配置好中断优先级后,当外部中断被触发时,NVIC
会根据中断优先级的设定决定是否打断当前正在执行的程序。如果中断被接受,NVIC
会跳转到相应的中断处理函数,该函数是由用户编写的,通常用于处理与中断相关的任务。在中断处理函数中,需要清除相应的中断标志,通常是通过EXTI_ClearITPendingBit
来完成。
7.SYSCFG 的介绍
SYSCFG
是STM32微控制器中的一个系统配置寄存器,用于配置和控制一些系统级别的功能和外设。主要的作用包括配置外部中断线、使能内部的传感器和控制外设的引脚映射。
1.中断线映射: SYSCFG
允许将外部中断线映射到不同的中断通道。在使用外部中断时,需要配置 SYSCFG
以选择相应的中断通道。
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
//上述例子将 GPIOA 的引脚0与外部中断线0相关联。
2.定时器触发映射: 对于某些定时器,SYSCFG
允许配置触发源。例如,可以将外部信号映射到定时器的触发输入。
TIM1_ETR_Config(TIM1_ETRSource_TI1, TIM1_ExtTRGPSC_DIV1, TIM1_ExtTRGPolarity_NonInverted, 0x00);
//上述例子配置了TIM1定时器的触发源。
3.内部传感器使能: SYSCFG
允许使能内部的温度传感器和电压检测器。
SYSCFG_VBATMonitoringCmd(ENABLE);
//上述例子使能了电池电压检测器。
4.BOOT模式配置: SYSCFG
允许配置启动时的引脚状态,以确定系统启动时的模式。
SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM);
//上述例子将内部SRAM映射到系统的起始地址。
第一步:时钟的使能
- 1)使能GPIO时钟
- 2)使能SYSCFG时钟
- ** EXTI和 NVIC的时钟默认是打开的
第二步:配置GPIO
第三步:配置SYSCFG
- 配置函数:SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
- 功能:选择中断引脚
第四步:初始化EXTI
- 初始化函数:EXTI_Init();
第五步:配置NVIC
- 1) 设置优先级:NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//整个⼯程只设置⼀次
- 2) 初始化:NVIC_Init();
第六步:编写中断函数
#include "stm32f4xx.h" // Device header
void EXTI3_IRQHandler(void){
//判断是否有中断
if(EXTI_GetITStatus(EXTI_Line3)==SET)
{
GPIO_SetBits(GPIOF,GPIO_Pin_9);
GPIO_SetBits(GPIOF,GPIO_Pin_10);
EXTI_ClearITPendingBit(EXTI_Line3);
}
}
void EXTI4_IRQHandler(void){
//判断是否有中断
if(EXTI_GetITStatus(EXTI_Line4)==SET)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_9);
GPIO_ResetBits(GPIOF,GPIO_Pin_10);
EXTI_ClearITPendingBit(EXTI_Line4);
}
}
int main(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);
GPIO_InitTypeDef GPIO_InitTypeDefStruct;
GPIO_InitTypeDef GPIO_InitTypeDefStruct1;
//初始化按键
GPIO_InitTypeDefStruct.GPIO_Mode = GPIO_Mode_IN;
//GPIO_InitTypeDefStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitTypeDefStruct.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_3;
GPIO_InitTypeDefStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitTypeDefStruct.GPIO_Speed = GPIO_High_Speed;
GPIO_Init(GPIOE,&GPIO_InitTypeDefStruct);
//初始化LED
GPIO_InitTypeDefStruct1.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitTypeDefStruct1.GPIO_OType = GPIO_OType_PP;
GPIO_InitTypeDefStruct1.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitTypeDefStruct1.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitTypeDefStruct1.GPIO_Speed = GPIO_High_Speed;
GPIO_Init(GPIOF,&GPIO_InitTypeDefStruct1);
//配置
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
//
EXTI_InitTypeDef EXIT_STRUCT;
EXIT_STRUCT.EXTI_Line=EXTI_Line4;
EXIT_STRUCT.EXTI_Mode=EXTI_Mode_Interrupt;
EXIT_STRUCT.EXTI_Trigger=EXTI_Trigger_Falling;
EXIT_STRUCT.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXIT_STRUCT);
EXTI_InitTypeDef EXIT_STRUCT1;
EXIT_STRUCT1.EXTI_Line=EXTI_Line3;
EXIT_STRUCT1.EXTI_Mode=EXTI_Mode_Interrupt;
EXIT_STRUCT1.EXTI_Trigger=EXTI_Trigger_Falling;
EXIT_STRUCT1.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXIT_STRUCT1);
//设置中断分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef nvic_struct;
nvic_struct.NVIC_IRQChannel=EXTI4_IRQn;
nvic_struct.NVIC_IRQChannelPreemptionPriority=0;
nvic_struct.NVIC_IRQChannelSubPriority=1;
nvic_struct.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&nvic_struct);
NVIC_InitTypeDef nvic_struct1;
nvic_struct1.NVIC_IRQChannel=EXTI3_IRQn;
nvic_struct1.NVIC_IRQChannelPreemptionPriority=1;
nvic_struct1.NVIC_IRQChannelSubPriority=2;
nvic_struct1.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&nvic_struct1);
while(1)
{
EXTI3_IRQHandler();
EXTI4_IRQHandler();
}
}