4. 深入理解低功耗运行模式:LP Run

好,咱们进入第四章。前面几章我们把睡眠、停机、待机这些“大杀器”都聊了一遍。但有个问题:有时候我们既想省电,又不能让CPU彻底停下来。比如,传感器还在采集数据,CPU还得做点简单的滤波运算。这时候,让芯片进Stop模式再定时唤醒,来回折腾反而更费电。

那怎么办?ST给咱们准备了一个折中方案——低功耗运行模式(Low-Power Run,简称LP Run)。说白了,就是让CPU继续跑,但把它的“饭量”减到最低。

4.1 LP Run模式的原理

LP Run模式的核心思路很简单:降低系统时钟频率,同时关闭不必要的稳压器。

在正常的Run模式下,STM32L4内部有一个主稳压器(MR),它输出稳定,但功耗高。LP Run模式下,芯片会切换到低功耗稳压器(LPR)。这个LPR的输出能力弱一些,但静态电流只有MR的几分之一。

同时,系统时钟(SYSCLK)会被限制在2 MHz以下。你想想看,跑80 MHz和跑2 MHz,动态功耗差了多少?差不多是40倍的差距。

核心指标: LP Run模式下,典型功耗可以降到 8 µA/MHz 左右。而正常Run模式,这个数字是 100 µA/MHz 以上。

我刚开始用这个模式时,总觉得2 MHz太慢了,能干啥?后来发现,对于大部分传感器数据读取、IO翻转、简单的PID控制,2 MHz完全够用。而且,省下来的电量,可以让电池多用好几天。

4.2 进入与退出LP Run模式

进入LP Run模式,不是调一个寄存器就完事了。它有一套固定的“仪式”。我习惯把它总结成三步走:

  1. 降频: 先把系统时钟降到2 MHz以下。我一般用MSI时钟,直接设到2 MHz档位。
  2. 切稳压器: 通过PWR寄存器,把稳压器从MR切换到LPR。
  3. 切模式: 调用 HAL_PWREx_EnableLowPowerRunMode() 函数。

退出就更简单了:调用 HAL_PWREx_DisableLowPowerRunMode(),然后恢复时钟频率。注意,退出后稳压器会自动切回MR。

下面是一个典型的进入代码片段:

void Enter_LP_Run_Mode(void)
{
    // 1. 配置MSI时钟为2 MHz
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0, 2000000);

    // 2. 切换到低功耗稳压器
    HAL_PWREx_EnableLowPowerRunMode();

    // 3. 此时系统已工作在LP Run模式
    // 可以在这里执行低功耗任务
}
我的习惯: 在进入LP Run之前,我会先把不用的外设时钟关掉。比如,如果只用定时器和ADC,就把USART、SPI的时钟全关了。这样能再省下几个微安。

4.3 LP Run模式下的外设限制

LP Run模式不是万能的。它有一些“硬伤”,你必须心里有数。我曾经在一个项目中,因为没注意这些限制,导致ADC采样数据全是乱的,排查了半天。

主要的限制有这几条:

外设/功能 LP Run模式下的状态 说明
系统时钟 (SYSCLK) ≤ 2 MHz 只能用MSI或LSI作为时钟源
Flash读取 正常,但需0等待周期 2 MHz以下不需要插入等待周期
DMA 不可用 LPR无法驱动DMA,所有数据传输必须由CPU轮询
USB / LCD 不可用 这些高速外设需要MR供电
定时器 (TIM) 可用,但受时钟限制 定时器时钟最高2 MHz,意味着最大分辨率降低
ADC 可用,但采样率受限 ADC时钟最高2 MHz,采样时间需要重新计算
避坑指南: 我曾经在LP Run模式下试图用DMA搬运ADC数据,结果DMA根本没反应。后来查手册才发现,LPR稳压器根本不给DMA供电。所以,在LP Run模式下,老老实实用CPU轮询吧。

4.4 实战:在LP Run模式下运行定时器与ADC

