锂电池充电管理
本页介绍 fw-AD16N 平台内置锂电池充电管理模块(charge.c / charge.h),涵盖充电初始化、涓流/恒流切换、充满检测、复充机制、VPWR 插拔检测、低功耗协同以及相关配置与 API。
Purpose and Scope
本页聚焦 UC03 内核的电池充电管理子系统(sdk/apps/app/bsp/cpu/uc03/charge.c 及其头文件 charge.h),详细说明:
- 充电硬件控制(内置 P33 充电模块寄存器操作)
- 充电状态机:涓流充电 → 恒流充电 → 充满判停 → 复充
- VPWR(充电口)插拔检测与软件滤波
- 与系统电源管理(低功耗准入)和应用层(电量上报)的协作方式
以下相关主题由兄弟页面覆盖,本页不做展开:
- 电池电量检测/ADC 采样:参见电源管理相关页面(
app_power_mg.c负责电量计算与低电量关机策略) - 低功耗/休眠管理:充电模块仅通过
charge_check_is_idle()参与低功耗准入判断 - 按键/触摸充电检测(
key_touch.c中的charge_time):属于触摸按键调参范畴
概述
AD16N 系列 MCU 内置锂电池充电控制器(P33 电源域寄存器:P3_CHG_CON0~CON3、P3_VPWR_CON0/CON1),支持 4.041V 至 4.534V 的充满电压档位(由 efuse trim 校准)与 2mA 至 180mA 的多级充电电流档位。充电管理模块在此基础上实现完整的软件充电策略:
- 插入检测:通过 VPWR 边沿唤醒中断 + 软件滤波识别充电器插入/拔出;
- 分阶段充电:电池电压低于 3000mV 时以小电流涓流预充,达到阈值后切换为配置的恒流电流;
- 充满判停:充电电流降至判满电流以下并持续滤波时间后停止充电;
- 可选复充:充满后电池电压跌落至复充阈值时自动重新充电(
RECHARGE_ENABLE默认关闭); - 低功耗协同:充电过程中(插拔检测/充满检测进行中)禁止系统进入低功耗,避免丢失充电状态。
该模块通过 2ms 周期定时器(charge_timer_handle())驱动全部检测逻辑,采用"中断置位 + 轮询滤波"的经典低功耗嵌入式设计:中断只负责标记事件,滤波、状态切换、寄存器操作全部在定时器上下文完成,保证临界区短、功耗低、抗抖动。
架构
flowchart TD
subgraph sg_App["应用层"]
AppMain["app.c 主循环<br/>charge_timer_handle()"]
InitApp["init_app.c<br/>charge_init()"]
PowerMg["app_power_mg.c<br/>CHARGE_IN_DET / charge_check_is_idle()"]
end
subgraph sg_Charge["充电管理模块 charge.c"]
ChargeVar["CHARGE_VAR 状态变量<br/>trickle/vbat_isr/vpwr_isr/full_flag"]
TimerHandle["charge_timer_handle() 2ms"]
VpwrDetect["charge_vpwr_detect()<br/>插拔滤波"]
FullDetect["charge_full_detect()<br/>充满滤波"]
VbatDetect["charge_vbat_detect()<br/>涓流→恒流 / 复充"]
SetMA["charge_set_mA()<br/>电流档位"]
StartClose["charge_start() / charge_close()"]
Isr["vpwr_wakeup_isr()<br/>charge_wakeup_isr()"]
end
subgraph sg_HW["硬件层 (P33 电源域)"]
P33["P3_CHG_CON0~CON3<br/>P3_VPWR_CON0/CON1"]
Efuse["efuse trim<br/>vbat_trim_420/435"]
Ldo["LDO5V_DET / LVCMP_DET<br/>CHGFL_DET 唤醒 IO"]
end
AppMain --> TimerHandle
InitApp --> StartClose
PowerMg -->|"charge_check_is_idle()"| TimerHandle
TimerHandle --> VpwrDetect
TimerHandle --> FullDetect
TimerHandle --> VbatDetect
VpwrDetect --> StartClose
VbatDetect --> SetMA
FullDetect --> StartClose
StartClose --> P33
SetMA --> P33
Isr -->|"置位 vpwr_isr/vbat_isr"| ChargeVar
ChargeVar --> TimerHandle
P33 --> Ldo
Efuse -->|"charge_config()"| P33
Ldo -->|"边沿唤醒"| Isr
架构说明:
- 应用层三个调用点是充电模块的对外接口:
init_app.c在上电初始化时调用charge_init()完成寄存器配置;app.c主循环周期调用charge_timer_handle()驱动检测;app_power_mg.c在低功耗判断与低电量检测时查询充电状态(CHARGE_IN_DET宏展开为charge_get_lvcmp_det())。 - 充电模块内部采用"中断置位 → 2ms 轮询处理"模型:
vpwr_wakeup_isr()/charge_wakeup_isr()仅置位CHARGE_VAR中的标志位,真正的滤波、状态转移和寄存器写入都在charge_timer_handle()上下文完成。 - 硬件层:所有充电行为最终体现为对 P33 电源域寄存器
P3_CHG_CON0~CON3的读写;充满电压档位来自 efuse 的 trim 校准值(efuse_get_vbat_trim_420()/efuse_get_vbat_trim_435()),充电电流档位同样有 efuse 校准(efuse_get_charge_cur_trim())。
核心实现分析
状态变量与寄存器映射
充电模块的全部运行时状态保存在一个静态结构体 CHARGE_VAR 中,通过 __this 宏访问:
typedef struct _CHARGE_VAR {
u32 vbat_voltage;
u8 trickle_flag : 1;
u8 vbat_isr : 1;
u8 vpwr_isr : 1;
u8 init : 1;
u8 full_flag : 1;
} CHARGE_VAR;
#define __this (&charge_var)
static CHARGE_VAR charge_var;
来源:charge.c
位域设计意图:将 6 个布尔状态压缩进 1 个字节,其中 trickle_flag(是否涓流)、vbat_isr(电池电压检测待处理)、vpwr_isr(VPWR 插拔待处理)、init(模块已初始化)、full_flag(已充满)均为软件状态;vbat_voltage 由应用层通过 charge_set_vbat_voltage() 周期写入(来自 ADC 电量采样)。这种"中断只置位、定时器只消费"的标记位模型避免了中断上下文与主循环之间的并发访问冲突。
关键硬件信号与宏:
| 宏 | 含义 |
|---|---|
LDO5V_DET_GET() | VPWR 与约 1V 的比较结果(硬件在线检测) |
LVCMP_DET_GET() | VPWR 与 VBAT 的比较结果(VPWR > VBAT 为 1) |
IS_CHARGE_EN() | 充电使能状态(P3_CHG_CON0 bit0) |
CHARGE_FULL_FLAG_GET() | 硬件判满标志 |
CHARGE_FULL_FILTER_GET() | 硬件判满滤波标志(电流已低于判满阈值) |
P3_CHG_CON0 | 充电控制寄存器:bit0=CHARGE_EN、bit1=CHARGE_GO、bit3=TRICKLE_EN、bit5=TURBO_EN |
P3_CHG_CON1 | 充满电压档位(低 4 位)+ 恒流电流档位(bit4~7) |
P3_CHG_CON2 | 判满电流阈值(TCFG_CHARGE_FULL_MA << 4) |
P3_CHG_CON3 | 充电电流 efuse trim |
P3_VPWR_CON0/CON1 | VPWR 下拉电阻使能与档位 |
初始化流程:charge_init() 与 charge_config()
charge_init() 是充电模块的唯一入口,由 init_app.c 在上电初始化阶段调用:
void charge_init(void)
{
log_info("%s\r\n", __func__);
#ifdef SARADC_ISR_MODE
adc_add_sample_ch(AD_CH_PMU_VBAT_4);
#endif
power_awakeup_gpio_enable(IO_CHGFL_DET, 0);
//初始化充电寄存器
P3_CHG_CON0 = INIT_P3_CHG_CON0;
P3_CHG_CON1 = 0;
P3_CHG_CON2 = INIT_P3_CHG_CON2;
P3_CHG_CON3 = 0;
/*VPWR的100K下拉电阻使能*/
#if TCFG_VPWR_PULLDOWN_EN
P3_VPWR_CON1 = TCFG_VPWR_PULLDOWN_LEV;
P3_VPWR_CON0 = BIT(0);
#else
P3_VPWR_CON1 = 0;
P3_VPWR_CON0 = 0;
#endif
charge_config();
__this->init = 1;
__this->vpwr_isr = 1;
__this->full_flag = 0;
__this->vbat_voltage = adc_get_vbat_voltage();
charge_dump();
}
来源:charge.c
初始化顺序的设计意图:
- 先注册 VBAT ADC 采样通道(仅
SARADC_ISR_MODE模式),保证后续adc_get_vbat_voltage()可用; - 关闭 CHGFL(充满标志)唤醒 IO——初始阶段不期望被充满事件唤醒;
- 将所有充电寄存器清零后写入编译期配置值,保证确定性起点;
- VPWR 下拉电阻:充电口悬空时提供确定的电平,防止误判插拔(
TCFG_VPWR_PULLDOWN_EN默认开启,50KΩ); charge_config()依据 efuse trim 校准充满电压;- 最后主动置
vpwr_isr = 1触发首轮插拔检测——这样即使上电前充电器已插入,也能在第一次charge_timer_handle()时被识别并启动充电,而不是依赖边沿中断(上电前的边沿不会被捕获)。
charge_config() 的关键在于把编译期充满电压配置 TCFG_CHARGE_FULL_V 与 efuse 实际 trim 值结合:
static void charge_config(void)
{
#if (TCFG_CHARGE_FULL_V == CHARGE_FULL_V_4199)
P3_CHG_CON1 |= (u8)efuse_get_vbat_trim_420();//4.20V对应的trim出来的实际档位
#elif (TCFG_CHARGE_FULL_V == CHARGE_FULL_V_4354)
P3_CHG_CON1 |= (u8)efuse_get_vbat_trim_435();//4.35V对应的trim出来的实际档位
#else
u8 charge_trim_val;
u8 offset = 0;
u8 charge_full_v_val = 0;
if (TCFG_CHARGE_FULL_V < CHARGE_FULL_V_4237) {
charge_trim_val = (u8)efuse_get_vbat_trim_420();//4.20V对应的trim出来的实际档位
log_info("low charge_trim_val = %d\n", charge_trim_val);
if (TCFG_CHARGE_FULL_V >= CHARGE_FULL_V_4199) {
offset = TCFG_CHARGE_FULL_V - CHARGE_FULL_V_4199;
charge_full_v_val = charge_trim_val + offset;
if (charge_full_v_val > 0xf) {
charge_full_v_val = 0xf;
}
} else {
offset = CHARGE_FULL_V_4199 - TCFG_CHARGE_FULL_V;
if (charge_trim_val >= offset) {
charge_full_v_val = charge_trim_val - offset;
} else {
charge_full_v_val = 0;
}
}
} else {
charge_trim_val = (u8)efuse_get_vbat_trim_435();//4.35V对应的trim出来的实际档位
log_info("high charge_trim_val = %d\n", charge_trim_val);
if (TCFG_CHARGE_FULL_V >= CHARGE_FULL_V_4354) {
offset = TCFG_CHARGE_FULL_V - CHARGE_FULL_V_4354;
charge_full_v_val = charge_trim_val + offset;
if (charge_full_v_val > 0xf) {
charge_full_v_val = 0xf;
}
} else {
offset = CHARGE_FULL_V_4354 - TCFG_CHARGE_FULL_V;
if (charge_trim_val >= offset) {
charge_full_v_val = 0;
}
}
}
log_info("charge_full_v_val = %d\n", charge_full_v_val);
/* CHARGE_FULL_V_SEL(charge_full_v_val); */
P3_CHG_CON1 |= charge_full_v_val;
#endif
/* CHGI_TRIM_SEL(efuse_get_charge_cur_trim()); */
P3_CHG_CON3 |= efuse_get_charge_cur_trim();
charge_set_mA(TCFG_CHARGE_TRICKLE_MA);
}
来源:charge.c
设计意图解读:
- 为什么需要 efuse trim:芯片间工艺偏差导致基准电压/电流漂移,直接写固定档位可能造成充满电压偏差。efuse 在出厂时烧录了 4.20V(低压电池)与 4.35V(高压电池)两档基准 trim 值,
charge_config()以此为锚点,用"目标档位 ± 偏移量"线性换算其他档位,并做 0~0xF 饱和钳位; - 高低压电池分界:
CHARGE_FULL_V_4237(4.237V)是分界点,低于它使用 4.20V trim,高于等于则使用 4.35V trim; - 初始化末尾调用
charge_set_mA(TCFG_CHARGE_TRICKLE_MA)预置涓流电流档位——充电未开始时保持小电流配置,避免插上瞬间大电流冲击。
充电启动与关闭
charge_start() 根据当前电池电压决定进入涓流还是恒流:
void charge_start(void)
{
u8 check_full = 0;
log_info("%s", __func__);
__this->full_flag = 0;
if (__this->vbat_voltage < CHARGE_CCVOL_V) {
charge_set_mA(TCFG_CHARGE_TRICKLE_MA);
__this->trickle_flag = 1;
} else {
check_full = 1;
__this->trickle_flag = 0;
charge_set_mA(TCFG_CHARGE_MA);
power_awakeup_gpio_enable(IO_CHGFL_DET, 1);
}
//CHARGE_EN(1);
//CHARGE_GO(1);
P3_CHG_CON0 |= BIT(1) | BIT(0);
//开启充电时,充满标记为1时,主动检测一次是否充满
if (check_full && CHARGE_FULL_FLAG_GET()) {
__this->vbat_isr = 1;
}
}
来源:charge.c
关键点:
- 电量阈值分档:
CHARGE_CCVOL_V(默认 3000mV)以下走涓流(TCFG_CHARGE_TRICKLE_MA,如 20mA),以上走配置的恒流(TCFG_CHARGE_MA)。这是锂电池安全充电的基本要求——过放电池直接大电流充电有起火风险; - 恒流路径下使能
IO_CHGFL_DET唤醒 IO:充满标志会拉低该引脚唤醒系统; P3_CHG_CON0 |= BIT(1) | BIT(0)同时置位 CHARGE_EN 与 CHARGE_GO,硬件开始充电;- 若上次充满标志仍为 1(硬件
CHARGE_FULL_FLAG_GET()),立即置vbat_isr触发一次充满复查,防止"已充满却再次启动充电"的错误状态。
charge_close() 是对称的停止操作:清除 CHARGE_EN/CHARGE_GO、复位涓流与充满标志、关闭 CHGFL 唤醒 IO,并打印日志。
电流档位管理:charge_set_mA()
void charge_set_mA(u8 charge_mA)
{
static u8 charge_mA_old = 0xff;
if (charge_mA_old != charge_mA) {
charge_mA_old = charge_mA;
if (charge_mA & BIT(4)) {
//CHG_TRICKLE_EN(1);
P3_CHG_CON0 |= BIT(3);
P3_CHG_CON0 &= ~BIT(5);
} else if (charge_mA & BIT(5)) {
//CHGI_TURBO_EN(1);
P3_CHG_CON0 &= ~BIT(3);
P3_CHG_CON0 |= BIT(5);
} else {
P3_CHG_CON0 &= ~(BIT(3) | BIT(5));
}
P3_CHG_CON1 &= ~0xf0;
P3_CHG_CON1 |= ((charge_mA & 0xf) << 4);
}
}
来源:charge.c
设计意图:
- 电流档位用一个字节编码,低 4 位是档位值(
CHARGE_mA_20~CHARGE_mA_120),BIT(4)标记涓流模式(2~12mA 小电流,启用硬件 TRICKLE_EN),BIT(5)标记 TURBO 模式(130~180mA 增强恒流); - 通过
static u8 charge_mA_old实现写前比较,只有档位真正变化才操作寄存器——避免 2ms 定时器高频路径上的无谓寄存器写入,降低功耗; - 档位值写入
P3_CHG_CON1的 bit4~7(& ~0xf0先清后置),与低 4 位的充满电压档位互不干扰。
VPWR 插拔检测(软件滤波)
static void charge_vpwr_detect(void)
{
static u32 vpwr_in_normal_cnt = 0;
static u32 vpwr_off_cnt = 0;
static u8 vpwr_status = BIT(BIT_VPWR_OFF);
if (__this->vpwr_isr == 0) {
return;
}
if (LVCMP_DET_GET()) {
//VPWR > vbat
if (vpwr_in_normal_cnt < TCFG_VPWR_ON_FILTER) {
vpwr_in_normal_cnt++;
} else {
if (vpwr_status != BIT_VPWR_IN) {
log_info("VPWR_IN\r\n");
vpwr_status = BIT_VPWR_IN;
charge_start();
}
goto __clr_exit;
}
} else if (LDO5V_DET_GET() == 0) {
//VPWR < VBAT
if (vpwr_off_cnt < (TCFG_VPWR_OFF_FILTER)) {
vpwr_off_cnt++;
} else {
if (vpwr_status != BIT_VPWR_OFF) {
log_info("VPWR_OFF\r\n");
vpwr_status = BIT_VPWR_OFF;
charge_close();
}
goto __clr_exit;
}
}
return;
__clr_exit:
vpwr_off_cnt = 0;
vpwr_in_normal_cnt = 0;
__this->vpwr_isr = 0;
return;
}
来源:charge.c
设计意图:
- 双重判据:插入判定用
LVCMP_DET_GET()(VPWR > VBAT,说明充电器电压足够),拔出判定用LDO5V_DET_GET() == 0(VPWR 低于约 1V,说明充电器已移除)。两者取不同比较器,避免单一比较器的滞回盲区; - 非对称滤波时间:插入滤波 50 次(100ms)、拔出滤波 100 次(200ms)。拔出需要更长的确认时间,因为线缆晃动、接触不良造成的瞬时掉电不应立刻终止充电;插入确认较短则为了尽快开始充电;
- 状态去重:
vpwr_status保证charge_start()/charge_close()只在状态翻转时调用一次;滤波不满足时直接 return,不清vpwr_isr,从而持续累计——中断只做首触发,滤波计数在定时器上下文完成,天然抗抖动。
涓流→恒流切换与复充
static void charge_vbat_detect(void)
{
static u16 det_cnt = 0;
//1s检测一次时间是否到
det_cnt++;
if (det_cnt < 500) {
return;
}
det_cnt = 0;
//判断涓流转恒流,检测电池电压,是否大于CHARGE_CCVOL_V
if (IS_CHARGE_EN() && __this->trickle_flag) {
log_info("vbat_voltage: %d\n", __this->vbat_voltage);
if (__this->vbat_voltage > CHARGE_CCVOL_V) {
__this->trickle_flag = 0;
charge_set_mA(TCFG_CHARGE_MA);
power_awakeup_gpio_enable(IO_CHGFL_DET, 1);
__this->vbat_isr = 1;
}
}
#if RECHARGE_ENABLE
//如果充满了,则检测是否需要复充
if (__this->full_flag && (__this->vbat_voltage < RECHARGE_V)) {
log_info("recharge!");
charge_start();
}
#endif
}
来源:charge.c
设计意图:
- 1 秒节流:
det_cnt每 2ms 加 1,累计 500 次(约 1s)才执行一次电压比较。电池电压变化是慢变量,1s 采样一次足以跟踪,同时把功耗压到最低; - 切换条件:涓流中且
vbat_voltage > CHARGE_CCVOL_V(严格大于,避免临界抖动)才切恒流,并同步使能 CHGFL 唤醒 IO(恒流阶段才允许充满唤醒)、置vbat_isr启动充满检测; - 复充(可选):
RECHARGE_ENABLE默认 0 关闭;开启后,充满状态(full_flag == 1)下若电压跌到RECHARGE_V(默认 4000mV)以下则调用charge_start()重新充电,防止自放电导致的"充不满"体验问题。
充满检测
static void charge_full_detect(void)
{
static u16 charge_full_cnt = 0;
if (__this->vbat_isr == 0) {
return;
}
if (IS_CHARGE_EN() == 0) {
__this->vbat_isr = 0;
return;
}
if (__this->trickle_flag == 1) {
return;
}
if (CHARGE_FULL_FILTER_GET()) {
if (CHARGE_FULL_FLAG_GET() && LVCMP_DET_GET()) {
if (charge_full_cnt < TCFG_CHARGE_FULL_FILTER) {
charge_full_cnt++;
} else {
charge_full_cnt = 0;
log_info("Charge Full!\r\n");
charge_close();
__this->vbat_isr = 0;
__this->full_flag = 1;
}
} else {
charge_full_cnt = 0;
}
} else {
charge_full_cnt = 0;
__this->vbat_isr = 0;
}
}
来源:charge.c
设计意图:
- 判满条件三重确认:硬件判满滤波标志(电流低于
TCFG_CHARGE_FULL_MA阈值)+ 硬件充满标志 +LVCMP_DET_GET()(VPWR 仍高于 VBAT,充电器还在)三者同时成立才算一次有效判满; - 滤波计数:连续
TCFG_CHARGE_FULL_FILTER(10 次 × 2ms = 20ms)稳定成立才真正判满并charge_close()——防止充电电流瞬时波动导致误判; - 条件不满足立即清零计数:
charge_full_cnt = 0,体现"连续满足才生效"的边沿检测语义; - 涓流阶段不参与判满(
trickle_flag == 1直接 return),因为判满电流阈值是恒流电流的 1/N,涓流电流可能本身就低于该值,会造成假充满。
核心流程
充电状态机
stateDiagram-v2
[*] --> IDLE: 上电 charge_init()
IDLE --> TRICKLE: VPWR_IN 且 VBAT < 3000mV
IDLE --> CC: VPWR_IN 且 VBAT >= 3000mV
TRICKLE --> CC: vbat > 3000mV (1s 检测)
CC --> FULL: 判满滤波 20ms 连续成立
FULL --> TRICKLE: RECHARGE_ENABLE 且 VBAT < 4000mV
FULL --> CC: RECHARGE_ENABLE 且 VBAT < 4000mV
TRICKLE --> IDLE: VPWR_OFF (200ms 滤波)
CC --> IDLE: VPWR_OFF (200ms 滤波)
FULL --> IDLE: VPWR_OFF
IDLE --> IDLE: VPWR 悬空/抖动被滤波
状态说明:
| 状态 | 触发条件 | 关键动作 |
|---|---|---|
IDLE(未充电) | 上电初始化 / VPWR 拔出 | 寄存器保持涓流电流配置,CHGFL 唤醒关闭 |
TRICKLE(涓流) | 插入且 VBAT < 3000mV | charge_set_mA(TCFG_CHARGE_TRICKLE_MA),TRICKLE_EN=1 |
CC(恒流) | VBAT ≥ 3000mV | charge_set_mA(TCFG_CHARGE_MA),使能 CHGFL 唤醒 |
FULL(充满) | 判满滤波连续 10 次成立 | charge_close(),置 full_flag |
VPWR 插入 → 充满 → 拔出 完整时序
sequenceDiagram
participant HW as 硬件比较器/边沿
participant ISR as vpwr_wakeup_isr()
participant TIMER as charge_timer_handle() (2ms)
participant VAR as CHARGE_VAR
participant REG as P3 充电寄存器
Note over HW,ISR: 充电器插入, VPWR 上升沿
HW->>ISR: 边沿唤醒
ISR->>VAR: vpwr_isr = 1
loop 插拔滤波
TIMER->>TIMER: charge_vpwr_detect() 每 2ms
TIMER->>HW: LVCMP_DET_GET() == 1 ?
Note over TIMER: 连续 50 次 (100ms) 成立
end
TIMER->>VAR: 读取 vbat_voltage
alt VBAT < 3000mV
TIMER->>REG: charge_set_mA(TRICKLE) + CHARGE_EN/GO
Note over TIMER,VAR: trickle_flag = 1
else VBAT >= 3000mV
TIMER->>REG: charge_set_mA(TCFG_CHARGE_MA) + CHARGE_EN/GO
TIMER->>VAR: vbat_isr = 1, 使能 CHGFL 唤醒
end
loop 充满检测 (vbat_isr == 1)
TIMER->>HW: CHARGE_FULL_FILTER_GET() 且 FULL_FLAG 且 LVCMP_DET ?
Note over TIMER: 连续 10 次 (20ms) 成立
end
TIMER->>REG: charge_close(): 清除 EN/GO
TIMER->>VAR: full_flag = 1
Note over HW: 充电器拔出, VPWR 下降
HW->>ISR: 边沿唤醒
ISR->>VAR: vpwr_isr = 1
loop 拔出滤波
TIMER->>HW: LDO5V_DET_GET() == 0 ?
Note over TIMER: 连续 100 次 (200ms) 成立
end
TIMER->>REG: charge_close() (确认) + 清标志
流程要点:整个充电生命周期中,硬件中断只负责把 vpwr_isr/vbat_isr 置 1 作为"待处理"信号;所有状态判断、滤波计数、寄存器写入均由 2ms 定时器串行完成。这保证了状态机在单线程上下文内是确定性的,也解释了为什么充电期间需要禁止系统进入低功耗——charge_check_is_idle() 在 vpwr_isr/vbat_isr 非零或充电使能中返回 0(禁止休眠),避免休眠丢失滤波进度与唤醒源。
配置选项
配置宏定义在 charge.h 与 charge.c 顶部,实际取值由 app_config.h / 板级配置覆盖(TCFG_ 前缀宏)。
充电策略配置
| 配置宏 | 类型 | 默认值 | 说明 |
|---|---|---|---|
TCFG_CHARGE_FULL_V | 枚举 | 板级配置 | 充满电压档位,见 CHARGE_FULL_V_4041~CHARGE_FULL_V_4534(31 档,4.041V~4.534V) |
TCFG_CHARGE_MA | 枚举 | 板级配置 | 恒流充电电流档位(CHARGE_mA_20~CHARGE_mA_120,20~120mA) |
TCFG_CHARGE_TRICKLE_MA | 枚举 | 板级配置 | 涓流预充电流档位(CHARGE_mA_2~CHARGE_mA_12,2~12mA) |
TCFG_CHARGE_FULL_MA | 枚举 | 板级配置 | 判满电流阈值(CHARGE_FULL_mA_DIV5/10/15/20,恒流电流的 1/N) |
CHARGE_CCVOL_V | 常量 | 3000 | 涓流转恒流电压阈值(mV) |
RECHARGE_ENABLE | 布尔 | 0 | 是否启用充满后自动复充 |
RECHARGE_V | 常量 | 4000 | 复充电压阈值(mV),低于此值重新充电 |
TCFG_CHARGE_FULL_FILTER | 常量 | 10 | 充满滤波次数(×2ms = 20ms) |
TCFG_VPWR_ON_FILTER | 常量 | 50 | VPWR 插入滤波次数(×2ms = 100ms) |
TCFG_VPWR_OFF_FILTER | 常量 | 100 | VPWR 拔出滤波次数(×2ms = 200ms) |
TCFG_VPWR_PULLDOWN_EN | 布尔 | 1 | 是否使能 VPWR 下拉电阻 |
TCFG_VPWR_PULLDOWN_LEV | 枚举 | CHARGE_PULLDOWN_50K | VPWR 下拉电阻档位(50K/100K/150K/200K) |
电流/电压档位枚举速查
| 档位宏 | 数值 | 电流 | 模式 |
|---|---|---|---|
CHARGE_mA_20 ~ CHARGE_mA_120 | 0~15 | 20~120mA | 恒流(CC) |
CHARGE_mA_2 ~ CHARGE_mA_12 | BIT(4) 组合 | 2~12mA | 涓流(TRICKLE) |
CHARGE_mA_130 ~ CHARGE_mA_180 | BIT(5) 组合 | 130~180mA | 增强恒流(TURBO,不建议高频使用) |
CHARGE_FULL_V_4041 ~ CHARGE_FULL_V_4338 | 0~15 | 4.041~4.338V | 低压电池充满电压 |
CHARGE_FULL_V_4237 ~ CHARGE_FULL_V_4534 | 16~31 | 4.237~4.534V | 高压电池充满电压 |
CHARGE_FULL_mA_DIV5/10/15/20 | 0~3 | 恒流/5、/10、/15、/20 | 判满电流 |
注意:恒流电流 ≥
CHARGE_mA_100时,CHARGE_FULL_mA_DIV10实际等效CHARGE_FULL_mA_DIV20(见 charge.h 注释)。
API 参考
以下为 charge.h 对外声明的公共接口(实现见 charge.c):
void charge_init(void)
充电模块初始化。必须在系统上电、ADC 可用之后调用一次(init_app.c 中调用)。完成充电寄存器配置、efuse trim 加载、VPWR 下拉配置,并置 vpwr_isr = 1 触发首次插拔检测。
- 参数:无
- 返回:无
void charge_timer_handle(void)
2ms 周期处理函数。必须在应用主循环中周期调用(app.c 中调用)。依次执行 charge_vpwr_detect() → charge_full_detect() → charge_vbat_detect()。模块未初始化(init == 0)时直接返回。
- 参数:无
- 返回:无
u8 charge_get_vpwr_online_hw(void)
读取硬件比较器:VPWR 是否大于约 1V(充电器在线粗判)。直接读寄存器,无软件滤波。
- 返回:
0=VPWR<1V,1=VPWR>1V
u8 charge_get_lvcmp_det(void)
读取硬件比较器:VPWR 是否大于 VBAT(充电电压充足判据)。直接读寄存器,无软件滤波。
- 返回:
0=VPWR<VBAT,1=VPWR>VBAT
void charge_set_vbat_voltage(u32 voltage)
设置当前电池电压(mV)。由应用层的电量检测周期调用,是涓流/恒流切换与复充判断的输入。
- 参数:
voltage(u32)— 电池电压,单位 mV - 返回:无
void charge_set_mA(u8 charge_mA)
设置充电电流档位。支持运行期动态切换(如温度降流场景)。内部有写前比较,档位未变化时不做寄存器操作。
- 参数:
charge_mA— 档位宏(CHARGE_mA_xx,含 BIT(4)/BIT(5) 模式位) - 返回:无
void charge_start(void)
启动充电。根据 vbat_voltage 与 CHARGE_CCVOL_V(3000mV)比较自动选择涓流或恒流,置位 CHARGE_EN/CHARGE_GO,恒流路径使能 CHGFL 唤醒。
- 参数:无
- 返回:无
void charge_close(void)
停止充电。清除 CHARGE_EN/CHARGE_GO、复位涓流/充满标志、关闭 CHGFL 唤醒。
- 参数:无
- 返回:无
u8 charge_check_is_idle(void)
低功耗准入查询。充电相关任务未处理或充电进行中时返回 0,禁止进入低功耗。
- 返回:
0=禁止进低功耗,1=允许进低功耗 - 判据:
vpwr_isr == 0 && vbat_isr == 0 && IS_CHARGE_EN() == 0
u8 charge_check_is_full(void)
充满状态查询。供 UI/应用层显示充电完成状态。
- 返回:
0=未充满或不在充电,1=已充满(full_flag)
void vpwr_wakeup_isr(void) / void charge_wakeup_isr(void)
充电口边沿唤醒 / 充满标记唤醒的中断回调。仅置位 vpwr_isr / vbat_isr,具体处理由定时器完成。与硬件唤醒 IO(IO_CHGFL_DET 等)绑定,须在中断上下文调用,保持极短执行时间。
- 参数:无
- 返回:无
失败模式、边界情况与并发
失败模式
| 场景 | 行为 | 源码依据 |
|---|---|---|
| 充电器插入瞬间抖动 | vpwr_in_normal_cnt 滤波 100ms,抖动期间只计数不动作 | charge_vpwr_detect() |
| 充电器拔出瞬间抖动 | vpwr_off_cnt 滤波 200ms(比插入更长) | charge_vpwr_detect() |
| 电池电压低于涓流切换阈值 | 恒流模式下不会自动回到涓流,由应用层电量策略处理 | charge_vbat_detect() 仅单向切换 |
| 充电电流波动导致判满标志瞬时消失 | charge_full_cnt 立即清零,需重新连续满足 20ms | charge_full_detect() |
| 充满后充电器仍插入 | 维持 full_flag = 1,硬件停止充电(EN/GO 已清);若启用复充,电压跌至 4000mV 以下自动重启 | charge_vbat_detect() + RECHARGE_ENABLE |
| 上电前充电器已插入 | charge_init() 主动置 vpwr_isr = 1,首个 2ms 周期即开始插拔检测,不依赖边沿中断 | charge_init() |
| efuse trim 缺失/越界 | charge_config() 对换算结果做 0~0xF 饱和钳位,trim 值异常时落到最近合法档位 | charge_config() |
边界情况
- 涓流阶段判满被屏蔽:
trickle_flag == 1时charge_full_detect()直接返回,避免涓流电流小于判满阈值造成的假充满; - 恒流 ≥ 100mA 的判满电流换算:
CHARGE_FULL_mA_DIV10在CHARGE_mA_100及以上时实际为 1/20(硬件限制,见 charge.h 注释); - LVCMP 与 LDO5V 双比较器分工:插入判据用 LVCMP(VPWR vs VBAT),拔出判据用 LDO5V(VPWR vs 1V),两者状态可能短暂不一致(如 VBAT 高于 1V 但 VPWR 略低于 VBAT),因此拔出判定不依赖 LVCMP,保证拔线一定被识别。
并发与一致性
充电模块全部状态机逻辑运行在单一上下文(2ms 定时器/主循环),中断回调只做位域置位(vpwr_isr/vbat_isr),因此不存在数据竞争。设计上的并发关注点:
- 中断 → 定时器:单向标志位传递,中断不读/写其他状态;
- 应用层 → 模块:
charge_set_vbat_voltage()写入vbat_voltage(32 位写),与定时器读取之间在单核上无撕裂风险,但语义上"电压是快照"——切换判断基于最近一次应用层上报值; - 低功耗协同:
charge_check_is_idle()必须在休眠前查询,返回 0 时系统需延后休眠;否则滤波计数与唤醒源丢失,充电状态机无法恢复。
性能与操作注意事项
- 定时器开销:
charge_timer_handle()每 2ms 执行,典型路径(无事件)仅做几次标志位判断与计数自增,CPU 占用可忽略;充满/插拔检测的寄存器读取频率受滤波条件限制,非事件期间不读P3_CHG_CONx; - 寄存器写优化:
charge_set_mA()的写前比较(charge_mA_old)与charge_vpwr_detect()的状态去重(vpwr_status)避免无谓的 P33 寄存器写入——P33 电源域寄存器写操作会唤醒电源域,减少写入次数可显著降低功耗; - 判满电流与电池容量匹配:判满阈值 = 恒流电流 / N(N=5/10/15/20)。大容量电池建议用 DIV10~DIV20 提高充满精度,小容量电池用 DIV5 避免永远判不满;
- TURBO 档位(≥130mA)谨慎使用:头文件注释明确"不建议使用后面的档位",高电流充电发热大,如确需使用需配合温度降流策略(
charge_set_mA()支持运行期动态切换即是为此预留); - 日志:模块使用
LOG_TAG "[charge]",可通过app_config.c中的CONFIG_DEBUG_LIBS控制i/d/e/c_CHARGE各级别输出;charge_dump()在初始化时打印全部充电寄存器值,是排查充电异常的第一手信息。
扩展点
- 温度降流:在应用层实现温度采集,温度升高时调用
charge_set_mA()下调档位(如 100mA → 50mA),charge_set_mA()的写前比较保证了低开销; - 复充策略:置
RECHARGE_ENABLE = 1并调整RECHARGE_V(默认 4000mV)即可启用充满后自动复充,无需改动状态机; - 充满/充电状态上报:应用层可通过
charge_check_is_full()与charge_get_lvcmp_det()组合判断"正在充电/已充满/未充电"三态,用于 UI 电量图标与充电指示灯; - 多电池档位适配:通过板级配置覆盖
TCFG_CHARGE_FULL_V/TCFG_CHARGE_MA/TCFG_CHARGE_TRICKLE_MA/TCFG_CHARGE_FULL_MA,同一固件可适配 4.2V/4.35V 电池体系; - 硬件唤醒源扩展:
IO_CHGFL_DET已接入唤醒框架(power_awakeup_gpio_enable()),若需在充电过程中唤醒系统(如充满上报),可直接复用charge_wakeup_isr()的置位机制。
相关链接
- charge.c 实现 — 充电管理核心实现(初始化/启停/检测/滤波)
- charge.h 定义 — 电压/电流档位枚举与配置宏
- charge_hw.h — P33 充电硬件寄存器层定义
- app_power_mg.c — 电源管理:充电时禁止低功耗、低电量关机策略(
CHARGE_IN_DET宏) - app.c — 主循环调用
charge_timer_handle()的集成点 - init_app.c — 上电调用
charge_init()的集成点 - 兄弟页面:电池电量检测与低功耗管理(
8-power-management目录下其他页面)