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

    • 项目概述与芯片平台
    • 环境搭建与工具链安装
    • 编译与烧录指南
    • 工程结构总览
  • 应用层与公共模块

    • GP MCU 主应用入口
    • AT 指令与调试模块
    • 电池检测与电源管理
    • EEPROM 与参数存储
    • 按键与 USB 设备驱动
    • 音频解码与 APA 语音播报
  • 外设驱动与示例

    • 高精度 ADC(HADC)
    • 通用 ADC 与定时器
    • UART / SPI / IIC 通信外设
    • MCPWM 与电机控制
    • RTC 与低功耗唤醒
    • 段码 LCD 驱动
    • NOR Flash 与红外编解码组件
  • 显示与 UI 系统

    • LCD 驱动与字库引擎
    • UI 平台与控件绘制
    • UI 工程与资源生成工具
  • 系统底层与芯片平台

    • cd09 芯片平台与预编译库
    • GPIO 与 IIC 底层驱动
    • 系统文件系统与设备模型
  • 启动引导与固件升级

    • UBOOT 引导工程
    • 固件升级机制
  • 开发工具与资源

    • 编译脚本与命令行工具
    • 音频文件转换工具
    • 硬件资料与文档资源

电池检测与电源管理

本文档介绍 AC82N GP-MCU SDK 中供电电源(电池)检测与电量管理子系统:它基于 GPADC 电压采样与分段线性插值将电源电压换算为 0~100% 电量,通过系统定时器周期扫描实现电量缓变跟踪、充放电状态判断,并在电量耗尽时通过系统消息事件通知业务层进入低电处理流程。

Purpose and Scope

本页面覆盖电源管理子系统的完整实现链路,包括:

  • 电量管理模块 sdk/apps/common/battery/battery.c 与其头文件 battery.h(电压-电量映射表、初始化/扫描/反初始化逻辑、充放电状态检测);
  • 底层电压采集所依赖的 GPADC 驱动配置(sdk/cpu/cd09/gpadc.c 中的电池检测 IO 与采样通道注册);
  • 应用层配置项 TCFG_BATTERY_TYPE_SEL、TCFG_BAT_DET_IO、TCFG_BAT_AD_CHANNEL 等(sdk/apps/gp_mcu/include/app_config.h)及其对电量模型的影响;
  • 低电事件 MSG_EVENT_BATTERY_LOWPOWER 的发送时机与业务层的消费方式。

以下内容属于其他页面,不在本文展开:具体低电事件在业务层(如 UI 提示、关机流程)的详细处理逻辑、GPADC 驱动本身的采样机制细节、以及充电管理硬件方案(充电芯片驱动)的实现。SDK 中 doc/软件工具/.../Battery.ctrl 与 UI 工程中的电池图标资源属于 UI 表现层,仅作参考。

Overview

本 SDK 面向 GP-MCU 系列(AC82N)嵌入式应用,常见产品形态为使用干电池或锂电池供电的消费电子设备。电源管理子系统需要解决三个核心问题:

  1. 电压怎么采:通过 PMU 内部 VPWR 通道(1/4 分压)或外部 GPIO 复用为 GPADC 通道(默认 1/3 分压)读取电源电压,单位为 mV。
  2. 电压怎么换算成电量:不同电池类型(2/3/4 节干电池、锂电池)的放电曲线差异很大,SDK 为每种类型预置一张 11 档电压表(0%~100%),在相邻档位之间做线性插值得到电量百分比。
  3. 电量怎么更新与上报:初始化时阻塞采样 8 次求平均值作为初值;之后由系统定时器每 30 秒触发一次 battery_scan() 周期采样,采用每次 ±1% 的缓变策略避免 UI 电量跳变;当电量降到 0% 且处于放电状态时,停止监测并通过 sys_msg_event_notify() 广播 MSG_EVENT_BATTERY_LOWPOWER 事件。

该设计刻意保持轻量:模块仅依赖 gpadc_api.h、timer.h、event.h、gpio.h,无独立线程、无阻塞等待(仅初始化时的一次性阻塞采样),非常适合资源受限的 MCU 环境。

