跳转至

电源模式切换与唤醒源配置

概述

电源模式切换是嵌入式系统低功耗设计的核心技术之一。通过在不同工作负载下切换到合适的电源模式,系统可以在保证功能的同时大幅降低功耗。本教程将深入讲解多级睡眠模式、各种唤醒源的配置方法、唤醒时间优化技术以及状态保存恢复策略。

什么是电源模式切换

电源模式切换是指根据系统的工作状态,在不同的功耗模式之间进行切换的技术。典型的电源模式包括:

  1. 运行模式 (Run Mode)
  2. CPU全速运行
  3. 所有外设可用
  4. 功耗最高
  5. 响应最快

  6. 睡眠模式 (Sleep Mode)

  7. CPU停止,外设运行
  8. 中断可唤醒
  9. 功耗中等
  10. 唤醒快速

  11. 停止模式 (Stop Mode)

  12. CPU和大部分外设停止
  13. 保持RAM数据
  14. 功耗较低
  15. 唤醒稍慢

  16. 待机模式 (Standby Mode)

  17. 几乎所有电路关闭
  18. 仅保持RTC和备份寄存器
  19. 功耗极低
  20. 唤醒最慢

电源模式的重要性

为什么需要电源模式切换:

1. 延长电池寿命
   - 运行模式:50-100 mA
   - 睡眠模式:5-10 mA
   - 停止模式:10-100 μA
   - 待机模式:1-10 μA
   - 合理切换可延长电池寿命10-100倍

2. 降低系统功耗
   - 减少发热
   - 提高可靠性
   - 降低运营成本
   - 符合环保要求

3. 优化用户体验
   - 快速响应
   - 长续航时间
   - 静音运行
   - 温度适宜

4. 满足应用需求
   - 物联网节点:长期待机
   - 可穿戴设备:频繁唤醒
   - 传感器网络:定时采集
   - 移动设备:按需工作

应用场景

  • 物联网传感器:定时采集数据,大部分时间处于睡眠状态
  • 可穿戴设备:待机时低功耗,交互时快速响应
  • 智能家居:待命状态功耗极低,接收指令快速唤醒
  • 便携设备:电池供电,需要最大化续航时间
  • 工业监控:长期运行,降低能耗和发热

第一部分:多级睡眠模式详解

STM32电源模式架构

STM32F1系列电源模式

电源模式层次结构:

┌─────────────────────────────────────────────────────┐
│                  运行模式 (Run)                      │
│  - CPU: 运行                                         │
│  - 时钟: 全部使能                                    │
│  - 外设: 全部可用                                    │
│  - 功耗: 50-100 mA @ 72MHz                          │
│  - 唤醒: N/A                                         │
└─────────────────────────────────────────────────────┘
                      ↓ WFI/WFE
┌─────────────────────────────────────────────────────┐
│                  睡眠模式 (Sleep)                    │
│  - CPU: 停止                                         │
│  - 时钟: 运行                                        │
│  - 外设: 运行                                        │
│  - 功耗: 5-10 mA                                     │
│  - 唤醒: 任意中断 (<1μs)                            │
└─────────────────────────────────────────────────────┘
                      ↓ PDDS=0
┌─────────────────────────────────────────────────────┐
│                  停止模式 (Stop)                     │
│  - CPU: 停止                                         │
│  - 时钟: HSI/HSE停止                                │
│  - 外设: 部分停止                                    │
│  - 功耗: 10-100 μA                                   │
│  - 唤醒: EXTI, RTC (5-10μs)                         │
└─────────────────────────────────────────────────────┘
                      ↓ PDDS=1
┌─────────────────────────────────────────────────────┐
│                  待机模式 (Standby)                  │
│  - CPU: 关闭                                         │
│  - 时钟: 全部关闭                                    │
│  - 外设: 关闭(除RTC)                              │
│  - 功耗: 1-10 μA                                     │
│  - 唤醒: WKUP, RTC, RSTn (>1ms)                     │
└─────────────────────────────────────────────────────┘

模式选择原则:
1. 短时间空闲(<1ms):睡眠模式
2. 中等时间空闲(1ms-1s):停止模式
3. 长时间空闲(>1s):待机模式
4. 需要保持外设状态:睡眠/停止模式
5. 极致低功耗:待机模式

睡眠模式实现

睡眠模式特点: - CPU时钟停止,外设时钟继续运行 - 任何中断或事件都可以唤醒 - 唤醒时间极短(<1μs) - 适合短时间等待

/**
 * @file    sleep_mode.c
 * @brief   睡眠模式实现
 */

#include "stm32f1xx_hal.h"
#include <stdio.h>

/**
 * @brief  进入睡眠模式
 * @param  sleep_on_exit: 是否在中断返回后继续睡眠
 * @retval None
 * 
 * @note   睡眠模式特性:
 *         - CPU停止,外设继续运行
 *         - 任意中断可唤醒
 *         - 唤醒后从停止处继续执行
 *         - 功耗:约5-10mA
 */
void Enter_SleepMode(bool sleep_on_exit) {
    printf("进入睡眠模式...\n");

    // 记录进入时间
    uint32_t enter_time = HAL_GetTick();

    if (sleep_on_exit) {
        // 设置SLEEPONEXIT位,中断返回后自动进入睡眠
        // 适用于中断驱动的应用
        HAL_PWR_EnableSleepOnExit();
    }

    // 进入睡眠模式
    // WFI: Wait For Interrupt
    // WFE: Wait For Event
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);

    // 唤醒后从这里继续执行
    uint32_t wake_time = HAL_GetTick();
    uint32_t sleep_duration = wake_time - enter_time;

    printf("从睡眠模式唤醒,睡眠时间: %lu ms\n", sleep_duration);
}

/**
 * @brief  睡眠模式示例:等待UART接收
 */
void Sleep_Example_UART_Wait(void) {
    printf("等待UART数据,进入睡眠模式\n");

    // 使能UART接收中断
    HAL_UART_Receive_IT(&huart1, rx_buffer, 1);

    // 进入睡眠模式,等待UART中断唤醒
    Enter_SleepMode(false);

    // UART中断唤醒后继续执行
    printf("UART数据接收完成\n");
}

