电源管理与低功耗
本文档介绍 AD24N SDK 的电源管理与低功耗机制:从应用层的电池电压检测、低电告警(app_power_mg),到 PMU 电源框架(power_api)中的 sleep/deep-sleep/soft-off 三种低功耗模式、电源控制命令、参数配置与回调钩子,说明整个电源子系统的端到端工作方式。
Purpose and Scope
本页面覆盖 AD24N(SH58 系列 CPU)电源管理子系统的完整实现:
- 应用层电源管理:
app_power_mg.c/.h的 VBAT 采样、低电检测与MSG_LOW_POWER消息上报; - 电源框架:
power_api.h定义的电源初始化、power_control()命令集、低功耗使能/关闭 API、soft-off 流程; - PMU 平台实现:
power_app.c中的进入/退出低功耗回调、软关机回调与power_early_flowing()启动流程; - 关键数据结构:
_power_param、_power_pdata、VDDIO_KEEP_TYPE、LOWPOWER_OSC_TYPE等。
以下内容属于兄弟页面范畴,本页仅作引导、不展开:RTC/时钟与晶振配置(见时钟管理)、GPIO 唤醒配置(见 GPIO 驱动)、VM 参数存储、按键长按复位等。
Overview
AD24N 是杰理科技(Jieli-Tech)的低功耗音频 MCU(SH58 内核)。其电源管理子系统解决两个核心问题:
- 运行时的电源健康监控——通过 PMU 内置 ADC 通道
ADC_CH_PMU_VBAT周期性采样电池电压,连续多次低于阈值后向应用层投递MSG_LOW_POWER消息,触发低电提醒/关机策略。 - 空闲时的功耗优化——当系统(含蓝牙协议栈)空闲时,由 FreeRTOS 的
vPortSuppressTicksAndSleep()或显式的sys_power_down()进入低功耗(sleep/deep-sleep),或由sys_softoff()进入 soft-off(软关机),在保持唤醒能力的同时把整机功耗压到最低。
整个框架的设计意图是把"硬件电源细节"与"应用策略"解耦:硬件相关的电压挡位、晶振选择、IO 保持策略通过 power_control() 命令接口和 _power_param 配置注入;应用只关心"检测电压、判断低电、发消息"和"空闲时请求睡眠",以及通过回调感知进入/退出低功耗的时刻。
Architecture
flowchart TD
subgraph sg_App["应用层 (sdk/app/bsp/common)"]
AppPM["app_power_mg.c<br/>VBAT采样/低电告警"]
MSG["MSG_LOW_POWER 消息"]
AppPM -->|"post_msg"| MSG
end
subgraph sg_PMU["电源框架 (include_lib/cpu/sh58/asm/power)"]
API["power_api.h<br/>power_control / lowpower_init / sys_power_down"]
PARAM["_power_param 配置<br/>osc_type / vddiom_lev / config"]
end
subgraph sg_Platform["平台实现 (sdk/app/bsp/cpu/sh58/power)"]
APP["power_app.c<br/>power_early_flowing / 回调"]
CALLBACK["sleep_enter_callback<br/>sleep_exit_callback<br/>power_soff_callback"]
end
subgraph sg_HW["硬件资源"]
ADC["PMU ADC 通道<br/>ADC_CH_PMU_VBAT"]
LDO["VDDIOM / VDDIOW LDO"]
OSC["LRC / BT_OSC 低功耗晶振"]
end
AppPM -->|"adc_get_voltage"| ADC
AppPM -->|"低电检测"| MSG
API -->|"进入/退出"| CALLBACK
API -->|"配置"| PARAM
API -->|"控制LDO/晶振"| LDO
API -->|"唤醒计时"| OSC
APP -->|"power_early_init(gpio_config)"| API
APP -->|"实现钩子"| CALLBACK
架构分四层:
- 应用层:
app_power_mg.c只做两件事——启动时连续采样 VBAT 得到稳定的开机电压值;周期扫描中连续 10 次电压低于阈值时打印告警并post_msg(1, MSG_LOW_POWER)。阈值来源由LVD_WARNING_FOR_LOW_POWER宏决定(默认用 LVD 警告电压 + 200mV,而非固定 3.3V),这保证低电检测与硬件复位点(LVD)保持合理余量。 - 电源框架:
power_api.h是 PMU 库对外契约,定义初始化序列(power_early_init→power_later_init→power_init)、power_control()命令集、低功耗开关(lowpower_init/lowpower_uninit)与睡眠/关机入口(vPortSuppressTicksAndSleep/sys_power_down/sys_softoff)。 - 平台实现:
power_app.c把"进入睡眠前把 USB IO 置高阻、退出后恢复 DIR"、"软关机前关闭平台 uninit 流程并配置 GPIO 为软关机状态"等板级细节以回调形式挂入框架,并通过power_early_flowing()在启动早期加载低功耗代码段(overlay)、保护调试打印口、再调用power_early_init((u32)gpio_config)。 - 硬件资源:PMU ADC、双 LDO 挡位、低功耗晶振(LRC 或蓝牙 24M 晶振)与唤醒计时器,均由框架按
_power_param配置驱动。
低功耗模式与状态
power_api.h 定义了三种电源策略,由 _power_param.config 字段(宏 LOWPOWER_CLOSE / SLEEP_EN / DEEP_SLEEP_EN)选择:
stateDiagram-v2
[*] --> RUN: power_early_init + 系统运行
RUN --> SLEEP: 空闲 && SLEEP_EN<br/>vPortSuppressTicksAndSleep/sys_power_down
SLEEP --> RUN: 定时器/IO唤醒 (sleep_exit_callback)
RUN --> DEEPSLEEP: 空闲 && DEEP_SLEEP_EN
DEEPSLEEP --> RUN: 唤醒源触发
RUN --> SOFF: sys_softoff / 低电关机策略
SOFF --> [*]: power_soff_callback<br/>GPIO软关机
- SLEEP(睡眠):
SLEEP_EN使能,系统空闲时按usec定时睡眠,通过 LRC/BT_OSC 计时,唤醒后自动恢复;FreeRTOS 通过vPortSuppressTicksAndSleep(usec)把 tick 中断与硬件睡眠对齐,保证调度器时间不漂移(配合low_power_trace_drift校正)。 - DEEP_SLEEP(深睡):
DEEP_SLEEP_EN使能,进一步关闭更多外设电源域,唤醒延迟更大、功耗更低。 - SOFF(软关机):
sys_softoff()触发,power_soff_callback()执行平台 uninit、mask 软标志(sfc_fast_boot = 0)、gpio_config_soft_poweroff()配置关机态 IO,最终整机进入极低功耗待机,靠开机键/充电检测唤醒。
应用层电源管理实现(app_power_mg)
低电检测阈值的选择
app_power_mg.h 通过编译期宏 LVD_WARNING_FOR_LOW_POWER 决定低电阈值来源,这是"硬件复位余量"设计意图的直接体现:如果低电告警阈值与 LVD(低压检测,硬件强制复位点)太接近,软件来不及保存状态系统就被复位;如果太远,则电池容量利用不足。
#define LVD_WARNING_FOR_LOW_POWER 1//1:使用LVD警告电压作低电提醒 0:使用固定值作低电提醒
#if LVD_WARNING_FOR_LOW_POWER
#define LOW_POWER_VOL lvd_warning_voltage
#define LOW_POWER_LOG "Warning!!! Vbat is near to lvd!\n"
#define low_power_warning_init() lvd_warning_init()
#else
#define LOW_POWER_VOL 3300//3.3v
#define LOW_POWER_LOG "low power\n"
#define low_power_warning_init()
#endif
Source: app_power_mg.h
默认路径(LVD_WARNING_FOR_LOW_POWER = 1)下,阈值在运行时从 efuse/寄存器读取的 LVD 电压加 200mV 得到,确保告警先于硬件复位发生:
static void lvd_warning_init(void)
{
u16 lvd_voltage = get_lvd_vol();
lvd_warning_voltage = lvd_voltage + 200;
}
Source: app_power_mg.c
初始化与上电电压采样
app_power_init() 注册 VBAT 采样通道后,通过 adc_kick_start() 连续触发 10 次采样(回调自嵌套),直到拿到稳定电压值才返回。这样开机阶段就能得到可信的电池电压(4 倍放大换算:PMU 内部电阻分压后软件乘 4 还原实际电压):
static volatile u8 powerup_kick_start_cnt = 0;
static void app_power_powerup_kick_start(void)
{
if (powerup_kick_start_cnt++ < 10) {
adc_kick_start(app_power_powerup_kick_start);
return;
}
log_info("vbat value:%d \n", app_power_get_vbat());
}
void app_power_init(void)
{
low_power_warning_init();
adc_add_sample_ch(ADC_CH_PMU_VBAT);
adc_kick_start(app_power_powerup_kick_start);
while (powerup_kick_start_cnt < 10);
}
u32 app_power_get_vbat(void)
{
u32 vol = adc_get_voltage(ADC_CH_PMU_VBAT);
if (-1 != vol) {
vol *= 4;
}
return vol;
}
Source: app_power_mg.c
周期扫描与低电消息
app_power_scan() 是去抖式的低电检测:只有连续 10 次扫描(约 10 个扫描周期)都低于阈值才判定为低电,避免电池电压因负载波动瞬时跌落造成的误报。一旦确认低电,立即 post_msg(1, MSG_LOW_POWER) 通知应用层(应用据此弹低电提醒、降频或执行关机策略)。若按键为 GPIO 直连(非 AD/矩阵按键),扫描末尾主动 adc_kick_start(NULL) 维持 ADC 采样链路:
void app_power_scan(void)
{
static u16 low_power_cnt = 0;
u32 vol = adc_get_voltage(ADC_CH_PMU_VBAT);
if (-1 != vol) {
vol = vol * 4;
if (vol <= LOW_POWER_VOL) {
low_power_cnt++;
if (low_power_cnt == 10) {
log_error(LOW_POWER_LOG);
post_msg(1, MSG_LOW_POWER);
}
} else {
low_power_cnt = 0;
}
}
#if (!(KEY_AD_EN || KEY_MATRIX_EN))
adc_kick_start(NULL);
#endif
}
Source: app_power_mg.c
端到端低电检测时序
sequenceDiagram
participant App as 应用任务
participant ADC as PMU ADC (ADC_CH_PMU_VBAT)
participant PM as app_power_mg
participant MSG as 消息队列
App->>PM: app_power_init()
PM->>ADC: adc_add_sample_ch + adc_kick_start ×10
ADC-->>PM: 稳定 VBAT (×4)
loop 周期扫描 app_power_scan()
PM->>ADC: adc_get_voltage
alt vol <= LOW_POWER_VOL
PM->>PM: low_power_cnt++ (连续10次)
PM->>MSG: post_msg(1, MSG_LOW_POWER)
else vol > LOW_POWER_VOL
PM->>PM: low_power_cnt = 0 (复位去抖计数)
end
end
电源框架 API 详解(power_api.h)
低功耗代码段放置
框架把"进出低功耗必须驻留"的驱动代码显式放入专用内存段,保证睡眠期间 DDR/Flash 掉电后代码仍可执行(overlay 段在低功耗期间保持有效):
#define AT_VOLATILE_RAM_POWER AT(.power_driver.data)
#define AT_VOLATILE_RAM_BSS_POWER AT(.power_driver.data.bss)
#define AT_VOLATILE_RAM_CODE_POWER AT(.power_driver.text.cache.L1)
#define AT_VOLATILE_RAM_LOWPOWER AT(.power_driver.data.overlay)
#define AT_VOLATILE_RAM_BSS_LOWPOWER AT(.power_driver.data.bss.overlay)
#define AT_VOLATILE_RAM_CODE_LOWPOWER AT(.power_driver.text.cache.L1.overlay)
Source: power_api.h
电源参数结构
_power_param 是平台电源配置的载体,分"睡眠"与"电源"两组:睡眠组描述低功耗晶振(btosc_hz 默认 24M、osc_type、预留的 t1~t4/lptmr_flow);电源组描述工作态与低功耗态的 LDO 挡位(vddiom_lev 工作电压、vddiow_lev 低功耗电压)以及 config 选择的低功耗模式。osc_delay_us 预留低功耗晶振起振延时,说明框架对晶振稳定时间有显式建模:
struct _power_param {
//sleep
u32 btosc_hz; //蓝牙晶振频率(默认使用24M)
u32 osc_delay_us; //低功耗晶振起振延时,为预留配置。
u32 t1; //低功耗参数,预留配置
u32 t2; //低功耗参数,预留配置
u32 t3; //低功耗参数,预留配置
u32 t4; //低功耗参数,预留配置
//power
u8 vddiom_lev; //vddiom,系统工作时使用vddiom ldo(使用enum VDDIOM_VOL配置)
u8 vddiow_lev; //vddiow,系统低功耗时使用vddiow ldo(使用enum VDDIOW_VOL配置)
//sleep
u8 config; //低功耗使能(使用LOWPOWER_CONFIG配置)
u8 osc_type; //低功耗晶振类型(使用enum LOWPOWER_OSC_TYPE配置)
u8 lptmr_flow; //低功耗参数由用户配置
};
Source: power_api.h
VDDIO 保持策略
进出低功耗时 VDDIOW 的供电策略通过 VDDIO_KEEP_TYPE 枚举表达,覆盖四种典型场景:使用配置值、沿用工作态 VDDIOM 挡位、使用 trim 校准值、或完全保持 VDDIOM 不关。PCONTROL_PD_VDDIO_KEEP / PCONTROL_SF_VDDIO_KEEP 两个命令分别控制 sleep 与 soft-off 的切换流程:
enum VDDIO_KEEP_TYPE {
VDDIO_KEEP_TYPE_NULL, //vddiow使用配置值
VDDIO_KEEP_TYPE_NORMAL, //vddiow使用配置值使用vddiom挡位,即vddiom_lev
VDDIO_KEEP_TYPE_TRIM, //vddiow使用trim值
VDDIO_KEEP_TYPE_PG, //保持vddiom不关闭
};
Source: power_api.h
power_control 命令集
power_control() 是平台电源行为的统一配置入口,命令按功能域分组(0x100 电源、0x200/0x300/0x400 预留子模块)。这种"命令字 + 参数"设计让 PMU 库与具体板级差异解耦:同一份库代码可通过不同命令参数适配 DCVDD 是否外挂电容、Flash 供电引脚类型、RTC 晶振类型、供电方式(IOVDD/VPWR)等硬件变体:
enum PCONTROL_CMD {
PCONTROL_POWER_DRIVER_RESERVE = 0,
PCONTROL_P_PUTBYTE, //串口调试函数
//power
PCONTROL_POWER_MODE = 0x100,
PCONTROL_DCVDD_CAP_SW, //0:DCVDD上没有外挂电容 1:DCVDD上有外挂电容
PCONTROL_FLASH_PG_VDDIO, //0:FLASH电源引脚使用IO 1:FLASH电源引脚没有使用IO
PCONTROL_RTC_CLK, //RTC_CLK类型,配置开机、关机晶振流程
PCONTROL_POWER_SUPPLY, //供电方式,0:IOVDD供电,1:VPWR供电
//sleep
PCONTROL_PD_VDDIO_KEEP, //pdown vddio切换流程(使用enum VDDIO_KEEP_TYPE配置)
PCONTROL_PD_WDVDD_LEV, //pdown wvdd挡位
PCONTROL_PD_DVDD_LEV, //pdown dvdd挡位
PCONTROL_PD_KEEP_LPCTMU, //pdown 触摸是否保持 0:不保持 1:保持
//soff
PCONTROL_SF_KEEP_LRC, //soff lrc是否保持 0:不保持 1:保持
PCONTROL_SF_VDDIO_KEEP, //soff vddio切换流程(使用enum VDDIO_KEEP_TYPE配置)
PCONTROL_SF_KEEP_NVDD, //soff nvdd是否保持 0:不保持 1:保持
PCONTROL_SF_KEEP_PVDD, //soff pvdd是否保持 0:不保持 1:保持
//以下配置为对应子模块的预留配置
PCONTROL_PHW_RESERVE = 0x100,
PCONTROL_P33_RESERVE = 0x200,
PCONTROL_P11_RESERVE = 0x300,
PCONTROL_LP_FLOW_IC_RESERVE = 0x400,
};
Source: power_api.h
低功耗入口与初始化序列
框架暴露两组关键接口:
- 初始化:
power_early_init(arg)(启动最早,仅带gpio_config参数,见power_early_flowing)→power_later_init(arg)→power_init(pdata)(携带_power_pdata即_power_param指针)。分阶段初始化保证:先建立最小电源环境(LDO/晶振),再在后期接入完整参数与平台策略。 - 低功耗开关:
lowpower_init()/lowpower_uninit()控制 overlay 代码段与低功耗驱动是否可用;low_power_sys_request(priv)供系统空闲时请求睡眠;low_power_trace_drift(usec)校正睡眠期间 FreeRTOS tick 漂移;low_power_reset_osc_type/low_power_get_osc_type/low_power_get_default_osc_type管理低功耗晶振类型。 - 睡眠/关机入口:
vPortSuppressTicksAndSleep(usec)由 FreeRTOS 调度器在 idle 任务调用,sys_power_down(usec)供非 RTOS 场景直接使用,sys_softoff()进入软关机。
void power_early_init(u32 arg);
void power_later_init(u32 arg);
void power_init(struct _power_pdata *pdata);
u32 power_control(enum PCONTROL_CMD cmd, u32 arg);
void lowpower_init();
void lowpower_uninit();
void low_power_sys_request(void *priv);
s32 low_power_trace_drift(u32 usec);
void low_power_reset_osc_type(u8 type);
u8 low_power_get_default_osc_type(void);
u8 low_power_get_osc_type(void);
void lowpower_dump();
void vPortSuppressTicksAndSleep(u32 usec);
void sys_power_down(u32 usec);
void sys_softoff(void);
Source: power_api.h
平台实现与回调机制(power_app.c)
进入/退出低功耗回调
power_app.c 以回调方式把板级动作注入框架。sleep_enter_callback(step) 在关中断、单核环境下执行(注释明确要求"禁止添加打印、勿做耗时操作"):打印 '<' 标记、保存 USB IO 方向寄存器(调试口在 DP/DM 时)、把 USB 口置高阻防止睡眠漏电;sleep_exit_callback(usec) 则恢复 USB IO 方向并打印 '>'。这里的 USB IO 特殊处理体现了调试口保护与低功耗的权衡——睡眠期间打印口必须高阻,退出后再恢复:
static u32 usb_io_con = 0;
void sleep_enter_callback(u8 step)
{
/* 此函数禁止添加打印 */
int putchar(int a);
putchar('<');
//USB IO打印引脚特殊处理
if (libs_debug) {
#if ((CONFIG_UART_DEBUG_PORT == IO_PORT_DP) || (CONFIG_UART_DEBUG_PORT == IO_PORT_DM))
usb_io_con = JL_PORTUSB->DIR;
#endif
}
usb_high_res();
}
void sleep_exit_callback(u32 usec)
{
//USB IO打印引脚特殊处理
if (libs_debug) {
#if ((CONFIG_UART_DEBUG_PORT == IO_PORT_DP) || (CONFIG_UART_DEBUG_PORT == IO_PORT_DM))
JL_PORTUSB->DIR = usb_io_con;
#endif
}
int putchar(int a);
putchar('>');
}
Source: power_app.c
软关机回调
power_soff_callback() 是 soft-off 前的最后一站:执行平台 uninit、清除快速开机软标志(sfc_fast_boot = 0,保证下次上电走完整启动而非快速启动)、把 IO 配置为软关机态。mask_softflag_config 写软标志说明 soft-off 后仍可由特定事件(充电插入/开机键)唤醒并继续执行:
static void __mask_io_cfg()
{
struct app_soft_flag_t app_soft_flag = {0};
app_soft_flag.sfc_fast_boot = 0;
mask_softflag_config(&app_soft_flag);
}
u8 power_soff_callback()
{
DO_PLATFORM_UNINITCALL();
__mask_io_cfg();
gpio_config_soft_poweroff();
GPIO_CONFIG_UNINIT();
return 0;
}
Source: power_app.c
开机电源早期流程
power_early_flowing() 是上电后电源子系统的最早执行序列,顺序本身即设计意图:先 lowpower_init() 把低功耗代码加载进 overlay 段(保证后续即使进入睡眠也有驻留代码可用)→ 初始化 boot ROM 与端口表 → 若开启调试则在电源初始化前用 PORT_PROTECT 保护打印口(否则电源初始化会把打印口置高阻导致日志丢失)→ 最后 power_early_init((u32)gpio_config) 把 GPIO 配置传给 PMU 驱动:
void power_early_flowing()
{
/*默认把低功耗部分代码加载到power overlay段*/
lowpower_init();
PORT_TABLE(g);
init_boot_rom();
//开打印则开机需要保护打印口,否则初始化完打印后面电源初始化会被设置成高阻
if (libs_debug) {
PORT_PROTECT(UART_OUTPUT_CH_PORT);
}
power_early_init((u32)gpio_config);
}
Source: power_app.c
核心流程:一次完整的低功耗睡眠周期
sequenceDiagram
participant RTOS as FreeRTOS idle 任务
participant PM as 电源框架 (power_api)
participant CB as power_app 回调
participant HW as 硬件 (LDO/晶振/定时器)
RTOS->>PM: vPortSuppressTicksAndSleep(usec)
PM->>PM: low_power_trace_drift(usec) 校正tick漂移
PM->>CB: sleep_enter_callback(step)
CB->>HW: USB IO置高阻 + 保存DIR
PM->>HW: 切换VDDIOW挡位(PD_VDDIO_KEEP)<br/>保持/关闭外设电源域
PM->>HW: 切换到低功耗晶振(OSC_TYPE_LRC/BT_OSC)<br/>启动lptmr计时
HW-->>PM: 计时到/唤醒源触发
PM->>HW: 恢复工作晶振与LDO挡位
PM->>CB: sleep_exit_callback(usec)
CB->>HW: 恢复USB IO方向
PM->>PM: 补偿睡眠期间的tick
PM-->>RTOS: 返回,继续调度
流程要点:
- FreeRTOS 的 idle 钩子通过
vPortSuppressTicksAndSleep(usec)请求睡眠,usec由调度器根据下一个定时事件计算——睡眠时长被精确限制,不会错过任务唤醒点。 - 框架先做
low_power_trace_drifttick 漂移校正,再进入硬件睡眠,保证醒来后系统时间连续。 sleep_enter_callback在关中断的单核环境下执行板级收尾(USB 高阻),随后框架切换 VDDIOW 挡位、保持策略(VDDIO_KEEP_TYPE)、低功耗晶振并启动 lptmr。- 唤醒后按逆序恢复:晶振/LDO →
sleep_exit_callback恢复 IO → tick 补偿 → 返回调度器。整个进出过程对应用任务透明。
配置选项
以下配置项分布于板级配置与电源框架,供开发者按硬件变体调整:
| 配置项 | 类型 | 默认/可选值 | 说明 |
|---|---|---|---|
LVD_WARNING_FOR_LOW_POWER | 宏 | 1 (0/1) | 1:低电阈值用 LVD 警告电压+200mV;0:固定 3300mV |
LOW_POWER_VOL | 宏/变量 | lvd_warning_voltage 或 3300 | 低电判定阈值(mV) |
_power_param.config | u8 | LOWPOWER_CLOSE(0) / SLEEP_EN(1) / DEEP_SLEEP_EN(2) | 低功耗模式使能 |
_power_param.osc_type | u8 | OSC_TYPE_LRC(0) / OSC_TYPE_BT_OSC(1) / OSC_TYPE_NULL(2) | 低功耗晶振类型 |
_power_param.btosc_hz | u32 | 24M 默认 | 蓝牙晶振频率 |
_power_param.osc_delay_us | u32 | 预留 | 低功耗晶振起振延时 |
_power_param.vddiom_lev | u8 | VDDIOM_VOL 枚举 | 工作态 VDDIOM LDO 挡位 |
_power_param.vddiow_lev | u8 | VDDIOW_VOL 枚举 | 低功耗态 VDDIOW LDO 挡位 |
_power_param.lptmr_flow | u8 | 用户配置 | 低功耗参数(预留) |
PCONTROL_DCVDD_CAP_SW | 命令参数 | 0/1 | DCVDD 是否外挂电容 |
PCONTROL_FLASH_PG_VDDIO | 命令参数 | 0/1 | Flash 电源引脚是否使用 IO |
PCONTROL_RTC_CLK | 命令参数 | 类型值 | RTC_CLK 类型,决定开/关机晶振流程 |
PCONTROL_POWER_SUPPLY | 命令参数 | 0/1 | 0:IOVDD 供电;1:VPWR 供电 |
PCONTROL_PD_VDDIO_KEEP / PCONTROL_SF_VDDIO_KEEP | 命令参数 | VDDIO_KEEP_TYPE 枚举 | sleep/soff 的 VDDIO 切换流程 |
PCONTROL_PD_KEEP_LPCTMU | 命令参数 | 0/1 | 睡眠时触摸通道是否保持 |
PCONTROL_SF_KEEP_LRC | 命令参数 | 0/1 | 软关机时 LRC 是否保持 |
CONFIG_UART_DEBUG_PORT | 宏 | TCFG_UART0_TX_PORT 或 -1 | 调试口端口,决定 USB IO 保护逻辑 |
API 参考
void app_power_init(void)
应用层电源初始化:初始化低电阈值(LVD+200mV)、注册 ADC_CH_PMU_VBAT 采样通道并阻塞采样 10 次直到拿到稳定 VBAT。
void app_power_scan(void)
周期扫描函数(通常由定时任务周期调用):读取 VBAT,连续 10 次低于 LOW_POWER_VOL 时打印告警并 post_msg(1, MSG_LOW_POWER);非 AD/矩阵按键配置下主动 adc_kick_start(NULL) 维持采样。
u32 app_power_get_vbat(void)
返回当前电池电压(mV),内部将 ADC 采样值乘 4 还原分压;采样无效时返回 -1(即 0xFFFFFFFF)。
void power_early_init(u32 arg) / power_later_init(u32 arg) / void power_init(struct _power_pdata *pdata)
电源三段式初始化:早期(携带 gpio_config)建立最小电源环境;后期完成平台策略;power_init 注入 _power_param 参数。注意:power_early_init 由 power_early_flowing() 调用,必须先 lowpower_init() 并保护调试口。
u32 power_control(enum PCONTROL_CMD cmd, u32 arg)
- 参数:
cmd为PCONTROL_CMD命令字(0x100 电源 / sleep / soff 分组);arg为对应命令参数(如VDDIO_KEEP_TYPE或 0/1)。 - 返回:执行结果/状态值。
void lowpower_init(void) / void lowpower_uninit(void)
加载/卸载低功耗 overlay 代码段与驱动,必须在进入低功耗能力前调用 lowpower_init()。
void vPortSuppressTicksAndSleep(u32 usec) / void sys_power_down(u32 usec) / void sys_softoff(void)
vPortSuppressTicksAndSleep:FreeRTOS 低功耗 tickless 入口,睡眠usec微秒。sys_power_down:非 RTOS 场景直接睡眠入口。sys_softoff:软关机入口,触发power_soff_callback()后整机待机。
s32 low_power_trace_drift(u32 usec)
- 参数:
usec计划睡眠时长。 - 返回:
s32漂移校正值,用于补偿睡眠期间 FreeRTOS tick 误差。
回调契约(平台实现)
| 回调 | 触发时机 | 约束 |
|---|---|---|
void sleep_enter_callback(u8 step) | 进入睡眠前 | 单核、关中断;禁止打印/耗时操作 |
void sleep_exit_callback(u32 usec) | 退出睡眠后 | 恢复 IO 方向等板级状态 |
u8 power_soff_callback(void) | soft-off 前 | 平台 uninit + GPIO 软关机态配置 |
故障模式、边界情况与并发
低电检测的误报与漏报
- 误报抑制:
app_power_scan()采用连续 10 次计数去抖。电池在播放/射频大电流瞬间电压跌落不会立刻触发告警,只有持续低压才判定低电。设计权衡:计数阈值过小易误报,过大则告警滞后。 - 漏报风险:阈值
lvd_warning_voltage = lvd_voltage + 200依赖get_lvd_vol()正确读取 efuse/寄存器。若 LVD 配置缺失,应回退到LVD_WARNING_FOR_LOW_POWER = 0的固定 3300mV 方案。 - 采样失败:
adc_get_voltage返回 -1 时app_power_get_vbat()与app_power_scan()均直接跳过,不会用脏数据触发低电。
睡眠时序与并发约束
sleep_enter_callback运行于关中断单核环境,任何耗时操作(如打印、阻塞 IO)都会拉长中断关闭窗口,可能导致蓝牙协议栈超时——源码注释明确"此函数禁止添加打印"。vPortSuppressTicksAndSleep(usec)的usec由调度器约束,睡眠期间不可调度;low_power_trace_drift用于消除 tick 计数漂移,若未校正会导致任务唤醒时刻整体偏移。- overlay 代码段是低功耗正确性的前提:若
lowpower_init()未在power_early_flowing()中执行,睡眠恢复后代码可能因 Flash/DDR 掉电而取指失败。
soft-off 与快速启动的相互作用
power_soff_callback() 通过 mask_softflag_config 把 sfc_fast_boot 清零,保证软关机唤醒后走完整启动流程(重新初始化外设而非快速恢复)。若应用依赖快速启动语义,需自行管理软标志,否则软关机后的启动路径会与预期不符。
USB 调试口与低功耗冲突
当 CONFIG_UART_DEBUG_PORT 为 DP/DM 时,睡眠前必须保存 JL_PORTUSB->DIR 并置高阻,退出后恢复。若省略该处理,睡眠期间 USB 口漏电会显著抬高整机功耗;若恢复逻辑缺失,调试口在唤醒后失效。
性能与运维考量
- 低功耗收益:sleep 模式保留 LRC/BT_OSC 计时与快速唤醒路径,适合蓝牙耳机等频繁空闲唤醒场景;deep-sleep 关闭更多电源域,唤醒延迟增大;soft-off 功耗最低但需外部事件唤醒。三档功耗/延迟权衡由
_power_param.config与PCONTROL_*命令组合决定。 - 开机电压稳定化:
app_power_init()阻塞采样 10 次,保证应用拿到稳定 VBAT;app_power_powerup_kick_start自嵌套回调避免了启动瞬间 ADC 未就绪的无效读数。 - 调试技巧:
sleep_enter_callback/sleep_exit_callback的'<'/'>'字符输出可作为睡眠进出频率的低成本观测手段;lowpower_dump()可导出低功耗内部状态用于问题定位。 - ADC 采样维持:非 AD/矩阵按键方案下
app_power_scan()末尾主动adc_kick_start(NULL),保证下次扫描时已有最新采样结果,避免扫描周期与采样周期错位。
扩展点
- 板级电源策略:通过
_power_param(vddiom/vddiow 挡位、osc_type、config)与power_control(PCONTROL_*, arg)适配不同硬件:DCVDD 电容、Flash 供电、供电方式(IOVDD/VPWR)、触摸保持、soff 时 LRC/NVDD/PVDD 保持。 - 低功耗回调:在
power_app.c的sleep_enter_callback/sleep_exit_callback/power_soff_callback中挂接板级动作(如矩阵按键行 IO 拉低、外设掉电)。注意回调运行环境约束(关中断、禁耗时操作)。 - 晶振选择:
low_power_reset_osc_type/low_power_get_osc_type允许运行时切换 LRC 与 BT_OSC,适合不同唤醒精度/功耗需求的动态策略。 - 低电策略:
MSG_LOW_POWER消息由应用自行消费,可扩展为"降频 → 停止功能 → 关机"的多级策略(SDK 仅负责检测与上报)。
相关文件与链接
- app_power_mg.h — 应用层电源宏与接口
- app_power_mg.c — VBAT 采样与低电检测实现
- power_api.h — 电源框架 API 与参数结构
- power_app.c — 平台回调与启动流程
- power_config.c — 板级电源参数配置
- power_interface.h — 电源接口汇总
- power_wakeup.h — 唤醒源定义
- lib_power_config.c — 库级电源配置
相关主题指引:唤醒源与 GPIO 配置见 GPIO 驱动文档;RTC/晶振流程见时钟管理;VM 参数存储(
mask_softflag_config等软标志)见系统参数文档。