Architecture

flowchart TD
    subgraph sg_App["应用层 (sdk/apps)"]
        AppInit["系统启动/初始化流程"]
        BMS["battery.c<br/>电量管理模块"]
        UI["业务/UI层"]
    end

    subgraph sg_Driver["驱动与系统服务"]
        GPADC["gpadc.c / gpadc_api.h<br/>GPADC 驱动"]
        TM["sys_timer 系统定时器"]
        EVT["event 系统消息事件"]
    end

    subgraph sg_HW["硬件"]
        VPWR["PMU VPWR 电压<br/>(1/4 分压)"]
        ExtIO["外部IO采样<br/>(默认1/3 分压)"]
        ChargeIO["充电检测IO<br/>(仅锂电池)"]
    end

    AppInit -->|"battery_init()"| BMS
    BMS -->|"adc_get_voltage / adc_get_voltage_by_blocking"| GPADC
    BMS -->|"sys_timer_add(battery_scan, 30s)"| TM
    TM -->|"周期回调 battery_scan()"| BMS
    BMS -->|"sys_msg_event_notify(MSG_EVENT_BATTERY_LOWPOWER)"| EVT
    EVT -->|"事件分发"| UI
    UI -->|"get_battery_lvl() / get_battery_charge_state()"| BMS
    GPADC -->|"采样"| VPWR
    GPADC -->|"采样"| ExtIO
    BMS -->|"gpio_read(TCFG_CHARGE_DET_IO)"| ChargeIO

架构说明

  • battery.c 电量管理模块是整个子系统的核心,持有唯一的全局状态 struct _battery_info(当前电量 battery_lvl + 定时器句柄 battery_timer),对外暴露 battery_init()、get_battery_lvl()、get_battery_charge_state() 等接口,对内通过 GPADC 驱动读取电压、通过系统定时器驱动周期扫描、通过事件机制上报低电状态。
  • gpadc.c 驱动层负责把硬件 IO 配置成 GPADC 功能并注册采样通道。源码中 #ifdef TCFG_BAT_DET_IO 分支会将该 IO 设置为 PORT_INPUT_FLOATING + PORT_FUNC_GPADC,并调用 adc_add_sample_ch(TCFG_BAT_AD_CHANNEL) 注册采样通道(见 gpadc.c)。
  • 系统定时器与事件服务是模块与外界交互的两个通道:定时器负责低频(30 秒)周期采样;事件通道负责把低电告警异步通知给业务层,使业务层(如 UI 提示、强制关机)不必轮询。
  • 硬件侧存在两条电压采样路径:AD_ANA_PMU_CH_VPWR_DIV_4 走 PMU 内部 1/4 分压通道;其他外部 IO 通道按 1/3 分压处理。代码中根据通道宏选择 ×4 或 ×3 的还原系数(见 battery.c)。

电量模型与电压表

电量百分比由电压-电量映射表 + 线性插值计算而来,映射表由 TCFG_BATTERY_TYPE_SEL 在编译期决定。battery.h 针对四种电池类型分别定义了 0%~100% 共 11 档电压(单位 mV),例如锂电池的映射关系:

#elif (TCFG_BATTERY_TYPE_SEL == BATTERY_TYPE_LITHIUM_DELL)
#define BATTERY_LVL_0		(3300)
#define BATTERY_LVL_10		(3680)
#define BATTERY_LVL_20		(3740)
#define BATTERY_LVL_30		(3770)
#define BATTERY_LVL_40		(3790)
#define BATTERY_LVL_50		(3820)
#define BATTERY_LVL_60		(3870)
#define BATTERY_LVL_70		(3920)
#define BATTERY_LVL_80		(3980)
#define BATTERY_LVL_90		(4060)
#define BATTERY_LVL_100		(4100)
#endif

Source: battery.h

四种电池类型的电压表差异显著,体现了不同的放电特性:

电池类型宏定义0% 电压100% 电压特性说明
2 节干电池BATTERY_TYPE_2_DRY_DELL2000 mV3200 mV近似线性,每 10% 间隔 120 mV
3 节干电池BATTERY_TYPE_3_DRY_DELL3000 mV4800 mV近似线性,每 10% 间隔 180 mV(默认)
4 节干电池BATTERY_TYPE_4_DRY_DELL4000 mV6400 mV近似线性,每 10% 间隔 240 mV
锂电池BATTERY_TYPE_LITHIUM_DELL3300 mV4100 mV非线性:10%~40% 区间间隔很小(30~60 mV),贴近锂电池平台期特性

设计意图:干电池放电曲线接近线性,采用等间隔电压表即可;锂电池在平台期(3.7~3.9V 附近)电压变化极缓,若用等间隔表会长期显示同一电量,因此低电量段(10%~50%)电压间隔刻意收窄,使平台期内的电量变化仍可被分辨。

核心实现详解

模块状态与全局结构

模块使用单一静态全局实例保存运行时状态,并通过 __this 宏访问,避免传入指针的样板代码:

struct _battery_info {
    u8 battery_lvl; 	//供电电源电量
    u16 battery_timer;  //供电电源检测定时器
};
static struct _battery_info battery_info;
#define __this (&battery_info)

const u16 voltage_table[] = {
    BATTERY_LVL_0, 	BATTERY_LVL_10, BATTERY_LVL_20, BATTERY_LVL_30, BATTERY_LVL_40,
    BATTERY_LVL_50, BATTERY_LVL_60, BATTERY_LVL_70, BATTERY_LVL_80, BATTERY_LVL_90,
    BATTERY_LVL_100,
};

Source: battery.c

voltage_table[] 是编译期根据 battery.h 中宏生成的 11 项常量数组,battery_lvl 为 0~100 的电量值,battery_timer 为系统定时器句柄(0 表示未注册)。

电压采集:分压还原

battery_get_voltage() 依据采样通道决定电压还原系数——这是硬件分压比在软件侧的补偿:

static u16 battery_get_voltage(void)
{
    //锂电池和4节电池时,采用外部IO采集电压,默认分压1/3
    if (TCFG_BAT_AD_CHANNEL == AD_ANA_PMU_CH_VPWR_DIV_4) {
        return (adc_get_voltage(TCFG_BAT_AD_CHANNEL) * 4);
    } else {
        return (adc_get_voltage(TCFG_BAT_AD_CHANNEL) * 3);
    }
}

Source: battery.c

设计要点:

  • 当 TCFG_BAT_AD_CHANNEL == AD_ANA_PMU_CH_VPWR_DIV_4 时,走 PMU 内部 VPWR 通道,硬件已做 1/4 分压,软件乘 4 还原真实电压;
  • 其他外部 IO 通道(如 AD_CH_PA0)按 1/3 分压设计(外部电阻分压网络),软件乘 3 还原;
  • 注释特别说明"锂电池和 4 节电池时采用外部 IO 采集"——高电压场景(4.2V 锂电池满电、4 节干电池 6.4V)超出内部通道量程或精度需求时,必须改用外部电阻分压网络接入 GPADC IO。

电量换算:分段线性插值

cal_battery_lvl() 是电压→电量的核心算法:先做上下界钳制,再在相邻电压档位间线性插值:

static u8 cal_battery_lvl(u16 voltage)
{
    u16 tmp0, tmp1;
    if (voltage >= BATTERY_LVL_100)	{
        return 100;
    } else if (voltage <= BATTERY_LVL_0) {
        return 0;
    }

    for (u8 i = 1; i < ARRAY_SIZE(voltage_table); i++) {
        if (voltage <= voltage_table[i]) {
            tmp0 = voltage_table[i]	- voltage_table[i - 1];
            tmp1 = voltage - voltage_table[i - 1];
            return (tmp1 * 10 / tmp0 + (i - 1) * 10);
        }
    }
    return 0;
}

Source: battery.c

