4. 使用外部中断唤醒

任务定义

本案例目标:在MCU进入低功耗停机模式(STOP)后,通过外部GPIO中断将其唤醒,并验证唤醒后程序能正确恢复执行。以STM32L0系列为例,实现按键触发的外部中断唤醒,同时展示唤醒源判断与低功耗模式进入/退出的完整流程。

硬件连接

STM32L0 MCU STOP Mode PA0 (EXTI0) PA5 (LED) NRST KEY LED GND 内部上拉 VDD = 3.3V 100nF

按键连接到PA0(EXTI0),内部上拉,按下时产生下降沿触发中断。PA5驱动LED作为唤醒指示。MCU进入STOP模式前点亮LED,唤醒后翻转LED状态。

代码实现


#include "stm32l0xx_hal.h"

/* 简单延时(非精确,仅用于视觉观察) */
static void delay_volatile(uint32_t count) {
    while (count--) {
        __NOP();
    }
}

/* LED初始化:PA5 */
static void LED_Init(void) {
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitTypeDef gpio = {0};
    gpio.Pin = GPIO_PIN_5;
    gpio.Mode = GPIO_MODE_OUTPUT_PP;
    gpio.Pull = GPIO_NOPULL;
    gpio.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOA, &gpio);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);  /* 初始点亮 */
}

/* 外部中断初始化:PA0下降沿触发 */
static void EXTI_Init(void) {
    __HAL_RCC_GPIOA_CLK_ENABLE();
    GPIO_InitTypeDef gpio = {0};
    gpio.Pin = GPIO_PIN_0;
    gpio.Mode = GPIO_MODE_IT_FALLING;
    gpio.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &gpio);

    HAL_NVIC_SetPriority(EXTI0_1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI0_1_IRQn);
}

/* EXTI0中断服务函数 */
void EXTI0_1_IRQHandler(void) {
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}

/* 中断回调:翻转LED */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == GPIO_PIN_0) {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
    }
}

int main(void) {
    HAL_Init();
    LED_Init();
    EXTI_Init();

    while (1) {
        /* 进入STOP模式前关闭LED */
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
        delay_volatile(200000);  /* 短暂保持熄灭,便于观察 */

        /* 进入STOP模式,等待外部中断唤醒 */
        HAL_SuspendTick();       /* 暂停SysTick,避免唤醒干扰 */
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
        /* 唤醒后恢复 */
        HAL_ResumeTick();
        HAL_Delay(2);            /* 等待时钟稳定 */

        /* 点亮LED,表示已唤醒 */
        HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
        delay_volatile(500000);  /* 保持点亮,便于观察 */
    }
}

关键实现说明

  • 唤醒源配置:PA0配置为下降沿中断(GPIO_MODE_IT_FALLING),内部上拉保证按键未按下时为高电平,按下时产生下降沿。
  • STOP模式进入:调用HAL_PWR_EnterSTOPMode()前先暂停SysTick(HAL_SuspendTick()),防止唤醒后时钟未稳定时SysTick产生干扰。
  • 唤醒后处理:从STOP模式唤醒后,系统时钟自动恢复为MSI(默认),需调用HAL_ResumeTick()恢复SysTick,并短暂延时等待时钟稳定。
  • 中断回调:在HAL_GPIO_EXTI_Callback()中翻转LED,该回调在中断上下文中执行,操作应保持简短。

工程配置与编译


/* 使用STM32CubeMX生成基础工程,需包含以下配置:
 * - 时钟:MSI 2.097MHz(默认),无需PLL
 * - PA0: GPIO_EXTI0,下降沿触发,内部上拉
 * - PA5: GPIO_Output
 * - NVIC: 使能EXTI0_1中断
 * - 功耗:选择STOP模式(PWR_LOWPOWERREGULATOR_ON)
 */

编译命令(以GCC工具链为例):


arm-none-eabi-gcc -c main.c -mcpu=cortex-m0plus -mthumb -I./Inc \
    -DSTM32L0xx -DUSE_HAL_DRIVER
arm-none-eabi-gcc -o firmware.elf main.o \
    startup_stm32l0xx.s system_stm32l0xx.c \
    -T stm32l0xx_flash.ld -lc -lm

验证方法与判定标准

  1. 烧录与运行:将编译生成的固件烧录至STM32L0开发板,连接调试器(ST-Link)与串口(可选,用于观察)。
  2. 观察流程:
    • 上电后LED点亮约0.5秒(初始状态),随后熄灭并进入STOP模式(此时整板电流应显著下降,可用万用表测量VDD电流,预期从mA级降至µA级)。
    • 按下按键(PA0接地),LED应立即点亮约1秒,然后再次熄灭进入STOP模式。
  3. 判定通过:
    • 每次按键按下,LED均能可靠点亮,说明外部中断成功唤醒MCU。
    • LED熄灭期间(STOP模式)电流明显低于运行模式,证明低功耗状态生效。
    • 连续多次按键(≥10次)无失效,说明唤醒路径稳定。
  4. 调试器验证(可选):在HAL_GPIO_EXTI_Callback()入口设断点,按键后能停住,证明中断确实触发;单步执行可观察PC从STOP唤醒后正确恢复。

注意事项

  • 若使用其他STM32系列,需调整中断向量名称(如EXTI0_IRQn)与时钟配置,但原理相同。
  • STOP模式下调试器连接可能断开,建议先通过LED现象验证,再连接调试器观察。
  • 按键需做硬件消抖(RC滤波或软件延时),本案例为演示中断唤醒,未包含消抖逻辑,实际产品需补充。