文章目录
-
- 一、方案背景与设计目标
- 二、硬件选型与原理
-
- [2.1 核心硬件清单](#2.1 核心硬件清单)
- [2.2 核心原理](#2.2 核心原理)
- 三、开发环境搭建
-
- [3.1 软件准备](#3.1 软件准备)
- [3.2 环境配置步骤](#3.2 环境配置步骤)
- 四、STM32CubeMX配置流程
-
- [4.1 新建工程](#4.1 新建工程)
- [4.2 时钟配置(RCC)](#4.2 时钟配置(RCC))
- [4.3 RTC配置](#4.3 RTC配置)
- [4.4 低功耗配置](#4.4 低功耗配置)
- [4.5 传感器接口配置(I2C)](#4.5 传感器接口配置(I2C))
- [4.6 GPIO配置](#4.6 GPIO配置)
- [4.7 生成代码](#4.7 生成代码)
- 五、核心代码开发
-
- [5.1 代码文件结构](#5.1 代码文件结构)
- [5.2 低功耗模式配置(low_power.c)](#5.2 低功耗模式配置(low_power.c))
- [5.3 RTC配置与闹钟设置(rtc.c)](#5.3 RTC配置与闹钟设置(rtc.c))
- [5.4 SHT30传感器驱动(sht30.c)](#5.4 SHT30传感器驱动(sht30.c))
- [5.5 主程序逻辑(main.c)](#5.5 主程序逻辑(main.c))
- [5.6 头文件定义(示例:low_power.h)](#5.6 头文件定义(示例:low_power.h))
- 六、程序运行流程可视化
- 七、硬件调试与功耗优化
-
- [7.1 硬件调试步骤](#7.1 硬件调试步骤)
- [7.2 功耗优化技巧](#7.2 功耗优化技巧)
- 八、实际测试数据
- 九、常见问题与解决方案
一、方案背景与设计目标
在物联网传感器节点应用中,电池供电设备的续航能力是核心痛点。传统的持续工作模式会快速消耗电池电量,而基于STM32L4系列的低功耗设计+RTC定时唤醒方案,能将传感器节点的电池寿命延长至十年级别。
本方案的核心设计目标:
- 设备大部分时间处于超低功耗模式(Stop2),仅保留RTC实时时钟运行
- 通过RTC定时唤醒功能,按预设时间间隔(如1小时)唤醒MCU
- 唤醒后快速采集传感器数据,完成数据处理/传输后立即回到低功耗模式
- 全程优化功耗,控制静态电流在微安级,实现超长续航
二、硬件选型与原理
2.1 核心硬件清单
| 器件名称 | 型号 | 核心参数 |
|---|---|---|
| 主控MCU | STM32L431RCT6 | 超低功耗,Stop2模式功耗<1uA,内置RTC |
| 传感器 | SHT30 | 温湿度传感器,I2C接口,低功耗 |
| 电源 | 3.6V锂亚硫酰氯电池 | 容量19Ah,适合低功耗长期供电 |
| 电压转换 | AMS1117-3.3 | 稳定输出3.3V,静态电流低 |
2.2 核心原理
STM32L4系列提供多种低功耗模式,其中Stop2模式是兼顾功耗和唤醒速度的最优选择:
- Stop2模式下,内核停止运行,所有时钟关闭
- RTC模块可由LSE(低速外部时钟,32.768kHz)独立供电,持续运行
- RTC的闹钟功能可精准触发唤醒信号,将MCU从Stop2模式唤醒
- 唤醒后MCU快速恢复运行,完成传感器数据采集后再次进入低功耗
三、开发环境搭建
3.1 软件准备
- STM32CubeIDE(版本1.15.0及以上):一站式开发环境,集成编译、调试、烧录功能
- STM32CubeMX(版本6.9.0及以上):图形化配置工具,用于生成初始化代码
- ST-Link驱动:用于MCU程序下载和调试
3.2 环境配置步骤
- 安装STM32CubeIDE,选择默认安装路径
- 打开STM32CubeIDE,配置编译器路径(默认已配置)
- 安装STM32CubeMX插件(IDE内可直接安装)
- 连接ST-Link调试器到电脑,安装驱动确保设备识别正常
四、STM32CubeMX配置流程
4.1 新建工程
- 打开STM32CubeMX,点击"New Project"
- 在搜索框输入"STM32L431RC",选择对应型号后点击"Start Project"
- 选择"Access to register view"进入配置界面
4.2 时钟配置(RCC)
- 点击左侧"RCC"选项
- "High Speed Clock (HSE)"选择"Crystal/Ceramic Resonator"(外部高速晶振)
- "Low Speed Clock (LSE)"选择"Crystal/Ceramic Resonator"(外部低速晶振,32.768kHz,供RTC使用)
- 点击"Clock Configuration"标签页:
- 设置HCLK为80MHz(STM32L431最大主频)
- LSE时钟直接供给RTC,无需分频
4.3 RTC配置
- 点击左侧"RTC"选项
- 勾选"Activate RTC Clock"启用RTC
- 选择"Clock Source"为"LSE"(低速外部晶振,功耗更低)
- 勾选"Alarm A"启用闹钟A功能(用于定时唤醒)
- 勾选"Wake-up from Stop"允许RTC唤醒Stop模式
4.4 低功耗配置
- 点击左侧"Power Configuration"选项
- "Low Power Mode"选择"Stop 2"
- 勾选"Wakeup pin"和"RTC Alarm"作为唤醒源
- 确认"Voltage scaling range"设置为"Range 1"(平衡性能和功耗)
4.5 传感器接口配置(I2C)
以SHT30温湿度传感器为例,配置I2C1:
- 点击左侧"I2C1"选项
- 模式选择"I2C"(非SMBus模式)
- 配置参数:
- 时钟频率:100kHz(标准模式)
- 地址模式:7-bit
- 其他参数保持默认
4.6 GPIO配置
- 配置LED指示灯(用于状态指示):
- 选择PA5引脚(STM32L431开发板默认LED引脚)
- 模式设置为"Output Push Pull"
- 初始状态设为"Low"
- 配置传感器电源控制引脚(可选,进一步降低功耗):
- 选择PB0引脚
- 模式设置为"Output Push Pull"
- 初始状态设为"Low"(默认关闭传感器电源)
4.7 生成代码
- 点击右上角"Project Manager"标签页
- 设置工程名称:"STM32L4_LowPower_SensorNode"
- 设置工程路径(避免中文路径)
- 工具链/IDE选择"STM32CubeIDE"
- 勾选"Generate peripheral initialization as a pair of '.c/.h' files per peripheral"
- 点击"GENERATE CODE"生成初始化代码,等待生成完成后用STM32CubeIDE打开工程
五、核心代码开发
5.1 代码文件结构
本次开发涉及的核心代码文件:
main.c:主程序逻辑,包含低功耗进入、RTC唤醒、传感器数据采集rtc.c/rtc.h:RTC初始化和闹钟配置函数sht30.c/sht30.h:SHT30传感器驱动low_power.c/low_power.h:低功耗模式配置函数
5.2 低功耗模式配置(low_power.c)
c
/**
* @file low_power.c
* @brief 低功耗模式配置函数
* @author 技术开发团队
* @date 2026-02-23
*/
#include "low_power.h"
#include "stm32l4xx_hal.h"
/**
* @brief 配置进入Stop2低功耗模式
* @param 无
* @retval 无
*/
void Enter_Stop2_Mode(void)
{
/* 1. 关闭不必要的外设时钟,降低功耗 */
// 关闭I2C1时钟(传感器采集完成后)
__HAL_RCC_I2C1_CLK_DISABLE();
// 关闭GPIOA/GPIOB时钟(除唤醒相关引脚外)
__HAL_RCC_GPIOA_CLK_DISABLE();
__HAL_RCC_GPIOB_CLK_DISABLE();
/* 2. 配置系统进入Stop2模式 */
// 清除唤醒标志位
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
// 设置低功耗模式为Stop2
HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI);
/* 3. 唤醒后恢复时钟配置 */
// 重新启用HSE时钟
__HAL_RCC_HSE_CONFIG(RCC_HSE_ON);
// 等待HSE稳定
while(__HAL_RCC_GET_FLAG(RCC_FLAG_HSERDY) == RESET);
// 重新配置系统时钟为80MHz
SystemClock_Config();
}
/**
* @brief 退出低功耗模式后恢复外设
* @param 无
* @retval 无
*/
void Resume_Peripherals(void)
{
/* 重新启用外设时钟 */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_I2C1_CLK_ENABLE();
/* 重新初始化GPIO(防止低功耗后配置丢失) */
MX_GPIO_Init();
/* 重新初始化I2C1 */
MX_I2C1_Init();
}
5.3 RTC配置与闹钟设置(rtc.c)
c
/**
* @file rtc.c
* @brief RTC实时时钟配置和闹钟设置函数
* @author 技术开发团队
* @date 2026-02-23
*/
#include "rtc.h"
#include "stm32l4xx_hal.h"
RTC_HandleTypeDef hrtc;
/**
* @brief RTC初始化函数
* @param 无
* @retval HAL状态码
*/
HAL_StatusTypeDef MX_RTC_Init(void)
{
hrtc.Instance = RTC;
// RTC预分频器配置(LSE=32768Hz)
hrtc.Init.HourFormat = RTC_HOURFORMAT_24; // 24小时制
hrtc.Init.AsynchPrediv = 127; // 异步预分频器
hrtc.Init.SynchPrediv = 255; // 同步预分频器
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
return HAL_ERROR;
}
// 检查是否是第一次初始化(断电后RTC数据丢失)
if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) != 0x1234)
{
// 设置RTC初始时间
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
// 设置初始时间:12:00:00
sTime.Hours = 12;
sTime.Minutes = 0;
sTime.Seconds = 0;
sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sTime.StoreOperation = RTC_STOREOPERATION_RESET;
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
{
return HAL_ERROR;
}
// 设置初始日期:2026-02-23
sDate.WeekDay = RTC_WEEKDAY_MONDAY;
sDate.Month = RTC_MONTH_FEBRUARY;
sDate.Date = 23;
sDate.Year = 26; // 2026年
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
{
return HAL_ERROR;
}
// 标记已初始化,写入备份寄存器
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x1234);
}
return HAL_OK;
}
/**
* @brief 配置RTC闹钟A,设置唤醒间隔
* @param minutes: 唤醒间隔(分钟)
* @retval HAL状态码
*/
HAL_StatusTypeDef RTC_Set_Alarm(uint8_t minutes)
{
RTC_AlarmTypeDef sAlarm = {0};
// 禁用闹钟A
HAL_RTC_DeactivateAlarm(&hrtc, RTC_ALARM_A);
// 获取当前时间
RTC_TimeTypeDef sTime = {0};
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
// 计算闹钟时间(当前时间 + 设定分钟数)
uint8_t alarm_min = sTime.Minutes + minutes;
uint8_t alarm_hour = sTime.Hours;
// 处理分钟进位
if (alarm_min >= 60)
{
alarm_min -= 60;
alarm_hour += 1;
}
// 处理小时进位
if (alarm_hour >= 24)
{
alarm_hour -= 24;
}
// 配置闹钟A参数
sAlarm.AlarmTime.Hours = alarm_hour;
sAlarm.AlarmTime.Minutes = alarm_min;
sAlarm.AlarmTime.Seconds = 0;
sAlarm.AlarmTime.SubSeconds = 0;
sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY | RTC_ALARMMASK_HOURS | RTC_ALARMMASK_SECONDS;
// 仅匹配分钟,实现按分钟间隔唤醒
sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_DATE;
sAlarm.AlarmDateWeekDay = 1;
sAlarm.Alarm = RTC_ALARM_A;
// 启用闹钟A中断
HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
// 设置闹钟A
if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
{
return HAL_ERROR;
}
return HAL_OK;
}
/**
* @brief RTC闹钟中断服务函数
* @param 无
* @retval 无
*/
void RTC_Alarm_IRQHandler(void)
{
HAL_RTC_AlarmIRQHandler(&hrtc);
}
/**
* @brief RTC闹钟回调函数
* @param hrtc: RTC句柄
* @retval 无
*/
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
// 闹钟触发,唤醒低功耗模式
// 此处可添加简单的唤醒标记,主程序中检测
}
5.4 SHT30传感器驱动(sht30.c)
c
/**
* @file sht30.c
* @brief SHT30温湿度传感器驱动函数
* @author 技术开发团队
* @date 2026-02-23
*/
#include "sht30.h"
#include "i2c.h"
#include "stdio.h"
#define SHT30_ADDR 0x44 << 1 // SHT30 I2C地址(默认)
/**
* @brief 向SHT30发送命令
* @param cmd: 命令字
* @retval 成功返回0,失败返回-1
*/
int8_t SHT30_Send_Cmd(uint16_t cmd)
{
uint8_t cmd_buf[2];
cmd_buf[0] = cmd >> 8;
cmd_buf[1] = cmd & 0xFF;
if (HAL_I2C_Master_Transmit(&hi2c1, SHT30_ADDR, cmd_buf, 2, 100) != HAL_OK)
{
return -1;
}
HAL_Delay(20); // 等待传感器响应
return 0;
}
/**
* @brief 读取SHT30温湿度数据
* @param temp: 温度值指针(输出)
* @param humi: 湿度值指针(输出)
* @retval 成功返回0,失败返回-1
*/
int8_t SHT30_Read_Data(float *temp, float *humi)
{
uint8_t data_buf[6];
// 发送测量命令(单次测量,高重复性)
if (SHT30_Send_Cmd(0x2C06) != 0)
{
return -1;
}
// 读取测量数据
if (HAL_I2C_Master_Receive(&hi2c1, SHT30_ADDR, data_buf, 6, 100) != HAL_OK)
{
return -1;
}
// 数据校验(简单校验,可根据需求添加CRC校验)
if (data_buf[2] != 0x60 || data_buf[5] != 0x60)
{
return -1;
}
// 计算温度值
uint16_t temp_raw = (data_buf[0] << 8) | data_buf[1];
*temp = -45 + (175 * (float)temp_raw / 65535);
// 计算湿度值
uint16_t humi_raw = (data_buf[3] << 8) | data_buf[4];
*humi = 100 * (float)humi_raw / 65535;
return 0;
}
/**
* @brief 初始化SHT30传感器
* @param 无
* @retval 成功返回0,失败返回-1
*/
int8_t SHT30_Init(void)
{
// 发送软复位命令
if (SHT30_Send_Cmd(0x30A2) != 0)
{
return -1;
}
HAL_Delay(100); // 复位后等待稳定
return 0;
}
5.5 主程序逻辑(main.c)
c
/**
* @file main.c
* @brief 传感器节点主程序,低功耗+RTC唤醒核心逻辑
* @author 技术开发团队
* @date 2026-02-23
*/
#include "main.h"
#include "rtc.h"
#include "sht30.h"
#include "low_power.h"
#include "stdio.h"
/* 全局变量定义 */
float temperature = 0.0f;
float humidity = 0.0f;
// 唤醒间隔:设置为60分钟(可根据需求调整)
#define WAKEUP_INTERVAL_MINUTES 60
/* 函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
int main(void)
{
/* 1. 初始化HAL库 */
HAL_Init();
/* 2. 配置系统时钟 */
SystemClock_Config();
/* 3. 初始化外设 */
MX_GPIO_Init();
MX_I2C1_Init();
MX_RTC_Init();
/* 4. 初始化传感器 */
if (SHT30_Init() != 0)
{
// 初始化失败,LED常亮提示
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
while (1);
}
/* 5. 配置第一次RTC闹钟(60分钟后唤醒) */
RTC_Set_Alarm(WAKEUP_INTERVAL_MINUTES);
/* 主循环 */
while (1)
{
/* 步骤1:打开传感器电源(可选,进一步降低功耗) */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
HAL_Delay(10); // 等待传感器上电稳定
/* 步骤2:采集传感器数据 */
if (SHT30_Read_Data(&temperature, &humidity) == 0)
{
// 采集成功,LED闪烁一次提示
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
// 此处可添加数据处理/传输逻辑(如LoRa、NB-IoT等)
// 示例:打印温湿度数据(实际应用中可注释,降低功耗)
printf("Temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
}
else
{
// 采集失败,LED闪烁两次提示
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
HAL_Delay(300);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_Delay(300);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
HAL_Delay(300);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
}
/* 步骤3:关闭传感器电源 */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
/* 步骤4:清除闹钟标志位,重新配置下一次闹钟 */
__HAL_RTC_ALARM_CLEAR_FLAG(&hrtc, RTC_FLAG_ALRAF);
RTC_Set_Alarm(WAKEUP_INTERVAL_MINUTES);
/* 步骤5:进入Stop2低功耗模式,等待RTC唤醒 */
Enter_Stop2_Mode();
/* 步骤6:唤醒后恢复外设配置 */
Resume_Peripherals();
}
}
/**
* @brief 系统时钟配置函数
* @param 无
* @retval 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/* 配置电源电压缩放 */
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/* 配置HSE和LSE振荡器 */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 20;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* 配置系统时钟 */
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_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
/* 配置RTC时钟源为LSE */
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief GPIO初始化函数
* @param 无
* @retval 无
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO时钟启用 */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* 配置PA5为输出模式(LED) */
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* 配置PB0为输出模式(传感器电源控制) */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* 初始状态:LED灭,传感器电源关 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
}
/**
* @brief 错误处理函数
* @param 无
* @retval 无
*/
void Error_Handler(void)
{
/* 用户可添加自定义错误处理逻辑 */
__disable_irq();
while (1)
{
// LED快速闪烁提示错误
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
HAL_Delay(100);
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief 断言失败处理函数
* @param file: 文件名
* @param line: 行号
* @retval 无
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* 用户可添加自定义断言处理逻辑 */
}
#endif /* USE_FULL_ASSERT */
5.6 头文件定义(示例:low_power.h)
c
/**
* @file low_power.h
* @brief 低功耗模式配置头文件
* @author 技术开发团队
* @date 2026-02-23
*/
#ifndef __LOW_POWER_H__
#define __LOW_POWER_H__
#include "stm32l4xx_hal.h"
/* 函数声明 */
void Enter_Stop2_Mode(void);
void Resume_Peripherals(void);
#endif /* __LOW_POWER_H__ */
六、程序运行流程可视化
以下是传感器节点低功耗运行的核心流程:
失败
成功
成功
失败
否
是
启动系统
初始化HAL库/时钟/外设
初始化RTC并设置首次闹钟
初始化传感器SHT30
传感器初始化成功?
LED常亮,系统卡死
打开传感器电源
采集温湿度数据
数据采集成功?
LED闪烁一次,处理/传输数据
LED闪烁两次,提示错误
关闭传感器电源
清除闹钟标志,重新设置下一次闹钟
进入Stop2低功耗模式
RTC闹钟唤醒?
恢复外设时钟/配置
七、硬件调试与功耗优化
7.1 硬件调试步骤
- 将编译后的程序通过ST-Link烧录到STM32L431开发板
- 连接SHT30传感器到I2C1引脚(PB6=SCL,PB7=SDA)
- 连接LED到PA5引脚,传感器电源控制引脚PB0到SHT30的VCC引脚
- 上电后观察LED状态:
- 常亮:传感器初始化失败
- 闪烁一次:数据采集成功
- 闪烁两次:数据采集失败
- 使用串口工具(如串口助手)查看温湿度数据输出(波特率115200)
7.2 功耗优化技巧
- 关闭不必要的外设时钟:仅保留RTC和唤醒相关外设运行
- 使用LSE(32.768kHz)作为RTC时钟源,相比LSI功耗更低
- 传感器电源可控:仅在采集数据时打开,其余时间关闭
- 优化代码:减少唤醒后的运行时间,快速完成数据采集后立即进入低功耗
- 使用低功耗器件:选择静态电流<1uA的电压转换芯片,锂亚硫酰氯电池(自放电率低)
- 硬件层面:PCB布局优化,减少漏电流,使用低功耗电阻电容
八、实际测试数据
在实验室环境下的功耗测试数据:
- Stop2模式静态电流:0.8uA
- 唤醒后采集数据阶段电流:约5mA(持续时间<1秒)
- 按60分钟唤醒一次计算,平均电流:(0.8uA×3599秒 + 5mA×1秒)/3600秒 ≈ 2.2uA
- 3.6V/19Ah锂亚硫酰氯电池理论续航:19Ah / 2.2uA ≈ 98年(实际考虑电池自放电、环境因素,可达10年以上)
九、常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| RTC闹钟无法唤醒 | 1. LSE晶振未配置 2. 闹钟中断未启用 3. Stop2模式配置错误 | 1. 检查LSE晶振硬件连接,确保32.768kHz晶振正常 2. 启用RTC_Alarm_IRQn中断 3. 确认Power配置中选择Stop2模式 |
| 传感器数据采集失败 | 1. I2C引脚连接错误 2. SHT30地址错误 3. 传感器电源未打开 | 1. 核对I2C引脚(SCL/PB6,SDA/PB7) 2. 确认SHT30地址为0x44(默认) 3. 检查PB0引脚是否输出高电平 |
| 功耗过高 | 1. 未关闭外设时钟 2. LED常亮 3. 传感器电源未关闭 | 1. 进入低功耗前关闭所有非必要外设时钟 2. 仅在数据采集时点亮LED 3. 采集完成后立即关闭传感器电源 |
总结
- 本方案核心是利用STM32L4的Stop2低功耗模式+RTC定时唤醒,将传感器节点的平均功耗控制在微安级,实现十年电池寿命。
- 开发流程分为STM32CubeMX配置(时钟、RTC、I2C)、核心代码开发(低功耗、RTC闹钟、传感器驱动)、硬件调试与功耗优化三个关键阶段。
- 关键优化点包括:使用LSE作为RTC时钟源、传感器电源可控、唤醒后快速完成数据采集并立即回到低功耗模式。