|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Remaining Usable Capacity
|
|
|
|
|
|
|
|
|
|
|
2. 相关的代码
kernel\drivers\power\
Qpnp-vm-bms.c//电池BMS算法
kernel\drivers\power\
Qpnp-linear-charger.c//电池充电相关的代码
batterydata-XX_3000
mah.dtsi//电池相关的参数
Msm-pm8X
16.dtsi//针对PMU芯片,进行的相关设置
其他一些的代码就不一一列出来,其实高通的代码还是比较好的,基本大部分的文件都会.txt的介绍文档,我个人建议在不管调什么模块,
看到有与之对应的.txt说明文档,都应该去看一下,起码对一些基本的概念有个了解。
3. 相关的原理
3.1
BMS 主要任务
1. 防止过充
2. 避免深放
3. 温度控制
4. 电池组件电压和温度的均衡
5. 预测电池的SOC
6. 电池诊断
7. 总电压及单体电压测量
8. 总电流及单体电流测量
9. 报警1
0. 通信SOC
方法:能量积分 + 误差矫正 (大众化技术,准确性高。)
传统的电池电量测试方法有:密度法,开路电压法,内阻法和安时法等。
新型算法有:自适应神经模糊推断模型、模糊逻辑算法模型、线性模型法、阻抗光谱法和卡尔曼滤波估计模型算法 。
3.2开路电压法
通过实验方法描述在不同放电电流情况下的电池的端电压与电池的剩余能量的关系曲线,并存储特征关系曲线。
实时采样电池放电时的端电压,查表求出电池的剩余能量,同时考虑电池的使用寿命以及内阻对电池SOC的影响,对求得的电池剩余能量
进行校正。 优点:简单易行。 缺点:但是需要电池长时间静置,不能满足在线检测的要求。soc>40%,阻抗变化很小。
3.3 8916的BMS的相关思路:
3.3.1
PMU通过PMIC总线读取电池的AD值,通过一个数据运算转化为电压值。
3.3.2
在不同的电池参数文件中,都有相对应的表,
pc-temp-ocv-lut,为温度、SOC对应得电压表,PMU8916获取的电压值,通过查该表,在温度和电压下,可得到当前的SOC。
rbatt-sf-lut,为温度、soc对应的电池内阻表,这里主要考虑内阻的影响,对OCV的修正,new_ocv=ocv+rbatt(内阻)*current(当前电流)。
fcc-temp-lut,为温度对应的fcc表,ibat-acc-luit,为温度、电流对应的acc表,这两个是起到修正SOC的作用,相关计算为:
soc_uuc = ((fcc - acc) * 100) / fcc,fcc、acc均为查表所得,
soc_acc = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_uuc),(100 - soc_uuc)); //最终
soc_acc,为上报的SOC.
3.3.3
PMU8916的bms算法和PMU8612的有区别,其中对last_ocv_uv的估值计算的源码已经不开放,在monitor_soc_work的工作线程,
会上报事件uevent,当HAL层,收到消息,然后调用getprop的方法,获取相关的参数,如,电阻、电流、fcc、acc等,来估算出
last_ocv_uv,然后调用setprop,把该值设下去,并启动工作线程,根据last_ocv_uv,查表得到soc,并经过修正SOC,并再次上
报事件,循环下去。这个估值算法,我猜可能是一套学习算法,具体的没有源码,不清楚,只知道它把算法变为.bin文件,用了
binder机制,作为服务一直运行。
上一篇主要讲电池相关的一些知识,上节忘记讲了,电池一般分为电量计电池和非电流计电池,电量计电池,就不需要用pmu8916的IC,当然这只是只,不需要BMS来计算soc,而jni层也需要读取电流计的电池相关属性。
这一节主要是根据代码进行相关的分析。
1. 先看probe的代码:
- static int qpnp_vm_bms_probe(struct spmi_device *spmi)
- {
- ...........
- ..........
- ...........
- ..........
-
-
- rc = set_battery_data(chip);
- rc = config_battery_data(chip->batt_data);
- ..........
- ..........
-
- INIT_DELAYED_WORK(&chip->monitor_soc_work, monitor_soc_work);
- ..........
- ..........
-
-
- battery_insertion_check(chip);
- battery_status_check(chip);
-
-
- rc = register_bms_char_device(chip);
- if (rc) {
- pr_err("Unable to regiter '/dev/vm_bms' rc=%d\n", rc);
- goto fail_bms_device;
- }
-
- the_chip = chip;
-
- calculate_initial_soc(chip);
-
-
-
- chip->bms_psy.name = "bms";
- chip->bms_psy.type = POWER_SUPPLY_TYPE_BMS;
- chip->bms_psy.properties = bms_power_props;
- chip->bms_psy.num_properties = ARRAY_SIZE(bms_power_props);
- chip->bms_psy.get_property = qpnp_vm_bms_power_get_property;
- chip->bms_psy.set_property = qpnp_vm_bms_power_set_property;
- chip->bms_psy.external_power_changed = qpnp_vm_bms_ext_power_changed;
- chip->bms_psy.property_is_writeable = qpnp_vm_bms_property_is_writeable;
- chip->bms_psy.supplied_to = qpnp_vm_bms_supplicants;
- chip->bms_psy.num_supplicants = ARRAY_SIZE(qpnp_vm_bms_supplicants);
-
- rc = power_supply_register(chip->dev, &chip->bms_psy);
- if (rc < 0) {
- pr_err("power_supply_register bms failed rc = %d\n", rc);
- goto fail_psy;
- }
- .....................
- ....................
- ....................
-
- schedule_delayed_work(&chip->monitor_soc_work, 0);
- ..........................
- .......................
-
- }
2. 分析如何确定初始的last_ocv_uv:
- static int calculate_initial_soc(struct qpnp_bms_chip *chip)
- {
- ........
- ........
-
- rc = get_batt_therm(chip, &batt_temp);
- ............
-
- rc = read_and_update_ocv(chip, batt_temp, true);
- ..........
-
-
- rc = read_shutdown_ocv_soc(chip);
-
-
- if (chip->warm_reset) {
- if (chip->shutdown_soc_invalid) {
-
- est_ocv = estimate_ocv(chip);
- chip->last_ocv_uv = est_ocv;
- } else {
- chip->last_ocv_uv = chip->shutdown_ocv;
- pr_err("Hyan %d : set chip->last_ocv_uv = %d\n", __LINE__, chip->last_ocv_uv);
- chip->last_soc = chip->shutdown_soc;
- chip->calculated_soc = lookup_soc_ocv(chip,
- chip->shutdown_ocv, batt_temp);
- }
- } else {
-
- if (chip->workaround_flag & WRKARND_PON_OCV_COMP)
- adjust_pon_ocv(chip, batt_temp);
-
-
- chip->calculated_soc = lookup_soc_ocv(chip,
- chip->last_ocv_uv, batt_temp);
- if (!chip->shutdown_soc_invalid &&
- (abs(chip->shutdown_soc - chip->calculated_soc) <
- chip->dt.cfg_shutdown_soc_valid_limit)) {
- chip->last_ocv_uv = chip->shutdown_ocv;
- chip->last_soc = chip->shutdown_soc;
- chip->calculated_soc = lookup_soc_ocv(chip,
- chip->shutdown_ocv, batt_temp);
-
- } else {
- chip->shutdown_soc_invalid = true;
-
- }
- }
- .............
- ............
- }
-
-
- rc = read_and_update_ocv(chip, batt_temp, true);
- ocv_uv = convert_vbatt_raw_to_uv(chip, ocv_data, is_pon_ocv);
- uv = vadc_reading_to_uv(reading, true);
- uv = adjust_vbatt_reading(chip, uv);
- rc = qpnp_vbat_sns_comp_result(chip->vadc_dev, &uv, is_pon_ocv);
-
- read_shutdown_ocv_soc
- rc = qpnp_read_wrapper(chip, (u8 *)&stored_ocv,
- chip->base + BMS_OCV_REG, 2);
- rc = qpnp_read_wrapper(chip, &stored_soc, chip->base + BMS_SOC_REG, 1);
-
- adjust_pon_ocv(struct qpnp_bms_chip *chip, int batt_temp)
- rc = qpnp_vadc_read(chip->vadc_dev, DIE_TEMP, &result);
- pc = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
- batt_temp, chip->last_ocv_uv / 1000);
- rbatt_mohm = get_rbatt(chip, pc, batt_temp);
-
- die_temp = (int)result.physical / 100;
- current_ma = interpolate_current_comp(die_temp);
- delta_uv = rbatt_mohm * current_ma;
- chip->last_ocv_uv += delta_uv;
-
-
- lookup_soc_ocv(struct qpnp_bms_chip *chip, int ocv_uv, int batt_temp)
-
- soc_ocv = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
- batt_temp, ocv_uv / 1000);
- soc_cutoff = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
- batt_temp, chip->dt.cfg_v_cutoff_uv / 1000);
-
- soc_final = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_cutoff),
- (100 - soc_cutoff));
-
- if (batt_temp > chip->dt.cfg_low_temp_threshold)
- iavg_ma = calculate_uuc_iavg(chip);
- else
- iavg_ma = chip->current_now / 1000;
-
- fcc = interpolate_fcc(chip->batt_data->fcc_temp_lut,
- batt_temp);
- acc = interpolate_acc(chip->batt_data->ibat_acc_lut,
- batt_temp, iavg_ma);
-
- soc_uuc = ((fcc - acc) * 100) / fcc;
-
- if (batt_temp > chip->dt.cfg_low_temp_threshold)
- soc_uuc = adjust_uuc(chip, soc_uuc);
-
- soc_acc = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_uuc),
- (100 - soc_uuc));
-
- soc_final = soc_acc;
- chip->last_acc = acc;
3. 看工作线程,monitor_soc_work(struct work_struct *work):
- static void monitor_soc_work(struct work_struct *work)
- calculate_delta_time(&chip->tm_sec, &chip->delta_time_s);
- rc = get_batt_therm(chip, &batt_temp);
- new_soc = lookup_soc_ocv(chip, chip->last_ocv_uv,batt_temp);
- new_soc = clamp_soc_based_on_voltage(chip, new_soc);
- report_vm_bms_soc(chip);
- last_ocv_uv,并通过qpnp_vm_bms_power_set_property方法,设置last_ocv_uv,并启动monitor_soc_work。
4. 待续