进阶¶
进阶篇重点介绍电池管理服务,便于用户自主修改扩展。内容包括:电池管理服务框架,程序流程,电量服务算法优化,NTC温度计算
1. 电池管理服务¶
1.1 框架¶
电池管理服务大致包括充电,电量采集,温度采集三个部分。框架示意图如下:
1.2 单核(52x)流程¶
单核平台涉及到以下三个方面的管理:
电池电量检测管理
初始化:通过
INIT_PREV_EXPORT(battery_voltage_detection_init())隐式调用初始化函数,开机自动执行。
运行机制:定时器以
BAT_DETECT_INTERVAL为周期(单位:秒)释放信号量,触发线程charger_thread执行。调用
battery_voltage_detection()采集原始电压;若检测到充电器插入(
BAT_CHARGING_PLUG_IN),延迟 300ms 等待 ADC 稳定后,执行battery_temp_detection()采集温度;
电池充电管理
初始化:
battery_charger_init()通过INIT_COMPONENT_EXPORT注册为组件,开机自动初始化
消息通信
单核管理而言应用与驱动同属 HCPU,,但为了统一架构,因此虚拟了一个LCPU实现。
1.3 双核流程¶
电池管理服务双核管理一般在55x/56x/58x平台。
HCPU负责电量应用,充电检测,开机低电量检测;
LCPU负责电量检测。
任务分工与初始化¶
模块 |
LCPU(低功耗核) |
HCPU(主核) |
电池电量检测 |
负责周期性采集(核心任务) |
负责电量数据应用(如UI显示)无直接初始化,依赖LCPU数据上报 |
充电检测 |
无相关任务 |
负责充电器插入/拔出、充满等中断检测 |
消息管理 |
隐式初始化:通过 |
显式调用: |
核间通讯机制¶
LCPU采集电压、温度等数据后,通过data service将数据同步至HCPU;HCPU接收数据后更新UI界面,并在充电状态变化(如插入充电器)时触发中断响应,实现“低功耗采集”与“实时交互”的平衡。
具体代码位置¶
2. 电量服务算法优化¶
ADC采集的原始电压存在波动与误差,系统通过“三级处理”实现高精度电量计算,核心流程为:原始电压采集→滤波优化→百分比转换。
2.1 电压值平滑处理¶
针对电压高频波动,采用“8次滑动平均”算法,对最近8个原始电压值求平均,实现“削峰填谷”。核心代码逻辑如下:
2.2 相邻电压加权平均处理¶
针对电压突变(如充电瞬间波动),采用 “8:2 加权平均” 算法,赋予历史值更高权重,平滑突变数据。核心代码:
2.3 无效电池电压值过滤¶
若当前电压与上一次差值超过阈值(BAT_CHARGE_FILTER/BAT_DISCHARGE_FILTER),判定为无效值,沿用历史值并累计过滤次数;若连续多次无效,则更新历史值,避免异常数据影响精度。
2.4 电压转换成电量百分比曲线¶
通过battery_voltage_convert_to_percent()接口实现电压到百分比的转换,核心逻辑为:
先调用
battery_voltage_filter()过滤无效值;根据充电状态选择对应曲线表:
充电状态(
BATTERY_CHARGING_STATUS):使用chargeing_curve_table(充电曲线,适配充电时电压偏高特性);放电状态:使用
discharge_curve_table(放电曲线);
曲线表需根据实际电池特性自定义校准,确保不同电压下的百分比显示准确。
3. NTC温度计算¶
NTC(热敏电阻)温度采集通过 ADC 获取电压值后,通过 “公式法” 或 “查表法” 计算温度,可在menuconfig中配置。
公式法:高精度计算 基于 NTC 的 B 值公式计算温度,需根据硬件参数配置分压电阻、B 值等参数。核心代码与参数说明如下:
查表法:快速计算 预存 “ADC 值 - 温度” 对应表,采集后通过查表直接获取温度,无需复杂计算,适合 CPU 资源有限或对实时性要求极高的场景。配置路径:
Top → Board Config → Select board peripherals → Charger config → NTC temp config → lookup table。
4. 自定义消息传递¶
电池管理服务提供了应用层和驱动层的消息传递机制。
4.1 消息传递接口¶
应用层向驱动层发送消息
ipc_send_msg_to_bat(type, data, len);
应用层向驱动层发消息支持的消息ID
typedef enum
{
BATTERY_DETECT_REQ = BAT_SERVICE_CUSTOM_ID_BEGIN,
BATTERY_LEVEL_REQ,
BATTERY_SYNC_SHUTDOWN_INFO,
BATTERY_METRICS_REQ,
BATTERY_H2L_ID_MAX
} battery_h2l_t;
驱动层处理应用层发来的消息
static int battery_service_comm_cb(uint16_t msg_id, void *data, uint16_t len)
{
switch (msg_id)
{
case BATTERY_DETECT_REQ: /*发起一次电量检测*/
{
charger_detect_value = *((int *) data);
battery_set_charger_int_mask(BAT_DETECT_MASK, 1);
rt_sem_release(&charger_int_sem);
break;
}
case BATTERY_SYNC_SHUTDOWN_INFO:/*HCPU 和 LCPU同步一次关机时的电量信息*/
{
battery_shutdown_info = *((battery_info_t *) data);
rt_sem_release(&charger_int_sem);
break;
}
case BATTERY_LEVEL_REQ: /*HCPU强制LCPU发送当前的电量信息给HCPU*/
{
battery_send_rt_voltage_to_app(true);
break;
}
}
return 0;
}
驱动层向应用层发送消息
```c
ipc_send_bat_msg_to_hcpu(type, data, len);
驱动层向应用层发消息支持的消息ID
typedef enum
{
BATTERY_DETECT_RSP = BAT_SERVICE_CUSTOM_ID_BEGIN + 0x100,
BATTERY_RT_LEVEL_IND,
BATTERY_RT_INFO_IND,
BATTERY_RT_CHARGE_IND,
BATTERY_EVENT_IND,
BATTERY_RT_METRICS_IND,
BATTERY_L2H_ID_MAX
} battery_l2h_t;
应用层处理驱动层或者发来的消息
static int app_service_comm_cb(uint16_t msg_id, void *data, uint16_t len)
{
switch (msg_id)
{
case BATTERY_DETECT_RSP:
{
break;
}
case BATTERY_RT_LEVEL_IND:
{
break;
}
case BATTERY_RT_INFO_IND: /*LCPU发送电池电量信息给到HCPU,HCPU同步到BLE及应用层*/
{
battery_info_sync_ble(data, len);
battery_rt_info_notify(data, len);
app_rt_info_update(RT_BATTERY, data, len);
#ifdef BT_USING_HF
app_bt_update_battery_level();
#endif
break;
}
case BATTERY_RT_CHARGE_IND:
{
break;
}
case BATTERY_EVENT_IND: /*处理LCPU发送给HCPU的事件*/
{
send_msg_to_gui_thread(data, len, battery_evt_ind_cb, 0, NEED_WAKEUP_UI);
break;
}
case BATTERY_RT_METRICS_IND:
{
break;
}
default:
;
}
}
BATTERY_EVENT_IND 处理从LCPU发送给HCPU的事件,事件列表存在于枚举battery_event_t
typedef enum
{
BATTERY_REMIND_NONE,
BATTERY_REMIND_LOW_POWER, /*低电量提醒*/
BATTERY_REMIND_CHARGING, /*充电提醒*/
BATTERY_REMIND_CHARGING_FULL, /*充满提醒*/
BATTERY_REMIND_CHARGE_END, /*充电结束提醒*/
BATTERY_REMIND_SHUTDOWN, /*低电关机提醒*/
BATTERY_ID_MAX
} battery_event_t;
4.2 自定义消息传递¶
自定义消息传递一般分为:自定义消息ID,以及调整消息ID对应的处理。
例如:我们想增加一个应用层向驱动层的消息ID: BATTERY_ID_TEST;
在枚举battery_h2l_t增加消息ID:BATTERY_ID_TEST
typedef enum
{
BATTERY_DETECT_REQ = BAT_SERVICE_CUSTOM_ID_BEGIN,
BATTERY_LEVEL_REQ,
BATTERY_SYNC_SHUTDOWN_INFO,
BATTERY_METRICS_REQ,
BATTERY_ID_TEST,
BATTERY_H2L_ID_MAX
} battery_h2l_t;
在battery_service_comm_cb()增加对消息ID的处理
static int battery_service_comm_cb(uint16_t msg_id, void *data, uint16_t len)
{
switch (msg_id)
{
case BATTERY_DETECT_REQ: /*发起一次电量检测*/
{
charger_detect_value = *((int *) data);
battery_set_charger_int_mask(BAT_DETECT_MASK, 1);
rt_sem_release(&charger_int_sem);
break;
}
case BATTERY_SYNC_SHUTDOWN_INFO:/*HCPU 和 LCPU同步一次关机时的电量信息*/
{
battery_shutdown_info = *((battery_info_t *) data);
rt_sem_release(&charger_int_sem);
break;
}
case BATTERY_LEVEL_REQ: /*HCPU强制LCPU发送当前的电量信息给HCPU*/
{
battery_send_rt_voltage_to_app(true);
break;
}
case BATTERY_ID_TEST: /处理新增的消息ID*/
{
/*function to add*/
break;
}
}
return 0;
}
5. 制作充电曲线¶
充电曲线和放电曲线保存在solution\framework\service\battery\battery_curve_table.h
当充电功能无法进行暂停充电时,通过充电曲线可以有效解决充电时电压升高导致的电量百分比要比实际值偏高的问题;
充电时电压升高的原因示意图:
充电时,VCC charge = Vinner + I2_charge × (R5+R6)
放电时,VCC discharge = Vinner - I2_discharge × (R5+R6)
从充电到放电时,VCCdiff = VCC charge - VCC discharge = (I2_charge + I2_discharge) × (R5+R6)
反映出影响压差的因素:
充电电流的影响:充电电流越大,压差越大;
负载电流的影响:负载电流越大,压差越大;
电池内阻的影响:内阻越大,压差越大;
电池和充电芯片的阻抗的影响:电池和充电芯片的阻抗越大,压差越大;
对于电池而言,内阻的是固定的;
对于PCB而言,电池和充电芯片的阻抗也是固定;
充电电流由充电芯片决定,测量是的充电电流和实际配置的充电电流相同;
负载电流影响因素较多,不同页面,不同功能负载电流都不相同。所以一般取电量显示页面时的负载电流;
制作充电曲线大致步骤:
确定关机电压和满电电压
满电电压由电池确定,一般取4.2v,以实际情况为准;
关机电压要考虑芯片的承受的最低电压, 思澈平台一般取3.48v;确定充电电流;
为了确保充电速度以及开机时充电电流过大导致电压跌落过多,默认配置的300mA;电池放电完毕,确保电池电压不高于关机电压;
准备电压监测设备和电流监测设备;
插入充电后,开机后每30秒记录一次电压和电流,直到电池电压升到4.2v;
以电池电压3.48v作为0%,4.2v作为100%,横轴为时间,纵轴为电压绘制曲线;
横轴的时间分成100份,找到对应的电池电压,更新到充电曲线数组;
6. 扩展建议¶
算法参数调整:根据电池类型修改滤波阈值(
BAT_CHARGE_FILTER)、滑动窗口大小(如 8 次→16 次)或曲线表,适配不同场景;低功耗优化:双核平台可调整 LCPU 采集周期,单核平台通过动态调整 ADC 采样频率降低功耗;
故障诊断扩展:基于
battery_voltage_filter()的无效值统计,新增电池老化、接触不良等故障预警功能。


