这一章在干嘛?

外部中断实验:NVIC 优先级分组、EXTI 配置、中断服务函数、按键中断触发 LED。理解后所有外设中断(串口/TIM/ADC)都有思路。

16.1 NVIC 和 EXTI 简介

16.2 硬件设计

16.3 程序设计

16.4 下载验证

16.1 NVIC 和 EXTI 简介

16.1.1 NVIC 嵌套向量中断控制器

Cortex-M4 内核自带 NVIC,支持 256 个中断。F407 用了 82 个:

  • 10 个系统异常(Reset / NMI / HardFault / …);
  • 82 个可屏蔽中断(EXTI / TIM / USART / …)。

NVIC 中断优先级:

  • 抢占优先级(pre-emption):高优先级能打断低优先级(嵌套);
  • 响应优先级(sub):同时到达时谁先响应;
  • STM32 分 5 组(0~4),实际用到的「抢占优先级位数」不同:
优先级分组抢占优先级位数子优先级位数常用范围
00 bit4 bit0
11 bit3 bit0~1
22 bit2 bit0~3
33 bit1 bit0~7
44 bit0 bit0~15

原书图 16.1.1.1:NVIC 中断优先级分组原书图 16.1.1.1:NVIC 中断优先级分组

推荐分组 2(2 bit 抢占 + 2 bit 子),既支持嵌套又不复杂。

16.1.2 EXTI 外部中断

EXTI(External Interrupt)负责把 IO 电平变化转换为 NVIC 中断。

线路来源
EXTI0~EXTI15GPIOx_Pin0~15
EXTI16PVD 输出
EXTI17RTC 闹钟
EXTI18USB OTG_FS 唤醒
EXTI21RTC Tamper / TimeStamp
EXTI22RTC Wakeup

每条 EXTI 只能连接到一个 GPIO 的一个 pin,例如 EXTI0 可绑 PA0/PB0/…/PI0 中任意一个。

16.2 硬件设计

使用 KEY0/KEY1/KEY_UP 接 PE4/PE3/PA0 作外部中断触发。LED0/LED1 接 PF9/PF10 作中断响应。

16.3 程序设计

16.3.1 编程步骤

  1. 开启 GPIOE / GPIOA / SYSCFG 时钟;
  2. 配置 GPIO 为输入模式;
  3. HAL_NVIC_EnableIRQ(EXTI4_IRQn); HAL_NVIC_SetPriority(EXTI4_IRQn, 2, 0); 设置优先级;
  4. HAL_GPIO_Init 的 Mode 设为 GPIO_MODE_IT_FALLING(下降沿触发);
  5. 中断服务函数 EXTI4_IRQHandler → 调 HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4) → 回调 HAL_GPIO_EXTI_Callback

16.3.2 关键代码

void EXTI4_IRQHandler(void) {
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    delay_ms(20);
    if (GPIO_Pin == GPIO_PIN_4) {    // KEY0
        if (KEY0 == 0) LED0_TOGGLE();
    }
}

原书图 16.3.2.1:外部中断触发流程图原书图 16.3.2.1:外部中断触发流程图

中断里能做的事有限:不要调 HAL_Delay(SysTick 中断可能与 EXTI 中断冲突),用定时器延时;不要做耗时操作,只置标志位让 main 处理。

16.4 下载验证

按 KEY0 → LED0 翻转;KEY_UP → LED1 翻转。CPU 平时可做其他事,按下时才被「唤醒」。

中断标志位模式 最佳实践:中断回调里只 g_key0_pressed = 1; 在 main 里循环检查再处理。