杰理 SDK 文档中心
首页
首页
  • 项目概览

    • AD16N 系列芯片与 SDK 能力总览
  • 快速开始

    • 环境搭建与编译工具链
    • 编译构建指南
    • 烧录与固件升级
  • SDK 工程架构

    • SDK 目录结构与模块分层
    • 构建系统与批处理工具
    • BSP 板级支持包
  • mbox_flash 小音箱应用

    • 应用初始化与启动流程
    • 应用配置系统
    • 按键、UI 与用户交互
  • 音频子系统

    • 音频解码框架与调度
    • 音频格式解码器实现
    • MIDI 合成与播放
    • 音频编码与录音
    • EQ/DRC 与音效处理
    • DAC/ADC 音频接口与采样
  • 存储与文件系统

    • 媒体 IO 抽象层 MIO
    • 存储设备驱动
    • 文件系统支持
  • 平台系统库

    • 系统基础服务
    • CPU 平台与运行库
    • 固件升级与更新机制
    • 蓝牙与扩展连接接口
  • 电源与低功耗管理

    • 电源管理与低功耗设计
    • 锂电池充电管理
  • 硬件与文档参考

    • SDK 文档中心与版本发布记录
    • 芯片数据手册与硬件设计参考

锂电池充电管理

本页介绍 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 的多级充电电流档位。充电管理模块在此基础上实现完整的软件充电策略:

  1. 插入检测:通过 VPWR 边沿唤醒中断 + 软件滤波识别充电器插入/拔出;
  2. 分阶段充电:电池电压低于 3000mV 时以小电流涓流预充,达到阈值后切换为配置的恒流电流;
  3. 充满判停:充电电流降至判满电流以下并持续滤波时间后停止充电;
  4. 可选复充:充满后电池电压跌落至复充阈值时自动重新充电(RECHARGE_ENABLE 默认关闭);
  5. 低功耗协同:充电过程中(插拔检测/充满检测进行中)禁止系统进入低功耗,避免丢失充电状态。

该模块通过 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/CON1VPWR 下拉电阻使能与档位

初始化流程: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

初始化顺序的设计意图:

  1. 先注册 VBAT ADC 采样通道(仅 SARADC_ISR_MODE 模式),保证后续 adc_get_vbat_voltage() 可用;
  2. 关闭 CHGFL(充满标志)唤醒 IO——初始阶段不期望被充满事件唤醒;
  3. 将所有充电寄存器清零后写入编译期配置值,保证确定性起点;
  4. VPWR 下拉电阻:充电口悬空时提供确定的电平,防止误判插拔(TCFG_VPWR_PULLDOWN_EN 默认开启,50KΩ);
  5. charge_config() 依据 efuse trim 校准充满电压;
  6. 最后主动置 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 < 3000mVcharge_set_mA(TCFG_CHARGE_TRICKLE_MA),TRICKLE_EN=1
CC(恒流)VBAT ≥ 3000mVcharge_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常量50VPWR 插入滤波次数(×2ms = 100ms)
TCFG_VPWR_OFF_FILTER常量100VPWR 拔出滤波次数(×2ms = 200ms)
TCFG_VPWR_PULLDOWN_EN布尔1是否使能 VPWR 下拉电阻
TCFG_VPWR_PULLDOWN_LEV枚举CHARGE_PULLDOWN_50KVPWR 下拉电阻档位(50K/100K/150K/200K)

电流/电压档位枚举速查

档位宏数值电流模式
CHARGE_mA_20 ~ CHARGE_mA_1200~1520~120mA恒流(CC)
CHARGE_mA_2 ~ CHARGE_mA_12BIT(4) 组合2~12mA涓流(TRICKLE)
CHARGE_mA_130 ~ CHARGE_mA_180BIT(5) 组合130~180mA增强恒流(TURBO,不建议高频使用)
CHARGE_FULL_V_4041 ~ CHARGE_FULL_V_43380~154.041~4.338V低压电池充满电压
CHARGE_FULL_V_4237 ~ CHARGE_FULL_V_453416~314.237~4.534V高压电池充满电压
CHARGE_FULL_mA_DIV5/10/15/200~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 立即清零,需重新连续满足 20mscharge_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() 在初始化时打印全部充电寄存器值,是排查充电异常的第一手信息。

扩展点

  1. 温度降流:在应用层实现温度采集,温度升高时调用 charge_set_mA() 下调档位(如 100mA → 50mA),charge_set_mA() 的写前比较保证了低开销;
  2. 复充策略:置 RECHARGE_ENABLE = 1 并调整 RECHARGE_V(默认 4000mV)即可启用充满后自动复充,无需改动状态机;
  3. 充满/充电状态上报:应用层可通过 charge_check_is_full() 与 charge_get_lvcmp_det() 组合判断"正在充电/已充满/未充电"三态,用于 UI 电量图标与充电指示灯;
  4. 多电池档位适配:通过板级配置覆盖 TCFG_CHARGE_FULL_V/TCFG_CHARGE_MA/TCFG_CHARGE_TRICKLE_MA/TCFG_CHARGE_FULL_MA,同一固件可适配 4.2V/4.35V 电池体系;
  5. 硬件唤醒源扩展: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 目录下其他页面)
Prev
电源管理与低功耗设计