/**
 * @brief  睡眠模式示例:定时器周期唤醒
 */
void Sleep_Example_Timer_Periodic(void) {
    printf("启动定时器周期唤醒\n");

    // 配置定时器,每100ms产生一次中断
    HAL_TIM_Base_Start_IT(&htim2);

    while (1) {
        // 进入睡眠模式
        Enter_SleepMode(false);

        // 定时器中断唤醒后执行任务
        printf("定时器唤醒,执行周期任务\n");

        // 执行实际任务
        // ...
    }
}

停止模式实现

停止模式特点: - CPU和大部分外设时钟停止 - 保持SRAM和寄存器内容 - 可通过EXTI线或RTC唤醒 - 唤醒时间约5-10μs - 适合中等时间等待

/**
 * @file    stop_mode.c
 * @brief   停止模式实现
 */

#include "stm32f1xx_hal.h"
#include <stdio.h>

/**
 * @brief  进入停止模式
 * @param  regulator: 稳压器模式
 *         - PWR_MAINREGULATOR_ON: 主稳压器开启
 *         - PWR_LOWPOWERREGULATOR_ON: 低功耗稳压器开启
 * @retval None
 * 
 * @note   停止模式特性:
 *         - CPU和大部分外设停止
 *         - 保持SRAM和寄存器
 *         - EXTI和RTC可唤醒
 *         - 功耗:10-100μA
 *         - 唤醒后需要重新配置时钟
 */
void Enter_StopMode(uint32_t regulator) {
    printf("进入停止模式...\n");

    // 记录进入时间(使用RTC,因为SysTick会停止)
    uint32_t enter_time = HAL_GetTick();

    // 清除唤醒标志
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);

    // 进入停止模式
    HAL_PWR_EnterSTOPMode(regulator, PWR_STOPENTRY_WFI);

    // 唤醒后从这里继续执行
    // 注意:此时系统时钟为HSI (8MHz),需要重新配置

    // 重新配置系统时钟到72MHz
    SystemClock_Config();

    uint32_t wake_time = HAL_GetTick();
    uint32_t sleep_duration = wake_time - enter_time;

    printf("从停止模式唤醒,睡眠时间: %lu ms\n", sleep_duration);
}

/**
 * @brief  停止模式示例:外部中断唤醒
 */
void Stop_Example_EXTI_Wakeup(void) {
    printf("配置外部中断唤醒源\n");

    // 配置PA0为外部中断(按键)
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 使能GPIOA时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();

    // 配置PA0
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;  // 上升沿触发
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 使能EXTI0中断
    HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);

    printf("按下按键唤醒系统\n");

    // 进入停止模式
    Enter_StopMode(PWR_LOWPOWERREGULATOR_ON);

    printf("按键按下,系统唤醒\n");
}

/**
 * @brief  外部中断回调函数
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    if (GPIO_Pin == GPIO_PIN_0) {
        // PA0中断,系统将从停止模式唤醒
        printf("EXTI0中断触发\n");
    }
}

待机模式实现

待机模式特点: - 几乎所有电路关闭 - 仅保持RTC和备份寄存器 - 功耗极低(1-10μA) - 唤醒时间较长(>1ms) - 唤醒后相当于复位

/**
 * @file    standby_mode.c
 * @brief   待机模式实现
 */

#include "stm32f1xx_hal.h"
#include <stdio.h>

/**
 * @brief  进入待机模式
 * @param  None
 * @retval None
 * 
 * @note   待机模式特性:
 *         - 几乎所有电路关闭
 *         - 仅保持RTC和备份寄存器
 *         - 功耗:1-10μA
 *         - 唤醒后相当于系统复位
 *         - 需要在备份寄存器中保存状态
 */
void Enter_StandbyMode(void) {
    printf("进入待机模式...\n");

    // 使能PWR时钟
    __HAL_RCC_PWR_CLK_ENABLE();

    // 使能备份域访问
    HAL_PWR_EnableBkUpAccess();

    // 在备份寄存器中保存标志,表示从待机模式唤醒
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0xA5A5);

    // 清除唤醒标志
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);

    // 使能WKUP引脚唤醒功能
    HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);

    // 进入待机模式
    HAL_PWR_EnterSTANDBYMode();

    // 注意:这里的代码不会执行
    // 唤醒后系统会复位,从main函数开始执行
}

/**
 * @brief  检查是否从待机模式唤醒
 * @retval true: 从待机模式唤醒, false: 正常上电
 */
bool Check_StandbyWakeup(void) {
    // 使能PWR和备份域时钟
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_RCC_BKP_CLK_ENABLE();

    // 使能备份域访问
    HAL_PWR_EnableBkUpAccess();

    // 检查待机标志
    if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET) {
        // 清除待机标志
        __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);

        // 检查备份寄存器标志
        uint32_t backup_flag = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1);
        if (backup_flag == 0xA5A5) {
            printf("从待机模式唤醒\n");
            return true;
        }
    }

    printf("正常上电启动\n");
    return false;
}

/**
 * @brief  待机模式示例:定时唤醒
 */
void Standby_Example_RTC_Wakeup(void) {
    printf("配置RTC定时唤醒\n");

    // 配置RTC闹钟,10秒后唤醒
    RTC_AlarmTypeDef sAlarm = {0};

    // 获取当前时间
    RTC_TimeTypeDef sTime;
    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);

    // 设置闹钟时间(当前时间+10秒)
    sAlarm.AlarmTime.Hours = sTime.Hours;
    sAlarm.AlarmTime.Minutes = sTime.Minutes;
    sAlarm.AlarmTime.Seconds = (sTime.Seconds + 10) % 60;
    sAlarm.Alarm = RTC_ALARM_A;

    HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);

    printf("10秒后RTC唤醒系统\n");

    // 进入待机模式
    Enter_StandbyMode();
}

/**
 * @brief  待机模式示例:WKUP引脚唤醒
 */
