杰理 SDK 文档中心
首页
首页
  • 概述与快速入门

    • 芯片平台与 SDK 概述
    • 环境搭建与编译工具链
    • 快速开始:选型、编译与烧录
    • 烧录与量产工具
  • 构建系统与板级工程

    • 顶层 Makefile 与编译目标
    • 板级工程与配置
    • 后处理与配置工具
  • HID 人机交互应用

    • HID 应用架构总览
    • 键盘、翻页器与遥控应用
    • 鼠标应用:单模、双模与低延迟
    • 空闲应用与初始化流程
  • BLE 透传与数传应用

    • 透传应用总览
    • 多连接与无连接传输
    • AT 命令模组应用
    • Dongle 适配器应用
  • BSP 公共模块

    • 蓝牙公共处理
    • 按键、LED 与红外
    • 传感器与编码器
    • 存储、VM 与文件系统
    • 电源管理与低功耗
    • 消息调度与通信外设
  • 协议栈与预编译库

    • 蓝牙协议栈库
    • 设备驱动与文件系统库
    • 音频、升级与其他库
  • 开发资料与补丁发布

    • 文档资料中心
    • 版本补丁与兼容性修复

电源管理与低功耗

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 的电源管理子系统把这两级统一在一个分层结构中:

  1. 应用层(app_power_mg.c):负责电池电压周期采样、低电判定、事件上报、软关机触发与唤醒恢复。它是开发者最常接触的接口层,提供 app_power_init()、app_power_get_vbat()、app_power_get_vbat_percent() 等 API。
  2. 板级配置层(power_config.c):把 app_config.h 中的 TCFG_* 宏翻译成底层 struct _power_param / struct _power_pdata,并完成供电模式与采样通道匹配校验、power_init() 初始化、按键唤醒注册。
  3. 底层框架(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_ENbool板级决定低电压检测(LVD)总使能,关闭后低电事件分支不编译
TCFG_ADC_VBAT_CH_ENbool板级决定VBAT ADC 采样通道使能,app_power_init 依赖
TCFG_LOWPOWER_PATTERNenum板级决定软关机模式:SOFT_MODE / SOFT_BY_POWER_MODE 等
TCFG_LOWPOWER_LOWPOWER_SELint板级决定低功耗模式选择,映射为 power_param.config
TCFG_LOWPOWER_POWER_SELenum板级决定供电模式:PWR_DCDC15 / PWR_DCDC12 / PWR_LDO15 等
TCFG_LOWPOWER_VDDIOM_LEVELint板级决定内核/数字电压档位,映射 vddiom_lev
TCFG_LOWPOWER_VDDIOW_LEVELint板级决定IO 电压档位,映射 vddiow_lev
TCFG_LOWPOWER_OSC_TYPEint板级决定睡眠振荡器类型,映射 osc_type
TCFG_CLOCK_OSC_HZint板级决定振荡器频率(Hz),映射 btosc_hz
TCFG_POWER_SUPPLY_MODEenum板级决定供电方式:IOVDD / VPWR,须与 gpadc_ch_power 匹配
LOW_POWER_VOLintapp_power_lvd_warning 或 3300低电警戒线(mV),LVD 模式初值为 2800
LOW_POWER_CHECK_INTERVALint2000低电扫描周期(ms)
LOW_POWER_SOFTOFF_TIMEint60000连续低电软关机时间窗(ms)
LOW_POWER_SET_MAX_TIMElong4000000显式睡眠时间上限(us)
LOW_POWER_KEEPint-2进入低功耗并一直保持不退出
LOW_POWER_NO_USEint-1睡眠时间由蓝牙决定
LOW_POWER_SOFTOFF_BT_EXIT_TIMEint100软关机前等待蓝牙基带关闭 ADV/SCAN 的时间(ms)
POWEROFF_TONE_Vint2500关机电压阈值(mV),百分比计算下限
BATTERY_FULL_VALUEint4200满电电压(mV),百分比计算基准
SUPPORT_KEY_WAKEUP_ENbool1是否支持 IO/按键唤醒
CONFIG_CHARGE_ENABLEbool0是否使能充电
CONFIG_DCDC_TYPEenumPWR_DCDC12DCDC 类型

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_OPSphw_pmu_ops 指针驱动层接入 PMU 硬件操作(sleep/soff/deepsleep 全生命周期)
app_power_remap_vbat_percent()__attribute__((weak)) 弱函数重定义电量-百分比映射曲线
app_to_recover()__attribute__((weak)) 弱函数软关机唤醒后的应用级恢复动作
app_power_set_lvd()运行时 API动态调整低电警戒线(如充电时抬高/降低阈值)

相关链接

  • app_power_mg.h — 事件与阈值定义
  • app_power_mg.c — 低电检测与软关机实现
  • power_config.c — 板级电源初始化
  • power_manage.h — 低功耗框架契约
  • power_interface.h — 电源硬件接口
  • power_app.c — 电源应用入口
  • lib_power_config.c — 库级电源配置
Prev
存储、VM 与文件系统
Next
消息调度与通信外设