一、理论
NVIC(Nested Vectored Interrupt Controller,嵌套向量中断控制器)——它是ARM Cortex-M7内核(STM32H743基于该内核)的核心外设,负责管理所有中断的使能、优先级、响应顺序、嵌套规则,是STM32处理外部/内部事件(比如按键中断、定时器中断、串口中断)的“总调度室”。
一、NVIC到底是干啥的?
STM32H743运行过程中,会遇到很多“紧急事件”:比如按键按下需要立即响应、串口收到数据要及时处理、定时器到点要执行任务……这些事件就是「中断」。
如果没有NVIC,CPU只能“轮询”(不停检查有没有事件发生),效率极低;
有了NVIC:
- 能给不同中断分配“优先级”(比如串口中断比按键中断更重要,先处理);
- 能控制哪些中断生效(比如暂时屏蔽按键中断,只响应串口);
- 能处理“中断嵌套”(比如正在处理按键中断时,来了更高优先级的串口中断,先暂停按键、处理串口,再回来继续);
- 能精准跳转到对应中断的处理函数(向量表功能)。
NVIC是STM32H743的“中断管家”,所有中断的“生杀大权”都归它管。
二、STM32H743 NVIC的核心特性
1. 中断的“分类”:内核异常 + 外设中断
NVIC管理的中断分两类,其中外设中断实际开发用得最多:
- 内核异常:比如硬故障、内存访问错误(出问题一般是代码bug);
- 外设中断:STM32H743的片上外设产生的中断(比如GPIO中断、USART、TIM、DMA等),这是编程的重点。
STM32H743的外设中断数量很多(超过100个),每个中断都有唯一的「中断号」(比如EXTI0中断号是6,USART1中断号是37),可以在ST官方的《STM32H743xx参考手册》里查“中断向量表”找到。
2. 优先级的“两层规则”:抢占优先级 + 子优先级
这是NVIC最核心的点:STM32H743的NVIC支持「优先级分组」,把中断优先级拆成两部分:
- 抢占优先级(Preemption Priority):决定“能不能打断别人”
数值越小,优先级越高!比如抢占优先级0 > 抢占优先级1。
举例:正在处理抢占优先级1的按键中断,来了抢占优先级0的串口中断,NVIC会暂停按键中断,先处理串口(这就是「中断嵌套」)。 - 子优先级(Subpriority):决定“同时到了该先处理谁”
数值越小,优先级越高!但仅适用于「抢占优先级相同」的多个中断。
举例:串口和定时器的抢占优先级都是0,子优先级串口是0、定时器是1,若两个中断同时触发,先处理串口。
关键:优先级分组
Cortex-M7内核把优先级的“位数”分给抢占和子优先级,STM32H743的NVIC支持8位优先级配置(0~255),但实际开发中常用HAL库的「优先级分组宏」来划分,共5种分组方式(推荐用分组2):
优先级分组宏(HAL库) | 抢占优先级位数 | 子优先级位数 | 抢占优先级范围 | 子优先级范围 | 新手适配场景 |
NVIC_PRIORITYGROUP_0 | 0 级抢占优先级 | 0-15 级子优先级 | 仅0 | 0~15 | 无嵌套,仅排序 |
NVIC_PRIORITYGROUP_1 | 0-1 级抢占优先级 | 0-7 级子优先级 | 0~1 | 0~7 | 简单嵌套 |
NVIC_PRIORITYGROUP_2 | 0-3 级抢占优先级 | 0-3 级子优先级 | 0~3 | 0~3 | 中等嵌套 |
NVIC_PRIORITYGROUP_3 | 0-7 级抢占优先级 | 0-1 级子优先级 | 0~7 | 0~1 | 较复杂嵌套 |
NVIC_PRIORITYGROUP_4 | 0-15 级抢占优先级 | 0 级子优先级 | 0~15 | 0 | 全部可嵌套 |
👉整个工程只能设置一次优先级分组,一般在main函数开头、初始化外设前配置。
3. 中断的“使能/失能”
NVIC对每个中断都有“开关”:
- 使能(Enable):打开中断开关,对应中断发生时CPU会响应;
- 失能(Disable):关闭中断开关,就算中断发生,CPU也不理会;
- 挂起(Pending):中断“待处理”(比如中断触发了,但CPU暂时没空处理,NVIC会记录这个状态);
- 清除挂起:处理完中断后,需要清除“待处理”标记,否则NVIC会反复触发该中断。
三、NVIC的编程步骤(基于HAL库)
以「EXTI按键中断」为例,讲解STM32H743中NVIC的配置流程(新手优先用HAL库,避开寄存器直接操作):
步骤1:配置优先级分组(全局唯一)
在main函数开头配置,默认一般用分组4:
#include "stm32h7xx_hal.h" int main(void) { // 1. 初始化HAL库 HAL_Init(); // 2. 配置NVIC优先级分组(核心!整个工程只配一次) HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); // 后续:初始化时钟、GPIO、中断... }步骤2:配置具体中断的优先级 + 使能中断
比如配置EXTI0(PA0,WK_UP按键)中断:
// 配置EXTI0中断的NVIC参数 void EXTI0_IRQConfig(void) { // 第一步:设置中断优先级 // 参数1:中断号(EXTI0_IRQn是STM32H743定义的EXTI0中断号,查手册可知是6) // 参数2:抢占优先级 // 参数3:子优先级 HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0); // 第二步:使能该中断(打开开关) HAL_NVIC_EnableIRQ(EXTI0_IRQn); // 若要暂时关闭中断,用:HAL_NVIC_DisableIRQ(EXTI0_IRQn); }步骤3:编写中断处理函数(中断服务程序,ISR)
NVIC知道“哪个中断该执行哪个函数”,是因为「中断向量表」——STM32启动时会把所有中断的处理函数地址存在向量表中,触发中断时NVIC会直接跳转到对应函数。
对于EXTI0中断,函数名是固定的(必须和启动文件中的向量表一致):
// EXTI0中断处理函数(函数名不能改!启动文件里定义的) void EXTI0_IRQHandler(void) { // 第一步:HAL库固定操作,清除中断挂起标志(避免反复触发) if (HAL_GPIO_EXTI_GetITStatus(GPIO_PIN_0) != RESET) { // 第二步:执行你要做的事情(比如按键处理) rt_kprintf("WK_UP按键中断触发!\r\n"); // 第三步:清除中断标志位(必须!否则NVIC会认为中断还没处理完) HAL_GPIO_EXTI_ClearITPendingBit(GPIO_PIN_0); } }四、NVIC常见错误
- 优先级分组配置多次:整个工程只能调用一次
HAL_NVIC_SetPriorityGrouping,多次调用会导致所有中断优先级混乱; - 优先级数值搞反:新手容易以为“数值越大优先级越高”,实际是「数值越小,优先级越高」;
- 忘记清除中断标志:处理完中断后必须清标志,否则NVIC会反复触发同一中断(表现为程序卡死、疯狂执行中断函数);
- 中断号写错:比如把USART1_IRQn写成USART2_IRQn,中断永远不响应(查手册或STM32CubeMX确认中断号);
- 抢占优先级相同却想嵌套:只有抢占优先级不同时,才会触发中断嵌套,子优先级不影响嵌套。
五、用STM32CubeMX配置NVIC
不用死记寄存器/函数,STM32CubeMX可以图形化配置NVIC,自动生成代码:
- 打开CubeMX,选择STM32H743芯片;
- 点击「Pinout & Configuration」→ 「NVIC」;
- 先在「Priority Grouping」选择分组(比如Group4);
- 找到要配置的中断(比如EXTI0),勾选「Enabled」,然后设置「Preemption Priority」和「Sub Priority」;
- 生成代码,CubeMX会自动帮你调用
HAL_NVIC_SetPriority和HAL_NVIC_EnableIRQ,只需编写中断处理函数即可。
六、理论总结
- NVIC的核心作用:管理所有中断的使能、优先级、嵌套,是中断的“总管家”;
- 优先级规则:分抢占(决定能否嵌套)和子优先级(决定同抢占优先级的执行顺序),数值越小优先级越高;
- 编程三步法:配置优先级分组→设置中断优先级+使能→编写中断处理函数+清标志。
二、实际代码
原理图:
因为rtthread系统封装了中断及GPIO的驱动,所以使用此代码需要先将部分驱动屏蔽:
将drv_gpio.c全部屏蔽
屏蔽:
注意,因为这里是分析原理,做demo而已,实际项目新手需谨慎,如果不是精通rtthread,莫要轻易修改驱动层代码
然后,使用hal库的方式:
/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2019-03-05 whj4674672 first version */ #include <rtthread.h> #include <rtdevice.h> #include <board.h> //////////////////////////////////////////////GPIO定义///////////////////////////////////////////////////////////////// /* 引脚 定义 */ #define KEY0_GPIO_PORT GPIOH #define KEY0_GPIO_PIN GPIO_PIN_3 #define KEY0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0) /* PH口时钟使能 */ #define KEY1_GPIO_PORT GPIOH #define KEY1_GPIO_PIN GPIO_PIN_2 #define KEY1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0) /* PH口时钟使能 */ #define KEY2_GPIO_PORT GPIOC #define KEY2_GPIO_PIN GPIO_PIN_13 #define KEY2_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0) /* PC口时钟使能 */ #define WKUP_GPIO_PORT GPIOA #define WKUP_GPIO_PIN GPIO_PIN_0 #define WKUP_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */ #define KEY0 HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN) /* 读取KEY0引脚 */ #define KEY1 HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) /* 读取KEY1引脚 */ #define KEY2 HAL_GPIO_ReadPin(KEY2_GPIO_PORT, KEY2_GPIO_PIN) /* 读取KEY2引脚 */ #define WK_UP HAL_GPIO_ReadPin(WKUP_GPIO_PORT, WKUP_GPIO_PIN) /* 读取WKUP引脚 */ #define KEY0_PRES 1 /* KEY0按下 */ #define KEY1_PRES 2 /* KEY1按下 */ #define KEY2_PRES 3 /* KEY2按下 */ #define WKUP_PRES 4 /* KEY_UP按下(即WK_UP) */ //////////////////////////////////////////////引脚、中断编号和中断服务函数 定义//////////////////////////////////////////// #define KEY0_INT_GPIO_PORT GPIOH #define KEY0_INT_GPIO_PIN GPIO_PIN_3 #define KEY0_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0) /* PH口时钟使能 */ #define KEY0_INT_IRQn EXTI3_IRQn #define KEY0_INT_IRQHandler EXTI3_IRQHandler #define KEY1_INT_GPIO_PORT GPIOH #define KEY1_INT_GPIO_PIN GPIO_PIN_2 #define KEY1_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOH_CLK_ENABLE(); }while(0) /* PH口时钟使能 */ #define KEY1_INT_IRQn EXTI2_IRQn #define KEY1_INT_IRQHandler EXTI2_IRQHandler #define KEY2_INT_GPIO_PORT GPIOC #define KEY2_INT_GPIO_PIN GPIO_PIN_13 #define KEY2_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOC_CLK_ENABLE(); }while(0) /* PC口时钟使能 */ #define KEY2_INT_IRQn EXTI15_10_IRQn #define KEY2_INT_IRQHandler EXTI15_10_IRQHandler #define WKUP_INT_GPIO_PORT GPIOA #define WKUP_INT_GPIO_PIN GPIO_PIN_0 #define WKUP_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PA口时钟使能 */ #define WKUP_INT_IRQn EXTI0_IRQn #define WKUP_INT_IRQHandler EXTI0_IRQHandler /** * @brief 按键初始化函数 * @param 无 * @retval 无 */ void key_gpio_init(void) { GPIO_InitTypeDef gpio_init_struct; /* GPIO配置参数存储变量 */ KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */ KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */ KEY2_GPIO_CLK_ENABLE(); /* KEY2时钟使能 */ WKUP_GPIO_CLK_ENABLE(); /* WKUP时钟使能 */ gpio_init_struct.Pin = KEY0_GPIO_PIN; /* KEY0引脚 */ gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */ gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct); /* KEY0引脚模式设置,上拉输入 */ gpio_init_struct.Pin = KEY1_GPIO_PIN; /* KEY1引脚 */ gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */ gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct); /* KEY1引脚模式设置,上拉输入 */ gpio_init_struct.Pin = KEY2_GPIO_PIN; /* KEY2引脚 */ gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */ gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(KEY2_GPIO_PORT, &gpio_init_struct); /* KEY2引脚模式设置,上拉输入 */ gpio_init_struct.Pin = WKUP_GPIO_PIN; /* WKUP引脚 */ gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */ gpio_init_struct.Pull = GPIO_PULLDOWN; /* 下拉 */ gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */ HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP引脚模式设置,下拉输入 */ } /** * @brief 外部中断初始化程序 * @param 无 * @retval 无 */ void exit_init(void) { GPIO_InitTypeDef gpio_init_struct; gpio_init_struct.Pin = KEY0_INT_GPIO_PIN; gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下降沿触发 */ gpio_init_struct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY0_INT_GPIO_PORT, &gpio_init_struct); /* KEY0配置为下降沿触发中断 */ gpio_init_struct.Pin = KEY1_INT_GPIO_PIN; gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下降沿触发 */ gpio_init_struct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY1_INT_GPIO_PORT, &gpio_init_struct); /* KEY1配置为下降沿触发中断 */ gpio_init_struct.Pin = KEY2_INT_GPIO_PIN; gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下降沿触发 */ gpio_init_struct.Pull = GPIO_PULLUP; HAL_GPIO_Init(KEY2_INT_GPIO_PORT, &gpio_init_struct); /* KEY2配置为下降沿触发中断 */ gpio_init_struct.Pin = WKUP_INT_GPIO_PIN; gpio_init_struct.Mode = GPIO_MODE_IT_RISING; /* 上升沿触发 */ gpio_init_struct.Pull = GPIO_PULLDOWN; HAL_GPIO_Init(WKUP_GPIO_PORT, &gpio_init_struct); /* WKUP配置为上升沿触发中断 */ HAL_NVIC_SetPriority(KEY0_INT_IRQn, 0, 2); /* 抢占0,子优先级2 */ HAL_NVIC_EnableIRQ(KEY0_INT_IRQn); /* 使能中断线3 */ HAL_NVIC_SetPriority(KEY1_INT_IRQn, 1, 2); /* 抢占1,子优先级2 */ HAL_NVIC_EnableIRQ(KEY1_INT_IRQn); /* 使能中断线2 */ HAL_NVIC_SetPriority(KEY2_INT_IRQn, 2, 2); /* 抢占2,子优先级2 */ HAL_NVIC_EnableIRQ(KEY2_INT_IRQn); /* 使能中断线13 */ HAL_NVIC_SetPriority(WKUP_INT_IRQn, 3, 2); /* 抢占3,子优先级2 */ HAL_NVIC_EnableIRQ(WKUP_INT_IRQn); /* 使能中断线0 */ } /** * @brief KEY0 外部中断服务程序 * @param 无 * @retval 无 */ void KEY0_INT_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN); /* 调用中断处理函数,清除中断标志位 */ __HAL_GPIO_EXTI_CLEAR_IT(KEY0_INT_GPIO_PIN); /* 退出时再清一次中断,避免按键抖动误触发 */ } /** * @brief KEY1 外部中断服务程序 * @param 无 * @retval 无 */ void KEY1_INT_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(KEY1_INT_GPIO_PIN); /* 调用中断处理函数,清除中断标志位 */ __HAL_GPIO_EXTI_CLEAR_IT(KEY1_INT_GPIO_PIN); /* 退出时再清一次中断,避免按键抖动误触发 */ } /** * @brief KEY2 外部中断服务程序 * @param 无 * @retval 无 */ void KEY2_INT_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(KEY2_INT_GPIO_PIN); /* 调用中断处理函数,清除中断标志位 */ __HAL_GPIO_EXTI_CLEAR_IT(KEY2_INT_GPIO_PIN); /* 退出时再清一次中断,避免按键抖动误触发 */ } /** * @brief WK_UP 外部中断服务程序 * @param 无 * @retval 无 */ void WKUP_INT_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler( WKUP_INT_GPIO_PIN); /* 调用中断处理公用函数,清除KEY_UP所在中断线的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */ __HAL_GPIO_EXTI_CLEAR_IT(WKUP_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */ } /** * @brief 中断服务程序中需要做的事情 在HAL库中所有的外部中断服务函数都会调用此函数 * @param GPIO_Pin:中断引脚号 * @retval 无 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { switch (GPIO_Pin) { case KEY0_INT_GPIO_PIN: if (KEY0 == 0) { rt_kprintf("\n key0 key pressed\r\n"); } break; case KEY1_INT_GPIO_PIN: if (KEY1 == 0) { rt_kprintf("\n key1 key pressed\r\n"); } break; case KEY2_INT_GPIO_PIN: if (KEY2 == 0) { rt_kprintf("\n key2 key pressed\r\n"); } break; case WKUP_INT_GPIO_PIN: if (WK_UP == 1) { rt_kprintf("\n up key pressed\r\n"); } break; default : break; } } int main(void) { key_gpio_init(); exit_init(); while(1) { rt_thread_mdelay(100); } }通过代码,会发现编码过程就是三部曲:配置优先级分组→设置中断优先级+使能→编写中断处理函数+清标志。配置正确,最关心的就是中断优先级,中断函数中处理是否恰当。中断函数中处理方法论,见:
https://carrylee.blog.csdn.net/article/details/155187711
三、现象
这里只是为了说明中断的使用方法,未对按键进行消抖处理
四、仿真
这里介绍一个技巧,cubemux中配置了一般比较靠谱,但是如果想再次确认中断优先级配置是否正确可以放真时:
如图所示,4个按键的中断优先级:
五、rtthread中使用中断
这里附上,如果使用rtthread封装好的驱动,使用中断的方法:
/* * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2019-03-05 whj4674672 first version */ #include <rtthread.h> #include <rtdevice.h> #include <board.h> #define KEY0_PIN GET_PIN(H, 3) #define KEY1_PIN GET_PIN(H, 2) #define KEY2_PIN GET_PIN(C, 13) #define WKUP_PIN GET_PIN(A, 0) /* 中断回调函数 */ void key0_on(void *args) { rt_kprintf("\n key0 key pressed\r\n"); } /* 中断回调函数 */ void key1_on(void *args) { rt_kprintf("\n key1 key pressed\r\n"); } /* 中断回调函数 */ void key2_on(void *args) { rt_kprintf("\n key2 key pressed\r\n"); } /* 中断回调函数 */ void keyup_on(void *args) { rt_kprintf("\n up key pressed\r\n"); } static void pin_beep_sample(void) { /* 按键0引脚为输入模式 */ rt_pin_mode(KEY0_PIN, PIN_MODE_INPUT_PULLUP); /* 绑定中断,下降沿模式,回调函数名为beep_on */ rt_pin_attach_irq(KEY0_PIN, PIN_IRQ_MODE_FALLING, key0_on, RT_NULL); /* 使能中断 */ rt_pin_irq_enable(KEY0_PIN, PIN_IRQ_ENABLE); /* 按键0引脚为输入模式 */ rt_pin_mode(KEY1_PIN, PIN_MODE_INPUT_PULLUP); /* 绑定中断,下降沿模式,回调函数名为beep_on */ rt_pin_attach_irq(KEY1_PIN, PIN_IRQ_MODE_FALLING, key1_on, RT_NULL); /* 使能中断 */ rt_pin_irq_enable(KEY1_PIN, PIN_IRQ_ENABLE); /* 按键0引脚为输入模式 */ rt_pin_mode(KEY2_PIN, PIN_MODE_INPUT_PULLUP); /* 绑定中断,下降沿模式,回调函数名为beep_on */ rt_pin_attach_irq(KEY2_PIN, PIN_IRQ_MODE_FALLING, key2_on, RT_NULL); /* 使能中断 */ rt_pin_irq_enable(KEY2_PIN, PIN_IRQ_ENABLE); /* 按键0引脚为输入模式 */ rt_pin_mode(WKUP_PIN, PIN_MODE_INPUT_PULLUP); /* 绑定中断,下降沿模式,回调函数名为beep_on */ rt_pin_attach_irq(WKUP_PIN, PIN_IRQ_MODE_RISING, keyup_on, RT_NULL); /* 使能中断 */ rt_pin_irq_enable(WKUP_PIN, PIN_IRQ_ENABLE); } int main(void) { pin_beep_sample(); while(1) { rt_thread_mdelay(100); } }相较于直接调用 HAL 库编写 GPIO 中断、按键检测等功能(需编写大量底层初始化、中断服务函数、状态管理代码),基于 RT-Thread 的开发方式更为简洁精炼 —— 通过封装好的 PIN 设备接口(如rt_pin_attach_irq/rt_pin_irq_enable),仅需几行代码即可完成中断配置、事件响应,无需编写冗余的硬件操作代码,大幅简化开发流程。
仿真可以看这4个的优先级设置:
可以看到中断优先级都是5,分析源码:
是因为RTthread 底层驱动将优先级固定设置成了5,如果有时这个设置不合理,童鞋可以修改该代码。
六、寄存器
NVIC的中断设置:
截自:《Cortex M7 Generic User Guide》
SCB中AIRCR:
NVIC寄存器:
截自:《Cortex M7 Technical Reference Manual》
__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 的基础使用方法并不复杂,但它堪称单片机系统的 “命脉”:操作系统的调度依赖中断驱动,而各类外设中断优先级的设置,更是系统稳定运行的关键 —— 不合理的优先级配置可能引发中断嵌套混乱、响应时序异常,最终导致系统崩溃。