void Standby_Example_WKUP_Pin(void) {
    printf("配置WKUP引脚唤醒\n");
    printf("按下WKUP按键唤醒系统\n");

    // 进入待机模式
    // WKUP引脚(PA0)上升沿可唤醒
    Enter_StandbyMode();
}

/**
 * @brief  主函数中的待机模式处理
 */
void Standby_Main_Handler(void) {
    // 检查是否从待机模式唤醒
    if (Check_StandbyWakeup()) {
        // 从待机模式唤醒,恢复状态
        printf("恢复系统状态\n");

        // 从备份寄存器读取保存的数据
        uint32_t saved_data = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
        printf("恢复的数据: 0x%08lX\n", saved_data);

        // 清除备份寄存器标志
        HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0);
    } else {
        // 正常上电,初始化系统
        printf("系统初始化\n");
    }
}

第二部分:RTC唤醒配置

RTC基础配置

RTC (Real-Time Clock) 是一个独立的定时器,即使在停止和待机模式下也能继续运行,是实现定时唤醒的关键。

/**
 * @file    rtc_wakeup.c
 * @brief   RTC唤醒配置实现
 */

#include "stm32f1xx_hal.h"
#include <stdio.h>

RTC_HandleTypeDef hrtc;

/**
 * @brief  初始化RTC
 * @retval None
 */
void RTC_Init(void) {
    // 使能PWR和BKP时钟
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_RCC_BKP_CLK_ENABLE();

    // 使能备份域访问
    HAL_PWR_EnableBkUpAccess();

    // 配置RTC时钟源为LSE(32.768kHz外部晶振)
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
    RCC_OscInitStruct.LSEState = RCC_LSE_ON;
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    // 选择RTC时钟源
    RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
    HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit);

    // 使能RTC时钟
    __HAL_RCC_RTC_ENABLE();

    // 配置RTC
    hrtc.Instance = RTC;
    hrtc.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
    HAL_RTC_Init(&hrtc);

    printf("RTC初始化完成\n");
}

/**
 * @brief  配置RTC闹钟唤醒
 * @param  seconds: 多少秒后唤醒
 * @retval None
 */
void RTC_SetAlarmWakeup(uint32_t seconds) {
    RTC_AlarmTypeDef sAlarm = {0};
    RTC_TimeTypeDef sTime;
    RTC_DateTypeDef sDate;

    // 获取当前时间
    HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
    HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);

    // 计算唤醒时间
    uint32_t total_seconds = sTime.Hours * 3600 + 
                             sTime.Minutes * 60 + 
                             sTime.Seconds + seconds;

    sAlarm.AlarmTime.Hours = (total_seconds / 3600) % 24;
    sAlarm.AlarmTime.Minutes = (total_seconds / 60) % 60;
    sAlarm.AlarmTime.Seconds = total_seconds % 60;
    sAlarm.Alarm = RTC_ALARM_A;

    // 设置闹钟
    HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);

    printf("RTC闹钟设置: %d秒后唤醒\n", seconds);
    printf("唤醒时间: %02d:%02d:%02d\n",
           sAlarm.AlarmTime.Hours,
           sAlarm.AlarmTime.Minutes,
           sAlarm.AlarmTime.Seconds);
}

/**
 * @brief  RTC闹钟中断回调
 */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) {
    printf("RTC闹钟触发,系统唤醒\n");
}

/**
 * @brief  RTC周期唤醒示例
 */
void RTC_Periodic_Wakeup_Example(void) {
    printf("RTC周期唤醒示例\n");

    uint32_t wakeup_count = 0;

    while (1) {
        // 设置10秒后唤醒
        RTC_SetAlarmWakeup(10);

        printf("进入停止模式,等待RTC唤醒...\n");

        // 进入停止模式
        Enter_StopMode(PWR_LOWPOWERREGULATOR_ON);

        // RTC唤醒后继续执行
        wakeup_count++;
        printf("第 %lu 次唤醒\n", wakeup_count);

        // 执行周期任务
        // 例如:读取传感器数据
        printf("执行数据采集任务\n");

        // 任务完成后继续睡眠
    }
}

RTC秒中断唤醒

/**
 * @brief  配置RTC秒中断
 * @retval None
 * 
 * @note   RTC秒中断每秒触发一次
 *         可用于精确的周期唤醒
 */
void RTC_SecondInterrupt_Config(void) {
    // 使能RTC秒中断
    __HAL_RTC_SECOND_ENABLE_IT(&hrtc, RTC_IT_SEC);

    // 配置NVIC
    HAL_NVIC_SetPriority(RTC_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(RTC_IRQn);

    printf("RTC秒中断配置完成\n");
}

/**
 * @brief  RTC秒中断回调
 */
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc) {
    static uint32_t second_count = 0;
    second_count++;

    printf("RTC秒中断: %lu 秒\n", second_count);
}

/**
 * @brief  RTC秒中断唤醒示例
 */
void RTC_Second_Wakeup_Example(void) {
    printf("RTC秒中断唤醒示例\n");

    // 配置RTC秒中断
    RTC_SecondInterrupt_Config();

    while (1) {
        printf("进入停止模式,每秒唤醒一次\n");

        // 进入停止模式
        Enter_StopMode(PWR_LOWPOWERREGULATOR_ON);

        // 每秒唤醒一次,执行任务
        printf("秒中断唤醒,执行任务\n");
    }
}

第三部分:外部中断唤醒

EXTI配置

EXTI (External Interrupt) 可以在停止模式下唤醒系统,支持GPIO、RTC、USB等多种唤醒源。

/**
 * @file    exti_wakeup.c
 * @brief   外部中断唤醒配置
 */

#include "stm32f1xx_hal.h"
#include <stdio.h>

/**
 * @brief  配置GPIO外部中断唤醒
 * @param  GPIO_Port: GPIO端口
 * @param  GPIO_Pin: GPIO引脚
 * @param  trigger: 触发方式
 * @retval None
 */
