引言

在物联网传感器节点、可穿戴设备等电池供电场景中,系统通常需要在大部分时间处于低功耗模式,仅周期性唤醒执行任务。STM32F4 系列支持多种低功耗模式,其中 STOP 模式在保留 SRAM 和寄存器内容的同时,将系统时钟关闭,功耗可降至 µA 级。然而,唤醒定时器通常依赖外部 RTC 或内部 LSI,而 LSI 的精度(典型 ±5%,最差 ±15%)直接导致唤醒时间漂移。LPTIM 作为新一代低功耗定时器,可在 STOP 模式下运行,但同样受限于时钟源。本文以 STM32F407 为例,详细讲解如何配置 LPTIM 实现毫秒级唤醒,并通过软件补偿消除误差。

LPTIM 在 STOP 模式下的工作原理

LPTIM 是一个 16 位递减计数器,支持多种时钟源:LSI、LSE、HSE 或外部时钟。在 STOP 模式下,系统时钟(HCLK)停止,但 LPTIM 可由 LSI(典型 32 kHz)或 LSE(32.768 kHz)继续运行。其核心优势在于:

  • 无需 CPU 干预,计数器自动递减至零并触发唤醒事件。
  • 支持连续计数和单次模式,可配置中断或事件输出。
  • 功耗极低(典型 <1 µA),适合长期待机。

但 LSI 的精度受温度、电压影响较大,导致实际唤醒时间偏离预期。例如,若 LSI 实际频率为 30 kHz(标称 32 kHz),则 1 秒唤醒将延迟约 62 ms。因此,精确配置必须考虑时钟校准。

硬件与软件准备

  • 硬件:STM32F407 开发板(或任何 STM32F4 系列),连接调试器。
  • 软件:STM32CubeIDE 或 Keil MDK,使用 HAL 库或标准外设库。
  • 目标:在 STOP 模式下,通过 LPTIM 实现 100 ms 唤醒周期,误差 < ±1 ms。

配置步骤

1. 时钟源选择与校准

LSI 频率可通过内部参考电压(VREFINT)进行校准。STM32F4 的 LSI 校准寄存器(LSICTRL)提供微调位,但更实用的方法是软件测量 LSI 实际频率。

// 使用 TIM5 的通道 4 捕获 LSI 频率(示例)
void LSI_Calibrate(void) {
    // 使能 TIM5 和 GPIO,配置输入捕获
    // 测量 LSI 在 1 秒内的脉冲数,得到实际频率
    // 存储到全局变量 lsi_freq
}

2. LPTIM 初始化

使用 HAL 库配置 LPTIM 时钟源为 LSI,并设置预分频器。

LPTIM_HandleTypeDef hlptim;

void LPTIM_Init(void) {
    __HAL_RCC_LPTIM1_CLK_ENABLE();
    __HAL_RCC_LSI_ENABLE();
    
    // 等待 LSI 就绪
    while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET);
    
    hlptim.Instance = LPTIM1;
    hlptim.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK; // 实际选择 LSI 作为内核时钟
    hlptim.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;
    hlptim.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
    hlptim.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH;
    hlptim.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE;
    hlptim.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL;
    HAL_LPTIM_Init(&hlptim);
}

注意:HAL 库中 LPTIM 的时钟源配置需通过 RCC 设置,确保 LSI 被选为 LPTIM 内核时钟。

3. 设置唤醒周期并进入 STOP 模式

唤醒周期由装载值决定:装载值 = 目标时间(秒) × 实际LSI频率。由于 LSI 频率已知,可计算精确装载值。

uint32_t target_ms = 100;
uint32_t load_value = (uint32_t)((target_ms / 1000.0) * lsi_freq);

void EnterStopWithLPTIM(void) {
    // 配置 LPTIM 中断
    HAL_LPTIM_SetOnce_IT(&hlptim, load_value);
    
    // 进入 STOP 模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    
    // 唤醒后重新配置系统时钟
    SystemClock_Config();
}

4. 唤醒中断处理

