3. 外部中断唤醒源:GPIO边沿触发从低功耗模式唤醒
外部中断唤醒,是STM32L4低功耗设计里最常用、也最灵活的方式。说白了,就是一根引脚上的电平变化——上升沿或者下降沿——把芯片从睡梦中叫醒。
我在项目里用过很多次。有一次做一款门磁传感器,电池供电,要求一节CR2032撑两年。主控大部分时间都在Stop模式下睡觉,只有门被打开时,磁簧开关断开,产生一个下降沿,才把MCU唤醒去发无线报文。嗯,这个场景就是典型的外部中断唤醒。
3.1 支持唤醒的低功耗模式
不是所有低功耗模式都能被外部中断唤醒。你得先搞清楚,你的芯片当前在哪个模式。我列个表,你一看就明白:
| 低功耗模式 | 是否支持GPIO外部中断唤醒 | 唤醒延迟 | 典型应用场景 |
|---|---|---|---|
| Sleep | ✅ 支持 | 极低(~几个CPU周期) | 需要快速响应的间歇任务 |
| Low-power Sleep | ✅ 支持 | 低 | 轻度省电,保留外设时钟 |
| Stop 0 / Stop 1 / Stop 2 | ✅ 支持 | 中等(~几微秒) | 电池供电产品的主流选择 |
| Standby | ❌ 不支持(需用WKUP引脚) | 高(~几十微秒) | 极低功耗,数据可丢的场景 |
核心结论:Stop模式 + 外部中断唤醒,是低功耗产品里性价比最高的组合。既省电,又能保证毫秒级响应。
3.2 配置流程:三步走
配置外部中断唤醒,其实就三步。我习惯按这个顺序来,不容易漏:
- 配置GPIO引脚——设为输入模式,选择上下拉
- 配置EXTI线——选择触发边沿(上升沿/下降沿/双边沿),使能中断
- 配置NVIC——使能对应的EXTI中断通道,设置优先级
你想想看,这三步缺一不可。我曾经在调试一个产品时,GPIO配了,EXTI也配了,但就是唤不醒。查了半天,发现NVIC里忘了使能中断线。这种低级错误,犯过一次就记住了。
3.3 代码实战:PA0下降沿唤醒
下面是一个完整的配置示例。我用PA0引脚,下降沿触发,从Stop 2模式唤醒:
// 第一步:使能GPIO时钟,配置PA0为输入
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING; // 下降沿中断
GPIO_InitStruct.Pull = GPIO_PULLUP; // 外部没上拉时,内部上拉
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// 第二步:配置NVIC,使能EXTI0中断
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
个人习惯:我通常把外部中断的优先级设得比内部定时器低一点。这样既能保证及时响应,又不会被频繁的定时中断抢了CPU。
然后,在进入低功耗之前,调用HAL库的暂停函数:
// 进入Stop 2模式,等待外部中断唤醒
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
唤醒后,系统会自动执行中断服务函数。你需要在中断里做两件事:清除中断标志位,以及处理唤醒后的业务逻辑。
void EXTI0_IRQHandler(void)
{
// 检查并清除中断标志位
if(__HAL_GPIO_EXTI_GET_IT(GPIO_PIN_0) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_PIN_0);
// 唤醒后的处理,比如启动传感器采集
// 注意:中断里不要做耗时操作
Wakeup_Handler();
}
}
注意:从Stop模式唤醒后,系统时钟默认使用MSI。如果你之前用的是HSE或者PLL,需要在唤醒后重新配置时钟。否则外设可能跑在错误的频率下。
3.4 上升沿 vs 下降沿 vs 双边沿
触发边沿的选择,取决于你的外部信号特性。我总结了一个选择指南:
| 触发方式 | 适用场景 | 注意事项 |
|---|---|---|
| 上升沿触发 | 按键按下时电平从低变高 | 注意按键抖动,建议硬件加RC滤波 |
| 下降沿触发 | 按键按下时电平从高变低 | 同上,抖动问题 |
| 双边沿触发 | 电平变化都需要响应 | 容易误触发,慎用 |
我曾经在一个项目中用了双边沿触发,结果因为信号线上有毛刺,芯片频繁被唤醒,电池两天就耗光了。后来改成下降沿触发,并在硬件上加了一个100nF的电容滤波,问题才解决。
3.5 核心流程:一张图看懂
下面这张图,把整个外部中断唤醒的流程串起来了。从配置到唤醒,再到恢复执行,一目了然:
3.6 避坑指南
做外部中断唤醒,有几个坑我踩过,写出来你注意一下:
- 中断标志位必须清除——不清除的话,退出中断后会再次触发,形成死循环。我见过有人在这里卡了一整天。
- 唤醒后时钟重新配置——Stop模式唤醒后,系统默认切到MSI。如果你需要高速运行,记得在中断返回前重新配置时钟。
- GPIO上下拉要匹配外部电路——如果外部已经有上拉电阻,内部就别再上拉了,否则会多耗电。我习惯用外部上拉,内部设成浮空。
- 不要在中断里做复杂操作——中断服务函数应该尽量短。设置一个标志位,回到主循环再处理。否则会影响后续中断的响应。
调试小技巧:如果你不确定外部中断有没有触发,可以在中断服务函数里翻转一个GPIO,用示波器看波形。这是我排查唤醒问题最常用的方法。
外部中断唤醒,说难不难,说简单也不简单。关键是理解清楚每个配置项的作用,以及唤醒后的系统状态变化。掌握了这些,你就能在低功耗产品里灵活运用了。