void EXTI_GPIO_Wakeup_Config(GPIO_TypeDef* GPIO_Port, uint16_t GPIO_Pin, 
                              uint32_t trigger) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 使能GPIO时钟
    if (GPIO_Port == GPIOA) {
        __HAL_RCC_GPIOA_CLK_ENABLE();
    } else if (GPIO_Port == GPIOB) {
        __HAL_RCC_GPIOB_CLK_ENABLE();
    } else if (GPIO_Port == GPIOC) {
        __HAL_RCC_GPIOC_CLK_ENABLE();
    }

    // 配置GPIO为外部中断模式
    GPIO_InitStruct.Pin = GPIO_Pin;
    GPIO_InitStruct.Mode = trigger;  // GPIO_MODE_IT_RISING/FALLING/RISING_FALLING
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIO_Port, &GPIO_InitStruct);

    // 配置NVIC
    IRQn_Type IRQn;
    if (GPIO_Pin == GPIO_PIN_0) {
        IRQn = EXTI0_IRQn;
    } else if (GPIO_Pin == GPIO_PIN_1) {
        IRQn = EXTI1_IRQn;
    } else if (GPIO_Pin <= GPIO_PIN_4) {
        IRQn = EXTI2_IRQn + (GPIO_Pin - GPIO_PIN_2);
    } else if (GPIO_Pin <= GPIO_PIN_9) {
        IRQn = EXTI9_5_IRQn;
    } else {
        IRQn = EXTI15_10_IRQn;
    }

    HAL_NVIC_SetPriority(IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(IRQn);

    printf("EXTI唤醒配置完成: GPIO引脚 %d\n", GPIO_Pin);
}

/**
 * @brief  多按键唤醒示例
 */
void EXTI_MultiButton_Wakeup_Example(void) {
    printf("配置多按键唤醒\n");

    // 配置按键1(PA0)- 上升沿触发
    EXTI_GPIO_Wakeup_Config(GPIOA, GPIO_PIN_0, GPIO_MODE_IT_RISING);

    // 配置按键2(PA1)- 上升沿触发
    EXTI_GPIO_Wakeup_Config(GPIOA, GPIO_PIN_1, GPIO_MODE_IT_RISING);

    // 配置按键3(PA2)- 上升沿触发
    EXTI_GPIO_Wakeup_Config(GPIOA, GPIO_PIN_2, GPIO_MODE_IT_RISING);

    printf("按下任意按键唤醒系统\n");

    // 进入停止模式
    Enter_StopMode(PWR_LOWPOWERREGULATOR_ON);

    printf("按键唤醒成功\n");
}

/**
 * @brief  GPIO中断回调函数
 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
    switch (GPIO_Pin) {
        case GPIO_PIN_0:
            printf("按键1按下(PA0)\n");
            break;
        case GPIO_PIN_1:
            printf("按键2按下(PA1)\n");
            break;
        case GPIO_PIN_2:
            printf("按键3按下(PA2)\n");
            break;
        default:
            printf("其他GPIO中断: Pin %d\n", GPIO_Pin);
            break;
    }
}

传感器中断唤醒

/**
 * @brief  配置传感器中断唤醒
 * @retval None
 * 
 * @note   许多传感器支持中断输出
 *         例如:加速度计的运动检测中断
 *              温度传感器的阈值中断
 */
void Sensor_Interrupt_Wakeup_Config(void) {
    printf("配置传感器中断唤醒\n");

    // 示例:配置加速度计运动检测中断(假设连接到PB5)
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    __HAL_RCC_GPIOB_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_5;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
    GPIO_InitStruct.Pull = GPIO_PULLDOWN;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);

    printf("传感器中断配置完成\n");
}

/**
 * @brief  传感器中断唤醒示例
 */
void Sensor_Wakeup_Example(void) {
    printf("传感器中断唤醒示例\n");

    // 配置传感器中断
    Sensor_Interrupt_Wakeup_Config();

    // 配置传感器的运动检测阈值
    // Sensor_ConfigMotionDetection(threshold);

    printf("系统进入睡眠,等待运动检测\n");

    // 进入停止模式
    Enter_StopMode(PWR_LOWPOWERREGULATOR_ON);

    printf("检测到运动,系统唤醒\n");

    // 读取传感器数据
    // Sensor_ReadData();
}

第四部分:唤醒时间优化

唤醒时间分析

唤醒时间组成:

总唤醒时间 = 硬件唤醒时间 + 软件初始化时间

1. 硬件唤醒时间:
   - 睡眠模式:<1μs
   - 停止模式:5-10μs(HSI启动)
   - 待机模式:>1ms(完整复位)

2. 软件初始化时间:
   - 时钟配置:100-500μs
   - 外设初始化:取决于外设数量
   - 应用初始化:取决于应用复杂度

优化策略:
1. 选择合适的电源模式
2. 优化时钟配置代码
3. 延迟非关键外设初始化
4. 使用快速启动技术

快速唤醒优化

/**
 * @file    fast_wakeup.c
 * @brief   快速唤醒优化实现
 */

#include "stm32f1xx_hal.h"
#include <stdio.h>

/**
 * @brief  快速时钟配置(优化版)
 * @retval None
 * 
 * @note   从停止模式唤醒后,系统时钟为HSI (8MHz)
 *         需要快速配置到目标频率
 */
void Fast_SystemClock_Config(void) {
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    // 配置PLL(使用HSI作为时钟源,避免等待HSE启动)
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
    RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;  // HSI/2 = 4MHz
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;  // 4MHz × 16 = 64MHz
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    // 配置系统时钟
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
                                  RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);

    SystemCoreClockUpdate();
}

/**
 * @brief  延迟外设初始化
 * @retval None
 * 
 * @note   只初始化关键外设,其他外设按需初始化
 */
void Lazy_Peripheral_Init(void) {
    // 只初始化必要的外设
    // 例如:GPIO、UART(用于调试)

    // 其他外设在实际使用时再初始化
}

/**
 * @brief  测量唤醒时间
 * @retval 唤醒时间(微秒)
 */