算法说明:

  • 边界钳制:电压 ≥ 100% 档直接返回 100,≤ 0% 档返回 0,避免插值越界;
  • 线性插值:从低到高找到第一个 voltage <= voltage_table[i] 的档位,按比例 tmp1 * 10 / tmp0 计算档内偏移,再加上 (i-1)*10 的基准百分比;整数运算避免浮点,适合无 FPU 的 MCU;
  • 由于各档间隔不同(如锂电池 10%~40% 间隔仅 30~60 mV),同一电压落在不同档位时换算分辨率不同,这是有意的设计取舍。

充放电状态检测

get_battery_charge_state() 区分干电池与锂电池的充电管理差异——干电池只有放电状态,锂电池通过 IO 电平判断充电:

enum charge_state get_battery_charge_state(void)
{
#if (TCFG_BATTERY_TYPE_SEL != BATTERY_TYPE_LITHIUM_DELL)
    return DISCHARGE_STATE;
#else
    if (gpio_read(TCFG_CHARGE_DET_IO)) {
        return CHARGE_STATE;
    } else {
        return DISCHARGE_STATE;
    }
#endif
}

Source: battery.c

设计意图:干电池不可充电,直接编译期短路返回 DISCHARGE_STATE(零开销);锂电池配置了充电检测 IO TCFG_CHARGE_DET_IO,充电器插入时 IO 为高电平,返回 CHARGE_STATE。该状态被 battery_scan() 用来决定电量是否允许回升(充电中才允许 +1)。

周期扫描与低电告警

battery_scan() 是注册到系统定时器的回调(周期 30 秒),完成一次"采样 → 换算 → 缓变更新 → 低电判断"的完整周期:

void battery_scan(void *priv)
{
    u16 battery_volt;
    u8 curr_battery_lvl;
    enum charge_state state = get_battery_charge_state();

    battery_volt = battery_get_voltage();
    curr_battery_lvl = cal_battery_lvl(battery_volt);

    if (curr_battery_lvl < __this->battery_lvl) {
        __this->battery_lvl--;
    } else if (curr_battery_lvl > __this->battery_lvl) {
        if (state == CHARGE_STATE) {
            __this->battery_lvl++;
        }
    }
    log_info("scan volt: %dmV, lvl: %d, cur_lvl: %d", battery_volt, __this->battery_lvl, curr_battery_lvl);

    if (!__this->battery_lvl && (state == DISCHARGE_STATE)) {
        battery_uninit();
        sys_msg_event_notify(MSG_EVENT_BATTERY_LOWPOWER, 0);
    }
}

Source: battery.c

关键行为:

  • 缓变策略(防抖):battery_lvl 每次扫描最多变化 1 个百分点。即使采样电压突变导致 curr_battery_lvl 大幅下降,对外电量也只递减 1,避免 UI 电量"跳水";同理,仅当处于充电状态时电量才允许递增,防止放电瞬间的电压回弹造成电量虚高。这是典型的软件滤波/迟滞设计。
  • 低电判定与自停:当电量已降到 0 且处于放电状态时,调用 battery_uninit() 主动停止采样与定时器(进入"节能自关闭"),并广播 MSG_EVENT_BATTERY_LOWPOWER 事件,由业务层决定提示用户或强制关机。
  • 日志可观测:每次扫描输出 volt/lvl/cur_lvl 三个量,便于现场用 log_info 排查电量异常。

初始化与反初始化

battery_init() 是模块入口,阻塞采样 8 次求平均作为电量初值;若初值即低电则直接返回低电状态且不启动定时器,否则注册 30 秒周期扫描:

enum battery_state battery_init(void)
{
    u16 battery_volt;
    enum charge_state state = get_battery_charge_state();

    if (TCFG_BAT_AD_CHANNEL == AD_ANA_PMU_CH_VPWR_DIV_4) {
        battery_volt = adc_get_voltage_by_blocking(TCFG_BAT_AD_CHANNEL, 8) * 4;
    } else {
        battery_volt = adc_get_voltage_by_blocking(TCFG_BAT_AD_CHANNEL, 8) * 3;
    }
    __this->battery_lvl = cal_battery_lvl(battery_volt);

