前言
什么是NVIC?即嵌套向量中断控制器(Nested Vectored Interrupt Controller)。STM32的中有一个强大而方便的NVIC,它是属于Cortex内核的器件,不可屏蔽中断 (NMI)和外部中断都由它来处理,SYSTICK不是由 NVIC来控制的。
CM4内核支持256个中断,其中包含了16个内核中断和240个外部中断,并且具有256级的可编程中断设置。
STM32F4并没有使用CM4内核的全部东西,而是只用了它的一部分。STM32F40xx/STM32F41xx总共有92个中断。
STM32F40xx/STM32F41xx的92个中断里面,包括10个内核中断和82个可屏蔽中断,具有16级可编程的中断优先级,而我们常用的就是这82个可屏蔽中断。
中断向量表
STM32有两个优先级的概念--抢占式优先级和响应优先级,有人把响应优先级称作'亚优先级'或'副优先级',每个中断源都需要被指定这两种优先级。
具有高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套,或者说高抢占式优先级的中断可以嵌套在低抢占式优先级的中断中。
当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。如果这两个中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。
Cortex内核具有强大的异常响应系统,它把能够打断当前代码执行流程的事件分为异常(exception)和中断(interrupt),并把它们用一个表管理起来,编号为-3~6的称为内核异常(内核中断),而7以上至88的则称为外部中断,这个表就称为中断向量表。
![](https://i-blog.csdnimg.cn/blog_migrate/022f2d5648d3f3b73ce41c36346efcd0.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4a7b79541e7b1f8c654dfe982373ac26.png)
![](https://i-blog.csdnimg.cn/blog_migrate/136c395f91b1ee92b43461561746e98e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/273d12fc6edc17b4944b2dfdabc413b8.png)
10个内核中断
![](https://i-blog.csdnimg.cn/blog_migrate/724b5653c42f06907be1223f95a1fb68.png)
中断管理方法
首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。
分组配置是在寄存器SCB->AIRCR中配置:
![](https://i-blog.csdnimg.cn/blog_migrate/cac7ced68bdf13cf1597477717cf6285.png)
Preemption Priority(抢占优先级)
抢占优先级高的可抢占低的,插队先执行;
若相同抢占优先级,先发生 先执行;
抢占优先级低的不可抢占低的,排队等待执行;
Sub Priority(子优先级又称响应优先级)
在抢占优先级相同时,响应优先级高的不可以打断的响应优先级低的;
在抢占优先级相同,且同时请求中断时,响应优先级高的先执行。
在抢占优先级相同,且同时请求中断时,响应优先级也相同时,根据异常中断向量表排位顺序决定谁先执行
举例
假定设置中断优先级组为2,然后设置中断3(RTC中断)的抢占优先级为2,响应优先级为1。中断6(外部中断0)的抢占优先级为3,响应优先级为0。中断7(外部中断1)的抢占优先级为2,响应优先级为0。
由于使用中断优先级组2,那么抢占优先级与响应优先级为0b00-0b11(0-3)。数值越接近0说明中断越优先;
中断之间先对抢占优先级进行比较,再进行响应优先级比较
中断3与中断7同为抢占优先级2,中断6的抢占优先级为3。 所以中断3=中断7>中断6
虽然中断3与中断7同为抢占优先级2,但中断7的响应优先级为0,中断3的响应优先级为1。所以中断7>中断3。
所以:中断7>中断3>中断6。
特别注意
一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
中断优先级分组函数
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
入参函数
#define NVIC_PriorityGroup_0 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
4 bits for subpriority */
#define NVIC_PriorityGroup_1 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
3 bits for subpriority */
#define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
2 bits for subpriority */
#define NVIC_PriorityGroup_3 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
1 bits for subpriority */
#define NVIC_PriorityGroup_4 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
0 bits for subpriority */
#define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PriorityGroup_0) || \
((GROUP) == NVIC_PriorityGroup_1) || \
((GROUP) == NVIC_PriorityGroup_2) || \
((GROUP) == NVIC_PriorityGroup_3) || \
((GROUP) == NVIC_PriorityGroup_4))
NVIC_PriorityGroup_0 中断优先级分组0
NVIC_PriorityGroup_1 中断优先级分组1
NVIC_PriorityGroup_2 中断优先级分组2
NVIC_PriorityGroup_3 中断优先级分组3
NVIC_PriorityGroup_4 中断优先级分组4
示例
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
中断设置相关寄存器
__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
{
__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;
如何设置中断优先级
中断优先级控制的寄存器组:IP[240]
全称是:InterruptPriority Registers
240个8位寄存器,每个中断使用一个寄存器来确定优先级。STM32F40x系列一共82个可屏蔽中断,使用IP[81]~IP[0]。
每个IP寄存器的高4位用来设置抢占和响应优先级(根据分组),低4位没有用到。
中断使能寄存器组:ISER[8]
作用:用来使能中断
32位寄存器,每个位控制一个中断的使能。STM32F40x只有82个可屏蔽中断,所以只使用了其中的ISER[0]~ISER[2]。
ISER[0]的bit0~bit31分别对应中断0~31。ISER[1]的bit0~27对应中断32~63;ISER[2]的bit0~17对应中断64~81;
中断失能寄存器组:ICER[8]
作用:用来失能中断
32位寄存器,每个位控制一个中断的失能。STM32F40x只有82个可屏蔽中断,所以只使用了其中的ICER[0]和ICER[1]。
ICER[0]的bit0~bit31分别对应中断0~31。ICER[1]的bit0~27对应中断32~63;ICER[3]的bit0~17对应中断64~82;
配置方法跟ISER一样。
中断失能寄存器组:ICER[8]
作用:用来失能中断
32位寄存器,每个位控制一个中断的失能。STM32F40x只有82个可屏蔽中断,所以只使用了其中的ICER[0]和ICER[1]。
ICER[0]的bit0~bit31分别对应中断0~31。ICER[1]的bit0~27对应中断32~63;ICER[3]的bit0~17对应中断64~82;
配置方法跟ISER一样。
中断挂起控制寄存器组:ISPR[8]
作用:用来挂起中断
中断解挂控制寄存器组:ICPR[8]
作用:用来解挂中断
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn);
static __INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn);
static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
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;
所有中断标志
#if defined (STM32F40_41xxx)
CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */
CAN1_RX0_IRQn = 20, /*!< CAN1 RX0 Interrupt */
CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */
CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */
EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */
TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break interrupt and TIM9 global interrupt */
TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global interrupt */
TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */
TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */
TIM2_IRQn = 28, /*!< TIM2 global Interrupt */
TIM3_IRQn = 29, /*!< TIM3 global Interrupt */
TIM4_IRQn = 30, /*!< TIM4 global Interrupt */
I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */
I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */
I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */
I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */
SPI1_IRQn = 35, /*!< SPI1 global Interrupt */
SPI2_IRQn = 36, /*!< SPI2 global Interrupt */
USART1_IRQn = 37, /*!< USART1 global Interrupt */
USART2_IRQn = 38, /*!< USART2 global Interrupt */
USART3_IRQn = 39, /*!< USART3 global Interrupt */
EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */
RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */
OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS Wakeup through EXTI line interrupt */
TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */
TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */
TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */
TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */
DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */
FSMC_IRQn = 48, /*!< FSMC global Interrupt */
SDIO_IRQn = 49, /*!< SDIO global Interrupt */
TIM5_IRQn = 50, /*!< TIM5 global Interrupt */
SPI3_IRQn = 51, /*!< SPI3 global Interrupt */
UART4_IRQn = 52, /*!< UART4 global Interrupt */
UART5_IRQn = 53, /*!< UART5 global Interrupt */
TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */
TIM7_IRQn = 55, /*!< TIM7 global interrupt */
DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */
DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */
DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */
DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */
DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */
ETH_IRQn = 61, /*!< Ethernet global Interrupt */
ETH_WKUP_IRQn = 62, /*!< Ethernet Wakeup through EXTI line Interrupt */
CAN2_TX_IRQn = 63, /*!< CAN2 TX Interrupt */
CAN2_RX0_IRQn = 64, /*!< CAN2 RX0 Interrupt */
CAN2_RX1_IRQn = 65, /*!< CAN2 RX1 Interrupt */
CAN2_SCE_IRQn = 66, /*!< CAN2 SCE Interrupt */
OTG_FS_IRQn = 67, /*!< USB OTG FS global Interrupt */
DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */
DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */
DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */
USART6_IRQn = 71, /*!< USART6 global interrupt */
I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */
I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */
OTG_HS_EP1_OUT_IRQn = 74, /*!< USB OTG HS End Point 1 Out global interrupt */
OTG_HS_EP1_IN_IRQn = 75, /*!< USB OTG HS End Point 1 In global interrupt */
OTG_HS_WKUP_IRQn = 76, /*!< USB OTG HS Wakeup through EXTI interrupt */
OTG_HS_IRQn = 77, /*!< USB OTG HS global interrupt */
DCMI_IRQn = 78, /*!< DCMI global interrupt */
CRYP_IRQn = 79, /*!< CRYP crypto global interrupt */
HASH_RNG_IRQn = 80, /*!< Hash and Rng global interrupt */
FPU_IRQn = 81 /*!< FPU global interrupt */
#endif /* STM32F40_41xxx */
示例
设置串口1中断,抢占优先级
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口1中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化NVIC寄存器
中断优先级设置步骤
①系统运行后先设置中断优先级分组。调用函数:
voidNVIC_PriorityGroupConfig(uint32_tNVIC_PriorityGroup);
整个系统执行过程中,只设置一次中断分组。
②针对每个中断,设置对应的抢占优先级和响应优先级:
voidNVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
③ 如果需要挂起/解挂,查看中断当前激活状态,分别调用相关函数即可。
EXTI概述
STM32F4的每个IO都可以作为外部中断输入。
STM32F4的中断控制器支持22个外部中断/事件请求:
EXTI线0~15:对应外部IO口的输入中断。
EXTI线16:连接到PVD输出。
EXTI线17:连接到RTC闹钟事件。
EXTI线18:连接到USBOTG FS唤醒事件。
EXTI线19:连接到以太网唤醒事件。
EXTI线20:连接到USBOTG HS(在FS中配置)唤醒事件。
EXTI线21:连接到RTC入侵和时间戳事件。
EXTI线22:连接到RTC唤醒事件。
每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。
![](https://i-blog.csdnimg.cn/blog_migrate/c165151ef80e509e65cfd3421fb797f1.png)
从GPIOx.0一直到GPIOx.15,所有GPIO口引脚均可使用外部中断
GPIOx.0映射到EXTI0
GPIOx.15映射到EXTI15
注意:输入只能多选一,不能同时输入多个信号源。
中断方式
对于每个中断线,我们可以设置相应的触发方式(上升沿触发,下降沿触发,边沿触发)以及使能。
初始化函数
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); //外部中断初始化函数
入口参数
typedef struct
{
u32 EXTI_Line;
EXTIMode_TypeDef EXTI_Mode;
EXTIrigger_TypeDef EXTI_Trigger;
FunctionalState EXTI_LineCmd;
} EXTI_InitTypeDef;
EXTI_Line线选择
EXTI_Line | 描述 | ||||
EXTI_Line0 | 外部中断线0 | EXTI_Line7 | 外部中断线7 | EXTI_Line14 | 外部中断线14 |
EXTI_Line1 | 外部中断线1 | EXTI_Line8 | 外部中断线8 | EXTI_Line15 | 外部中断线15 |
EXTI_Line2 | 外部中断线2 | EXTI_Line9 | 外部中断线9 | EXTI_Line16 | 外部中断线16 |
EXTI_Line3 | 外部中断线3 | EXTI_Line10 | 外部中断线10 | EXTI_Line17 | 外部中断线17 |
EXTI_Line4 | 外部中断线4 | EXTI_Line11 | 外部中断线11 | EXTI_Line18 | 外部中断线18 |
EXTI_Line5 | 外部中断线5 | EXTI_Line12 | 外部中断线12 | ||
EXTI_Line6 | 外部中断线6 | EXTI_Line13 | 外部中断线13 |
EXTI_Mode的选择
EXTI_Mode_Event | 设置EXTI线路为事件请求 |
EXTI_Mode_Interrupt | 设置EXTI线路为中断请求 |
EXTI_Trigger的选择
EXTI_Trigger_Falling | 设置线路下降沿为中断请求 |
EXTI_Trigger_Rising | 设置线路上升沿为中断请求 |
EXTI_Trigger_Rising_Falling | 设置线路上升沿和下降沿为中断请求 |
EXTI_LineCmd的选择
ENABLE | 设置为启用 |
DISENABLE | 设置为不启用 |
例程
void KEY_Init(void)
{
GPIO_InitTypeDef GPIO_KEYTYPE;
EXTI_InitTypeDef EXTI_KEYTYPE;
NVIC_InitTypeDef NVIC_KEYTYPE;
/RCC时钟定义///
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);
//
IO口初始化
GPIO_KEYTYPE.GPIO_Pin = GPIO_Pin_2;
GPIO_KEYTYPE.GPIO_Mode = GPIO_Mode_IN;
GPIO_KEYTYPE.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_KEYTYPE.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Init(GPIOE,&GPIO_KEYTYPE);
//
//IO口外部中断
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource2);
EXTI_KEYTYPE.EXTI_Mode = EXTI_Mode_Interrupt; //模式选择中断
EXTI_KEYTYPE.EXTI_Trigger = EXTI_Trigger_Rising; //模式选择下降沿
EXTI_KEYTYPE.EXTI_Line = EXTI_Line2; //初始化中断线2
EXTI_KEYTYPE.EXTI_LineCmd = ENABLE; //使能
EXTI_Init(&EXTI_KEYTYPE);
/
/中断优先级//
NVIC_KEYTYPE.NVIC_IRQChannel = EXTI2_IRQn; //中断线2
NVIC_KEYTYPE.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_KEYTYPE.NVIC_IRQChannelPreemptionPriority = 0X01;
NVIC_KEYTYPE.NVIC_IRQChannelSubPriority = 0X02;
NVIC_Init(&NVIC_KEYTYPE);
}
中断函数
void EXTI2_IRQHandler(void)
{
delay_ms(5);
if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2)==0)//询问是否中断了
{
LED0 = !LED0;
EXTI_ClearITPendingBit(EXTI_Line2); //清除标注位
}
}