光说不练假把式。咱们来一个实战:在LP Run模式下,用定时器触发ADC,每隔100 ms采集一次内部温度传感器数据。

这个场景很常见:电池供电的设备,需要定期监测芯片温度,防止过热。但又不想为了这点事把整个系统唤醒到正常模式。

4.4.1 硬件与时钟配置

首先,时钟配置。我们使用MSI,频率设为2 MHz。定时器TIM6挂载在APB1上,时钟也是2 MHz。

要产生100 ms的间隔,我们需要一个200 ms的周期(因为后续会配置ADC为连续触发)。TIM6的预分频器设为1999,自动重装载值设为199。这样:

定时周期 = (1999 + 1) * (199 + 1) / 2 MHz = 2000 * 200 / 2,000,000 = 0.2 s = 200 ms

4.4.2 代码实现

// 定时器句柄
TIM_HandleTypeDef htim6;

void LP_Run_Timer_ADC_Example(void)
{
    // 1. 进入LP Run模式
    Enter_LP_Run_Mode();

    // 2. 配置TIM6
    __HAL_RCC_TIM6_CLK_ENABLE();
    htim6.Instance = TIM6;
    htim6.Init.Prescaler = 1999;       // 2 MHz / 2000 = 1 kHz
    htim6.Init.Period = 199;           // 1 kHz / 200 = 5 Hz (200 ms)
    HAL_TIM_Base_Init(&htim6);

    // 3. 配置ADC(使用内部温度传感器通道)
    // 注意:ADC时钟来自SYSCLK,此时为2 MHz
    // 采样时间需要适当增加,比如设为最大采样周期
    // ... (ADC初始化代码省略,重点在模式切换)

    // 4. 启动定时器,触发ADC
    HAL_TIM_Base_Start(&htim6);
    HAL_ADC_Start(&hadc, ADC_TRIGGER_TIM6_TRGO);

    // 5. 主循环中轮询ADC完成标志
    while (1)
    {
        if (HAL_ADC_PollForConversion(&hadc, 10) == HAL_OK)
        {
            uint32_t adc_value = HAL_ADC_GetValue(&hadc);
            // 处理温度数据...
        }
        // 注意:这里没有HAL_Delay,因为SysTick在LP Run下可能不准
        // 我们依赖定时器触发的ADC来完成时序
    }
}
经验之谈: 在LP Run模式下,我建议把SysTick中断关掉。因为2 MHz的时钟下,SysTick中断频繁进出,反而会增加功耗。用硬件定时器触发外设,CPU只在需要时处理数据,这才是低功耗的精髓。

4.4.3 功耗实测对比

咱们用数据说话。我在STM32L496G-DISCO开发板上实测了三种情况:

运行模式 系统时钟 外设状态 实测功耗
正常Run模式 80 MHz TIM6 + ADC 运行 ~8.5 mA
LP Run模式 2 MHz TIM6 + ADC 运行 ~280 µA
LP Run + 关断未用外设时钟 2 MHz 仅TIM6 + ADC ~210 µA

看到了吗?从8.5 mA降到210 µA,降了整整40倍。而且,功能完全没丢,定时器和ADC都在正常工作。

这就是LP Run模式的魅力。它让你在“省电”和“干活”之间找到了一个完美的平衡点。

总结一下: LP Run模式适合那些需要CPU持续处理、但处理速度要求不高的场景。记住三个关键词:降频、切稳压器、关无用外设。掌握了这个,你的电池续航就能再上一个台阶。
LP Run 模式知识体系 LP Run 模式 原理:LPR + 降频 操作:进入与退出 限制:DMA/USB不可用 主稳压器 (MR) 低功耗稳压器 (LPR) 降频至 ≤2 MHz 调用HAL函数 DMA、USB、LCD 不可用 实战:LP Run + TIM6 + ADC 配置MSI为2 MHz 启动TIM6触发ADC CPU轮询读取结果 核心收益:功耗从 8.5 mA 降至 210 µA,降幅达 40 倍