在中断回调中清除标志并执行任务。

void HAL_LPTIM_CompareMatchCallback(LPTIM_HandleTypeDef *hlptim) {
    // 清除中断标志(HAL 已处理)
    // 执行周期性任务
    HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0);
}

误差分析与补偿策略

误差来源

  • LSI 频率偏差:±5% 典型,随温度漂移。
  • 预分频器量化误差:若分频后频率非整数,产生舍入误差。
  • 中断响应延迟:从唤醒到执行任务的时间。

软件补偿方法

  1. 动态校准:每次唤醒后,利用系统时钟(如 HSE)测量 LSI 实际频率,更新装载值。
  2. 线性补偿:根据温度传感器(内部温度传感器)查表修正频率。
  3. 多次平均:连续测量多次取平均,减少瞬时波动。

示例:在唤醒后重新校准 LSI。

void RecalibrateLSI(void) {
    // 使用 TIM5 捕获,测量 1 秒内 LSI 脉冲数
    // 更新 lsi_freq 和 load_value
}

完整代码示例

以下为整合代码(关键部分):

#include "main.h"

LPTIM_HandleTypeDef hlptim;
uint32_t lsi_freq = 32000; // 初始假设

void SystemClock_Config(void);
void LSI_Calibrate(void);
void LPTIM_Init(void);
void EnterStopWithLPTIM(uint32_t target_ms);

int main(void) {
    HAL_Init();
    SystemClock_Config();
    
    LSI_Calibrate(); // 首次校准
    LPTIM_Init();
    
    while (1) {
        EnterStopWithLPTIM(100);
        // 唤醒后执行任务
        RecalibrateLSI(); // 动态校准
    }
}

void LSI_Calibrate(void) {
    // 假设使用 TIM5 捕获,此处简化
    // 实际代码需配置 TIM5 输入捕获,测量 LSI 频率
    lsi_freq = 32000; // 示例值
}

void LPTIM_Init(void) {
    __HAL_RCC_LPTIM1_CLK_ENABLE();
    __HAL_RCC_LSI_ENABLE();
    while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET);
    
    hlptim.Instance = LPTIM1;
    hlptim.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK;
    hlptim.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;
    hlptim.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
    hlptim.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH;
    hlptim.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE;
    hlptim.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL;
    HAL_LPTIM_Init(&hlptim);
    
    HAL_NVIC_SetPriority(LPTIM1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(LPTIM1_IRQn);
}

void EnterStopWithLPTIM(uint32_t target_ms) {
    uint32_t load = (uint32_t)((target_ms / 1000.0) * lsi_freq);
    HAL_LPTIM_SetOnce_IT(&hlptim, load);
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
    SystemClock_Config();
}

void LPTIM1_IRQHandler(void) {
    HAL_LPTIM_IRQHandler(&hlptim);
}

void HAL_LPTIM_CompareMatchCallback(LPTIM_HandleTypeDef *hlptim) {
    // 任务处理
}

注意事项

  • 时钟源选择:LPTIM 在 STOP 模式下必须使用 LSI 或 LSE,不能使用 HSI/HSE(它们会关闭)。
  • 预分频器:合理设置预分频器,确保装载值不超过 16 位(最大 65535)。若目标时间较长,可增大分频。
  • 中断优先级:唤醒中断应设为最高优先级,避免延迟。
  • 电源稳压器:STOP 模式可选择低功耗稳压器,但唤醒时间略长,需权衡。
  • 校准时机:首次校准时系统时钟需运行,建议在进入低功耗前完成。
  • 误差补偿:若要求极高精度,建议使用 LSE(32.768 kHz),精度可达 ±20 ppm,但需外部晶振。

结语

通过合理配置 LPTIM 和动态校准 LSI,STM32F4 在 STOP 模式下可实现毫秒级精确唤醒,误差控制在 ±1 ms 内。本文提供的方法可广泛应用于低功耗传感器节点、智能仪表等场景,兼顾功耗与实时性。开发者可根据实际需求调整校准频率和补偿算法,进一步优化性能。