QPNP 8909 8916 充电相关

最近一直在搞电源管理相关内容,之前是8610的bms,现在8916的bms,发现两者还是有点区别的,8916把对last_ocv_uv的估值算法分装成执行文件,作为服务一直运行。

电源管理方面,应该是Android驱动开发的一大难点,主要涉及的方面多,如充、放电、休眠唤醒等。这一部分主要讲BMS相关的一些基本概念。电池这一块刚开始入手时,感觉很难,很复杂,完全不知道如何入手,后来搞久了,才发现,是对电池一些基本的知识,在脑海中缺少相关的概念,所以才感觉难,这里主要是介绍一些相关的术语与知识。

1. 相关的名词

术语 

全称 

注释 

FCC 

Full-Charge Capacity 

满电荷电量 

UC 

Remaining Capacity 

RC 剩余电量 

CC 

Coulumb Counter 

电量计 

UUC 

Unusable Capacity 

不可用电量 

RUC 

Remaining Usable Capacity 

RUC=RC-CC-UUC,剩余可用电量 

OCV 

Open Circuit Voltage 

开路电压,电池在开路状态下的端电压称为开路电压 

SoC 

State of Charge 

电量百分比 

PC 

Percentage Charge 

剩余电荷占FCC百分比 


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的代码:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static int qpnp_vm_bms_probe(struct spmi_device *spmi)  
  2. {  
  3.     ...........  
  4.     ..........  
  5.     ...........  
  6.     ..........  
  7.   
  8.     //这里把电池的配置文件dtsi的读出来,并存到当前的结构体。  
  9.     rc = set_battery_data(chip);  
  10.     rc = config_battery_data(chip->batt_data);  
  11.     ..........  
  12.     ..........  
  13.     //这个是核心的工作,一个线程,BMS的主要内容在此  
  14.     INIT_DELAYED_WORK(&chip->monitor_soc_work, monitor_soc_work);  
  15.     ..........  
  16.     ..........  
  17.   
  18.     //电池一些常规的检测,主要从PMIC上读到的相关信息  
  19.     battery_insertion_check(chip);  
  20.     battery_status_check(chip);  
  21.   
  22.     /* character device to pass data to the userspace */  
  23.     rc = register_bms_char_device(chip);  
  24.     if (rc) {  
  25.         pr_err("Unable to regiter '/dev/vm_bms' rc=%d\n", rc);  
  26.         goto fail_bms_device;  
  27.     }  
  28.   
  29.     the_chip = chip;  
  30.     //这个也很重要,我们从上节知道,初值last_ocv_soc是非常重要的,决定着后面的soc估值算法  
  31.     calculate_initial_soc(chip);  
  32.   
  33.     //设置和注册电池的power supply  
  34.     /* setup & register the battery power supply */  
  35.     chip->bms_psy.name = "bms";  
  36.     chip->bms_psy.type = POWER_SUPPLY_TYPE_BMS;  
  37.     chip->bms_psy.properties = bms_power_props;  
  38.     chip->bms_psy.num_properties = ARRAY_SIZE(bms_power_props);  
  39.     chip->bms_psy.get_property = qpnp_vm_bms_power_get_property;  
  40.     chip->bms_psy.set_property = qpnp_vm_bms_power_set_property;  
  41.     chip->bms_psy.external_power_changed = qpnp_vm_bms_ext_power_changed;  
  42.     chip->bms_psy.property_is_writeable = qpnp_vm_bms_property_is_writeable;  
  43.     chip->bms_psy.supplied_to = qpnp_vm_bms_supplicants;  
  44.     chip->bms_psy.num_supplicants = ARRAY_SIZE(qpnp_vm_bms_supplicants);  
  45.   
  46.     rc = power_supply_register(chip->dev, &chip->bms_psy);  
  47.     if (rc < 0) {  
  48.         pr_err("power_supply_register bms failed rc = %d\n", rc);  
  49.         goto fail_psy;  
  50.     }  
  51.     .....................  
  52.     ....................  
  53.     ....................  
  54.     //这里启动工作线程  
  55.     schedule_delayed_work(&chip->monitor_soc_work, 0);  
  56.     ..........................  
  57.     .......................  
  58.   
  59. }  