uint32_t Measure_Wakeup_Time(void) {
    // 使用DWT计数器精确测量
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    DWT->CYCCNT = 0;
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

    // 记录进入睡眠前的周期数
    uint32_t start_cycles = DWT->CYCCNT;

    // 进入停止模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

    // 唤醒后立即读取周期数
    uint32_t end_cycles = DWT->CYCCNT;

    // 重新配置时钟
    Fast_SystemClock_Config();

    // 计算唤醒时间
    uint32_t cycles = end_cycles - start_cycles;
    uint32_t wakeup_time_us = cycles / (SystemCoreClock / 1000000);

    printf("唤醒时间: %lu μs\n", wakeup_time_us);

    return wakeup_time_us;
}

/**
 * @brief  快速唤醒示例
 */
void Fast_Wakeup_Example(void) {
    printf("快速唤醒优化示例\n");

    // 配置外部中断唤醒
    EXTI_GPIO_Wakeup_Config(GPIOA, GPIO_PIN_0, GPIO_MODE_IT_RISING);

    printf("测量唤醒时间...\n");

    // 测量10次取平均值
    uint32_t total_time = 0;
    for (int i = 0; i < 10; i++) {
        uint32_t wakeup_time = Measure_Wakeup_Time();
        total_time += wakeup_time;
        HAL_Delay(100);
    }

    uint32_t avg_time = total_time / 10;
    printf("平均唤醒时间: %lu μs\n", avg_time);
}

第五部分:状态保存与恢复

状态保存策略

状态保存方案对比:

1. SRAM保存(睡眠/停止模式)
   优点:速度快、容量大
   缺点:待机模式会丢失
   适用:短期睡眠

2. 备份寄存器(所有模式)
   优点:待机模式保持
   缺点:容量小(20字节)
   适用:关键状态

3. Flash存储(所有模式)
   优点:永久保存、容量大
   缺点:写入慢、寿命有限
   适用:配置参数

4. 外部EEPROM(所有模式)
   优点:容量大、寿命长
   缺点:需要外部器件
   适用:大量数据

状态保存实现

/**
 * @file    state_management.c
 * @brief   状态保存与恢复实现
 */

#include "stm32f1xx_hal.h"
#include <stdio.h>
#include <string.h>

/**
 * @brief  系统状态结构
 */
typedef struct {
    uint32_t magic_number;      // 魔数,用于验证数据有效性
    uint32_t system_state;      // 系统状态
    uint32_t sensor_data[10];   // 传感器数据
    uint32_t timestamp;         // 时间戳
    uint32_t checksum;          // 校验和
} SystemState_t;

static SystemState_t system_state = {0};

#define STATE_MAGIC_NUMBER  0x12345678

/**
 * @brief  计算校验和
 * @param  data: 数据指针
 * @param  length: 数据长度(字节)
 * @retval 校验和
 */
uint32_t Calculate_Checksum(uint8_t* data, uint32_t length) {
    uint32_t checksum = 0;
    for (uint32_t i = 0; i < length; i++) {
        checksum += data[i];
    }
    return checksum;
}

/**
 * @brief  保存状态到备份寄存器
 * @param  state: 状态结构指针
 * @retval 是否成功
 * 
 * @note   STM32F1有10个备份寄存器(BKP_DR1-BKP_DR10)
 *         每个寄存器16位,共20字节
 */
bool Save_State_To_Backup(SystemState_t* state) {
    // 使能PWR和BKP时钟
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_RCC_BKP_CLK_ENABLE();

    // 使能备份域访问
    HAL_PWR_EnableBkUpAccess();

    // 设置魔数
    state->magic_number = STATE_MAGIC_NUMBER;

    // 计算校验和
    state->checksum = Calculate_Checksum((uint8_t*)state, 
                                         sizeof(SystemState_t) - sizeof(uint32_t));

    // 保存到备份寄存器(示例:只保存部分数据)
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, (state->magic_number >> 16) & 0xFFFF);
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR2, state->magic_number & 0xFFFF);
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR3, (state->system_state >> 16) & 0xFFFF);
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR4, state->system_state & 0xFFFF);
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR5, (state->timestamp >> 16) & 0xFFFF);
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR6, state->timestamp & 0xFFFF);

    printf("状态已保存到备份寄存器\n");
    return true;
}

/**
 * @brief  从备份寄存器恢复状态
 * @param  state: 状态结构指针
 * @retval 是否成功
 */
bool Restore_State_From_Backup(SystemState_t* state) {
    // 使能PWR和BKP时钟
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_RCC_BKP_CLK_ENABLE();

    // 使能备份域访问
    HAL_PWR_EnableBkUpAccess();

    // 读取魔数
    uint32_t magic_high = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1);
    uint32_t magic_low = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR2);
    state->magic_number = (magic_high << 16) | magic_low;

    // 验证魔数
    if (state->magic_number != STATE_MAGIC_NUMBER) {
        printf("备份寄存器数据无效\n");
        return false;
    }

    // 读取其他数据
    uint32_t state_high = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR3);
    uint32_t state_low = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR4);
    state->system_state = (state_high << 16) | state_low;

    uint32_t time_high = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR5);
    uint32_t time_low = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR6);
    state->timestamp = (time_high << 16) | time_low;

    printf("状态已从备份寄存器恢复\n");
    printf("系统状态: 0x%08lX\n", state->system_state);
    printf("时间戳: %lu\n", state->timestamp);

    return true;
}

/**
 * @brief  保存状态到Flash
 * @param  state: 状态结构指针
 * @retval 是否成功
 */
