电源管理与低功耗
AW31N BLE SDK 的电源管理与低功耗子系统,覆盖板级电源初始化、电池电压监测、低电告警与软关机、多级睡眠(LIGHT_SLEEP / SLEEP / DEEP_SLEEP / SOFF)以及基于链接器段注册的低功耗框架,是 TWS 耳机等低功耗蓝牙设备续航与可靠关机的核心机制。
Purpose and Scope
本页完整介绍 SDK 中电源管理与低功耗相关的能力,包括:
- 板级电源参数配置与初始化(
power_config.c中的board_power_init()); - 电池电压(VBAT)采样、低电检测与软关机流程(
app_power_mg.c); - 电源事件模型(
POWER_EVENT_*)及向应用层的分发机制; - 底层低功耗框架(
power_manage.h):lp_target/lp_request/deepsleep_target/phw_dev_ops注册与回调机制; - 低功耗等级划分、睡眠/唤醒的协作流程与配置项说明。
以下内容属于兄弟页面,不在本页展开:蓝牙协议栈与连接管理、按键/IO 唤醒的按键扫描细节、LED 低电闪烁的具体实现(led_control)、GPADC 模拟采样通道的底层驱动。本页仅在涉及电源行为时引用它们。
Overview
低功耗蓝牙设备的功耗策略通常分两级:运行期功耗优化(通过低频时钟、外设关断降低平均电流)与低电/关机期的安全策略(低电告警、软关机保护电池与数据)。AW31N 的电源管理子系统把这两级统一在一个分层结构中:
- 应用层(
app_power_mg.c):负责电池电压周期采样、低电判定、事件上报、软关机触发与唤醒恢复。它是开发者最常接触的接口层,提供app_power_init()、app_power_get_vbat()、app_power_get_vbat_percent()等 API。 - 板级配置层(
power_config.c):把app_config.h中的TCFG_*宏翻译成底层struct _power_param/struct _power_pdata,并完成供电模式与采样通道匹配校验、power_init()初始化、按键唤醒注册。 - 底层框架(
power_manage.h及asm/power系列):提供模块化的低功耗协作机制——各模块通过REGISTER_LP_TARGET把自己注册进.lp_target链接器段,由低功耗线程逐个询问"是否空闲、允许进入哪个睡眠等级";深度睡眠目标与 PMU 硬件操作则通过deepsleep_target段和phw_dev_ops接口注册。
理解这套设计的关键是"注册-协商-挂起-恢复"的协作模型:没有任何模块可以单方面决定系统睡眠,低功耗线程会综合所有注册者的意见选择睡眠等级,并在睡眠前后依次回调每个模块的 suspend_probe / suspend_post / resume / resume_post。
Architecture
flowchart TD
subgraph sg_App["应用层 (common/power_manage)"]
AppMgC["app_power_mg.c<br/>VBAT采样 / 低电检测 / 事件分发 / 软关机"]
AppMgH["app_power_mg.h<br/>LOW_POWER_* 阈值与 POWER_EVENT_* 定义"]
Led["led_control<br/>低电LED闪烁(10s后)"]
Key["key / p33_io_wakeup_edge<br/>按键唤醒"]
end
subgraph sg_BSP["板级配置层 (cpu/bd47/power)"]
PowerConfig["power_config.c<br/>board_power_init()"]
PowerApp["power_app.c<br/>电源应用初始化"]
PowerApi["power_api.c / power_interface.h<br/>power_init / power_control"]
end
subgraph sg_Framework["底层低功耗框架 (asm/power)"]
PM["power_manage.h<br/>lp_target / lp_request<br/>deepsleep_target / phw_dev_ops"]
SysPD["sys_power_down()<br/>睡眠/软关机执行"]
GPADC["gpadc<br/>电池电压采样通道"]
end
subgraph sg_HW["硬件"]
P33["P33 PMU"]
Battery["电池 VBAT"]
end
AppMgC --> AppMgH
AppMgC --> Led
AppMgC --> GPADC
AppMgC --> SysPD
AppMgC --> Key
PowerConfig --> PowerApp
PowerConfig --> PowerApi
AppMgC --> PowerApi
PowerApi --> PM
PM --> SysPD
GPADC --> Battery
SysPD --> P33
各组件职责说明:
app_power_mg.c是电源行为的中枢:注册 VBAT ADC 采样通道、启动 2 秒周期的低电扫描定时器、在连续低电后通过系统事件通知应用层并触发软关机。power_config.c是板级"翻译层",它把编译期宏(TCFG_LOWPOWER_*)写入power_param结构,并在board_power_init()中做供电模式与采样通道的一致性断言——设计意图是在启动阶段尽早暴露硬件配置错误,而不是在运行期静默失效(如assert(0, "gpadc_power_supply_mode and gpadc_ch_power not match"))。power_manage.h定义了跨模块的低功耗协作契约:lp_target(模块空闲与睡眠等级协商)、low_power_operation(挂起/恢复生命周期)、lp_request(主动请求进入低功耗)、deepsleep_target(深睡前后处理)、phw_dev_ops(PMU 硬件操作)。所有注册都通过SEC_USED()段属性实现,模块只需声明一个const结构体即可被低功耗框架发现,无需修改框架代码——这是典型的"反转控制"扩展点设计。
底层低功耗框架:注册-协商-挂起-恢复
power_manage.h 是整个低功耗协作模型的契约定义处。它不依赖任何具体外设,而是通过链接器段收集各模块的注册项,实现模块与框架的解耦。
核心数据结构与注册宏
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
框架通过段边界符号 lp_target_begin / lp_target_end 遍历所有注册项:
extern const struct lp_target lp_target_begin[];
extern const struct lp_target lp_target_end[];
#define list_for_each_lp_target(p) \
for (p = lp_target_begin; p < lp_target_end; p++)
Source: power_manage.h
设计意图:SEC_USED(.lp_target) 让每个模块的注册项在编译期就被链接器收集到同一段中,新增一个低功耗参与者只需写一行 REGISTER_LP_TARGET(...) 声明,框架代码零改动。这与"回调注册表 + 中央调度"相比,避免了初始化顺序依赖,也天然支持模块裁剪(未编译的模块不会进入段)。
classDiagram
class lp_target {
+char* name
+level_handler_t level
+idle_handler_t is_idle
}
class low_power_operation {
+get_timeout(priv) u32
+suspend_probe(priv)
+suspend_post(priv, usec)
+resume(priv, usec)
+resume_post(priv, usec)
}
class lp_request {
+char* name
+request_enter(timeout) u8
+request_exit(timeout) u8
}
class deepsleep_target {
+char* name
+enter() u8
+exit() u8
}
class phw_dev_ops {
+early_init(arg)
+init(dev, arg) u32
+ioctl(dev, cmd, arg) u32
+sleep_already(dev, arg) u32
+sleep_prepare(dev, arg) u32
+sleep_enter(dev, arg) u32
+sleep_exit(dev, arg) u32
+sleep_post(dev, arg) u32
+soff_prepare(dev, arg) u32
+soff_enter(dev, arg) u32
+deepsleep_enter(dev, arg) u32
+deepsleep_exit(dev, arg) u32
}
lp_target --> low_power_operation : 协商使用
lp_request --> low_power_operation : 主动请求
deepsleep_target --> phw_dev_ops : 硬件操作
睡眠等级与判定
enum LOW_POWER_LEVEL {
LOW_POWER_MODE_LIGHT_SLEEP = 1,
LOW_POWER_MODE_SLEEP,
LOW_POWER_MODE_DEEP_SLEEP,
LOW_POWER_MODE_SOFF,
};
u8 is_low_power_mode(enum LOW_POWER_LEVEL level);
Source: power_manage.h
等级从 1(浅睡)到 4(软关机)逐级加深:LIGHT_SLEEP 仅关闭 CPU 时钟(最快唤醒、功耗最低收益);SLEEP 进一步关闭外设时钟;DEEP_SLEEP 需要所有模块通过 deepsleep_target 完成外设掉电保存;SOFF 即软关机,系统保持极低功耗直到被唤醒事件拉起。每个 lp_target 的 level() 回调返回自己允许的最低等级,框架取所有注册者中的最浅等级作为本次实际进入的等级——任何一个模块"不空闲/不允许深睡"都会限制整机睡眠深度。
挂起/恢复生命周期
low_power_operation 定义了标准的 get_timeout -> suspend_probe -> suspend_post -> resume -> resume_post 五阶段流程,配套的 low_power_get() / low_power_put() 用于模块在睡眠窗口内获取/释放使用权;low_power_sys_get() / low_power_sys_put() 是系统级版本。模块通过 low_power_on() 开启低功耗调度、low_power_request() 主动请求进入低功耗。
这个五阶段设计解决的核心问题是一致性:suspend_probe 阶段先询问"能否挂起"(不能则放弃本次睡眠),suspend_post 阶段才真正保存现场;唤醒时 resume 恢复现场、resume_post 完成延迟初始化。把"询问"与"执行"分离,避免了模块在睡眠窗口中途发现资源被占用而回滚的复杂状态。
主动请求与深度睡眠注册
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)
struct deepsleep_target {
char *name;
u8(*enter)(void);
u8(*exit)(void);
};
#define DEEPSLEEP_TARGET_REGISTER(target) \
const struct deepsleep_target target SEC_USED(.deepsleep_target)
Source: power_manage.h
lp_request:供低功耗线程/模块主动申请进入或退出低功耗,带超时参数;适用于"我知道自己空闲了,请调度器考虑睡眠"的场景。deepsleep_target:进入 DEEP_SLEEP 前后各模块需要做的保存/恢复动作(如关外设、备份寄存器)。phw_dev_ops+REGISTER_PHW_DEV_PMU_OPS:PMU 硬件操作集合,覆盖sleep_*、soff_*、deepsleep_*全生命周期,是驱动层接入电源框架的唯一切口。
应用层:低电检测、事件分发与软关机
app_power_mg.c 是应用层电源行为的执行者。它的核心循环是一个 2 秒周期的 VBAT 扫描定时器,配合计数实现"短时抖动忽略 + 持续低电告警 + 长时间低电软关机"的三段式策略。
初始化与周期采样
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
}
Source: app_power_mg.c
初始化只做三件事:把低电警戒线初始化为 POWEROFF_TONE_V + 300(即 2800 mV,比关机音电压 2500 mV 高 300 mV,保证告警先于关机)、注册 VBAT ADC 采样通道、立即执行一次扫描。app_power_vbat_check() 则用 sys_timer_add() 注册周期回调:
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
三段式低电判定
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;
}
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 或高于警戒线 | 直接返回/清零计数,避免采样毛刺误触发 |
| 低电告警 | 连续 5 次(约 10 秒)低于警戒线 | led_low_power(1) 打开低电 LED 闪烁 |
| 软关机 | 连续 60s/2s + 1 = 31 次(约 62 秒) | 上报 POWER_EVENT_POWER_LOW 触发软关机 |
| 恢复 | 任一周期电压回到警戒线以上 | 计数清零、关闭低电 LED |
采用"连续计数 + 时间窗"而不是单次阈值比较,是为了抵抗电池在负载瞬变下的电压跌落(耳机播放/配对瞬间电流可达数百 mA,VBAT 会瞬时下降)。只有当电压持续低于警戒线约 1 分钟才判定为真实低电,这一设计直接决定了用户体验(不会因播放启动瞬间的压降而误关机)。
警戒线本身可由 LVD 动态值或固定值决定(LOW_POWER_VOL 宏):当 LVD_WARNING_FOR_LOW_POWER == 1 时使用 app_power_lvd_warning(初值 2800 mV,可通过 app_power_set_lvd() 运行时调整);否则固定 3300 mV。
电源事件分发
低电状态通过系统事件队列传递给应用主循环,避免在定时器上下文直接执行关机等重操作:
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
app_power_mg.h 定义了完整的事件枚举:POWER_EVENT_POWER_NORMAL、POWER_EVENT_POWER_WARNING、POWER_EVENT_POWER_LOW、POWER_EVENT_POWER_CHANGE、POWER_EVENT_SYNC_TWS_VBAT_LEVEL、POWER_EVENT_POWER_CHARGE、POWER_EVENT_POWER_SOFTOFF——覆盖正常/告警/低电/电压变化/TWS 电量同步/充电/软关机等场景,为上层 UI(如电量图标、TWS 对耳同步)提供统一入口。
事件消费方是 app_power_event_handler():
int app_power_event_handler(struct device_event *dev, void (*set_soft_poweroff_call)(void))
{
int ret = false;
switch (dev->event) {
#if(TCFG_SYS_LVD_EN == 1)
case POWER_EVENT_POWER_NORMAL:
break;
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;
#endif
case POWER_EVENT_POWER_SOFTOFF:
set_soft_poweroff_call();
default:
break;
}
return ret;
}
Source: app_power_mg.c
注意 POWER_EVENT_POWER_LOW 分支会先删除 VBAT 检测定时器再执行软关机回调——防止关机过程中定时器再次触发导致重复关机或竞态。
软关机执行与唤醒恢复
软关机行为由 TCFG_LOWPOWER_PATTERN 编译期决定,支持两种模式:
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);
#endif
}
Source: app_power_mg.c
SOFT_MODE:直接调用power_set_soft_poweroff()进入软关机。SOFT_BY_POWER_MODE(蓝牙软关机模式):先清零按键激活计数、再延迟LOW_POWER_SOFTOFF_BT_EXIT_TIME(100 ms)执行soft_by_power_mode(),该延迟窗口用于等待蓝牙基带关闭 ADV/SCAN,然后调用sys_power_down(LOW_POWER_KEEP)保持关机状态:
static void soft_by_power_mode(void)
{
sys_timeout_del(app_power_soft.power_soft_handle);
app_power_soft.power_soft_handle = 0;
app_power_soft.power_soft_flag |= BIT(0);
putchar('{');
sys_power_down(LOW_POWER_KEEP); //sys set no wukeup time
}
Source: app_power_mg.c
LOW_POWER_KEEP(-2) 表示"进入低功耗并一直保持不退出",LOW_POWER_NO_USE(-1) 表示"睡眠时间交由蓝牙决定",LOW_POWER_SET_MAX_TIME(4000000 us)是显式睡眠时间的上限。唤醒后由 power_wakeup_init() 恢复系统:清软关机标志、wdt_init(WDT_8S) 重新使能看门狗、ble_module_enable(1) 重启蓝牙、调用弱函数 app_to_recover()(应用可重写做恢复动作)。
电量查询 API
uint16_t app_power_get_vbat(void) // 返回最近一次扫描的电压值
uint16_t app_power_get_vbat_level(void) // 实时调用 gpadc_battery_get_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 * 100 / BATTERY_FULL_VALUE;
if (tmp_bat_val > 100) {
tmp_bat_val = 100;
}
}
return (uint8_t)tmp_bat_val;
}
Source: app_power_mg.c
电量百分比计算以 4200 mV 为满电(BATTERY_FULL_VALUE)、2500 mV 以下返回 0(POWEROFF_TONE_V),并预留了 __attribute__((weak)) 的 app_power_remap_vbat_percent() 钩子——默认线性换算,应用可重写为分段映射曲线以获得更真实的剩余电量显示。弱函数 + 默认实现的组合是本 SDK 常见的扩展模式。
Core Flow:低电 → 软关机 → 唤醒
sequenceDiagram
participant T as sys_timer (2s)
participant S as app_power_scan
participant E as app_power_event_to_user
participant H as app_power_event_handler
participant P as app_power_set_soft_poweroff
participant L as sys_power_down
participant W as power_wakeup_init
T->>S: 每 2s 触发
S->>S: gpadc_battery_get_voltage()
alt vol <= LOW_POWER_VOL
S->>S: low_power_cnt++ (连续低电)
Note over S: 第 5 次(10s): led_low_power(1)
Note over S: 第 31 次(62s): 进入事件上报
S->>E: POWER_EVENT_POWER_LOW
E->>H: sys_event (SYS_DEVICE_EVENT)
H->>H: sys_timer_del(VBAT检查定时器)
H->>P: set_soft_poweroff_call()
P->>L: power_set_soft_poweroff / sys_power_down(LOW_POWER_KEEP)
L-->>P: 保持低功耗
else vol > LOW_POWER_VOL
S->>S: low_power_cnt = 0, 关低电LED
end
Note over L,W: 唤醒事件(按键/P33 IO)到达
W->>W: 清 power_soft_flag
W->>W: wdt_init(WDT_8S)
W->>W: ble_module_enable(1)
W->>W: app_to_recover() (弱函数)
流程要点:整个链路刻意把"判定"(app_power_scan,定时器上下文)与"执行"(app_power_event_handler,应用主循环上下文)分离。定时器上下文只做采样、计数与事件投递,绝不直接关机;关机动作由应用层回调执行,并先停掉检测定时器,从而避免上下文重入与竞态。唤醒恢复则依赖 power_wakeup_init() 完成看门狗、蓝牙与应用现场的三级重建。
板级电源配置与初始化
power_config.c 位于 CPU 目录(cpu/bd47/power/),把 app_config.h 的宏映射为底层结构体,并完成电源初始化。它定义了三个关键对象:power_param(电源参数)、power_pdata(传递给底层的数据载体)与 board_power_init()(板级初始化入口)。
struct _power_param power_param = {
.config = POWER_PARAM_CONFIG, // TCFG_LOWPOWER_LOWPOWER_SEL
.btosc_hz = POWER_PARAM_BTOSC_HZ, // TCFG_CLOCK_OSC_HZ
.vddiom_lev = POWER_PARAM_VDDIOM_LEV, // TCFG_LOWPOWER_VDDIOM_LEVEL
.vddiow_lev = POWER_PARAM_VDDIOW_LEV, // TCFG_LOWPOWER_VDDIOW_LEVEL
.osc_type = POWER_PARAM_OSC_TYPE, // TCFG_LOWPOWER_OSC_TYPE
};
struct _power_pdata power_pdata = {
.power_param_p = &power_param,
};
Source: power_config.c
board_power_init() 是电源子系统在板级启动阶段的入口,它依次完成:供电方式打印 → 供电模式与采样通道一致性校验 → 电源控制项设置 → power_init() 核心初始化 → 可选 DCDC 外置开关 → 按键唤醒初始化:
void board_power_init()
{
GPIO_CONFIG_INIT();
printf("power_supply:%s, lowper_mode:%d", power_support_type[TCFG_POWER_SUPPLY_MODE], TCFG_LOWPOWER_LOWPOWER_SEL);
//供电模式 和 电池采样通道 匹配检查
if (gpadc_power_supply_mode == 0) { //VDDIO 供电
if (gpadc_ch_power != AD_CH_IOVDD) {
assert(0, "gpadc_power_supply_mode and gpadc_ch_power not match");
}
if (gpadc_battery_mode == 0) {
assert(0, "gpadc_power_supply_mode == IOVDD not supply gpadc_battery_mode == MEAN_FILTERING_MODE");
}
} else if (gpadc_power_supply_mode == 1) { //vpwr 供电
if (gpadc_ch_power != AD_CH_PMU_VPWR_4) {
assert(0, "gpadc_power_supply_mode and gpadc_ch_power not match");
}
}
//电池采样通道 和 分压系数检查
if (gpadc_ch_power == AD_CH_PMU_VPWR_4) {
assert(gpadc_ch_power_div == 4, "gpadc_ch_power and gpadc_ch_power_div not match");
}
power_control(PCONTROL_POWER_SUPPLY, TCFG_POWER_SUPPLY_MODE);
power_control(PCONTROL_PD_VDDIO_KEEP, VDDIO_KEEP_TYPE_NORMAL);
power_control(PCONTROL_SF_VDDIO_KEEP, VDDIO_KEEP_TYPE_NORMAL);
power_init(&power_pdata);
#if (!CONFIG_CHARGE_ENABLE)
if (CONFIG_POWER_MODE == PWR_DCDC15) {
extern_dcdc_switch(1);
}
#endif
#if SUPPORT_KEY_WAKEUP_EN
key_wakeup_init();
#endif
}
Source: power_config.c
设计意图分析:
- 启动期断言代替运行期故障:供电方式(IOVDD/VPWR)、电池采样通道(
AD_CH_IOVDD/AD_CH_PMU_VPWR_4)与分压系数(gpadc_ch_power_div == 4)三者必须严格匹配,任何不一致都会导致电压读数系统性偏差——可能在 LVD 判据上产生 ±4 倍的误差。在board_power_init()中assert,让硬件配置错误在开机瞬间暴露,而不是让设备在低电误关机后才发现。 power_control(PCONTROL_POWER_SUPPLY, ...)设置供电模式;PCONTROL_PD_VDDIO_KEEP/PCONTROL_SF_VDDIO_KEEP分别控制掉电(power down)与软关机(soft off)时 VDDIO 的保持策略,保证唤醒后 IO 状态不丢失。extern_dcdc_switch(1):当使用PWR_DCDC15且未使能充电时,需要外部 DCDC 配合;CONFIG_CHARGE_ENABLE默认关闭(0)。key_wakeup_init()由SUPPORT_KEY_WAKEUP_EN(默认 1)控制,配合p33_io_wakeup_edge()实现 IO 唤醒配置。
配置选项
| 宏 / 常量 | 类型 | 默认值 | 说明 |
|---|---|---|---|
TCFG_SYS_LVD_EN | bool | 板级决定 | 低电压检测(LVD)总使能,关闭后低电事件分支不编译 |
TCFG_ADC_VBAT_CH_EN | bool | 板级决定 | VBAT ADC 采样通道使能,app_power_init 依赖 |
TCFG_LOWPOWER_PATTERN | enum | 板级决定 | 软关机模式:SOFT_MODE / SOFT_BY_POWER_MODE 等 |
TCFG_LOWPOWER_LOWPOWER_SEL | int | 板级决定 | 低功耗模式选择,映射为 power_param.config |
TCFG_LOWPOWER_POWER_SEL | enum | 板级决定 | 供电模式:PWR_DCDC15 / PWR_DCDC12 / PWR_LDO15 等 |
TCFG_LOWPOWER_VDDIOM_LEVEL | int | 板级决定 | 内核/数字电压档位,映射 vddiom_lev |
TCFG_LOWPOWER_VDDIOW_LEVEL | int | 板级决定 | IO 电压档位,映射 vddiow_lev |
TCFG_LOWPOWER_OSC_TYPE | int | 板级决定 | 睡眠振荡器类型,映射 osc_type |
TCFG_CLOCK_OSC_HZ | int | 板级决定 | 振荡器频率(Hz),映射 btosc_hz |
TCFG_POWER_SUPPLY_MODE | enum | 板级决定 | 供电方式:IOVDD / VPWR,须与 gpadc_ch_power 匹配 |
LOW_POWER_VOL | int | app_power_lvd_warning 或 3300 | 低电警戒线(mV),LVD 模式初值为 2800 |
LOW_POWER_CHECK_INTERVAL | int | 2000 | 低电扫描周期(ms) |
LOW_POWER_SOFTOFF_TIME | int | 60000 | 连续低电软关机时间窗(ms) |
LOW_POWER_SET_MAX_TIME | long | 4000000 | 显式睡眠时间上限(us) |
LOW_POWER_KEEP | int | -2 | 进入低功耗并一直保持不退出 |
LOW_POWER_NO_USE | int | -1 | 睡眠时间由蓝牙决定 |
LOW_POWER_SOFTOFF_BT_EXIT_TIME | int | 100 | 软关机前等待蓝牙基带关闭 ADV/SCAN 的时间(ms) |
POWEROFF_TONE_V | int | 2500 | 关机电压阈值(mV),百分比计算下限 |
BATTERY_FULL_VALUE | int | 4200 | 满电电压(mV),百分比计算基准 |
SUPPORT_KEY_WAKEUP_EN | bool | 1 | 是否支持 IO/按键唤醒 |
CONFIG_CHARGE_ENABLE | bool | 0 | 是否使能充电 |
CONFIG_DCDC_TYPE | enum | PWR_DCDC12 | DCDC 类型 |
API Reference
应用层(app_power_mg.h / .c)
void app_power_init(void) 系统启动时调用:初始化低电警戒线、注册 AD_CH_PMU_VBAT 采样通道并执行首次扫描。
void app_power_vbat_check(void) 用 sys_timer_add 注册 2 秒周期的 app_power_scan 回调,开启持续低电监测。
u16 app_power_get_vbat(void) 返回最近一次扫描缓存的最新 VBAT 电压(mV),无额外 ADC 开销,适合频繁读取。
u16 app_power_get_vbat_level(void) 实时调用 gpadc_battery_get_voltage() 获取当前电池电压,有采样延迟但更实时。
u8 app_power_get_vbat_percent(void) 返回电量百分比:低于 2500 mV 返回 0;否则优先走 app_power_remap_vbat_percent()(弱函数,可重写),默认按 vol*100/4200 线性换算并封顶 100。
void app_power_set_lvd(u16 lvd_value) 运行时设置低电警戒线(app_power_lvd_warning),用于 LVD 模式下动态调整。
void app_power_event_to_user(u8 event) 把电源事件打包为 SYS_DEVICE_EVENT(来源 DEVICE_EVENT_FROM_POWER)投递给应用主循环。事件:POWER_EVENT_POWER_NORMAL / POWER_WARNING / POWER_LOW / POWER_CHANGE / SYNC_TWS_VBAT_LEVEL / POWER_CHARGE / POWER_SOFTOFF。
int app_power_event_handler(struct device_event *dev, void (*set_soft_poweroff_call)(void)) 应用层消费电源事件的处理器:收到 POWER_EVENT_POWER_LOW 时删除 VBAT 定时器并回调软关机;POWER_EVENT_POWER_SOFTOFF 直接回调软关机。返回 false(未处理)。
void app_power_set_soft_poweroff(void *priv) 按 TCFG_LOWPOWER_PATTERN 执行软关机:SOFT_MODE 直接 power_set_soft_poweroff();SOFT_BY_POWER_MODE 延迟 100 ms 待蓝牙关闭后 sys_power_down(LOW_POWER_KEEP)。
void p33_io_wakeup_edge(u32 gpio, P33_IO_WKUP_EDGE edge) 配置 P33 PMU 的 IO 唤醒边沿,供按键唤醒使用。
底层框架(power_manage.h)
void *low_power_get(void *priv, const struct low_power_operation *ops) / void low_power_put(void *priv) 模块获取/释放一次低功耗窗口使用权;low_power_sys_get / low_power_sys_put 为系统级版本。
void low_power_on(void) 开启低功耗调度;void low_power_request(void) 主动请求调度器评估进入低功耗。
u8 is_low_power_mode(enum LOW_POWER_LEVEL level) 查询当前是否处于指定睡眠等级(LIGHT_SLEEP / SLEEP / DEEP_SLEEP / SOFF)。
u32 low_power_sys_not_idle_cnt(void) / u8 low_power_sys_is_idle(void) 统计/查询系统非空闲计数,供调度器判断整机是否可睡眠。
板级与硬件(power_interface.h / power_config.c)
void power_init(struct _power_pdata *pdata) 以板级参数(power_param)初始化电源硬件(P33 PMU)。
u32 power_control(u32 cmd, u32 arg) 电源控制命令接口,如 PCONTROL_POWER_SUPPLY、PCONTROL_PD_VDDIO_KEEP、PCONTROL_SF_VDDIO_KEEP。
void sys_power_down(u32 usec) 进入系统睡眠/软关机,usec 传 LOW_POWER_KEEP(-2) 表示保持不退出。
故障模式、边界情况与并发
低电判定相关的边界情况
- 采样值为 0 时跳过:
app_power_scan中if (0 == vol) return;——ADC 尚未就绪或采样失败时不计数、不报警,避免把无效读数当作真实低电。 - 电压抖动恢复:只要一次采样回到警戒线以上,
low_power_cnt立即清零并关闭低电 LED。这保证了耳机在低电边界上"进进出出"时不会累积计数误触发关机,但代价是关机时间窗被拉长(要求连续约 62 秒低于阈值)。 POWER_EVENT_POWER_NORMAL分支为空:LVD 使能时该事件仅作为占位,恢复逻辑由扫描函数本身完成(计数清零),事件通道只负责"恶化"方向的通知。- 百分比计算下限:
bat_val <= POWEROFF_TONE_V(2500)直接返回 0%,防止关机边缘显示负值或虚假残电;超过 4200 mV 封顶 100%。
并发与上下文安全
- 定时器上下文 vs 主循环上下文:
app_power_scan运行在sys_timer回调中,app_power_event_handler运行在应用主循环。二者通过sys_event队列解耦,事件投递使用event_pool_alloc()——分配失败时只打日志并返回,不会阻塞或崩溃,但意味着低电通知可能丢失(此时只能依赖下一次扫描计数)。 - 软关机竞态防护:
POWER_EVENT_POWER_LOW处理时先sys_timer_del(app_power_vbat_check_timer)删除周期定时器,确保关机流程执行期间不再有新的扫描/事件注入;soft_by_power_mode()开头又sys_timeout_del(power_soft_handle)清理可能残留的延时回调,双重保险。 - 唤醒重入:
power_wakeup_init()以power_soft_flag & BIT(0)判断是否为软关机唤醒,非软关机唤醒不会执行蓝牙重开等重操作;唤醒后先wdt_init(WDT_8S)再开蓝牙,防止唤醒初始化阶段看门狗超时复位。
软关机模式的差异风险
SOFT_BY_POWER_MODE 依赖蓝牙基带在 100 ms 内关闭 ADV/SCAN,若蓝牙栈忙(如正在连接),sys_power_down(LOW_POWER_KEEP) 仍会按计划执行——这是"时间窗 + 强制"的取舍:宁可略早关机也不让功耗泄漏。若应用对蓝牙状态有强依赖,需在 app_to_recover()(弱函数)中自行处理恢复后的状态重协商。
性能与运维
- 采样开销:VBAT 扫描每 2 秒一次、单次
gpadc_battery_get_voltage(),对系统负载影响可忽略;app_power_get_vbat()使用缓存值避免高频读取触发 ADC。 - 睡眠功耗:最低功耗依赖
DEEP_SLEEP/SOFF下所有deepsleep_target/phw_dev_ops正确关断外设;新增外设模块时必须注册对应deepsleep_target,否则深睡后外设漏电会显著拉高整机电流。 - 看门狗:软关机唤醒后强制
wdt_init(WDT_8S),防止恢复流程挂死;开发调试时注意关机状态下 WDT 行为,避免低功耗测量被看门狗复位污染。 - 调试手段:
app_power_scan在低电时打印vbat voltage : %d与LOW_POWER_LOG("Waring!!! Vbat is near to lvd!" 或 "low power");board_power_init启动时打印power_supply:%s, lowper_mode:%d,可用于核对供电配置与模式选择。
扩展点
| 扩展点 | 机制 | 典型用途 |
|---|---|---|
REGISTER_LP_TARGET | .lp_target 链接器段 | 新模块声明空闲状态与允许的睡眠等级 |
REGISTER_LP_REQUEST | .lp_request 段 | 模块主动请求进入/退出低功耗 |
DEEPSLEEP_TARGET_REGISTER | .deepsleep_target 段 | 深睡前后保存/恢复外设状态 |
REGISTER_PHW_DEV_PMU_OPS | phw_pmu_ops 指针 | 驱动层接入 PMU 硬件操作(sleep/soff/deepsleep 全生命周期) |
app_power_remap_vbat_percent() | __attribute__((weak)) 弱函数 | 重定义电量-百分比映射曲线 |
app_to_recover() | __attribute__((weak)) 弱函数 | 软关机唤醒后的应用级恢复动作 |
app_power_set_lvd() | 运行时 API | 动态调整低电警戒线(如充电时抬高/降低阈值) |