2. 分析如何确定初始的last_ocv_uv:

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static int calculate_initial_soc(struct qpnp_bms_chip *chip)  
  2. {  
  3.     ........  
  4.     ........  
  5.     //读当前电池温度  
  6.     rc = get_batt_therm(chip, &batt_temp);  
  7.     ............  
  8.     //读PON OCV  
  9.     rc = read_and_update_ocv(chip, batt_temp, true);  
  10.     ..........  
  11.     //读关机保存的soc和last_soc_uv  
  12.       
  13.     rc = read_shutdown_ocv_soc(chip);  
  14.   
  15.     //这里判断是使用估计soc还是估值soc。如果chip->warm_reset 为真  
  16.     if (chip->warm_reset) {  
  17.         if (chip->shutdown_soc_invalid) { //这个是dtsi的一个配置选项,若没有配置,  
  18.                         //则不使用关机soc  
  19.             est_ocv = estimate_ocv(chip); //估值soc  
  20.             chip->last_ocv_uv = est_ocv;  
  21.         } else {  
  22.             chip->last_ocv_uv = chip->shutdown_ocv;//使用关机的soc和ocv  
  23.             pr_err("Hyan %d : set chip->last_ocv_uv = %d\n", __LINE__, chip->last_ocv_uv);  
  24.             chip->last_soc = chip->shutdown_soc;  
  25.             chip->calculated_soc = lookup_soc_ocv(chip,  
  26.                         chip->shutdown_ocv, batt_temp);  
  27.         }  
  28.     } else {  
  29.   
  30.         if (chip->workaround_flag & WRKARND_PON_OCV_COMP)  
  31.             adjust_pon_ocv(chip, batt_temp);  
  32.   
  33.          /* !warm_reset use PON OCV only if shutdown SOC is invalid */  
  34.         chip->calculated_soc = lookup_soc_ocv(chip,  
  35.                     chip->last_ocv_uv, batt_temp);  
  36.         if (!chip->shutdown_soc_invalid &&  
  37.             (abs(chip->shutdown_soc - chip->calculated_soc) <  
  38.                 chip->dt.cfg_shutdown_soc_valid_limit)) {  
  39.             chip->last_ocv_uv = chip->shutdown_ocv;   
  40.             chip->last_soc = chip->shutdown_soc;  
  41.             chip->calculated_soc = lookup_soc_ocv(chip,  
  42.                         chip->shutdown_ocv, batt_temp);//使用估值soc  
  43.               
  44.         } else {  
  45.             chip->shutdown_soc_invalid = true//使用关机soc  
  46.               
  47.         }  
  48.     }  
  49.     .............  
  50.     ............  
  51. }  
  52.   
  53.     //得到PON OCV  
  54.     rc = read_and_update_ocv(chip, batt_temp, true);  
  55.         ocv_uv = convert_vbatt_raw_to_uv(chip, ocv_data, is_pon_ocv);  
  56.                 uv = vadc_reading_to_uv(reading, true); //读ADC值  
  57.                 uv = adjust_vbatt_reading(chip, uv);   //转化为soc_uv  
  58.                 rc = qpnp_vbat_sns_comp_result(chip->vadc_dev, &uv, is_pon_ocv); //根据IC的类型,进行温度补偿  
  59.     //从寄存器中读到储存的soc和ocv  
  60.     read_shutdown_ocv_soc  
  61.         rc = qpnp_read_wrapper(chip, (u8 *)&stored_ocv,  
  62.                 chip->base + BMS_OCV_REG, 2);  
  63.         rc = qpnp_read_wrapper(chip, &stored_soc, chip->base + BMS_SOC_REG, 1);  
  64.   
  65.     adjust_pon_ocv(struct qpnp_bms_chip *chip, int batt_temp)  
  66.         rc = qpnp_vadc_read(chip->vadc_dev, DIE_TEMP, &result);   
  67.         pc = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,  
  68.                     batt_temp, chip->last_ocv_uv / 1000); //根据ocv和temp,查表得PC(soc)。  
  69.         rbatt_mohm = get_rbatt(chip, pc, batt_temp); //根据soc和temp,得电池内阻zhi  
  70.         /* convert die_temp to DECIDEGC */  
  71.         die_temp = (int)result.physical / 100;       
  72.         current_ma = interpolate_current_comp(die_temp);  //当前电流  
  73.         delta_uv = rbatt_mohm * current_ma;  
  74.         chip->last_ocv_uv += delta_uv;   //修正last_ocv_uv  
  75.   
  76.     //这个函数主要根据last_ocv_uv,计算出soc的  
  77.     lookup_soc_ocv(struct qpnp_bms_chip *chip, int ocv_uv, int batt_temp)  
  78.             //查表得到soc_ocv,soc_cutoff  
  79.             soc_ocv = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,  
  80.                     batt_temp, ocv_uv / 1000);  
  81.             soc_cutoff = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,  
  82.                 batt_temp, chip->dt.cfg_v_cutoff_uv / 1000);  
  83.   
  84.             soc_final = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_cutoff),  
  85.                             (100 - soc_cutoff));  
  86.   
  87.             if (batt_temp > chip->dt.cfg_low_temp_threshold)  
  88.                 iavg_ma = calculate_uuc_iavg(chip);  
  89.             else  
  90.                 iavg_ma = chip->current_now / 1000;  
  91.             //查表得到FCC,ACC  
  92.             fcc = interpolate_fcc(chip->batt_data->fcc_temp_lut,  
  93.                                 batt_temp);  
  94.             acc = interpolate_acc(chip->batt_data->ibat_acc_lut,  
  95.                             batt_temp, iavg_ma);  
  96.             //计算出UUC  
  97.             soc_uuc = ((fcc - acc) * 100) / fcc;  
  98.   
  99.             if (batt_temp > chip->dt.cfg_low_temp_threshold)  
  100.                 soc_uuc = adjust_uuc(chip, soc_uuc);  
  101.             //得到soc_acc  
  102.             soc_acc = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_uuc),  
  103.                             (100 - soc_uuc));  
  104.   
  105.             soc_final = soc_acc;   //这个为上报的soc  
  106.             chip->last_acc = acc;  
3. 看工作线程,monitor_soc_work(struct work_struct *work):

[cpp]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. static void monitor_soc_work(struct work_struct *work)  
  2.     calculate_delta_time(&chip->tm_sec, &chip->delta_time_s);  
  3.     rc = get_batt_therm(chip, &batt_temp);  
  4.     new_soc = lookup_soc_ocv(chip, chip->last_ocv_uv,batt_temp);  
  5.     new_soc = clamp_soc_based_on_voltage(chip, new_soc);  
  6.     report_vm_bms_soc(chip);//上报事件,上层得到消息,调用qpnp_vm_bms_power_get_property,获取相关的属性,计算出  
  7.                 last_ocv_uv,并通过qpnp_vm_bms_power_set_property方法,设置last_ocv_uv,并启动monitor_soc_work。  
4. 待续
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值