bool Save_State_To_Flash(SystemState_t* state) {
    // Flash地址(使用最后一页)
    #define STATE_FLASH_ADDR  0x0801FC00  // 假设Flash最后一页

    HAL_StatusTypeDef status;

    // 解锁Flash
    HAL_FLASH_Unlock();

    // 擦除页
    FLASH_EraseInitTypeDef EraseInitStruct;
    uint32_t PageError;

    EraseInitStruct.TypeErase = FLASH_TYPEERASE_PAGES;
    EraseInitStruct.PageAddress = STATE_FLASH_ADDR;
    EraseInitStruct.NbPages = 1;

    status = HAL_FLASHEx_Erase(&EraseInitStruct, &PageError);
    if (status != HAL_OK) {
        printf("Flash擦除失败\n");
        HAL_FLASH_Lock();
        return false;
    }

    // 写入数据
    uint32_t* data = (uint32_t*)state;
    uint32_t length = sizeof(SystemState_t) / 4;

    for (uint32_t i = 0; i < length; i++) {
        status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD,
                                    STATE_FLASH_ADDR + i * 4,
                                    data[i]);
        if (status != HAL_OK) {
            printf("Flash写入失败\n");
            HAL_FLASH_Lock();
            return false;
        }
    }

    // 锁定Flash
    HAL_FLASH_Lock();

    printf("状态已保存到Flash\n");
    return true;
}

/**
 * @brief  从Flash恢复状态
 * @param  state: 状态结构指针
 * @retval 是否成功
 */
bool Restore_State_From_Flash(SystemState_t* state) {
    #define STATE_FLASH_ADDR  0x0801FC00

    // 从Flash读取数据
    memcpy(state, (void*)STATE_FLASH_ADDR, sizeof(SystemState_t));

    // 验证魔数
    if (state->magic_number != STATE_MAGIC_NUMBER) {
        printf("Flash数据无效\n");
        return false;
    }

    // 验证校验和
    uint32_t checksum = Calculate_Checksum((uint8_t*)state,
                                           sizeof(SystemState_t) - sizeof(uint32_t));
    if (checksum != state->checksum) {
        printf("Flash数据校验失败\n");
        return false;
    }

    printf("状态已从Flash恢复\n");
    return true;
}

/**
 * @brief  状态保存恢复示例
 */
void State_Management_Example(void) {
    printf("状态管理示例\n");

    // 准备状态数据
    system_state.system_state = 0x12345678;
    system_state.sensor_data[0] = 100;
    system_state.sensor_data[1] = 200;
    system_state.timestamp = HAL_GetTick();

    // 保存到备份寄存器
    Save_State_To_Backup(&system_state);

    // 保存到Flash
    Save_State_To_Flash(&system_state);

    printf("进入待机模式...\n");
    HAL_Delay(100);

    // 进入待机模式
    Enter_StandbyMode();

    // 唤醒后,在main函数中恢复状态
    // SystemState_t restored_state;
    // Restore_State_From_Backup(&restored_state);
    // 或
    // Restore_State_From_Flash(&restored_state);
}

上下文保存优化

/**
 * @brief  最小上下文保存
 * @retval None
 * 
 * @note   只保存必要的状态,减少保存时间
 */
void Minimal_Context_Save(void) {
    // 只保存关键状态
    typedef struct {
        uint32_t critical_flag;
        uint32_t error_code;
        uint32_t wakeup_count;
    } MinimalContext_t;

    MinimalContext_t context;
    context.critical_flag = 0xABCD;
    context.error_code = 0;
    context.wakeup_count = 100;

    // 保存到备份寄存器
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR7, context.critical_flag);
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR8, context.error_code);
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR9, context.wakeup_count);

    printf("最小上下文已保存\n");
}

/**
 * @brief  压缩状态保存
 * @retval None
 * 
 * @note   使用位域压缩多个状态到一个寄存器
 */
void Compressed_State_Save(void) {
    // 使用位域压缩状态
    typedef struct {
        uint32_t led_state : 1;      // LED状态(1位)
        uint32_t motor_state : 1;    // 电机状态(1位)
        uint32_t sensor_enable : 1;  // 传感器使能(1位)
        uint32_t mode : 3;            // 工作模式(3位)
        uint32_t error_flags : 8;    // 错误标志(8位)
        uint32_t reserved : 18;      // 保留(18位)
    } CompressedState_t;

    CompressedState_t state;
    state.led_state = 1;
    state.motor_state = 0;
    state.sensor_enable = 1;
    state.mode = 3;
    state.error_flags = 0x12;

    // 保存到一个备份寄存器
    uint32_t packed_state = *(uint32_t*)&state;
    HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR10, packed_state & 0xFFFF);

    printf("压缩状态已保存: 0x%08lX\n", packed_state);
}

第六部分:综合应用示例

智能传感器节点

/**
 * @file    smart_sensor_node.c
 * @brief   智能传感器节点综合示例
 */

#include "stm32f1xx_hal.h"
#include <stdio.h>

/**
 * @brief  传感器节点配置
 */
typedef struct {
    uint32_t sample_interval_s;    // 采样间隔(秒)
    uint32_t tx_interval_s;        // 发送间隔(秒)
    uint8_t  samples_per_tx;       // 每次发送的样本数
    bool     motion_wakeup_enable; // 运动唤醒使能
} SensorNodeConfig_t;

static SensorNodeConfig_t node_config = {
    .sample_interval_s = 60,       // 每60秒采样一次
    .tx_interval_s = 600,          // 每10分钟发送一次
    .samples_per_tx = 10,          // 每次发送10个样本
    .motion_wakeup_enable = true   // 使能运动唤醒
};

/**
 * @brief  传感器数据缓冲
 */
#define MAX_SAMPLES  20
typedef struct {
    float temperature[MAX_SAMPLES];
    float humidity[MAX_SAMPLES];
    uint32_t timestamp[MAX_SAMPLES];
    uint8_t count;
} SensorDataBuffer_t;

static SensorDataBuffer_t data_buffer = {0};

/**
 * @brief  读取传感器数据
 */
void Read_Sensor_Data(void) {
    if (data_buffer.count >= MAX_SAMPLES) {
        printf("数据缓冲已满\n");
        return;
    }

    // 读取温湿度传感器
    // 这里使用模拟数据
    data_buffer.temperature[data_buffer.count] = 25.5f;
    data_buffer.humidity[data_buffer.count] = 60.0f;
    data_buffer.timestamp[data_buffer.count] = HAL_GetTick();
    data_buffer.count++;

    printf("采集数据 #%d: 温度=%.1f°C, 湿度=%.1f%%\n",
           data_buffer.count,
           data_buffer.temperature[data_buffer.count - 1],
           data_buffer.humidity[data_buffer.count - 1]);
}