    if (!__this->battery_lvl && (state == DISCHARGE_STATE)) {
        return BATTERY_LOWPOWER;
    } else {
        if (!__this->battery_timer) {
            __this->battery_timer = sys_timer_add(NULL, battery_scan, 30 * 1000);
        }
        return BATTERY_NORMAL;
    }
}

Source: battery.c

配套的 battery_uninit() 负责反初始化:移除 PMU 采样通道并删除定时器(句柄置 0,保证可重复初始化):

void battery_uninit(void)
{
    adc_remove_sample_ch(AD_ANA_PMU_CH_VPWR_DIV_4);
    if (__this->battery_timer) {
        sys_timer_del(__this->battery_timer);
        __this->battery_timer = 0;
    }
}

Source: battery.c

设计要点:

  • 初始化使用 adc_get_voltage_by_blocking(ch, 8) 一次性阻塞采样 8 次,以平均抑制上电瞬间的采样噪声;而周期扫描使用非阻塞的 adc_get_voltage(),避免在定时器回调中长时间阻塞;
  • battery_timer 判空后再 sys_timer_add,保证 battery_init() 可重复调用不会产生多个定时器;
  • 低电自检前置:开机即电量 0% 且未充电时,直接跳过定时器注册,系统可立即进入低电流程,不浪费任何资源。

核心流程

电量监测生命周期

sequenceDiagram
    participant App as 应用层
    participant Bat as battery.c
    participant ADC as GPADC 驱动
    participant TM as 系统定时器
    participant UI as 业务/UI层

    App->>Bat: battery_init()
    Bat->>Bat: 读取充放电状态 (get_battery_charge_state)
    Bat->>ADC: adc_get_voltage_by_blocking(ch, 8) 阻塞采样8次
    ADC-->>Bat: 原始采样值
    Bat->>Bat: 按分压比 ×3/×4 还原电压,cal_battery_lvl 求初值
    alt 初值 0% 且放电中
        Bat-->>App: 返回 BATTERY_LOWPOWER(不启动定时器)
    else 电量正常
        Bat->>TM: sys_timer_add(battery_scan, 30*1000)
        Bat-->>App: 返回 BATTERY_NORMAL
    end

    loop 每 30 秒
        TM->>Bat: 触发 battery_scan(priv)
        Bat->>ADC: adc_get_voltage(ch) 非阻塞采样
        Bat->>Bat: 换算电量,按 ±1 缓变更新 battery_lvl
        opt 电量已为 0 且放电中
            Bat->>Bat: battery_uninit() 移除通道、删除定时器
            Bat->>UI: sys_msg_event_notify(MSG_EVENT_BATTERY_LOWPOWER, 0)
            UI->>UI: 业务层低电处理(提示/关机)
        end
    end

电量状态机

stateDiagram-v2
    [*] --> Normal: battery_init() 返回 BATTERY_NORMAL,注册30s定时器
    Normal --> LowPower: battery_scan() 检测到 0% 且处于放电状态
    LowPower --> [*]: battery_uninit() 停止采样与定时器,广播 MSG_EVENT_BATTERY_LOWPOWER
    [*] --> LowPower: battery_init() 初值即 0% 且放电中(开机即低电)

状态说明:

  • Normal(正常):定时器活跃,每 30 秒扫描一次;电量随扫描缓变(放电递减、充电递增);
  • LowPower(低电):模块已自停(采样通道移除、定时器删除),系统仅靠 MSG_EVENT_BATTERY_LOWPOWER 事件驱动业务层后续动作;
  • 状态机不存在从 LowPower 自动回到 Normal 的路径——低电后需业务层重新调用 battery_init()(例如用户更换电池/插入充电器后重启监测)才能恢复,这是由 battery_uninit() 把 battery_timer 置 0、battery_init() 可重复调用共同保证的。

使用示例

初始化并获取电量

以下代码展示应用层如何启动电量监测并周期读取电量值(电量缓变更新,UI 可直接使用):

enum battery_state battery_init(void);
u8 get_battery_lvl(void);

