4、Deep Sleep模式深入:进入Deep Sleep的条件、寄存器配置、时钟与外设关闭策略

在TinyML设备中,Deep Sleep(深度睡眠)模式是降低系统平均功耗的核心手段。与Light Sleep相比,Deep Sleep会切断更多时钟源与电源域,代价是唤醒延迟显著增加(通常从几十微秒上升到数毫秒甚至更长)。理解Deep Sleep的进入条件、寄存器配置顺序以及时钟/外设的关闭策略,是设计低功耗边缘设备的基础。本章将围绕这三条主线展开,并结合典型MCU(以ARM Cortex-M系列及RISC-V内核为例)给出可落地的配置路径。

Deep Sleep 进入与恢复工程链路 ① 进入条件检查 • 无待处理中断 • 外设事务已排空 • DMA传输完成 • 电源电压稳定 • 唤醒源已使能 ② 寄存器配置序列 1. 配置唤醒源 2. 设置电源域保持 3. 关闭未用外设时钟 4. 设置Deep Sleep标志 5. 执行WFI/WFE指令 ③ 时钟/外设关闭策略 • 关闭PLL/高频晶振 • 保留32.768kHz RTC • 外设时钟门控 • 模拟外设下电 • GPIO保持/上拉 ④ 唤醒恢复流程 唤醒源触发 → 中断向量跳转 → 恢复系统时钟 → 重新使能外设时钟 → 初始化外设状态 → 继续执行主循环 关键风险点 • 唤醒源未配置导致无法唤醒 • 外设时钟未关闭导致功耗不降 • 唤醒后时钟稳定时间不足导致外设读写异常 TP1: 电流测量点

4.1 进入Deep Sleep的核心条件与前置检查

进入Deep Sleep并非简单地执行一条WFI(Wait For Interrupt)指令。MCU内部存在多个电源域和时钟域,如果未满足进入条件,轻则无法进入低功耗状态,重则导致外设数据丢失或系统死锁。以下是进入Deep Sleep前必须逐项确认的条件:

  • 中断状态检查:确保所有未处理的中断标志已被清除或屏蔽。若有挂起的中断,WFI指令会立即返回,无法进入睡眠。
  • 外设事务排空:UART发送缓冲区、SPI传输队列、I2C总线状态必须处于空闲。若在传输中途进入Deep Sleep,总线状态机可能卡死。
  • DMA传输完成:DMA控制器必须处于空闲状态,且传输完成中断已响应。否则DMA在睡眠期间尝试访问总线会引发总线错误。
  • 电源电压稳定:若使用内部LDO或DC-DC,需等待电源就绪标志(如PWR_FLAG)置位。电压不稳时进入Deep Sleep可能导致RAM数据丢失。
  • 唤醒源已使能:RTC闹钟、外部GPIO中断、比较器输出等唤醒源必须在进入睡眠前配置完成,否则设备将无法被唤醒。
风险提示:在进入Deep Sleep前,务必检查是否有外设仍处于忙状态(BUSY位)。例如,Flash编程操作未完成时进入睡眠,可能导致写入数据损坏。建议在进入睡眠前调用外设的等待空闲函数(如while(FLASH->SR & FLASH_SR_BSY))。

4.2 寄存器配置序列与关键位域

不同MCU的Deep Sleep寄存器设计差异较大,但核心逻辑一致:通过系统控制寄存器(SCR)和电源控制寄存器(PWR_CR)组合实现。以ARM Cortex-M3/M4内核为例,关键配置如下:

// 以STM32L4系列为例的Deep Sleep配置
// 1. 设置SLEEPDEEP位,选择深度睡眠模式
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;

// 2. 选择电压调节器模式(低功耗模式)
PWR->CR1 |= PWR_CR1_LPR;

// 3. 配置唤醒源(RTC闹钟唤醒)
RTC->CR |= RTC_CR_ALRAIE;
EXTI->IMR1 |= EXTI_IMR1_IM17;  // RTC闹钟事件连接到EXTI17

// 4. 关闭未使用外设时钟(APB1/APB2)
RCC->APB1ENR1 &= ~(RCC_APB1ENR1_USART2EN | RCC_APB1ENR1_I2C1EN);
RCC->APB2ENR &= ~(RCC_APB2ENR_SPI1EN | RCC_APB2ENR_ADC1EN);

// 5. 执行WFI指令进入睡眠
__WFI();

// 6. 唤醒后恢复时钟
SystemClock_Config();  // 重新配置PLL

寄存器配置顺序至关重要。正确的顺序是:先配置唤醒源 → 再设置电源模式 → 然后关闭外设时钟 → 最后执行WFI。如果先关闭外设时钟再配置唤醒源,可能导致唤醒源本身无法工作(例如RTC时钟被关闭)。

寄存器/位域 功能 典型值 注意事项
SCR.SLEEPDEEP 选择深度睡眠模式 1 必须与PWR_CR配合
PWR_CR.LPR 低功耗调节器模式 1 降低静态功耗,但唤醒时间变长
PWR_CR.PDDS 电源下电深度模式 0/1 1表示关闭主稳压器,仅保留备份域
RTC_CR.ALRAIE RTC闹钟中断使能 1 需同时配置EXTI映射
RCC_APB1ENR APB1外设时钟使能 按需清零 关闭未用外设可降低漏电流