/**
 * @brief  发送数据到服务器
 */
void Transmit_Data(void) {
    printf("发送数据到服务器...\n");

    // 打开无线模块
    PowerDomain_Enable(POWER_DOMAIN_WIRELESS);
    HAL_Delay(100);  // 等待模块启动

    // 发送数据
    for (uint8_t i = 0; i < data_buffer.count; i++) {
        printf("发送样本 #%d: T=%.1f°C, H=%.1f%%\n",
               i + 1,
               data_buffer.temperature[i],
               data_buffer.humidity[i]);

        // 实际发送代码
        // Wireless_Send(&data_buffer.temperature[i], ...);
    }

    // 清空缓冲
    data_buffer.count = 0;

    // 关闭无线模块
    PowerDomain_Disable(POWER_DOMAIN_WIRELESS);

    printf("数据发送完成\n");
}

/**
 * @brief  智能传感器节点主循环
 */
void Smart_Sensor_Node_Run(void) {
    printf("智能传感器节点启动\n");
    printf("采样间隔: %lu 秒\n", node_config.sample_interval_s);
    printf("发送间隔: %lu 秒\n", node_config.tx_interval_s);

    // 初始化RTC
    RTC_Init();

    // 配置运动检测唤醒(如果使能)
    if (node_config.motion_wakeup_enable) {
        Sensor_Interrupt_Wakeup_Config();
    }

    uint32_t sample_count = 0;

    while (1) {
        // 读取传感器数据
        Read_Sensor_Data();
        sample_count++;

        // 检查是否需要发送数据
        if (sample_count >= node_config.samples_per_tx) {
            Transmit_Data();
            sample_count = 0;
        }

        // 保存状态
        system_state.system_state = sample_count;
        system_state.timestamp = HAL_GetTick();
        Save_State_To_Backup(&system_state);

        // 配置RTC唤醒
        RTC_SetAlarmWakeup(node_config.sample_interval_s);

        printf("进入停止模式,%lu 秒后唤醒\n", node_config.sample_interval_s);

        // 进入停止模式
        Enter_StopMode(PWR_LOWPOWERREGULATOR_ON);

        // 唤醒后继续循环
        printf("系统唤醒\n");
    }
}

可穿戴设备电源管理

/**
 * @brief  可穿戴设备电源管理示例
 */
void Wearable_Device_Power_Management(void) {
    printf("可穿戴设备电源管理\n");

    typedef enum {
        DEVICE_STATE_ACTIVE,      // 活动状态(用户交互)
        DEVICE_STATE_MONITORING,  // 监测状态(后台监测)
        DEVICE_STATE_IDLE,        // 空闲状态(无活动)
        DEVICE_STATE_SLEEP        // 睡眠状态(深度睡眠)
    } DeviceState_t;

    DeviceState_t device_state = DEVICE_STATE_IDLE;
    uint32_t idle_time = 0;

    while (1) {
        switch (device_state) {
            case DEVICE_STATE_ACTIVE:
                printf("活动状态:全功能运行\n");

                // 使能所有外设
                PowerDomain_Enable(POWER_DOMAIN_DISPLAY);
                PowerDomain_Enable(POWER_DOMAIN_PERIPHERAL);

                // 高性能模式
                DVFS_SwitchOperatingPoint(3);  // 72MHz

                // 检测用户活动
                // if (no_user_activity_for_30s) {
                //     device_state = DEVICE_STATE_MONITORING;
                // }

                HAL_Delay(100);
                break;

            case DEVICE_STATE_MONITORING:
                printf("监测状态:后台监测\n");

                // 关闭显示
                PowerDomain_Disable(POWER_DOMAIN_DISPLAY);

                // 中等性能模式
                DVFS_SwitchOperatingPoint(1);  // 24MHz

                // 定期采集传感器数据
                Read_Sensor_Data();

                // 配置1秒后唤醒
                RTC_SetAlarmWakeup(1);

                // 进入睡眠模式
                Enter_SleepMode(false);

                idle_time++;

                // 检测是否进入空闲状态
                if (idle_time > 60) {  // 60秒无活动
                    device_state = DEVICE_STATE_IDLE;
                    idle_time = 0;
                }
                break;

            case DEVICE_STATE_IDLE:
                printf("空闲状态:低功耗待机\n");

                // 关闭大部分外设
                PowerDomain_Disable(POWER_DOMAIN_PERIPHERAL);

                // 配置运动检测唤醒
                Sensor_Interrupt_Wakeup_Config();

                // 配置按键唤醒
                EXTI_GPIO_Wakeup_Config(GPIOA, GPIO_PIN_0, GPIO_MODE_IT_RISING);

                // 进入停止模式
                Enter_StopMode(PWR_LOWPOWERREGULATOR_ON);

                // 唤醒后检查唤醒源
                printf("从空闲状态唤醒\n");
                device_state = DEVICE_STATE_ACTIVE;
                break;

            case DEVICE_STATE_SLEEP:
                printf("睡眠状态:深度睡眠\n");

                // 保存状态
                Save_State_To_Backup(&system_state);

                // 配置RTC唤醒(例如:8小时后)
                RTC_SetAlarmWakeup(8 * 3600);

                // 进入待机模式
                Enter_StandbyMode();
                break;
        }
    }
}

实践练习

练习1:多模式切换

目标:实现一个支持多种电源模式切换的系统

要求: 1. 实现睡眠、停止、待机三种模式 2. 支持按键、RTC、传感器三种唤醒源 3. 测量并优化唤醒时间 4. 实现状态保存和恢复

提示

// 伪代码框架
void Power_Mode_Exercise(void) {
    // 1. 初始化系统
    System_Init();
    RTC_Init();

    // 2. 配置唤醒源
    EXTI_GPIO_Wakeup_Config(...);
    RTC_SetAlarmWakeup(...);
    Sensor_Interrupt_Wakeup_Config();

    // 3. 主循环
    while (1) {
        // 执行任务
        Do_Task();

        // 保存状态
        Save_State_To_Backup(&system_state);

        // 选择电源模式
        PowerMode_t mode = Select_Power_Mode();

        // 进入电源模式
        Enter_Power_Mode(mode);

        // 唤醒后恢复状态
        Restore_State_From_Backup(&system_state);
    }
}

