文章目录
-
- [1. 概述](#1. 概述)
- [2. Power Profiler功耗测试](#2. Power Profiler功耗测试)
- [3. JLink寄存器状态查看](#3. JLink寄存器状态查看)
-
- [3.1 连接配置](#3.1 连接配置)
- [3.1 使用Ozone调试器实时监控](#3.1 使用Ozone调试器实时监控)
-
- [3.1.1 创建监控窗口](#3.1.1 创建监控窗口)
- [3.1.2 Timeline分析](#3.1.2 Timeline分析)
- [3.2 寄存器状态分析要点](#3.2 寄存器状态分析要点)
-
- [3.2.1 深度睡眠检查清单](#3.2.1 深度睡眠检查清单)
- [3.2.2 异常功耗排查](#3.2.2 异常功耗排查)
- [4. 功耗优化实践](#4. 功耗优化实践)
-
- [4.1 Zephyr电源管理配置](#4.1 Zephyr电源管理配置)
-
- [4.1.1 基础配置](#4.1.1 基础配置)
- [4.1.2 外设运行时电源管理](#4.1.2 外设运行时电源管理)
- [4.2 代码层面优化技巧](#4.2 代码层面优化技巧)
-
- [4.2.1 使用工作队列延迟处理](#4.2.1 使用工作队列延迟处理)
- [4.2.2 最小化中断唤醒](#4.2.2 最小化中断唤醒)
- [4.2.3 智能传感器调度](#4.2.3 智能传感器调度)
- [5 总结](#5 总结)
1. 概述
博主最近在处理非常头疼的功耗问题:在嵌入式系统开发中,功耗优化是至关重要的一环,特别是对于可穿戴设备和物联网设备。本文介绍基于Zephyr RTOS的功耗优化测试方法,主要使用两种工具:
- Power Profiler Kit II (PPK2) - 用于精确测量和分析功耗
- 正版JLink调试器与OZone - 用定位系统睡眠状态下的寄存器情况
通过这两种工具的配合使用,可以全面分析系统功耗特性,定位功耗异常点,实现功耗优化目标。
2. Power Profiler功耗测试
2.1 测试环境搭建
硬件连接
Power Profiler Kit II (PPK2)
│
├─ VIN ──> 电源输入(根据电池选取)
└─ GND ──> 公共地
软件准备
- 安装 nRF Connect for Desktop
- 安装 Power Profiler 应用
- 连接PPK2设备
2.2 功耗测试指标
2.2.1 基底功耗(Base Current)
定义 :睡眠时刻,系统设计最初电量损耗(与寄存器息息相关)

测试方法:
c
// 确保系统进入深度睡眠
void enter_deep_sleep(void)
{
// 关闭所有外设
disable_all_peripherals();
// 禁用不必要的时钟
pm_power_state_set(PM_STATE_SUSPEND_TO_IDLE, 0);
// 等待系统稳定
k_sleep(K_SECONDS(30));
}
测试步骤:
- 编译固件,禁用串口打印
- 烧录固件并让系统进入深度睡眠
- 在Power Profiler中观察电流稳定后的最低值
- 记录持续时间至少30秒的平均值
说明:
- 基底功耗偏高,系统功耗肯定高
- 基底功耗与CCU,GPIO等睡眠时刻配置息息相关,一个HOSC与LOSC时钟可能差20ua
- 系统设计最初,会提供最基本的基底功耗标准(均保留睡眠时,系统运行正常的部分功能)
2.2.2 10分钟平均功耗
定义 :模拟真实使用场景下的长时间平均功耗

测试步骤:
- 配置真实使用场景(如:心率监测、步数统计等)
- 启动Power Profiler录制
- 运行10分钟完整流程
- 分析平均电流和总能耗
计算公式:
平均功耗(mW) = 平均电流(mA) × 电压(V)
预估续航(h) = 电池容量(mAh) / 平均电流(mA)
Power Profiler设置:
- 采样间隔:100µs(高精度)或1ms(长时间)
- 测量模式:Source meter mode
- 电压设置:3.8V(模拟电池中等电量)
2.2.3 关闭串口功耗测试
重要性:一次串口打印高达40mA。相比睡眠700uA高很多。
对比测试:
| 模式 | 平均电流 | 峰值电流 | 说明 |
|---|---|---|---|
| 串口开启 | 850uA | 45mA | 调试模式 |
| 串口关闭 | 720µA | 18mA | 发布模式 |
2.2.4 行为功耗与时间分析
目的:不同的行为具有特定的波形,可分析不同应用行为的功耗特征和持续时间,甚至未知行为
典型行为分类:
- 传感器采样行为
c
// 心率传感器采样
void hr_sensor_sample(void)
{
uint64_t start_time = k_uptime_get();
// 使能传感器
hr_sensor_enable();
k_sleep(K_MSEC(100)); // 稳定时间
// 采样数据
uint32_t hr_value = hr_sensor_read();
// 关闭传感器
hr_sensor_disable();
uint64_t duration = k_uptime_get() - start_time;
printk("HR sampling: %lld ms\n", duration);
}
- 蓝牙通信行为
c
// BLE广播行为
void ble_advertising_cycle(void)
{
// 广播窗口:100ms
// 广播间隔:1000ms
// 功耗:广播时8mA,间隔时50µA
}
- 屏幕刷新行为
c
// LCD刷新
void lcd_refresh_screen(void)
{
// 刷新时间:~50ms
// 刷新功耗:15-20mA
// 静态显示:2-5mA
}
3. JLink寄存器状态查看
3.1 连接配置
硬件连接
JLink调试器
├─ SWDIO ──> 目标板SWDIO
├─ SWCLK ──> 目标板SWCLK
├─ GND ──> 目标板GND
└─ VTref ──> 目标板VDD(可选)
3.1 使用Ozone调试器实时监控
3.1.1 创建监控窗口
- 打开 Ozone
- 加载ELF文件
- 创建 Watch 窗口
- 添加寄存器监控
示例监控配置:
c
// 在Watch窗口添加以下表达式
*(uint32_t*)0xE000ED10 // SCR
*(uint32_t*)0xE000E100 // NVIC_ISER0
*(uint32_t*)0x40000000 // Clock Control
3.1.2 Timeline分析
使用Ozone的Timeline功能:
- 配置数据采样
- 设置采样率(如:1kHz)
- 记录系统状态变化
- 分析睡眠/唤醒时序
3.2 寄存器状态分析要点
3.2.1 深度睡眠检查清单
✓ SCR.SLEEPDEEP = 1(确认进入深度睡眠)
✓ SCR.SLEEPONEXIT = 0/1(根据需求)
✓ SysTick关闭(除非需要唤醒)
✓ 不必要的中断已禁用
✓ 外设时钟已关闭
✓ GPIO配置为低功耗模式
✓ DMA已停止
✓ PLL已关闭(使用内部低频时钟)
3.2.2 异常功耗排查
当功耗高于预期时,检查以下寄存器:
- 时钟树检查
bash
# 检查所有时钟使能寄存器
J-Link> mem32 0x40021000 0x20
# 查找意外使能的时钟
- 中断状态检查
bash
# 检查中断挂起状态
J-Link> mem32 0xE000E200 8 # NVIC_ISPR
# 非零值表示有挂起的中断
- 外设状态检查
bash
# 逐个检查外设控制寄存器
J-Link> mem32 0x40010000 4 # UART
J-Link> mem32 0x40005400 4 # I2C
J-Link> mem32 0x40013000 4 # SPI
4. 功耗优化实践
4.1 Zephyr电源管理配置
4.1.1 基础配置
conf
# prj.conf
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
CONFIG_PM_POLICY_CUSTOM=y
# Tickless kernel
CONFIG_TICKLESS_KERNEL=y
CONFIG_TICKLESS_IDLE_THRESH=2
# 低功耗定时器
CONFIG_SYSTEM_TIMER_HAS_DISABLE_SUPPORT=y
4.1.2 外设运行时电源管理
c
#include <zephyr/pm/device.h>
// 传感器使用完毕后立即挂起
const struct device *sensor = DEVICE_DT_GET(DT_NODELABEL(temp_sensor));
// 使用传感器
pm_device_action_run(sensor, PM_DEVICE_ACTION_RESUME);
sensor_sample_fetch(sensor);
sensor_channel_get(sensor, SENSOR_CHAN_AMBIENT_TEMP, &val);
// 挂起传感器
pm_device_action_run(sensor, PM_DEVICE_ACTION_SUSPEND);
4.2 代码层面优化技巧
4.2.1 使用工作队列延迟处理
c
// 避免频繁唤醒
static struct k_work_delayable periodic_work;
void periodic_work_handler(struct k_work *work)
{
// 批量处理多个任务
process_sensor_data();
update_display();
check_battery();
// 重新调度(延长间隔)
k_work_schedule(&periodic_work, K_SECONDS(10));
}
4.2.2 最小化中断唤醒
c
// 配置GPIO为低功耗模式
void configure_gpio_low_power(void)
{
// 未使用的引脚配置为输入+下拉
for (int i = 0; i < UNUSED_PIN_COUNT; i++) {
gpio_pin_configure(gpio_dev, unused_pins[i],
GPIO_INPUT | GPIO_PULL_DOWN);
}
// 按键配置为边沿触发(避免电平触发持续唤醒)
gpio_pin_configure(gpio_dev, BUTTON_PIN,
GPIO_INPUT | GPIO_INT_EDGE_FALLING);
}
4.2.3 智能传感器调度
c
// 根据运动状态动态调整采样率
void adaptive_sensor_sampling(void)
{
static int motion_level = 0;
if (motion_level == 0) {
// 静止:低频采样
k_timer_start(&sensor_timer, K_SECONDS(60), K_SECONDS(60));
} else if (motion_level == 1) {
// 轻度活动:中频采样
k_timer_start(&sensor_timer, K_SECONDS(10), K_SECONDS(10));
} else {
// 剧烈运动:高频采样
k_timer_start(&sensor_timer, K_SECONDS(1), K_SECONDS(1));
}
}
5 总结
5.1 最佳实践总结
功耗测试流程
1. 基线测试
├─ 测量基底功耗(最低功耗)
└─ 设定优化目标
2. 行为分析
├─ 使用Power Profiler记录各行为功耗
├─ 分析占空比和时间分布
└─ 识别高功耗行为
3. 寄存器验证
├─ 使用JLink检查睡眠状态
├─ 验证时钟和中断配置
└─ 确认外设挂起状态
4. 迭代优化
├─ 针对性优化高功耗模块
├─ 重新测量验证效果
└─ 达到目标功耗
功耗优化优先级
优先级1: 基底功耗优化(影响最大)
- 深度睡眠配置
- 不必要时钟关闭
- GPIO低功耗配置
优先级2: 高频行为优化
- 传感器采样频率
- 蓝牙连接间隔
- 屏幕刷新率
优先级3: 算法优化
- 减少计算复杂度
- 批处理减少唤醒
- 硬件加速使用