4.3 时钟与外设关闭策略

Deep Sleep的功耗优化核心在于时钟树裁剪和外设电源门控。系统主时钟(PLL、HSE)在Deep Sleep期间必须关闭,否则无法达到微安级功耗。以下是推荐的关闭策略:

  1. 关闭PLL和HSE:将系统时钟切换到内部低速时钟(LSI或LSI_RC),然后关闭PLL和外部高速晶振。注意切换顺序,避免系统无时钟运行。
  2. 保留RTC时钟:RTC通常使用32.768kHz外部晶振(LSE)或内部低速RC(LSI)。若需要定时唤醒,必须保留LSE或LSI运行。
  3. 外设时钟门控:通过RCC寄存器逐模块关闭APB1/APB2/AHB上的外设时钟。对于模拟外设(ADC、DAC、比较器),还需关闭其内部偏置电流。
  4. GPIO状态保持:进入Deep Sleep前,将未使用的GPIO配置为模拟输入或带上拉的输出低电平,避免浮空输入导致漏电流。
  5. 备份域管理:若需在睡眠期间保持RTC和备份寄存器数据,必须确保备份域电源(VBAT)正常供电。
经验法则:对于大多数MCU,Deep Sleep电流可降至2-10μA(保留RTC和RAM)。若需进一步降低至亚微安级,需关闭所有时钟源并进入Shutdown模式,但此时RAM内容丢失,唤醒后需重新初始化。

4.4 实战案例:TinyML传感器节点Deep Sleep调优

以一个基于Cortex-M4的TinyML振动传感器节点为例,目标是将平均功耗从3.2mA降至50μA以下。系统每10秒采集一次加速度计数据,运行轻量级CNN进行故障分类,其余时间处于Deep Sleep。

初始现象:设备在睡眠期间电流高达1.8mA,远高于数据手册标称的5μA。使用电流探头测量发现,睡眠电流呈阶梯状波动,且每次唤醒后系统时钟恢复异常。

分析过程:

  1. 使用调试器读取RCC寄存器,发现APB1上的USART2时钟未关闭(该外设用于调试输出)。
  2. 检查PWR_CR寄存器,发现LPR位未置位,电压调节器仍处于高性能模式。
  3. 通过示波器观察唤醒信号,发现RTC闹钟触发后,系统时钟恢复时间约需2.3ms,但代码在唤醒后立即访问SPI外设,导致读取失败。

优化操作:

// 优化后的睡眠配置
void enter_deep_sleep(void) {
    // 1. 关闭调试串口时钟
    RCC->APB1ENR1 &= ~RCC_APB1ENR1_USART2EN;
    
    // 2. 使能低功耗调节器
    PWR->CR1 |= PWR_CR1_LPR;
    
    // 3. 切换系统时钟到MSI(内部中速时钟)
    RCC->CR &= ~RCC_CR_PLLON;
    while((RCC->CR & RCC_CR_PLLRDY) != 0);
    
    // 4. 配置唤醒源为RTC闹钟
    RTC->CR |= RTC_CR_ALRAIE;
    EXTI->IMR1 |= EXTI_IMR1_IM17;
    
    // 5. 设置SLEEPDEEP并执行WFI
    SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
    __WFI();
    
    // 6. 唤醒后等待时钟稳定
    SystemClock_Config();
    while(!(RCC->CR & RCC_CR_PLLRDY));
    
    // 7. 重新初始化外设
    USART2_Init();
    SPI1_Init();
}

结果对比:

指标 优化前 优化后 改善幅度
睡眠电流 1.8mA 4.2μA ↓ 99.8%
唤醒时间 2.3ms 3.1ms ↑ 0.8ms(可接受)
平均功耗 3.2mA 38μA ↓ 98.8%

复盘经验:调试串口是常见的“功耗泄漏点”,量产固件中必须关闭。此外,唤醒后时钟稳定等待时间不能省略,否则外设访问会失败。该案例中,通过增加while(!(RCC->CR & RCC_CR_PLLRDY))循环,彻底解决了唤醒后SPI读取异常的问题。

4.5 常见误区与设计复盘

误区一:认为执行WFI就一定能进入Deep Sleep。实际上,若SLEEPDEEP位未置位,WFI只会进入Light Sleep,功耗降低有限。
误区二:关闭外设时钟后,外设寄存器值会丢失。对于大多数MCU,关闭时钟不会复位寄存器,但模拟外设(如ADC)可能需要重新校准。
误区三:唤醒后立即访问外设而不等待时钟稳定。PLL重新锁定需要时间,建议在唤醒代码中增加延时或状态检查。

设计复盘清单:

  • 进入睡眠前,使用__DMB()指令确保所有内存访问完成。
  • 在唤醒中断服务程序中,首先清除唤醒标志,再执行系统时钟重配。
  • 使用RTOS时,需在空闲任务中调用睡眠函数,并确保所有任务挂起。
  • 量产前使用功耗分析仪(如Joulescope)测量不同温度下的睡眠电流,验证极端情况。

Deep Sleep模式的设计本质是系统级功耗预算管理。开发者需要从唤醒源、时钟树、外设状态三个维度协同考虑,才能实现微安级待机与毫秒级响应的平衡。在TinyML场景中,这种平衡直接决定了设备在电池供电下的使用寿命。