电源与温度管理
本文档介绍 fw-AW33N_BLE_SDK 的电源与温度管理机制,涵盖应用层电源管理模块(app_power_mg)、低功耗调度框架(power_manage)、芯片级电源流程(power_app)以及低电量检测、软关机等关键流程。
Purpose and Scope
本页面覆盖 AW33N BLE SDK 中与电源相关的能力:
- 应用层电源管理:低电量检测、VBAT 采样、低电事件上报、软关机(
app_power_mg.c/h); - 低功耗调度框架:
lp_target/lp_request/deepsleep_target注册机制、低功耗等级(浅睡/睡/深睡/软关)、PMU 硬件操作钩子(power_manage.h); - 芯片级电源流程:睡眠进出回调、软关机回调、早期/后期电源初始化(
power_app.c)。
未纳入本页的内容:蓝牙协议栈内部的连接/广播功耗管理、充电管理(POWER_EVENT_POWER_CHARGE 仅作事件透传)、以及具体产品的按键唤醒/IO 唤醒配置属于各应用工程范畴。温度管理:在本次已读取的电源源码中未发现独立的应用层温度管理实现;温度相关控制(如 PMU 热保护)通常由芯片硬件/配置工具(如 post_build/bd57/AW33N_config_tool/conf/source/board/lowpower_v1.lua)承载,本页在"温度管理"一节中如实说明。
Overview
电源管理在 BLE 低功耗设备中承担三个核心职责:
- 电量感知:通过 GPADC 周期性采样 VBAT,计算百分比电量,供 UI(如低电 LED、提示音)与事件上报使用;
- 低电保护:当电压低于 LVD 警戒线并持续一段时间后,触发软关机(soft poweroff),避免电池过放损坏;
- 功耗控制:系统空闲时由低功耗框架统一仲裁各模块(蓝牙、系统组)的休眠请求,选择浅睡/深睡/软关等级,并在唤醒后恢复。
整个系统采用"注册-查询-仲裁"的经典低功耗设计:每个需要参与低功耗决策的模块通过链接段(.lp_target、.lp_request、.deepsleep_target)静态注册回调,电源管理器遍历注册表查询是否空闲、仲裁最终休眠时长与等级。这种设计避免了模块间互相依赖,新增模块只需注册即可被统一调度。
Architecture
下图展示了电源管理系统的整体架构与数据流:
flowchart TD
subgraph sg_App["应用层 (common/power_manage)"]
APM["app_power_mg.c<br/>低电检测/事件/软关机"]
LED["led_control<br/>低电LED闪烁"]
BLE["ble_module_enable<br/>蓝牙模块开关"]
end
subgraph sg_Framework["低功耗框架 (power_manage.h)"]
PMGR["_pmgr_hdl 电源管理器"]
GROUP0["group0 (bluetooth)"]
GROUP1["group1 (System)"]
TARGET["lp_target 注册表"]
REQUEST["lp_request 注册表"]
DEEP["deepsleep_target 注册表"]
end
subgraph sg_Chip["芯片级 (bd57/power)"]
PAPP["power_app.c<br/>睡眠/软关回调"]
PHW["phw_dev_ops<br/>PMU硬件操作"]
P33["P33 电源管理单元"]
GPADC["GPADC<br/>VBAT采样"]
end
GPADC -->|"gpadc_battery_get_voltage"| APM
APM -->|"POWER_EVENT_POWER_LOW"| LED
APM -->|"power_set_soft_poweroff"| PAPP
APM -->|"ble_module_enable"| BLE
PMGR --> TARGET
PMGR --> REQUEST
PMGR --> DEEP
PMGR --> GROUP0
PMGR --> GROUP1
PMGR -->|"low_power_suspend/resume"| PHW
PHW --> P33
PAPP -->|"sleep_enter/exit_callback"| PMGR
各组件职责:
app_power_mg.c:应用入口,初始化 VBAT 采样通道、启动低电扫描定时器、将低电事件转发给应用层(LED/关机)。power_manage.h框架:维护_pmgr_hdl(含蓝牙组group0与系统组group1),遍历注册表决定是否可休眠、休眠多久、进入哪一级休眠。power_app.c:芯片 BSP 层,提供睡眠进出回调(矩阵按键 IO 处理、USB IO 高阻)、软关机钩子与电源早期/后期初始化。phw_dev_ops:PMU 硬件操作抽象,覆盖 sleep/soff/deepsleep 各阶段的 prepare/enter/exit/post,是低功耗框架与 P33 硬件之间的唯一通道。
设计意图:分层隔离。应用层只关心"电量低不低、要不要关机",框架层只关心"哪些模块空闲、睡多久",BSP 层只关心"IO 怎么配、硬件怎么进睡"。任何一层变更都不影响其他层。
应用层电源管理(app_power_mg)
apps/app/bsp/common/power_manage/app_power_mg.c 是电源管理在应用层的中枢,围绕四个静态变量工作:
static uint8_t app_power_low_check = 0;
//低电检测电压,比lvd电压大300mV
static uint16_t app_power_lvd_warning;
static uint16_t app_power_vbat_voltage;
static uint16_t app_power_vbat_check_timer = 0;
Source: app_power_mg.c
app_power_lvd_warning 是低电警戒线,初始化为 POWEROFF_TONE_V + 300(即比关机提示音电压高 300mV),可由 app_power_set_lvd() 动态调整。LVD 警戒线与供电模式相关:
#if (TCFG_POWER_SUPPLY_MODE == 1) //VPWR
#define POWEROFF_TONE_V 2700
#define BATTERY_FULL_VALUE 3700
#else //IOVDD
#define POWEROFF_TONE_V 2100
#define BATTERY_FULL_VALUE 3000
#endif
Source: app_power_mg.c
设计意图:VPWR 与 IOVDD 两种供电方式下电池满电/关断电压不同,因此电量百分比计算与低电判定必须按供电模式区分,否则会出现"电量永远 0%"或"永不觉醒"的误判。
低电扫描与软关机流程
app_power_scan() 是低电检测的核心(由 sys_timer 每 2 秒触发一次):
static void app_power_scan(void *priv)
{
#if TCFG_SYS_LVD_EN && TCFG_ADC_VBAT_CH_EN
static uint16_t low_power_cnt = 0;
uint16_t vol = gpadc_battery_get_voltage();
if (0 == vol) {
return;
}
/* log_info("vbat voltage : %d\n", vol); */
if (vol <= LOW_POWER_VOL) {
log_info("vbat voltage : %d\n", vol);
log_error(LOW_POWER_LOG);
low_power_cnt++;
// 10s后打开低电led闪烁行为
#if TCFG_LED_ENABLE
if (low_power_cnt == 5) {
led_low_power(1);
app_power_low_check = 1;
}
#endif
// 检测低电多次后进入软关机
if (low_power_cnt == (LOW_POWER_SOFTOFF_TIME / LOW_POWER_CHECK_INTERVAL) + 1) {
app_power_event_to_user(POWER_EVENT_POWER_LOW);
}
} else {
low_power_cnt = 0;
#if TCFG_LED_ENABLE
if (app_power_low_check) {
led_low_power(0);
app_power_low_check = 0;
}
#endif
}
app_power_vbat_voltage = vol;
#endif
}
Source: app_power_mg.c
关键细节:
vol == 0时直接返回,防止采样异常(如 ADC 未就绪)导致误判;- 采用连续计数而非单次判断:必须连续
LOW_POWER_SOFTOFF_TIME / LOW_POWER_CHECK_INTERVAL + 1 = 60s/2s + 1 = 31次低于警戒线才上报POWER_EVENT_POWER_LOW,这是防抖设计,避免电压瞬时跌落(如蓝牙发射瞬间)触发关机; - 电压恢复后计数清零,LED 低电闪烁关闭,实现滞后回差;
- 第 5 次(约 10 秒)时先打开低电 LED 提示,再在约 60 秒时软关机——给用户"还能再撑一会儿"的缓冲。
事件上报通过 app_power_event_to_user() 封装为系统事件:
void app_power_event_to_user(uint8_t event)
{
struct sys_event *e = event_pool_alloc();
if (e == NULL) {
log_info("Memory allocation failed for sys_event");
return;
}
e->type = SYS_DEVICE_EVENT;
e->arg = (void *)DEVICE_EVENT_FROM_POWER;
e->u.dev.event = event;
e->u.dev.value = 0;
main_application_operation_event(NULL, e);
}
Source: app_power_mg.c
事件统一以 DEVICE_EVENT_FROM_POWER 源注入应用主循环,由 app_power_event_handler() 消费。内存不足时直接放弃上报并打日志——低电场景下内存可能紧张,宁可丢弃事件也不能让系统崩溃。
事件处理侧(app_power_event_handler)在收到 POWER_EVENT_POWER_LOW 时删除 VBAT 检测定时器并调用软关机回调:
case POWER_EVENT_POWER_LOW:
if (app_power_vbat_check_timer) {
sys_timer_del(app_power_vbat_check_timer);
app_power_vbat_check_timer = 0;
}
if (set_soft_poweroff_call) {
log_info("POWER_LOW TO SOFT_POWEROFF");
set_soft_poweroff_call();
}
break;
Source: app_power_mg.c
先停定时器再关机,保证关机流程不会被打断;set_soft_poweroff_call 由注册方注入,解耦事件与具体关机实现。
软关机实现
app_power_set_soft_poweroff() 按低功耗模式分派:
void app_power_set_soft_poweroff(void *priv)
{
#if (TCFG_LOWPOWER_PATTERN == SOFT_MODE)
log_info(">>>>>>>Enter softoff");
power_set_soft_poweroff();
#elif (TCFG_LOWPOWER_PATTERN == SOFT_BY_POWER_MODE)
log_info(">>>>>>>Enter softoff by poweroff");
key_active_num_set(0);
app_power_soft.power_soft_handle = sys_timeout_add(NULL, soft_by_power_mode, LOW_POWER_SOFTOFF_BT_EXIT_TIME);//等待蓝牙基带close
#endif
}
Source: app_power_mg.c
SOFT_MODE:立即调用 power_set_soft_poweroff() 直接软关机。
SOFT_BY_POWER_MODE:先清按键计数,再延迟 LOW_POWER_SOFTOFF_BT_EXIT_TIME(100ms)执行 soft_by_power_mode()。这 100ms 是给蓝牙基带关闭 ADV/SCAN 的窗口,随后以 LOW_POWER_KEEP(-2) 进入"一直保持不退出"的深度休眠。唤醒时 power_wakeup_init() 恢复:
void power_wakeup_init(void)
{
if (app_power_soft.power_soft_flag) {
putchar('}');
app_power_soft.power_soft_flag &= ~BIT(0);
//open bt
wdt_init(WDT_8S);
/* sys_timer_init(); */
ble_module_enable(1);
app_to_recover();
}
}
Source: app_power_mg.c
唤醒序列为:清标志 → 重开看门狗(8s)→ 使能蓝牙模块 → 调用弱函数 app_to_recover()(应用可覆盖实现恢复逻辑)。putchar('{')/('}') 是调试用串口标记,标识休眠进出边界。
低功耗调度框架(power_manage)
apps/include_lib/cpu/bd57/asm/power/power_manage.h 定义了 SDK 统一的低功耗框架,核心是链接段注册表 + 分组仲裁。
模块空闲注册(lp_target)
每个参与低功耗决策的模块通过 REGISTER_LP_TARGET 注册到 .lp_target 链接段:
struct lp_target {
char *name;
level_handler_t level;
idle_handler_t is_idle;
};
#define REGISTER_LP_TARGET(target) \
const struct lp_target target SEC_USED(.lp_target)
Source: power_manage.h
低功耗等级枚举(从浅到深):
enum LOW_POWER_LEVEL {
LOW_POWER_MODE_LIGHT_SLEEP = 1,
LOW_POWER_MODE_SLEEP,
LOW_POWER_MODE_DEEP_SLEEP,
LOW_POWER_MODE_SOFF,
LOW_POWER_MODE_EXIT = 0x10,
};
Source: power_manage.h
设计意图:level 回调返回该模块允许进入的最低等级,is_idle 回调判断模块是否空闲。框架取所有模块允许等级的最小值(即最保守的等级)作为系统休眠等级,保证任何模块都不会在未就绪时被强行休眠。
休眠操作契约(low_power_operation)
struct low_power_operation {
const char *name;
u32(*get_timeout)(void *priv);
void (*suspend_probe)(void *priv);
void (*suspend_post)(void *priv, u32 usec);
void (*resume)(void *priv, u32 usec);
void (*resume_post)(void *priv, u32 usec);
};
Source: power_manage.h
模块通过 low_power_get() / low_power_sys_get() 注册(蓝牙组/系统组),通过 low_power_put() 注销。生命周期为 get_timeout → suspend_probe → suspend_post → resume → resume_post,与 Linux PM 的 suspend/resume 回调链同构。low_power_sys_suspend() 等系统组 API 供非蓝牙系统模块使用,两组查询函数 lower_power_bt_group_query() / lower_power_sys_group_query() 分别汇总两组的最短超时。
主动请求注册(lp_request)
与"被动查询"互补,lp_request 允许线程主动请求进入/退出低功耗:
struct lp_request {
char *name;
u8(*request_enter)(u32 timeout);
u8(*request_exit)(u32 timeout);
};
#define REGISTER_LP_REQUEST(target) \
const struct lp_request target SEC_USED(.lp_request)
Source: power_manage.h
深睡目标注册(deepsleep_target)
深睡(RTC 唤醒类)模块注册 enter/exit/post 三回调,deepsleep_target_enter()/exit()/post() 由框架按序批量调用:
struct deepsleep_target {
char *name;
u8(*enter)(void);
u8(*exit)(void);
u8(*post)(void);
};
#define DEEPSLEEP_TARGET_REGISTER(target) \
const struct deepsleep_target target SEC_USED(.deepsleep_target)
Source: power_manage.h
PMU 硬件操作抽象(phw_dev_ops)
框架与硬件之间的桥梁是 phw_dev_ops,覆盖从 sleep 到 deepsleep/soff 的全部阶段钩子:
struct phw_dev_ops {
void *(*early_init)(u32 arg);
u32(*init)(struct _phw_dev *dev, u32 arg);
u32(*ioctl)(struct _phw_dev *dev, u32 cmd, u32 arg);
u32(*sleep_already)(struct _phw_dev *dev, u32 arg);
u32(*sleep_prepare)(struct _phw_dev *dev, u32 arg);
u32(*sleep_enter)(struct _phw_dev *dev, u32 arg);
u32(*sleep_exit)(struct _phw_dev *dev, u32 arg);
u32(*sleep_post)(struct _phw_dev *dev, u32 arg);
u32(*soff_prepare)(struct _phw_dev *dev, u32 arg);
u32(*soff_enter)(struct _phw_dev *dev, u32 arg);
u32(*soff_exit)(struct _phw_dev *dev, u32 arg);
u32(*deepsleep_enter)(struct _phw_dev *dev, u32 arg);
u32(*deepsleep_exit)(struct _phw_dev *dev, u32 arg);
};
Source: power_manage.h
框架句柄 _pmgr_hdl 内含两个休眠组、当前等级、固定大小的电源对象池与依赖 API 指针:
struct _pmgr_hdl {
struct low_power_group group0; //bluetooth
struct low_power_group group1; //System
enum LOW_POWER_LEVEL level;
struct _power_mem power_mem;
struct pmgr_depend_api *api;
};
Source: power_manage.h
_power_mem 使用固定数组(POWER_LIST_SIZE = 5)而非动态分配,因为低功耗路径上不允许堆分配失败。
芯片级电源流程(power_app)
apps/app/bsp/cpu/bd57/power/power_app.c 提供 BSP 层电源实现,所有低功耗路径代码通过 LP_RUNCODE_AT(.lp_running.bsp.text) 段属性常驻 RAM——深睡时 Flash 可能不可访问,代码必须驻留 RAM 才能执行唤醒逻辑。
睡眠进入回调(关中断环境,禁止耗时操作与打印):
LP_RUNCODE_AT(.lp_running.bsp.text)
void sleep_enter_callback(u8 step)
{
#if KEY_MATRIX_EN
extern void set_matrixkey_row_port_output();
// 矩阵按键进低功耗前先把行IO拉低使其可以被唤醒
set_matrixkey_row_port_output();
#endif
/* 此函数禁止添加打印 */
putchar('<');
if (sleep_enter_callback_api) {
sleep_enter_callback_api();
}
//USB IO打印引脚特殊处理
#if (CONFIG_UART_DEBUG_ENABLE && ((CONFIG_UART_DEBUG_PORT == IO_PORT_DP) || (CONFIG_UART_DEBUG_PORT == IO_PORT_DM)))
usb_io_con = JL_PORTUSB->DIR;
#endif
//配高阻需根据外围电路设计来决定
usb_high_res();
}
Source: power_app.c
要点:
- 矩阵按键行 IO 先拉低,保证休眠期间按键仍能产生唤醒边沿;
- 若调试串口复用在 USB DP/DM 引脚,需保存其方向寄存器并在退出时恢复;
- 进入前将 USB IO 配为高阻,降低休眠漏电;
sleep_set_enter_call_api()/sleep_set_exit_call_api()允许应用注入额外回调。
软关机钩子 power_set_soft_poweroff_hook() 在关机前做 overlay 重载检查(sleep_overlay_check_reload),确保 Flash overlay 机制在唤醒后能正确重映射。电源早期/后期初始化分别由 power_early_flowing()(关 MCLR、配长按复位、保护调试/按键 IO、power_early_init)与 power_later_flowing()(pmu_trim、power_later_init、strc_trim_flow)完成。
核心流程
低电检测 → 软关机时序
sequenceDiagram
participant Timer as sys_timer (2s)
participant Scan as app_power_scan
participant ADC as GPADC (VBAT)
participant App as 应用事件循环
participant LED as led_control
participant SOFF as 软关机回调
Timer->>Scan: 周期触发 app_power_scan()
Scan->>ADC: gpadc_battery_get_voltage()
ADC-->>Scan: 电压值 vol
alt vol == 0 (采样异常)
Scan-->>Scan: return (跳过本次)
else vol <= LOW_POWER_VOL
Scan-->>Scan: low_power_cnt++
alt cnt == 5 (约10s)
Scan->>LED: led_low_power(1)
else cnt == 31 (约60s)
Scan->>App: POWER_EVENT_POWER_LOW
App->>App: 删除VBAT检测定时器
App->>SOFF: set_soft_poweroff_call()
SOFF-->>SOFF: 按低功耗模式进入软关机
end
else vol > LOW_POWER_VOL
Scan-->>Scan: low_power_cnt = 0
Scan->>LED: led_low_power(0)
end
系统空闲 → 低功耗仲裁流程
flowchart TD
Idle["系统空闲"] --> Query{"low_power_sys_is_idle()?"}
Query -->|"否"| Stay["保持运行"]
Query -->|"是"| Level["遍历 lp_target 计算<br/>允许的最低等级"]
Level --> Group["蓝牙组/系统组分别<br/>查询超时 low_power_group_query"]
Group --> Decide{"存在有效超时?"}
Decide -->|"LOW_POWER_KEEP(-2)"| Deep["进入并保持休眠<br/>sys_power_down"]
Decide -->|"正常超时"| Suspend["low_power_suspend<br/>suspend_probe/post"]
Suspend --> Wake["唤醒 low_power_resume"]
Wake --> Recover["resume_post 恢复外设"]
Deep --> WakeUp["外部事件唤醒<br/>power_wakeup_init"]
WakeUp --> Recover
Recover --> Idle
Stay --> Idle
关键点:LOW_POWER_KEEP(-2) 表示"进入低功耗并一直保持不退出"(由外部事件如按键/充电唤醒),LOW_POWER_NO_USE(-1) 表示传参无效、睡眠时长交由蓝牙协议栈决定,LOW_POWER_SET_MAX_TIME(4000000µs) 是显式设置的睡眠时长上限。
使用示例
初始化与电量获取
以下代码展示了电源管理模块的典型用法——初始化、启动低电检测、读取电压与百分比:
void app_power_init(void)
{
#if TCFG_ADC_VBAT_CH_EN
app_power_lvd_warning_init();
adc_add_sample_ch(AD_CH_PMU_VBAT);
app_power_scan(NULL);
#endif
}
uint16_t app_power_get_vbat(void)
{
return app_power_vbat_voltage;
}
uint8_t app_power_get_vbat_percent(void)
{
uint16_t tmp_bat_val;
uint16_t bat_val = app_power_get_vbat_level();
if (bat_val <= POWEROFF_TONE_V) {
return 0;
}
tmp_bat_val = app_power_remap_vbat_percent(bat_val);
if (!tmp_bat_val) {
tmp_bat_val = (uint32_t)(bat_val - POWEROFF_TONE_V) * 100 / (BATTERY_FULL_VALUE - POWEROFF_TONE_V);
log_info("bat_val:%d, POWEROFF_TONE_V: %d, BATTERY_FULL_VALUE: %d\n", bat_val, POWEROFF_TONE_V, BATTERY_FULL_VALUE);
if (tmp_bat_val > 100) {
tmp_bat_val = 100;
}
}
return (uint8_t)tmp_bat_val;
}
Source: app_power_mg.c
说明:app_power_init() 先初始化 LVD 警戒线并加入 VBAT 采样通道,再立即做一次采样作为初始值。app_power_get_vbat_percent() 在满电值以下做线性映射,app_power_remap_vbat_percent() 是弱函数(默认返回 0),产品可覆盖它实现非线性电池曲线(锂电池放电曲线非线性,线性映射仅作默认)。
启动周期低电检测:
void app_power_vbat_check()
{
app_power_vbat_check_timer = sys_timer_add(NULL, app_power_scan, LOW_POWER_CHECK_INTERVAL);
}
Source: app_power_mg.c
动态调整 LVD 警戒线
void app_power_set_lvd(uint16_t lvd_value)
{
app_power_lvd_warning = lvd_value;
}
Source: app_power_mg.c
模块注册低功耗目标(框架用法)
框架的使用方式是在任意 .c 文件中定义 struct lp_target 并静态注册,链接器自动将其放入 .lp_target 段:
struct lp_target {
char *name;
level_handler_t level;
idle_handler_t is_idle;
};
#define REGISTER_LP_TARGET(target) \
const struct lp_target target SEC_USED(.lp_target)
Source: power_manage.h
框架遍历注册表:
#define list_for_each_lp_target(p) \
for (p = DEPEND_API->lp_target_begin; p < DEPEND_API->lp_target_end; p++)
Source: power_manage.h
注册 PMU 硬件操作
芯片 BSP 通过 REGISTER_PHW_DEV_PMU_OPS 将硬件操作集合交给框架:
#define REGISTER_PHW_DEV_PMU_OPS(ops) \
const struct phw_dev_ops *phw_pmu_ops = &ops
extern const struct phw_dev_ops *phw_pmu_ops;
Source: power_manage.h
睡眠进出回调注入
BSP 与应用的解耦接口——应用可注册自己的回调随系统一起进出休眠:
static void (*sleep_exit_callback_api)(void);
void sleep_set_exit_call_api(void *call_api)
{
sleep_exit_callback_api = call_api;
}
static void (*sleep_enter_callback_api)(void);
void sleep_set_enter_call_api(void *call_api)
{
sleep_enter_callback_api = call_api;
}
Source: power_app.c
配置选项
应用层电源配置(app_power_mg.h / app_power_mg.c)
| 宏/选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
LVD_WARNING_FOR_LOW_POWER | int | 1 | 1:以 LVD 警戒值作为低电警戒线;0:固定 3300mV |
LOW_POWER_VOL | 宏 | app_power_lvd_warning | 低电判定电压(LVD 模式取动态值) |
LOW_POWER_WARN_VAL | int | 240 | 低电告警档位值(预留) |
LOW_POWER_OFF_VAL | int | 220 | 低电关机档位值(预留) |
LOW_POWER_CHECK_INTERVAL | int | 2000 | 低电扫描周期(ms) |
LOW_POWER_SOFTOFF_TIME | int | 60000 | 连续低电多久后软关机(ms,60s) |
LOW_POWER_SET_MAX_TIME | long | 4000000 | 显式设置睡眠时长上限(µs) |
LOW_POWER_KEEP | int | -2 | 进入低功耗并一直保持不退出 |
LOW_POWER_NO_USE | int | -1 | 传参无效,睡眠时长由蓝牙决定 |
LOW_POWER_SOFTOFF_BT_EXIT_TIME | int | 100 | SOFT_BY_POWER_MODE 下等待蓝牙基带关闭时间(ms) |
TCFG_POWER_SUPPLY_MODE | 编译宏 | 0 (IOVDD) | 1=VPWR:关机音 2700mV、满电 3700mV;0=IOVDD:2100mV/3000mV |
TCFG_SYS_LVD_EN | 编译宏 | — | 使能系统 LVD 检测 |
TCFG_ADC_VBAT_CH_EN | 编译宏 | — | 使能 VBAT ADC 采样通道 |
TCFG_LOWPOWER_PATTERN | 编译宏 | — | SOFT_MODE 直接软关机;SOFT_BY_POWER_MODE 先关蓝牙再进深睡 |
TCFG_LED_ENABLE | 编译宏 | — | 使能低电 LED 闪烁提示 |
框架配置(power_manage.h)
| 选项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
POWER_LIST_SIZE | int | 5 | 电源对象池容量(低功耗路径禁用动态分配) |
LOW_POWER_MODE_LIGHT_SLEEP | enum | 1 | 浅睡等级 |
LOW_POWER_MODE_SLEEP | enum | 2 | 普通睡眠等级 |
LOW_POWER_MODE_DEEP_SLEEP | enum | 3 | 深睡等级 |
LOW_POWER_MODE_SOFF | enum | 4 | 软关机等级 |
LOW_POWER_MODE_EXIT | enum | 0x10 | 退出低功耗等级标记 |
API 参考
void app_power_init(void)
初始化电源管理:初始化 LVD 警戒线、加入 VBAT 采样通道并执行首次采样。在系统上电流程中调用。
u16 app_power_get_vbat(void)
返回最近一次采样的 VBAT 电压(mV)。返回值:电压值,未采样时为 0。
u16 app_power_get_vbat_level(void)
实时读取当前 VBAT 电压(直接调用 gpadc_battery_get_voltage(),不经缓存)。返回值:当前电压(mV)。
u8 app_power_get_vbat_percent(void)
计算电量百分比(0-100)。低于 POWEROFF_TONE_V 返回 0;优先使用弱函数 app_power_remap_vbat_percent() 的映射结果,否则按 (v - POWEROFF_TONE_V) * 100 / (BATTERY_FULL_VALUE - POWEROFF_TONE_V) 线性计算并封顶 100。
void app_power_event_to_user(u8 event)
将电源事件包装为 SYS_DEVICE_EVENT(源为 DEVICE_EVENT_FROM_POWER)投递到应用主循环。参数:event 为 POWER_EVENT_* 枚举值。注意:event_pool_alloc() 失败时丢弃事件并打日志。
int app_power_event_handler(struct device_event *dev, void (*set_soft_poweroff_call)(void))
处理电源设备事件。POWER_EVENT_POWER_LOW 时删除 VBAT 检测定时器并调用软关机回调;POWER_EVENT_POWER_SOFTOFF 直接调用软关机回调。返回:int(当前恒为 false)。
void app_power_vbat_check(void)
以 LOW_POWER_CHECK_INTERVAL 为周期注册 app_power_scan 定时器,开启低电周期检测。
void app_power_set_lvd(u16 lvd_value)
动态设置 LVD 警戒线(即 LOW_POWER_VOL)。
void app_power_set_soft_poweroff(void *priv)
按 TCFG_LOWPOWER_PATTERN 分派软关机:SOFT_MODE 直接调用 power_set_soft_poweroff();SOFT_BY_POWER_MODE 延迟 LOW_POWER_SOFTOFF_BT_EXIT_TIME 后以 LOW_POWER_KEEP 进入深睡。
void p33_io_wakeup_edge(u32 gpio, P33_IO_WKUP_EDGE edge)
配置 P33 唤醒 IO 边沿(由 app_power_mg.h 声明,实现位于 BSP 层)。
框架 API(power_manage.h)
| 函数 | 说明 |
|---|---|
u32 low_power_sys_not_idle_cnt(void) | 非空闲模块计数 |
u8 low_power_sys_is_idle(void) | 系统是否可休眠 |
enum LOW_POWER_LEVEL low_power_level(void) | 当前低功耗等级 |
u8 is_low_power_mode(enum LOW_POWER_LEVEL level) | 是否处于指定等级 |
void *low_power_get(void *priv, const struct low_power_operation *ops) | 蓝牙组注册休眠操作 |
void *low_power_sys_get(void *priv, const struct low_power_operation *ops) | 系统组注册休眠操作 |
void low_power_put(void *priv) / low_power_sys_put(void *priv) | 注销休眠操作 |
u32 low_power_group_query(struct list_head *request_head) | 查询组内最短休眠超时 |
void low_power_suspend(u32 usec) / low_power_resume(u32 usec) | 蓝牙组挂起/恢复 |
void low_power_sys_suspend(u32 usec) / low_power_sys_resume(u32 usec) | 系统组挂起/恢复 |
u8 low_power_sys_request_enter(u32 timeout) / low_power_sys_request_exit(u32 timeout) | 系统主动请求进/出低功耗 |
void deepsleep_target_enter(void) / exit(void) / post(void) / dump(void) | 遍历深睡注册表执行回调 |
void sleep_set_enter_call_api(void *call_api) / sleep_set_exit_call_api(void *call_api) | 注入睡眠进出应用回调 |
故障模式、边界情况与并发
低电误判与防抖
- 采样异常:
app_power_scan()对vol == 0直接跳过,防止 ADC 未就绪或通道异常时把 0V 误判为"严重低电"。 - 瞬时跌落:蓝牙发射瞬间 VBAT 可能瞬时低于警戒线,因此必须连续 31 次(约 60s)低于警戒线才触发软关机;电压恢复即清零计数,形成回差,避免频繁开关机。
- LVD 双保险:
LOW_POWER_VOL在LVD_WARNING_FOR_LOW_POWER=1时取app_power_lvd_warning(比关机电压高 300mV 的动态值),此时 ADC 检测与硬件 LVD 两套机制并行,任一路失效都有兜底。
软关机时序竞争
SOFT_BY_POWER_MODE下,app_power_set_soft_poweroff()先注册 100ms 延迟任务等待蓝牙基带关闭 ADV/SCAN,随后才sys_power_down(LOW_POWER_KEEP)。若这 100ms 内又发生按键等事件,soft_by_power_mode()中的sys_timeout_del会清理未处理的 timer,避免双重休眠。app_power_event_handler在收到POWER_EVENT_POWER_LOW时先删 VBAT 检测定时器再关机,保证关机路径不会被打断,也防止唤醒后定时器悬挂。
事件投递失败
app_power_event_to_user() 使用 event_pool_alloc() 分配系统事件;低电场景内存紧张时分配可能失败,此时静默丢弃事件并仅打日志——这是有意的降级策略:宁可丢失一次提示,也不让分配失败导致系统崩溃。
并发与中断上下文约束
sleep_enter_callback/sleep_exit_callback运行在单核、关中断环境(见 power_app.c 注释),禁止打印、禁止耗时操作;串口调试输出仅允许putchar单字符。- 低功耗路径代码必须放在
.lp_running.bsp.text段(LP_RUNCODE_AT)常驻 RAM,因为深睡时 Flash 不可访问。 - 低功耗路径禁用动态内存分配:
_power_mem使用固定数组(POWER_LIST_SIZE=5)管理电源对象池。
电源模式相关边界
TCFG_POWER_SUPPLY_MODE决定POWEROFF_TONE_V与BATTERY_FULL_VALUE,直接影响电量百分比映射。若产品更换供电方式而忘记改此宏,会出现"电量永远 0"或"永不满电"的错误。P3_SFLAG0 ~ P3_SFLAG7为 SDK 保留(见 power_app.c 的p33_sfr_save_byte),应用只能使用P3_SFLAG8 ~ P3_SFLAGB四个字节保存软关机标志。
性能与运维注意事项
- 采样开销:VBAT 采样周期 2s,单次采样为阻塞式 GPADC 读取,代价极低;但
app_power_get_vbat_level()每次都会实时采样,UI 刷屏场景(如常显电量)应改用app_power_get_vbat()读取缓存值。 - 定时器:低电检测定时器由
sys_timer_add创建,事件处理时显式删除,防止唤醒后重复触发关机流程。 - 看门狗:从
LOW_POWER_KEEP深睡唤醒时立即wdt_init(WDT_8S),保证唤醒序列(BLE 使能、应用恢复)在 8 秒内完成,否则系统被看门狗复位。 - 调试标记:
putchar('{')/('}')与putchar('<')/('>')分别是软关机路径与睡眠路径的串口进出标记,现场定位"是否真的睡着了"可直接抓串口观察这些字符。 - 温度管理现状:本次已读的电源源码中未发现应用层温度检测/上报实现;AW33N 的 PMU 热保护与充电温度控制由芯片硬件与配置工具承载(参考
post_build/bd57/AW33N_config_tool/conf/source/board/lowpower_v1.lua及 config 工具的 board 配置),需要在产品侧做温控时,可参考该目录下的板级配置。
扩展点
- 低电映射曲线:覆盖弱函数
uint8_t app_power_remap_vbat_percent(uint16_t bat_val),实现锂电池非线性放电曲线;返回 0 表示不覆盖(回退线性映射)。 - 唤醒恢复逻辑:覆盖弱函数
void app_to_recover(void),在SOFT_BY_POWER_MODE唤醒后执行产品级恢复动作(如重连提示、状态恢复)。 - 睡眠进出钩子:调用
sleep_set_enter_call_api()/sleep_set_exit_call_api()注册应用回调,随系统进出低功耗(如保存/恢复外设状态)。 - 模块低功耗注册:新模块通过
REGISTER_LP_TARGET(被动查询)或REGISTER_LP_REQUEST(主动请求)接入框架仲裁;深睡模块使用DEEPSLEEP_TARGET_REGISTER。 - PMU 硬件操作:更换 PMU 或新增电源域时实现
phw_dev_ops并用REGISTER_PHW_DEV_PMU_OPS注册,框架层无需改动。 - LVD 动态调整:运行期调用
app_power_set_lvd()改变低电警戒线(如不同电池档位)。
测试覆盖观察
- 低电检测与软关机路径的可测性依赖
TCFG_SYS_LVD_EN与TCFG_ADC_VBAT_CH_EN两个编译开关;关闭任一项时app_power_scan()为空操作,便于在无电池硬件时做功能调试。 - 事件投递失败(
event_pool_alloc返回 NULL)与弱函数默认返回(app_power_remap_vbat_percent返回 0、app_to_recover空实现)是源码中显式设计的可替换/可降级点,测试时可通过这些路径验证降级行为。
Related Links
- app_power_mg.h — 应用层电源管理接口与宏定义
- app_power_mg.c — 低电检测/软关机实现
- power_manage.h — 低功耗框架注册表与等级定义
- power_app.c — 芯片级睡眠/软关回调与电源初始化
- power_config.c — 板级电源配置
- power_api.c — 电源 API 封装
- lib_power_config.c — 库级电源配置
- power_api.h — 电源 API 头文件
- lowpower_v1.lua — 配置工具的低功耗板级配置(温度/硬件相关配置入口)
- bluetooth_powerprofile.lua — 蓝牙功耗配置入口