STM32F103 基于LSI时钟的RTC周期性唤醒与待机模式功耗优化实践(附标准库代码)

张开发
2026/4/16 12:10:26 15 分钟阅读

分享文章

STM32F103 基于LSI时钟的RTC周期性唤醒与待机模式功耗优化实践(附标准库代码)
1. 低功耗物联网节点的核心挑战在电池供电的物联网传感器节点设计中功耗控制直接决定了设备的续航能力。以STM32F103为例当它作为环境监测节点工作时90%以上的时间其实都在等待数据采集指令。这时候如果让CPU全速运转就像让汽车在红灯时保持发动机6000转空转一样浪费能源。我做过一个温湿度监测项目使用CR2032纽扣电池供电。最初没有启用低功耗模式电池不到两周就耗尽。后来采用待机模式RTC唤醒方案同样的电池续航延长到了8个月。这个案例让我深刻认识到低功耗设计的重要性。STM32F103提供三种省电模式睡眠模式仅关闭内核时钟外设保持运行功耗约15mA停止模式关闭所有高速时钟保留RAM数据功耗约20μA待机模式仅保留备份域供电功耗仅2μA对于周期性工作的传感器节点待机模式是最佳选择。虽然唤醒后相当于系统复位但我们的应用场景通常不需要保存运行状态。这里有个坑要注意进入待机模式前必须确保所有高阻态IO不会意外导通否则可能造成电流泄漏。2. 时钟系统的精妙平衡时钟源选择是低功耗设计的关键决策点。STM32F103的时钟树就像城市交通网络HSI/HSE相当于地铁速度快但耗能大LSI/LSE如同自行车道速度慢但几乎不耗能在待机模式下HSI/HSE会自动关闭只有低速时钟可以继续工作。我推荐使用LSI内部40kHz RC振荡器而非LSE外部32.768kHz晶振原因有三节省外部晶振的硬件成本避免晶振失效风险虽然LSI精度较差±1% vs LSE的±20ppm但对大多数传感器应用已经足够实测发现LSI有个有趣特性温度每升高10℃频率会漂移约0.3%。如果对定时精度要求严格可以在初始化时进行校准// LSI粗略校准示例 void LSI_Calibration(void) { uint32_t freq 0; RCC_ClocksTypeDef RCC_Clocks; // 使用TIM5测量LSI频率 TIM_ICInitTypeDef TIM_ICInitStructure; RCC_HSICmd(ENABLE); RCC_PLLCmd(DISABLE); RCC_SYSCLKConfig(RCC_SYSCLKSource_HSI); TIM_ICInitStructure.TIM_Channel TIM_Channel_1; TIM_ICInitStructure.TIM_ICPolarity TIM_ICPolarity_Rising; TIM_ICInitStructure.TIM_ICSelection TIM_ICSelection_DirectTI; TIM_ICInitStructure.TIM_ICPrescaler TIM_ICPSC_DIV1; TIM_ICInitStructure.TIM_ICFilter 0x0; TIM_ICInit(TIM5, TIM_ICInitStructure); // 测量计算实际频率 freq ... // 测量代码省略 RCC_AdjustHSICalibrationValue(freq/1000 - 16); }3. RTC唤醒机制的实战配置RTC闹钟唤醒是周期性任务的核心。配置时要注意三个关键点3.1 时钟分频的艺术LSI频率为40kHz要转换为1Hz的秒信号需要分频。标准做法是RTC_SetPrescaler(40000-1); // 40000分频得到1Hz但实际测试发现由于LSI频率偏差这个值可能需要微调。我通常会在初始化时加入动态调整逻辑uint32_t actual_freq Get_LSI_Frequency(); RTC_SetPrescaler(actual_freq - 1);3.2 闹钟设置的陷阱设置闹钟时间时新手常犯的错误是直接给绝对值RTC_SetAlarm(10); // 错误这是设置绝对时间点正确做法是计算相对于当前时间的增量uint32_t current RTC_GetCounter(); RTC_SetAlarm(current interval); // interval为需要的唤醒间隔3.3 中断处理的注意事项RTC中断服务函数必须精简高效。我曾遇到一个bug在中断里打印调试信息导致系统不稳定。正确的处理流程应该是void RTC_IRQHandler(void) { if(RTC_GetITStatus(RTC_IT_ALR)) { RTC_ClearITPendingBit(RTC_IT_ALR); // 仅设置标志位主循环中处理实际任务 wakeup_flag 1; } EXTI_ClearITPendingBit(EXTI_Line17); }4. 完整实现与优化技巧下面给出经过实战验证的完整代码框架4.1 系统初始化void LowPower_Init(void) { // 1. 启用必要的时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); // 2. 配置RTC时钟源 PWR_BackupAccessCmd(ENABLE); if(BKP_ReadBackupRegister(BKP_DR1) ! 0xA5A5) { // 首次上电初始化 RCC_LSICmd(ENABLE); while(!RCC_GetFlagStatus(RCC_FLAG_LSIRDY)); RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); RCC_RTCCLKCmd(ENABLE); RTC_WaitForSynchro(); RTC_SetPrescaler(39999); // 1Hz时钟 BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); } // 3. 配置唤醒中断 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel RTC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); RTC_ITConfig(RTC_IT_ALR, ENABLE); EXTI_ClearITPendingBit(EXTI_Line17); }4.2 低功耗任务调度void Task_Scheduler(void) { static uint32_t last_wakeup 0; if(wakeup_flag) { wakeup_flag 0; last_wakeup RTC_GetCounter(); // 执行传感器读取等任务 Sensor_Read(); // 判断是否需要立即再次休眠 if(No_Urgent_Tasks()) { Enter_StandbyMode(WAKEUP_INTERVAL); } } // 防止意外不进入休眠 if(RTC_GetCounter() - last_wakeup WAKEUP_INTERVAL*2) { Enter_StandbyMode(WAKEUP_INTERVAL); } }4.3 深度优化技巧IO状态配置void GPIO_PowerOptimize(void) { GPIO_InitTypeDef GPIO_InitStructure; // 将所有未使用的IO配置为模拟输入 GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin 0xFFFF; // 所有引脚 GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_Init(GPIOB, GPIO_InitStructure); GPIO_Init(GPIOC, GPIO_InitStructure); // 特殊处理唤醒引脚 GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPD; // 下拉输入 GPIO_InitStructure.GPIO_Pin WAKEUP_PIN; GPIO_Init(WAKEUP_PORT, GPIO_InitStructure); }ADC电源管理void ADC_PowerSave(void) { // 关闭ADC时钟和电源 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, DISABLE); ADC_Cmd(ADC1, DISABLE); // 配置ADC IO为模拟输入 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AIN; GPIO_InitStructure.GPIO_Pin ADC_PINS; GPIO_Init(ADC_PORT, GPIO_InitStructure); }调试接口处理void DebugPort_Disable(void) { // 禁用SWD接口以省电 GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable, ENABLE); }5. 实测数据与异常处理在我的环境监测项目中测得不同模式下的典型电流值工作模式电流消耗唤醒时间正常运行12mA-睡眠模式5mA2μs停止模式20μA50μs待机模式(LSI)2μA复位重启常见问题及解决方案无法唤醒检查RTC中断是否使能验证EXTI_Line17中断配置测量LSI是否正常起振唤醒周期不准重新校准LSI频率检查RTC预分频器配置避免在中断服务程序中执行耗时操作功耗偏高确认所有外设时钟已关闭检查IO口状态断开调试接口有个特别实用的调试技巧在进入待机模式前点亮LED并延时1秒。这样当设备异常时可以通过观察LED判断是否成功进入了低功耗模式。

更多文章