Source: battery.h

    // 系统启动流程中调用,返回 BATTERY_NORMAL 或 BATTERY_LOWPOWER
    enum battery_state bs = battery_init();
    if (bs == BATTERY_LOWPOWER) {
        // 开机即低电:提示用户更换电池/接入充电器
    }

    // 业务层任意位置读取当前电量(0~100,定时器后台维护)
    u8 lvl = get_battery_lvl();
    // 刷新 UI 电池图标:显示 lvl 对应档位图标

说明:get_battery_lvl() 在 battery.c 中实现,直接返回 __this->battery_lvl,无任何加锁/阻塞,可在任意上下文(包括定时器回调、中断外上下文)安全读取。

充放电状态判断(锂电池)

锂电池场景下,业务层可通过充电状态决定是否显示充电动画:

enum charge_state get_battery_charge_state(void)
{
#if (TCFG_BATTERY_TYPE_SEL != BATTERY_TYPE_LITHIUM_DELL)
    return DISCHARGE_STATE;
#else
    if (gpio_read(TCFG_CHARGE_DET_IO)) {
        return CHARGE_STATE;
    } else {
        return DISCHARGE_STATE;
    }
#endif
}

Source: battery.c

    if (get_battery_charge_state() == CHARGE_STATE) {
        // 显示"充电中"动画;电量只增不减(由 battery_scan 保证)
    }

低电事件订阅

系统消息事件 MSG_EVENT_BATTERY_LOWPOWER 由 battery_scan() 在电量耗尽时通过 sys_msg_event_notify() 广播(见 battery.c)。业务层在自身事件处理循环中订阅该事件,典型的处理是低电提示音 + 若干秒后强制关机,这部分逻辑属于业务层范畴,本文不再展开。

硬件层配置接线(gpadc.c)

gpadc.c 在初始化阶段根据 TCFG_BAT_DET_IO 配置电池检测引脚:先把 IO 设为浮空输入,再切换为 GPADC 功能,并注册采样通道:

#ifdef TCFG_BAT_DET_IO
#if (TCFG_BAT_DET_IO != NO_CONFIG_PORT)
    gpio_set_mode(IO_PORT_SPILT(TCFG_BAT_DET_IO), PORT_INPUT_FLOATING);
    gpio_set_function(IO_PORT_SPILT(TCFG_BAT_DET_IO), PORT_FUNC_GPADC);
#endif
    adc_add_sample_ch(TCFG_BAT_AD_CHANNEL);
#endif

Source: gpadc.c

设计意图:

  • 当 TCFG_BAT_DET_IO == NO_CONFIG_PORT(默认)时,不配置外部 IO,直接注册 AD_ANA_PMU_CH_VPWR_DIV_4 内部通道——此时电池直接供电给系统,由 PMU 内部 1/4 分压采样;
  • 当配置了具体 IO(如 IO_PORT_PA0)时,外部电阻分压网络接入该引脚,必须先设为浮空输入(避免上/下拉影响分压精度)再切换 GPADC 功能。

配置选项

电源管理相关配置集中在 sdk/apps/gp_mcu/include/app_config.h,编译期生效:

#define BATTERY_TYPE_4_DRY_DELL				(2) //4节干电池(6.4V),放电管理
#define BATTERY_TYPE_LITHIUM_DELL			(3) //锂电池(4.2V),充放电管理
#define TCFG_BATTERY_TYPE_SEL         		BATTERY_TYPE_3_DRY_DELL

#define TCFG_BAT_DET_IO                     NO_CONFIG_PORT //检测电池的IO
//若TCFG_BAT_DET_IO为NO_CONFIG_PORT,则TCFG_BAT_AD_CHANNEL默认配置为AD_ANA_PMU_CH_VPWR_DIV_4
//若TCFG_BAT_DET_IO为实际IO(例如IO_PORT_PA0),则TCFG_BAT_AD_CHANNEL需配置为对应AD通道(如AD_CH_PA0)
#define TCFG_BAT_AD_CHANNEL                 AD_ANA_PMU_CH_VPWR_DIV_4 //电池检测的AD通道

Source: app_config.h

