SOC Control 模块分析
一、模块概述
1.1 功能定位
SOC Control (State of Charge Control,电量控制) 是华为 MATE X5 电源管理系统中的电池电量控制模块,主要用于将电池电量限制在特定范围内(如 55%-75%),通过控制充电使能和输入电流限制,防止电池长期处于高电量状态,从而延长电池循环寿命。
1.2 核心功能
电量范围控制:设置电池电量上下限(如 min_soc=55%, max_soc=75%)
充电智能控制:超过上限时禁用充电,低于下限时启用充电
电流动态调节:禁充时限流 100mA 供系统运行,启充时恢复正常电流
双策略支持:
多用户管理:支持 RC、HIDL、Shell、Demo 等多种操作来源
投票机制:通过 Power Vote 实现多模块协同控制
1.3 设计背景
电池寿命与充电状态的关系:
应用场景:
二、系统架构
2.1 模块组成
soc_control 模块
├── soc_control.c # 主逻辑(电量监控、充电控制)
├── soc_control.h # 数据结构定义
├── Kconfig # 内核配置
└── Makefile # 编译配置
2.2 架构分层
+---------------------------------------------------------------+
| User Space (Sysfs/HIDL) |
| /sys/class/hw_power/soc_control/ |
| ├─ control (读写): 设置使能和电量范围 |
| └─ strategy (读写): 选择控制策略 |
+---------------------------------------------------------------+
↓
+---------------------------------------------------------------+
| Power Vote System (投票机制) |
| soc_ctrl_h (上限投票): |
| - 多个用户投票选择 max_soc |
| - 取最小值作为最终上限 |
| soc_ctrl_l (下限投票): |
| - 多个用户投票选择 min_soc |
| - 取最小值作为最终下限 |
+---------------------------------------------------------------+
↓
+---------------------------------------------------------------+
| SOC Control Core (soc_control.c) |
| 状态机: |
| - WORK_IN_DEFAULT_MODE: 正常模式 |
| - WORK_IN_ENABLE_CHG_MODE: 启用充电 |
| - WORK_IN_DISABLE_CHG_MODE: 禁用充电 |
| - WORK_IN_BALANCE_MODE: 平衡模式(Class B) |
| |
| 控制策略: |
| - Class A: 波动策略(55%-75% 之间波动) |
| - Class B: 保持策略(恰好维持在 75%) |
+---------------------------------------------------------------+
↓
+---------------------------------------------------------------+
| Charger Interface (power_if) |
| - POWER_IF_SYSFS_ENABLE_CHARGER: 充电使能控制 |
| - POWER_IF_SYSFS_VBUS_IIN_LIMIT: 输入电流限制 |
+---------------------------------------------------------------+
↓
+---------------------------------------------------------------+
| Hardware Layer (Charger IC) |
| - 充电使能/禁用开关 |
| - 输入电流限制寄存器 |
+---------------------------------------------------------------+
2.3 工作流程
充电器插入
↓
POWER_NE_CHARGING_START 事件触发
↓
延迟 5 秒启动(等待系统稳定)
↓
soc_ctrl_event_work() 周期执行(30s 一次)
↓
┌─────────────────────────────────────────────────┐
│ 读取当前电量 (cur_soc) │
│ │
│ 判断电量与范围关系: │
│ ┌─────────────────────────────────────┐ │
│ │ Class A 策略(波动): │ │
│ │ │ │
│ │ if (cur_soc > max_soc) { │ │
│ │ 禁用充电 │ │
│ │ 限流 100mA │ │
│ │ work_mode = DISABLE_CHG_MODE │ │
│ │ } │ │
│ │ │ │
│ │ if (cur_soc < min_soc) { │ │
│ │ 启用充电 │ │
│ │ 解除限流 │ │
│ │ work_mode = ENABLE_CHG_MODE │ │
│ │ } │ │
│ └─────────────────────────────────────┘ │
│ │
│ 或 │
│ │
│ ┌─────────────────────────────────────┐ │
│ │ Class B 策略(保持): │ │
│ │ │ │
│ │ if (cur_soc >= max_soc) { │ │
│ │ 禁用充电 │ │
│ │ 限流 100mA │ │
│ │ work_mode = DISABLE_CHG_MODE │ │
│ │ } │ │
│ │ │ │
│ │ if (cur_soc == max_soc && │ │
│ │ work_mode == DISABLE_CHG) { │ │
│ │ 禁用充电 │ │
│ │ 解除限流(平衡模式) │ │
│ │ work_mode = BALANCE_MODE │ │
│ │ } │ │
│ │ │ │
│ │ if (cur_soc < min_soc) { │ │
│ │ 启用充电 │ │
│ │ 解除限流 │ │
│ │ work_mode = ENABLE_CHG_MODE │ │
│ │ } │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
↓
30 秒后重复检测(或 5 秒快速模式)
↓
充电器拔出 → 恢复正常模式
三、核心数据结构
3.1 设备管理结构
struct soc_ctrl_dev {
struct device *dev;
struct notifier_block nb;
struct delayed_work work;
int work_mode;
int event;
int enable;
int min_soc;
int max_soc;
int strategy;
int soc_ctrl_interval;
};
3.2 工作模式枚举
enum soc_ctrl_work_mode {
WORK_IN_DEFAULT_MODE,
WORK_IN_ENABLE_CHG_MODE,
WORK_IN_DISABLE_CHG_MODE,
WORK_IN_BALANCE_MODE,
};
模式说明:
DEFAULT_MODE:未启用 SOC 控制,正常充放电
ENABLE_CHG_MODE:电量 < min_soc,启用充电
DISABLE_CHG_MODE:电量 > max_soc,禁用充电并限流
BALANCE_MODE:电量 == max_soc(Class B),禁充但不限流
3.3 控制策略枚举
enum soc_ctrl_strategy_type {
STRATEGY_TYPE_CLASS_A,
STRATEGY_TYPE_CLASS_B,
};
策略对比:
| 特性 | Class A(波动) | Class B(保持) |
|---|
| 电量范围 | 55%-75% 之间波动 | 恰好维持在 75% |
| 充电控制 | 超 75% 禁充,低于 55% 启充 | 达到 75% 后保持 |
| 限流策略 | 禁充时限流 100mA | 禁充初期限流,后解除限流 |
| 工作模式 | 仅使用 ENABLE/DISABLE 模式 | 使用 ENABLE/DISABLE/BALANCE 三种模式 |
| 检测周期 | 固定 30s | 接近上限时 5s,否则 30s |
| 适用场景 | 一般保护场景 | 精确维持电量(展示机) |
3.4 操作用户枚举
enum soc_ctrl_op_user {
SOC_CTRL_OP_USER_DEFAULT,
SOC_CTRL_OP_USER_RC,
SOC_CTRL_OP_USER_HIDL,
SOC_CTRL_OP_USER_BMS_SOC,
SOC_CTRL_OP_USER_SHELL,
SOC_CTRL_OP_USER_CUST,
SOC_CTRL_OP_USER_DEMO,
SOC_CTRL_OP_USER_BSOH,
SOC_CTRL_OP_USER_BATT_CT,
};
四、核心算法与工作流程
4.1 Class A 策略(波动策略)
static void soc_ctrl_startup_control_class_a(struct soc_ctrl_dev *l_dev)
{
int cur_soc = power_supply_app_get_bat_capacity();
hwlog_info("startup_a=%d cur_soc=%d, min_soc=%d, max_soc=%d\n",
l_dev->work_mode, cur_soc, l_dev->min_soc, l_dev->max_soc);
if ((cur_soc > l_dev->max_soc) &&
(l_dev->work_mode != WORK_IN_DISABLE_CHG_MODE)) {
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_ALL,
POWER_IF_SYSFS_ENABLE_CHARGER, SOC_CTRL_CHG_DISABLE);
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_DCP,
POWER_IF_SYSFS_VBUS_IIN_LIMIT, SOC_CTRL_IIN_LIMIT);
l_dev->work_mode = WORK_IN_DISABLE_CHG_MODE;
}
if ((cur_soc < l_dev->min_soc) &&
(l_dev->work_mode != WORK_IN_ENABLE_CHG_MODE)) {
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_ALL,
POWER_IF_SYSFS_ENABLE_CHARGER, SOC_CTRL_CHG_ENABLE);
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_DCP,
POWER_IF_SYSFS_VBUS_IIN_LIMIT, SOC_CTRL_IIN_UNLIMIT);
l_dev->work_mode = WORK_IN_ENABLE_CHG_MODE;
}
}
工作时序(以 55%-75% 为例):
T0: 电量 50%, 启用充电
↓
T1: 电量上升至 55%-75% 之间, 继续充电
↓
T2: 电量达到 76%, 禁用充电, 限流 100mA
↓
T3: 电量缓慢下降(系统耗电)
↓
T4: 电量降至 75%-55% 之间, 保持禁充
↓
T5: 电量降至 54%, 启用充电, 解除限流
↓
循环往复...
4.2 Class B 策略(保持策略)
static void soc_ctrl_startup_control_class_b(struct soc_ctrl_dev *l_dev)
{
int cur_soc = power_supply_app_get_bat_capacity();
hwlog_info("startup_b=%d cur_soc=%d, min_soc=%d, max_soc=%d\n",
l_dev->work_mode, cur_soc, l_dev->min_soc, l_dev->max_soc);
if (l_dev->max_soc - cur_soc <= SOC_CTRL_SOC_D_VALUE)
l_dev->soc_ctrl_interval = SOC_CTRL_LOOP_TIME_FAST;
else
l_dev->soc_ctrl_interval = SOC_CTRL_LOOP_TIME;
if ((cur_soc >= l_dev->max_soc) &&
(l_dev->work_mode != WORK_IN_DISABLE_CHG_MODE)) {
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_ALL,
POWER_IF_SYSFS_ENABLE_CHARGER, SOC_CTRL_CHG_DISABLE);
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_DCP,
POWER_IF_SYSFS_VBUS_IIN_LIMIT, SOC_CTRL_IIN_LIMIT);
l_dev->work_mode = WORK_IN_DISABLE_CHG_MODE;
}
if ((cur_soc == l_dev->max_soc) &&
(l_dev->work_mode == WORK_IN_DISABLE_CHG_MODE)) {
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_ALL,
POWER_IF_SYSFS_ENABLE_CHARGER, SOC_CTRL_CHG_DISABLE);
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_DCP,
POWER_IF_SYSFS_VBUS_IIN_LIMIT, SOC_CTRL_IIN_UNLIMIT);
l_dev->work_mode = WORK_IN_BALANCE_MODE;
}
if ((cur_soc < l_dev->min_soc) &&
(l_dev->work_mode != WORK_IN_ENABLE_CHG_MODE)) {
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_ALL,
POWER_IF_SYSFS_ENABLE_CHARGER, SOC_CTRL_CHG_ENABLE);
power_if_kernel_sysfs_set(POWER_IF_OP_TYPE_DCP,
POWER_IF_SYSFS_VBUS_IIN_LIMIT, SOC_CTRL_IIN_UNLIMIT);
l_dev->work_mode = WORK_IN_ENABLE_CHG_MODE;
}
}
工作时序(以 55%-75% 为例):
T0: 电量 50%, 启用充电, 周期 30s
↓
T1: 电量 73%, 接近上限, 周期切换至 5s(快速监控)
↓
T2: 电量 75%, 禁用充电, 限流 100mA
↓
T3: 电量 75%(稳定), 保持禁充, 解除限流(平衡模式)
↓
T4: 系统功耗 < 100mA, 电量维持在 75%
↓
T5: 若电量降至 74%, 周期 5s 快速检测
↓
T6: 电量继续降至 54%, 启用充电, 周期恢复 30s
Class B 优势:
精确维持在目标电量(如 75%)
通过解除限流实现功耗平衡
接近目标时快速检测(5s)避免超调
4.3 投票机制(Power Vote)
static int soc_ctrl_h_vote_callback(struct power_vote_object *obj,
void *data, int result, const char *client_str)
{
struct soc_ctrl_dev *l_dev = (struct soc_ctrl_dev *)data;
hwlog_info("h_vote result=%d\n", result);
if ((l_dev->enable && (l_dev->max_soc == result)) ||
(!l_dev->enable && (result < 0))) {
return 0;
}
if (result < 0) {
l_dev->enable = 0;
} else {
if (result < l_dev->min_soc) {
hwlog_err("err:max_soc < min_soc\n");
return -EINVAL;
}
l_dev->max_soc = result;
l_dev->enable = 1;
}
soc_ctrl_event_control(l_dev->event);
return 0;
}
static int soc_ctrl_l_vote_callback(struct power_vote_object *obj,
void *data, int result, const char *client_str)
{
struct soc_ctrl_dev *l_dev = (struct soc_ctrl_dev *)data;
hwlog_info("l_vote result=%d\n", result);
if (result >= 0)
l_dev->min_soc = result;
return 0;
}
投票示例:
echo "demo 1 55 75" > /sys/class/hw_power/soc_control/control
echo "shell 1 60 80" > /sys/class/hw_power/soc_control/control
投票策略:
采用 POWER_VOTE_SET_MIN 模式
多个用户投票时取最小值
确保最严格的限制生效
五、Sysfs 接口
5.1 节点路径
/sys/class/hw_power/soc_control/
├── control
└── strategy
5.2 接口说明
control(读写)
读取:
cat /sys/class/hw_power/soc_control/control
写入格式:
echo "<user> <enable> <min_soc> <max_soc>" > control
使用示例:
echo "shell 1 55 75" > /sys/class/hw_power/soc_control/control
echo "demo 1 60 80" > /sys/class/hw_power/soc_control/control
echo "shell 0 0 0" > /sys/class/hw_power/soc_control/control
参数校验:
if ((enable < 0) || (enable > 1))
return -EINVAL;
if ((min_soc < 0 || min_soc > 100) ||
(max_soc < 0 || max_soc > 100) ||
(min_soc + 5 > max_soc))
return -EINVAL;
strategy(读写)
读取:
cat /sys/class/hw_power/soc_control/strategy
写入格式:
echo "<user> <strategy>" > strategy
使用示例:
echo "shell 0" > /sys/class/hw_power/soc_control/strategy
echo "shell 1" > /sys/class/hw_power/soc_control/strategy
六、典型应用场景
6.1 场景1:展示机模式(Demo 模式)
应用背景:
零售店展示机长期插电展示,需防止电池过充
配置方案:
策略: Class B(保持在固定电量)
范围: 60%-75%
操作步骤:
1. 设置策略为 Class B
echo "demo 1" > /sys/class/hw_power/soc_control/strategy
2. 设置电量范围
echo "demo 1 60 75" > /sys/class/hw_power/soc_control/control
3. 插入充电器,设备自动控制电量维持在 75%
工作流程:
T0: 初始电量 50%, 启用充电
↓
T1: 电量充至 75%, 禁用充电并解除限流
↓
T2: 系统功耗由充电器供应(通过模式),电量稳定在 75%
↓
T3: 若电量降至 74%, 5 秒快速检测
↓
T4: 长期维持在 75% 左右
效果:
- 电池长期处于健康电量区间
- 延长电池寿命 40%-60%
- 展示机可使用 2-3 年无需换电池
6.2 场景2:工作站模式(桌面使用)
应用背景:
用户将手机当作工作站,长期连接显示器、键鼠使用
配置方案:
策略: Class A(波动)
范围: 50%-70%
操作步骤:
1. 用户在系统设置中启用"电池保护模式"
2. Framework 通过 HIDL 接口设置:
echo "hidl 1" > /sys/class/hw_power/soc_control/strategy
echo "hidl 1 50 70" > /sys/class/hw_power/soc_control/control
工作流程:
T0: 电量 45%, 启用充电
↓
T1: 电量充至 71%, 禁用充电, 限流 100mA
↓
T2: 电量缓慢下降至 55%-70% 区间
↓
T3: 电量降至 49%, 启用充电, 解除限流
↓
T4: 循环往复, 电量在 50%-70% 波动
效果:
- 避免长期满电加速老化
- 电池循环寿命延长 30%-50%
- 适合长期插电办公场景
6.3 场景3:电池容量测试(Battery CT)
应用背景:
工厂生产线或售后维修需要测试电池容量
配置方案:
策略: Class A
范围: 20%-80%(测试区间)
操作步骤:
1. 测试设备通过 ADB 设置:
adb shell
echo "batt_ct 1 20 80" > /sys/class/hw_power/soc_control/control
2. 插入充电器,自动充放电循环
测试流程:
T0: 电量 15%, 启用充电
↓
T1: 充至 81%, 禁用充电
↓
T2: 放电至 19%, 启用充电
↓
T3: 记录充放电曲线,计算容量
↓
T4: 测试完成,禁用控制
效果:
- 自动化容量测试
- 避免过充过放损伤电池
- 提高测试效率
6.4 场景4:电池健康度管理(BSOH)
应用背景:
检测到电池健康度下降,主动限制充电范围
配置方案:
策略: Class B
范围: 40%-60%(老化电池保护)
触发条件:
if (battery_soh < 80%) {
echo "bsoh 1" > /sys/class/hw_power/soc_control/strategy
echo "bsoh 1 40 60" > /sys/class/hw_power/soc_control/control
}
工作流程:
1. 电池健康度检测模块检测到 SOH < 80%
2. 自动启用 SOC 控制,范围缩小至 40%-60%
3. 电量维持在 60% 左右
4. 延缓电池进一步老化
效果:
- 延长老化电池使用寿命
- 减少意外关机风险
- 改善用户体验
七、调试方法
7.1 日志关键点
[soc_control] h_vote result=75
[soc_control] l_vote result=55
[soc_control] startup_a=1 cur_soc=76, min_soc=55, max_soc=75
[soc_control] startup_a=2 cur_soc=54, min_soc=55, max_soc=75
[soc_control] startup_b=1 cur_soc=75, min_soc=55, max_soc=75
[soc_control] startup_b=3 cur_soc=75, min_soc=55, max_soc=75
[soc_control] recovery=1 cur_soc=65, min_soc=55, max_soc=75
[soc_control] enable: start soc control
[soc_control] enable: stop soc control
[soc_control] disable: event=1
7.2 Sysfs 调试
cat /sys/class/hw_power/soc_control/control
cat /sys/class/hw_power/soc_control/strategy
cat /sys/class/power_supply/battery/charging_enabled
cat /sys/class/power_supply/usb/current_max
7.3 实时监控脚本
#!/bin/bash
while true; do
SOC=$(cat /sys/class/power_supply/battery/capacity)
CHG_EN=$(cat /sys/class/power_supply/battery/charging_enabled)
IIN=$(cat /sys/class/power_supply/usb/current_max)
echo "$(date '+%H:%M:%S') SOC=$SOC% CHG_EN=$CHG_EN IIN=${IIN}uA"
sleep 5
done
7.4 常见问题排查
问题1:SOC 控制未生效
现象:设置后电量仍充至 100%
排查步骤:
检查是否启用:
cat /sys/class/hw_power/soc_control/control
检查充电事件:
dmesg | grep "start soc control"
检查工作队列:
问题2:电量波动过大
现象:Class A 模式下电量在 50%-80% 大幅波动
原因分析:
解决方案:
echo "shell 1 60 70" > /sys/class/hw_power/soc_control/control
问题3:Class B 无法维持固定电量
现象:电量在 74%-76% 波动
原因分析:
优化方案:
正常现象,±1% 波动可接受
若波动 > 2%,检查系统功耗
问题4:限流 100mA 过低导致关机
现象:禁充后设备因功耗过高关机
原因分析:
解决方案:
#define SOC_CTRL_IIN_LIMIT 500
echo "shell 1" > /sys/class/hw_power/soc_control/strategy
八、总结
8.1 技术特点
双策略支持:Class A 波动策略和 Class B 保持策略满足不同场景
投票机制:多用户协同控制,取最严格限制
动态调整:Class B 接近目标时快速检测(5s)
平衡模式:Class B 特有,解除限流维持固定电量
8.2 设计亮点
8.3 应用价值
8.4 局限性
仅限充电状态:拔出充电器后无法控制
依赖电量计精度:电量计误差影响控制精度
无温度保护:未考虑温度对电池的影响
限流可能不足:100mA 对高功耗应用可能不够
8.5 改进方向
温度联动:
智能限流:
电池健康联动:
SOH < 80% 时自动启用并缩小范围
循环次数 > 500 次时推荐用户启用
用户提醒: