基于 STM32F103 + HAL 库,代码可直接编译运行,并且教你如何测量电流验证是否真的睡下去了。
一、STM32 低功耗模式速查
| 模式 | 唤醒方式 | 唤醒后 | 典型功耗 | 适用场景 |
|---|---|---|---|---|
| Sleep | 任意中断 | 立即执行 | mA 级 | 只是让 CPU 歇一下 |
| Stop | EXTI(按键) | 需要重配时钟 | μA 级 | 电池供电首选 |
| Standby | WakeUp 引脚 / RTC | 相当于复位 | < 5μA | 极低功耗,但麻烦 |
90% 的项目用 Stop 模式就够了
二、Stop 模式测试程序
1、GPIO 设计
| 引脚 | 功能 |
|---|---|
| PA0 | 唤醒按键(EXTI0) |
| PC13 | LED(闪烁表示运行) |
| 其余 IO | 设为 Analog 省电 |
2、主程序(main.c)
c
#include "main.h"
void SystemClock_Config(void);
void Enter_STOP_Mode(void);
int main(void)
{
HAL_Init();
SystemClock_Config();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
// PC13 LED
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// PA0 唤醒按键
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
while (1)
{
// 运行指示
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
HAL_Delay(200);
// 运行 2 秒后进入 STOP
static uint32_t cnt = 0;
if (++cnt > 10)
{
cnt = 0;
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
Enter_STOP_Mode();
}
}
}
3、进入 Stop 模式
c
void Enter_STOP_Mode(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
// 关闭所有不需要的外设时钟
__HAL_RCC_USART1_CLK_DISABLE();
__HAL_RCC_SPI1_CLK_DISABLE();
__HAL_RCC_TIM2_CLK_DISABLE();
// 配置所有 IO 为模拟输入(最省电)
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Pin = GPIO_PIN_All;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
// 进入 STOP 模式,唤醒后使用 HSI
HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
}
4、唤醒后恢复时钟
c
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_0)
{
// 重新初始化系统时钟(STOP 唤醒后默认用 HSI)
SystemClock_Config();
// 重新初始化 GPIO
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
}
三、SystemClock_Config(标准 72MHz)
c
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_HCLK |
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);
}
四、如何验证是否真的睡下去了
方法 1:万用表测电流(最准)
| 状态 | 电流 |
|---|---|
| 运行 | 10~30 mA |
| Sleep | 5~10 mA |
| Stop | ≈ 20~50 μA |
| Standby | < 5 μA |
如果 Stop 模式 > 1mA,一定有问题
2、方法 2:LED 行为
- 运行:LED 快速闪烁
- 睡眠:LED 熄灭
- 唤醒:LED 再次闪烁
参考代码 睡眠模式测试程序 www.youwenfan.com/contentcsv/103598.html
五、常见问题
| 问题 | 原因 |
|---|---|
| 睡不下去 | 外设没关时钟 |
| 唤醒后死机 | 没重配系统时钟 |
| 电流很大 | IO 没设为 Analog |
| 自动唤醒 | 没关滴答定时器 |
| 串口失效 | STOP 后 USART 时钟没了 |
六、彻底省电
关闭 SysTick 再睡
c
HAL_SuspendTick(); // 禁止 SysTick
HAL_PWR_EnterSTOPMode(...);
HAL_ResumeTick(); // 恢复 SysTick
七、Standby 模式
c
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
HAL_PWR_EnterSTANDBYMode();
唤醒 = 复位重启