配置项类型默认值说明
TCFG_BATTERY_TYPE_SEL枚举宏BATTERY_TYPE_3_DRY_DELL电池类型,决定 battery.h 中电压表与充放电管理方式;可选 BATTERY_TYPE_2_DRY_DELL(2 节干电池)、BATTERY_TYPE_3_DRY_DELL(3 节干电池)、BATTERY_TYPE_4_DRY_DELL(4 节干电池)、BATTERY_TYPE_LITHIUM_DELL(锂电池)
TCFG_BAT_DET_IOIO 端口宏NO_CONFIG_PORT外部电压采样 IO;NO_CONFIG_PORT 时使用 PMU 内部 VPWR 通道,配置实际 IO 时走外部电阻分压网络
TCFG_BAT_AD_CHANNELAD 通道宏AD_ANA_PMU_CH_VPWR_DIV_4电池检测的 AD 采样通道;与 TCFG_BAT_DET_IO 配套,外部 IO 需配置为对应 AD 通道(如 AD_CH_PA0)
TCFG_CHARGE_DET_IOIO 端口宏锂电池工程必配充电检测 IO(锂电池类型时使用),gpio_read() 高电平表示充电中;干电池类型下该宏不参与编译

配置约束:

  • 内部通道与外部 IO 二选一:TCFG_BAT_DET_IO = NO_CONFIG_PORT 时只能使用 AD_ANA_PMU_CH_VPWR_DIV_4;配置外部 IO 时需同步切换 TCFG_BAT_AD_CHANNEL,否则采样通道与引脚不匹配;
  • 高电压场景建议外部采样:4 节干电池(满电 6.4V)与锂电池满电(4.2V)场景注释明确建议走外部 IO 分压采样(默认 1/3 分压);
  • 修改 TCFG_BATTERY_TYPE_SEL 后需全量重编译,因为电压表宏在 battery.h 中以 #if/#elif 编译期展开。

API 参考

enum battery_state battery_init(void)

供电电源初始化函数,阻塞采样 8 次得到电量初值,电量正常时注册 30 秒周期扫描定时器。

  • 参数:无
  • 返回:BATTERY_NORMAL(正常状态,监测已启动);BATTERY_LOWPOWER(低电状态,监测未启动)
  • 说明:可重复调用;battery_timer 非 0 时不会重复注册定时器;低电时返回 BATTERY_LOWPOWER 且不注册定时器
  • Source: battery.h / battery.c

u8 get_battery_lvl(void)

获取当前供电电源电量百分比。

  • 参数:无
  • 返回:0~100 的电量值(0 为耗尽,100 为满电)
  • 说明:读取全局结构体字段,无阻塞,可在任意非中断上下文调用;数值由 30 秒扫描缓变维护,不会突跳
  • Source: battery.h / battery.c

enum charge_state get_battery_charge_state(void)

获取供电电源充电状态(实现在 .c 中,.h 未声明,同编译单元内或自行声明后使用)。

  • 参数:无
  • 返回:CHARGE_STATE(充电中,仅锂电池且 TCFG_CHARGE_DET_IO 为高电平);DISCHARGE_STATE(放电中,干电池恒返回此值)
  • Source: battery.c

void battery_scan(void *priv)

供电电源电量检测回调,一般半分钟扫描一次(注册于系统定时器)。

  • 参数:priv(定时器回调私有参数,当前实现未使用,传 NULL)
  • 返回:无
  • 行为:采样电压 → 换算电量 → 按 ±1 缓变更新 → 电量 0% 且放电时 battery_uninit() 并广播 MSG_EVENT_BATTERY_LOWPOWER
  • Source: battery.c

void battery_uninit(void)

退出供电电源检测:移除 PMU 采样通道、删除定时器并将句柄置 0。

  • 参数:无
  • 返回:无
  • 说明:低电自停与业务主动关停共用此函数;调用后模块回到未初始化状态,可再次 battery_init()
  • Source: battery.c

