# 引言 在物联网传感器节点、可穿戴设备等电池供电场景中,系统通常需要在大部分时间处于低功耗模式,仅周期性唤醒执行任务。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 实际频率。 ```c // 使用 TIM5 的通道 4 捕获 LSI 频率(示例) void LSI_Calibrate(void) { // 使能 TIM5 和 GPIO,配置输入捕获 // 测量 LSI 在 1 秒内的脉冲数,得到实际频率 // 存储到全局变量 lsi_freq } ``` ## 2. LPTIM 初始化 使用 HAL 库配置 LPTIM 时钟源为 LSI,并设置预分频器。 ```c 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 频率已知,可计算精确装载值。 ```c 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. 唤醒中断处理 在中断回调中清除标志并执行任务。 ```c void HAL_LPTIM_CompareMatchCallback(LPTIM_HandleTypeDef *hlptim) { // 清除中断标志(HAL 已处理) // 执行周期性任务 HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_0); } ``` # 误差分析与补偿策略 ## 误差来源 - LSI 频率偏差:±5% 典型,随温度漂移。 - 预分频器量化误差:若分频后频率非整数,产生舍入误差。 - 中断响应延迟:从唤醒到执行任务的时间。 ## 软件补偿方法 1. **动态校准**:每次唤醒后,利用系统时钟(如 HSE)测量 LSI 实际频率,更新装载值。 2. **线性补偿**:根据温度传感器(内部温度传感器)查表修正频率。 3. **多次平均**:连续测量多次取平均,减少瞬时波动。 示例:在唤醒后重新校准 LSI。 ```c void RecalibrateLSI(void) { // 使用 TIM5 捕获,测量 1 秒内 LSI 脉冲数 // 更新 lsi_freq 和 load_value } ``` # 完整代码示例 以下为整合代码(关键部分): ```c #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 内。本文提供的方法可广泛应用于低功耗传感器节点、智能仪表等场景,兼顾功耗与实时性。开发者可根据实际需求调整校准频率和补偿算法,进一步优化性能。