NVIC(Nested Vectored Interrupt Controller) ,嵌套向量中断控制器,是ARM Cortex-M系列微控制器内核中集成的一个关键组件,用于管理中断和异常。
在芯片配置中启动外部中断
-
左键芯片引脚,在功能中选择GPIO_EXTIxx(以PB12和GPIO_EXTI12为例):
-
在左侧选择System Core展开后选择SYS,在其中的GPIO选项可以调整GPIO口的具体信息:
GPIO mode:触发中断的模式,例如External Interrupt Mode with Falling edge trigger detection指的是下降沿触发,即由高电平变为低电平时触发中断;
-
在System Core的NVIC中启用EXTI line[15<10>10>] interrupts;
-
配置好其他内容后,生成代码。
编辑代码启用中断
目的:LED_Red规律闪烁,按下KEY1实现切换LED_Green亮灭。
先在main函数的while(1)中写入以下程序,实现红灯规律闪烁:
HAL_GPIO_WritePin(LED_Red_GPIO_Port, LED_Red_Pin, GPIO_PIN_SET);HAL_Delay(500);HAL_GPIO_WritePin(LED_Red_GPIO_Port, LED_Red_Pin, GPIO_PIN_RESET);HAL_Delay(500);中断函数位于\Core\Src中stm32f1xx.c文件的void EXTI15_10_IRQHandler(void):
void EXTI15_10_IRQHandler(void){ /* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(KEY1_Pin); /* USER CODE BEGIN EXTI15_10_IRQn 1 */
/* USER CODE END EXTI15_10_IRQn 1 */}由于我们设置的GPIO mode是External Interrupt Mode with Falling edge trigger detection,在KEY1被按下时,会从高电平变为低电平,从而触发中断函数EXTI15_10_IRQHandler()。
在/* USER CODE BEGIN EXTI15_10_IRQn 0 */和/* USER CODE END EXTI15_10_IRQn 0 */之间写入以下代码:
HAL_GPIO_TogglePin(LED_Green_GPIO_Port, LED_Green_Pin);下载程序后,KEY1基本可以实现绿灯的切换,但是多次尝试切换后,还是会发现有时不灵敏,这是因为硬件消抖还是不足以完全清除抖动,因此我们需要为其添加软件消抖:
HAL_Delay(10);if(HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)==GPIO_PIN_RESET){ HAL_GPIO_TogglePin(LED_Green_GPIO_Port, LED_Green_Pin);}下载程序后按下KEY1,我们却发现程序一动不动,甚至LED_Red都不再闪烁了。
这是因为发生了多个中断的优先级问题,HAL_Delay()函数依赖于Time base:System tick timer的中断,但是此中断的优先级低于EXTI line[15:10] interrupts,因而不能在EXTI15_10_IRQHandler中执行。
解决方法如下:
- 回到CubeMX中,在左侧的System Core中找到NVIC,修改各中断的优先级,我们让
Time base:System tick timer的优先级高于EXTI line[15:10] interrupts
注:数字越小,代表其优先级越高
- 完成修改后,重新生成代码,下载程序,便可以发现目的已经达成:LED_Red规律闪烁,按下KEY1切换LED_Green亮灭。
CAUTION不建议在中断中使用
HAL_Delay()的阻塞式延时,对于按键中断,在之后会有更加巧妙的方法来实现。
深入中断
参考视频
外部中断/事件控制器:
去除与中断无关的部分,简化后,可以变化为以下的电路:

边沿检测电路
上升/下降沿触发选择寄存器:决定是检测到上升沿向后传递高电平信号,还是检测到下降沿向后传递高电平信号,或者二者都。
配置GPIO mode,实际上就是在配置上升沿触发选择寄存器和下降沿触发选择寄存器。
请求挂起寄存器
接受到边沿检测电路发送来的高电平时,其对应位会被置1。
中断屏蔽寄存器
配置GPIO引脚为GPIO_EXTIxx时,实际上就是在配置中断屏蔽寄存器的对应位置1。
NVIC(嵌套向量中断控制器)
通过与门和请求挂起寄存器,中断屏蔽寄存器相连,当且仅当二者都置1时,才向后发送高电平信号。
掌管中断向量表,即由名称指向中断处理函数。
| 名称 | 处理向量 |
|---|---|
| EXTI0 | EXTI0_IRQHandler |
| EXTI1 | EXTI1_IRQHandler |
| EXTI2 | EXTI2_IRQHandler |
| EXTI3 | EXTI3_IRQHandler |
| EXTI4 | EXTI4_IRQHandler |
| EXTI9_5 | EXTI9_5_IRQHandler |
| EXTI15_10 | EXTI15_10_IRQHandler |
例如,配置为`EXTI12`且**下降沿触发**的`KEY1`被按下,会依次发生以下过程:
- 生成代码阶段:
- 上升沿触发选择寄存器的第12位被置0;
- 下降沿触发选择寄存器的第12位被置1;
- 中断屏蔽寄存器的第12位被置1。
- 经过输入驱动器处理的PB12的电平信号经由输入线进入到边沿检测电路;
- 边沿检测电路检测到高电平转化为低电平;
- 边沿检测电路向后发送高电平信号;
- 请求挂起寄存器接受到高电平信号,将第12位置1;
- 中断屏蔽寄存器和请求挂起寄存器的第12位均被置1,通过与门向后输出高电平信号;
- NVIC找到中断向量
EXTI15_10,执行函数EXTI15_10_IRQHandler; - 执行函数
HAL_GPIO_EXTI_IRQHandler(),将请求挂起寄存器的第12位置0,终止中断执行,防止中断函数EXTI15_10_IRQHandler被反复执行。
中断优先级
分为抢占优先级(Preemption Priority) 和 响应优先级(Sub Priority)。
在STM32中,每个中断向量都有4个二进制位来存储中断优先级信息,我们可以分别为抢占优先级和响应优先级分配不同的位数,默认地,会为抢占优先级分配4位,而为响应优先级分配0位,此时每个中断向量的抢占优先级可设置为0-15,而响应优先级只能是0。不过响应优先级的实际作用不大,一般保持默认设置为抢占优先级分配4位即可。
两个中断同时发生时
- 优先比较抢占优先级;
- 若抢占优先级相同,则再比较响应优先级;
- 若响应优先级也相同,则按照中断向量表中顺序决定执行顺序。
在其一中断执行中,接受到另一中断的信号
仅比较抢占优先级:
- 若后者中断B抢占优先级更高,则会中断当前执行的中断A,优先执行中断B,待中断B完成后,继续完成中断A。
- 若后者中断B抢占优先级更低,则当前的中断A会继续执行,执行完中断A之后,再执行中断B。
在刚才的项目中,设置Time base:System tick timer的优先级高于EXTI line[15:10] interrupts,便是为了系统计时的中断能够在外部中断执行过程中抢占执行,从而为HAL_Delay()函数提供时间基准。
总结
相关函数
void EXTIxx_IRQHandler(void)位于Src\stm32f1xx_it.c中,触发外部中断时会调用此函数。
…好像没啥要总结的
部分信息可能已经过时





