停止模式1(Stop1)调试:进入方法与调试器保持

Stop1 模式,说白了就是 STM32L4 在低功耗和唤醒速度之间取了个中间值。我刚开始接触 L4 系列时,总觉得 Stop 模式就一种,后来在项目里踩了坑才发现——Stop1 和 Stop2 的差别,直接影响了你的调试策略。

这一节,我们专门聊 Stop1。怎么进去?调试器会不会掉线?电流到底能压到多少?

进入 Stop1 模式的标准流程

进入 Stop1 其实不复杂。核心就两步:配置寄存器,然后执行 WFI 或 WFE 指令。

我个人习惯用 HAL 库的接口,但也会直接操作寄存器来确认状态。先看代码:

/* 进入 Stop1 模式的标准配置 */
void Enter_Stop1_Mode(void)
{
    /* 1. 配置电源控制寄存器 */
    HAL_PWREx_EnableLowPowerRunMode();   // 可选,用于更低电压
    __HAL_RCC_PWR_CLK_ENABLE();
    
    /* 2. 设置 Stop1 模式 */
    HAL_PWREx_EnterSTOP1Mode(PWR_STOPENTRY_WFI);
    
    /* 3. 醒来后恢复时钟 */
    SystemClock_Config();
}

嗯,这里要注意:PWR_STOPENTRY_WFI 和 PWR_STOPENTRY_WFE 的区别。WFI 是等待中断,WFE 是等待事件。我建议调试阶段用 WFI,因为你可以用调试器手动触发中断来唤醒。

关键寄存器: PWR_CR1 的 LPMS 位域,设置为 0b001 即选择 Stop1 模式。

调试器在 Stop1 下的连接保持

这是很多工程师头疼的地方。进入 Stop1 后,调试器会不会断开?

答案是:取决于你的调试器配置和芯片选项。

STM32L4 有个叫 DBGMCU 的外设,专门控制调试接口在低功耗模式下的行为。你需要确保 DBGMCU_CR 寄存器的 DBG_STOP 位被置 1。

我曾经在一个电池供电的项目里,调试器一进 Stop1 就掉线,每次都要重新连接。后来发现是 DBGMCU 没配置。那叫一个郁闷。

/* 保持调试器在 Stop1 下连接 */
void Keep_Debug_Alive_In_Stop1(void)
{
    /* 使能 DBGMCU 时钟 */
    __HAL_RCC_DBGMCU_CLK_ENABLE();
    
    /* 设置 DBG_STOP 位,保持调试接口时钟 */
    DBGMCU->CR |= DBGMCU_CR_DBG_STOP_Msk;
    
    /* 可选:保持定时器在 Stop1 下继续运行 */
    // DBGMCU->APB1FZ |= DBGMCU_APB1_FZ_DBG_TIM2_STOP;
}
注意: 保持调试接口会额外消耗电流。实测大约增加 5-10 µA。量产时记得关闭 DBG_STOP 位。

实测电流与调试响应

我拿 STM32L496ZG 开发板做了几组实测。环境是 3.3V 供电,室温 25°C。结果如下:

配置条件 典型电流 调试器状态 唤醒时间
Stop1 默认(无调试保持) ~3.5 µA 断开 ~5 µs
Stop1 + DBG_STOP 使能 ~12 µA 保持连接 ~5 µs
Stop1 + DBG_STOP + 定时器保持 ~18 µA 保持连接 ~5 µs
Stop1 + RAM 保持全部 ~8 µA 断开 ~5 µs

你看,调试器保持连接要多花将近 9 µA。对于低功耗设计来说,这个代价不小。所以我建议:调试阶段开 DBG_STOP,量产阶段关掉。

另外,唤醒时间基本不受影响。Stop1 的唤醒速度比 Stop2 快很多,这也是它最大的优势。

调试响应:你能做什么?不能做什么?

进入 Stop1 后,如果调试器保持连接,你可以:

  • 查看变量值(RAM 内容保持)
  • 设置断点(但不会立即触发,需要唤醒后)
  • 单步执行(需要先唤醒)
  • 查看外设寄存器状态

你不能做的:

  • 直接修改 Flash(写保护)
  • 实时跟踪外设行为(时钟已关闭)
  • 使用 ITM/SWO 输出(需要时钟)
小技巧: 我习惯在进入 Stop1 前,把关键状态变量存到 RAM 的固定地址。这样即使调试器在 Stop1 下只能读 RAM,也能拿到足够的信息。

常见故障与定位

我在项目里遇到过几个典型问题,列出来供你参考:

  1. 进不去 Stop1 —— 检查是否有未处理的中断挂起。WFI 指令会被挂起的中断直接唤醒。
  2. 电流降不下来 —— 检查 GPIO 是否配置为模拟模式。浮空引脚会漏电。
  3. 调试器频繁断开 —— 检查 DBGMCU 配置,以及调试器供电是否稳定。
  4. 唤醒后程序跑飞 —— 检查时钟恢复配置。Stop1 退出后需要重新配置系统时钟。

我曾经遇到一个特别隐蔽的问题:某个外设在 Stop1 下没有正确关闭,导致电流比预期高了 20 µA。后来用调试器逐个外设排查,才发现是 USART 的 RX 引脚没拉高。

Stop1 模式知识结构图

下面这张图总结了 Stop1 调试的核心要点,你可以对照着梳理自己的代码:

Stop1 模式调试知识结构 Stop1 模式调试知识结构 Stop1 模式 进入方法 配置 PWR_CR1.LPMS = 0b001 执行 WFI / WFE 指令 调试器保持 DBGMCU_CR.DBG_STOP = 1 额外功耗 ~9 µA 实测电流 典型值 3.5 µA ~ 18 µA 取决于外设保持配置 调试响应 可读 RAM / 寄存器 不可写 Flash / 实时跟踪 故障定位 检查中断挂起 / GPIO 配置 逐个外设排查漏电路径

这张图把 Stop1 调试分成了五个维度。你调试时遇到问题,可以对照着看是哪个环节出了岔子。

调试实战:一个完整的例子

最后,我给你一个完整的调试流程。假设你要验证 Stop1 模式下的功耗和唤醒:

/* 调试用:进入 Stop1 并保持调试连接 */
void Debug_Stop1_Test(void)
{
    volatile uint32_t status;
    
    /* 1. 配置调试保持 */
    __HAL_RCC_DBGMCU_CLK_ENABLE();
    DBGMCU->CR |= DBGMCU_CR_DBG_STOP_Msk;
    
    /* 2. 记录进入前状态 */
    status = PWR->CR1;
    
    /* 3. 进入 Stop1 */
    HAL_PWREx_EnterSTOP1Mode(PWR_STOPENTRY_WFI);
    
    /* 4. 醒来后检查状态 */
    /* 在这里设置断点,查看 status 和当前功耗 */
}

你在第 4 步设置断点。进入 Stop1 后,调试器不会断开。你可以用电流表测量实际功耗。然后通过调试器发送一个中断(比如按键中断),芯片会立即唤醒并停在断点处。

这个方法我用了很多次,定位问题效率很高。

我的建议: 调试阶段不要追求极致低功耗。先把功能调通,再逐步关闭调试保持功能,观察电流变化。这样能快速定位是哪个外设在漏电。