练习2:自适应电源管理

目标:实现根据系统负载自动选择电源模式

要求: 1. 监测系统空闲时间 2. 根据空闲时间选择合适的电源模式 3. 实现快速唤醒机制 4. 统计功耗数据

提示

PowerMode_t Select_Power_Mode_Adaptive(uint32_t idle_time_ms) {
    if (idle_time_ms < 10) {
        return POWER_MODE_RUN;      // 继续运行
    } else if (idle_time_ms < 100) {
        return POWER_MODE_SLEEP;    // 睡眠模式
    } else if (idle_time_ms < 1000) {
        return POWER_MODE_STOP;     // 停止模式
    } else {
        return POWER_MODE_STANDBY;  // 待机模式
    }
}

练习3:物联网节点实现

目标:实现一个低功耗物联网传感器节点

要求: 1. 定时采集传感器数据 2. 批量发送数据到服务器 3. 支持运动检测唤醒 4. 实现超低功耗(平均<100μA)

参考设计

工作流程:
1. 采集数据(10秒)
2. 进入停止模式(50秒)
3. RTC唤醒,重复步骤1-2
4. 累积10个样本后发送
5. 发送完成后继续采集

功耗分析:
- 采集:10mA × 10s = 100mAs
- 睡眠:50μA × 50s = 2.5mAs
- 发送:100mA × 5s = 500mAs
- 平均:(100 + 2.5 + 500) / 600s ≈ 1mA

常见问题与解决方案

问题1:从停止模式唤醒后系统不工作

原因: - 时钟未重新配置 - 外设未重新初始化

解决方案

void Enter_StopMode_Safe(void) {
    // 进入停止模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

    // 唤醒后立即重新配置时钟
    SystemClock_Config();

    // 重新初始化必要的外设
    // HAL_UART_Init(&huart1);
}

问题2:RTC唤醒不工作

原因: - RTC未正确初始化 - LSE晶振未起振 - 中断未使能

解决方案

void RTC_Debug(void) {
    // 检查LSE是否起振
    if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET) {
        printf("错误:LSE未起振\n");
        // 尝试使用LSI
        // ...
    }

    // 检查RTC时钟是否使能
    if (__HAL_RCC_RTC_GET_FLAG() == RESET) {
        printf("错误:RTC时钟未使能\n");
    }

    // 检查中断是否使能
    if (HAL_NVIC_GetEnableIRQ(RTC_IRQn) == 0) {
        printf("错误:RTC中断未使能\n");
    }
}

问题3:待机模式唤醒后数据丢失

原因: - 未保存到备份寄存器或Flash - 备份域未使能

解决方案

void Standby_Data_Protection(void) {
    // 使能备份域
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_RCC_BKP_CLK_ENABLE();
    HAL_PWR_EnableBkUpAccess();

    // 保存关键数据到备份寄存器
    Save_State_To_Backup(&system_state);

    // 或保存到Flash
    Save_State_To_Flash(&system_state);

    // 进入待机模式
    Enter_StandbyMode();
}

问题4:功耗仍然很高

原因: - 外设未关闭 - GPIO配置不当 - 调试接口未禁用

解决方案

void Minimize_Power_Consumption(void) {
    // 1. 关闭所有不需要的外设时钟
    __HAL_RCC_USART1_CLK_DISABLE();
    __HAL_RCC_SPI1_CLK_DISABLE();
    __HAL_RCC_I2C1_CLK_DISABLE();

    // 2. 配置所有GPIO为模拟输入(最低功耗)
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Pin = GPIO_PIN_All;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    // 3. 禁用调试接口(可选,会影响调试)
    // __HAL_AFIO_REMAP_SWJ_DISABLE();

    // 4. 使用低功耗稳压器
    Enter_StopMode(PWR_LOWPOWERREGULATOR_ON);
}

总结

本教程深入讲解了嵌入式系统的电源模式切换与唤醒源配置技术。通过学习本教程,你应该掌握:

  1. 多级睡眠模式
  2. 睡眠模式:快速唤醒,中等功耗
  3. 停止模式:平衡功耗和唤醒时间
  4. 待机模式:极低功耗,唤醒较慢

  5. 唤醒源配置

  6. RTC定时唤醒:周期性任务
  7. 外部中断唤醒:事件驱动
  8. 传感器中断唤醒:智能唤醒

  9. 唤醒时间优化

  10. 快速时钟配置
  11. 延迟外设初始化
  12. 精确测量唤醒时间

  13. 状态保存恢复

  14. 备份寄存器:小容量,快速
  15. Flash存储:大容量,永久
  16. 压缩存储:节省空间

  17. 综合应用

  18. 智能传感器节点
  19. 可穿戴设备
  20. 物联网应用

关键要点

电源模式选择原则:
1. 空闲时间 < 1ms:睡眠模式
2. 空闲时间 1ms-1s:停止模式
3. 空闲时间 > 1s:待机模式
4. 需要保持状态:睡眠/停止模式
5. 极致低功耗:待机模式

唤醒源选择原则:
1. 定时任务:RTC唤醒
2. 外部事件:GPIO中断
3. 传感器事件:传感器中断
4. 组合唤醒:多种唤醒源

优化建议:
1. 测量实际功耗
2. 优化唤醒时间
3. 合理保存状态
4. 选择合适模式
5. 持续监测优化

下一步学习

  • 学习动态电源管理(DPM)技术
  • 深入研究电池管理系统(BMS)
  • 探索能量收集技术
  • 学习超低功耗设计技巧

参考资源

  • STM32参考手册:电源控制(PWR)章节
  • AN4365: STM32F1系列低功耗模式
  • AN4621: STM32电源管理最佳实践
  • 《嵌入式系统低功耗设计》

作者: 嵌入式知识平台内容团队
最后更新: 2026-03-07
版本: 1.0.0