失败模式、边界情况与并发

  • 开机即低电:battery_init() 初值为 0% 且放电中,直接返回 BATTERY_LOWPOWER 且不注册定时器。业务层必须处理该返回值,否则设备会"无感知"地跳过整个电量监测生命周期。
  • 电量耗尽自停:battery_scan() 检测到 0% 且放电时调用 battery_uninit() 主动停止采样。此时若电池电压在负载移除后回弹(如干电池静置回升),电量也不会自动恢复——需业务层重新初始化;这是刻意设计,避免死机临界区反复唤醒。
  • 充电电量回升受限:battery_lvl 仅在 CHARGE_STATE 时才允许 +1。若锂电池充电检测 IO 配置错误(恒为低),充电中电量将长期不变;反之干电池类型即使电压上升也不会增加电量显示。
  • 采样噪声与缓变:单次采样电压抖动会被"每次 ±1"的缓变策略吸收,但代价是电量反映存在最长 30 秒的滞后;对需要实时电压的应用,应直接调用 GPADC 接口而非读取 battery_lvl。
  • 并发与重入:模块无锁。battery_lvl 由定时器回调写入、业务上下文读取,均为单字节原子操作,MCU 单核环境下无撕裂风险;battery_init()/battery_uninit() 不应在定时器回调内调用自身(battery_uninit 由 battery_scan 内部调用是受控路径:先删通道删定时器再发事件,不会重入)。
  • VPWR 通道反初始化不对称:battery_uninit() 固定移除 AD_ANA_PMU_CH_VPWR_DIV_4 通道;若工程配置为外部 IO 通道,该调用不会移除对应通道,外部通道采样会持续运行(功耗略增),属于当前实现的已知局限。

性能与运维

  • CPU 开销极低:每 30 秒一次定时器回调,每次仅一次 ADC 采样 + 一次整数插值运算,无浮点、无阻塞(初始化除外),对 MCU 几乎无负载。
  • 初始化阻塞时间:battery_init() 内 adc_get_voltage_by_blocking(ch, 8) 连续阻塞采样 8 次,在低主频下可能耗时数毫秒,建议在系统启动流程早期(业务未繁忙时)调用。
  • 日志观测:log_info("scan volt: %dmV, lvl: %d, cur_lvl: %d", ...) 输出每次扫描的电压、缓变后电量与即时电量,现场排查电量异常(电压跳变、电量不更新)可直接依赖该日志。
  • 功耗注意:低电自停后 GPADC 外部通道(若配置)未被移除,对超低功耗待机场景,业务层可自行调用 adc_remove_sample_ch() 关闭对应通道。

扩展点

  • 新增电池类型:在 app_config.h 增加电池类型枚举,并在 battery.h 增加对应的 #elif 电压表分支(0%~100% 共 11 档),即可支持新的电池规格,无需改动 battery.c 算法逻辑。
  • 调整扫描周期:修改 battery_init() 中 sys_timer_add(NULL, battery_scan, 30 * 1000) 的周期参数即可改变电量刷新频率;更快的扫描可提高实时性,但会放大采样噪声影响,建议配合缓变策略调整。
  • 自定义低电行为:MSG_EVENT_BATTERY_LOWPOWER 是标准的系统消息事件,业务层可在事件分发中注册自己的处理(提示、关机、进入低功耗模式等),模块本身不耦合具体业务。
  • 自定义电压还原系数:若外部分压网络不是 1/3,可修改 battery_get_voltage() 中 ×3 分支的系数,或按通道宏扩展新的分支。

Related Links

  • 电池检测硬件通道配置:gpadc.c(sdk/cpu/cd09/gpadc.c)
  • 应用层电池配置项:app_config.h(sdk/apps/gp_mcu/include/app_config.h)
  • 电量管理实现:battery.c / battery.h(sdk/apps/common/battery/)
  • UI 电池图标资源(表现层参考):Battery.ctrl(sdk/UI工程/UI界面/示例/project/config/ctrl/Battery.ctrl)
  • 低电事件 MSG_EVENT_BATTERY_LOWPOWER 的系统消息定义见 SDK 事件头文件(event.h),业务层消费逻辑详见对应业务模块文档。
Prev
AT 指令与调试模块
Next
EEPROM 与参数存储