## 一、EXTI 原理与中断路径 STM32 的 EXTI(External Interrupt/Event Controller)负责管理外部引脚的中断/事件触发。每个 GPIO 引脚均可配置为 EXTI 输入,但同一时刻只能有一个引脚映射到同一中断线(例如 PA0 和 PB0 共享 EXTI0)。 - **触发方式**:上升沿、下降沿、双边沿,或软件触发(用于测试)。 - **中断路径**:引脚电平变化 → EXTI 边沿检测 → 挂起寄存器置位 → NVIC 响应 → 执行中断服务函数(ISR)。 - **事件路径**:不产生中断,仅触发其他外设(如 ADC 转换),适合低功耗场景。 关键寄存器: - `EXTI_IMR`:中断屏蔽寄存器(使能中断线) - `EXTI_RTSR` / `EXTI_FTSR`:上升/下降沿触发选择 - `EXTI_PR`:挂起寄存器,写 1 清除中断标志 ## 二、硬件连接与低功耗模式选择 实战场景:使用按键(连接到 PA0)作为外部中断源,在睡眠模式下按下按键唤醒 MCU,并翻转 LED(PB5)。 - **按键电路**:PA0 接按键到 GND,内部上拉,按下时产生下降沿。 - **LED 电路**:PB5 接 LED 串联电阻到 VCC,低电平点亮。 STM32 低功耗模式对比: - **睡眠模式**:仅关闭 CPU 时钟,外设仍运行,唤醒延迟最短(微秒级)。 - **停止模式**:关闭所有时钟,仅保留 SRAM 和寄存器内容,唤醒延迟较长(毫秒级),但功耗更低。 本例采用睡眠模式,兼顾快速响应与功耗优化。 ## 三、HAL 库配置步骤(以 STM32F4 为例) ### 1. 初始化 GPIO 和 EXTI ```c void EXTI_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; // 使能 GPIOA 和 GPIOB 时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); // 配置 PA0 为外部中断输入,上拉,下降沿触发 GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 配置 PB5 为推挽输出 GPIO_InitStruct.Pin = GPIO_PIN_5; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); // 使能 EXTI0 中断并设置优先级 HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); } ``` ### 2. 中断服务函数编写 HAL 库将中断处理分为两个阶段: - `EXTI0_IRQHandler`:硬件中断入口,调用 HAL 库通用处理函数。 - `HAL_GPIO_EXTI_Callback`:用户回调函数,需在外部重写。 ```c // 中断入口函数 void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); } // 用户回调函数 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) { HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5); // 翻转 LED } } ``` ### 3. 进入睡眠模式并唤醒 在 main 函数中,初始化后进入睡眠模式。使用 `__WFI()`(Wait For Interrupt)指令,当 EXTI 中断发生时,CPU 被唤醒并执行中断服务,然后返回主循环。 ```c int main(void) { HAL_Init(); EXTI_Init(); while (1) { // 进入睡眠模式,等待外部中断唤醒 __WFI(); // 唤醒后执行其他任务(例如延时消抖) HAL_Delay(50); // 简单消抖 } } ``` ## 四、深入理解:中断标志清除与消抖 - **标志清除**:HAL 库在 `HAL_GPIO_EXTI_IRQHandler` 中自动清除挂起位,无需手动操作。但若使用寄存器编程,必须在 ISR 中写 1 到 `EXTI_PR` 对应位。 - **按键消抖**:机械按键存在抖动,通常产生多次边沿。简单方法是在回调中延时 10-20ms 后再次检测电平;更优方案是使用定时器或状态机。 ## 五、进阶:停止模式唤醒与 RTC 结合 若需更低功耗,可进入停止模式(`HAL_PWR_EnterSTOPMode`)。此时需配置 EXTI 为事件模式(`GPIO_MODE_EVT_FALLING`),并确保唤醒后重新配置系统时钟(因为停止模式会关闭 HSI/HSE)。 ```c // 进入停止模式 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 唤醒后重新初始化时钟 SystemClock_Config(); ``` 注意:停止模式下,EXTI 事件可唤醒,但中断需在唤醒后重新使能 NVIC。 ## 六、注意事项与调试技巧 - **中断优先级**:若系统有其他中断,需合理设置抢占优先级,避免死锁。 - **引脚冲突**:同一 EXTI 线只能映射一个引脚,若多个引脚共用,需在回调中判断 `GPIO_Pin`。 - **调试建议**:在 ISR 中设置断点可能影响时序,建议使用串口打印或 LED 指示。 - **功耗测量**:使用万用表或功耗分析仪,确保睡眠模式电流符合预期(通常 µA 级)。 ## 七、总结 本文通过一个按键唤醒 LED 的实例,完整展示了 STM32 EXTI 的配置、中断处理及低功耗唤醒流程。掌握 EXTI 不仅有助于实现快速响应,更是低功耗设计的关键。建议读者进一步尝试停止模式、事件模式,并结合 RTC 定时唤醒,构建更复杂的低功耗系统。