高通BMS的研究 高通电量计

高通8064 8974 8926等pm芯片都集成了电量计,估计后续芯片都会一直存在,现在许多项目UI状态栏电池都有百分比显示,所以需要深入分析BMS有助于解决电量方面的BUG。


一: SOC(荷电状态)计算方法

名词:

FCC  Full-charge capacity      

UC     Remaining capacity
CC     Coulumb counter    
UUC  Unusable capacity
RUC   Remaining usable capacity //    RUC=RC-CC-UUC
SoC   State of charge    
OCV    Open circuit voltage

 SOC=(RC-CC-UUC)/(FCC-UUC)


以下是各个变量的计算方法:
1:FCC:
   在校准的电池profile中有定义,会随温度有变化;
  1. static struct single_row_lut fcc_temp = {  
  2.  .x  = {-20, 0, 25, 40, 60},  
  3.  .y  = {3193, 3190, 3190, 3180, 3183},  
  4.  .cols = 5  
  5. };  

2:RC: 开机通过开始获取的开路电压(ocv)来查表(电池校准的profile文件)计算百分比,来比对计算(电压与电荷量正比);(ocv=vbatt+rbatt*i_ma)
内核计算方法:
  1. static int calculate_remaining_charge_uah(struct pm8921_bms_chip *chip,  
  2.       struct pm8921_soc_params *raw,  
  3.       int fcc_uah, int batt_temp,  
  4.       int chargecycles)  
  5. {  
  6.  int  ocv, pc, batt_temp_decidegc;  
  7.   
  8.   
  9.  ocv = raw->last_good_ocv_uv;  
  10.  batt_temp_decidegc = chip->last_ocv_temp_decidegc;  
  11.  pc = calculate_pc(chip, ocv, batt_temp_decidegc, chargecycles);  
  12.  pr_info("ocv = %d pc = %d\n", ocv, pc);  
  13.  return (fcc_uah * pc) / 100;  
  14. }  

但是通常情况下开机使用计算RC的ocv是上次关机存下的百分比,反向查表算出的ocv;
现在我们做法是通过判断开机时的ocv与关机的ocv如果偏差太大,我们将采用开机ocv来计算RC,所以开机的ocv对开机的百分比影响非常大;


3:CC:pmic库伦计 ADC采样到的:
内核获取方法:
  1. /**  
  2. * calculate_cc_uah -  
  3. * @chip:  the bms chip pointer  
  4. * @cc:   the cc reading from bms h/w  
  5. * @val:  return value  
  6. * @coulumb_counter: adjusted coulumb counter for 100%  
  7. *  
  8. * RETURNS: in val pointer coulumb counter based charger in uAh  
  9. *        (micro Amp hour)  
  10. */  
  11. static void calculate_cc_uah(struct pm8921_bms_chip *chip, int cc, int *val)  
  12. {  
  13.  int64_t cc_voltage_uv, cc_pvh, cc_uah;  
  14.   
  15.   
  16.  cc_voltage_uv = cc;  
  17.  pr_debug("cc = %d\n", cc);  
  18.  cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);  
  19.  cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);  
  20.  pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);  
  21.  cc_pvh = ccmicrovolt_to_pvh(cc_voltage_uv);  
  22.  pr_debug("cc_pvh = %lld pico_volt_hour\n", cc_pvh);  
  23.  cc_uah = div_s64(cc_pvh, chip->r_sense_uohm);  
  24.  *val = cc_uah;  
  25. }  

4:UUC:计算方法和UC一致,但是rbatt是动态变化的,会复杂点;
  1. static int calculate_termination_uuc(struct pm8921_bms_chip *chip,  
  2.      int batt_temp, int chargecycles,  
  3.     int fcc_uah, int i_ma,  
  4.     int *ret_pc_unusable)  
  5. {  
  6.  int unusable_uv, pc_unusable, uuc;  
  7.  int i = 0;  
  8.  int ocv_mv;  
  9.  int batt_temp_degc = batt_temp / 10;  
  10.  int rbatt_mohm;  
  11.  int delta_uv;  
  12.  int prev_delta_uv = 0;  
  13.  int prev_rbatt_mohm = 0;  
  14.  int prev_ocv_mv = 0;  
  15.  int uuc_rbatt_uv;  
  16.   
  17.   
  18.  for (i = 0; i <= 100; i++) {  
  19.   ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut,  
  20.     batt_temp_degc, i);  
  21.   rbatt_mohm = get_rbatt(chip, i, batt_temp);  
  22.   unusable_uv = (rbatt_mohm * i_ma) + (chip->v_cutoff * 1000);  
  23.   delta_uv = ocv_mv * 1000 - unusable_uv;  
  24.   
  25.   
  26.   pr_debug("soc = %d ocv = %d rbat = %d u_uv = %d delta_v = %d\n",  
  27.     i, ocv_mv, rbatt_mohm, unusable_uv, delta_uv);  
  28.   
  29.   
  30.   if (delta_uv > 0)  
  31.    break;  
  32.   
  33.   
  34.   prev_delta_uv = delta_uv;  
  35.   prev_rbatt_mohm = rbatt_mohm;  
  36.   prev_ocv_mv = ocv_mv;  
  37.  }  
  38.   
  39.   
  40.  uuc_rbatt_uv = linear_interpolate(rbatt_mohm, delta_uv,  
  41.      prev_rbatt_mohm, prev_delta_uv,  
  42.      0);  
  43.   
  44.   
  45.  unusable_uv = (uuc_rbatt_uv * i_ma) + (chip->v_cutoff * 1000);  
  46.   
  47.   
  48.  pc_unusable = calculate_pc(chip, unusable_uv, batt_temp, chargecycles);  
  49.  uuc = (fcc_uah * pc_unusable) / 100;  
  50.  pr_debug("For i_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d uuc = %d\n",  
  51.      i_ma, uuc_rbatt_uv, unusable_uv,  
  52.      pc_unusable, uuc);  
  53.  *ret_pc_unusable = pc_unusable;  
  54.  return uuc;  
  55. }  


 高通的这套BMS算法运行起来由于ocv的校准和温度等等原因,会有一定的偏差,高通还有一套通过校准OCV来估算SOC(简称soc_est)的机制,下面就是使用这套来校准SOC;


二:校准SOC
 
 高通算法通过对soc与soc_est比较计算出ocv的差值,来改变last_ocv_uv的值,主要是改变RC,重新计算soc,将会使得soc与soc_est越来越接近,越来越准;

 ocv在以下2种情况会被改变:

1:系统睡眠唤醒期间,cov被更新,库仑计RST;

                2:低电进入adjust_soc()方法调节;


    在高通8064平台由于电量计对大电流计算不准确,一直亮屏的情况(没有经历睡眠唤醒的ocv更新与CC RST)会导致关机电压到达3.74V。要想解决这个问题必须使得校准SOC可以正常工作。但是当满电时开机就会记录ocv的值偏高,导致快要低电时不能很好的校准soc。所以有必要在马上进入低电(15%)时做一次模拟开机一次(电量计RERST CC=0从soc找出ocv )使得last_ocv_uv降下来,才可以完美发挥adjust_soc的作用,使得关机电压能一直能到3.4V左右。
 
  1. <6>[ 7796.038269] read_soc_params_raw: 333333333 last_good_ocv_uv3777000uV  
  2.   
  3.   
  4. <6>[ 7796.038360] read_soc_params_raw: last_good_ocv_raw0x943flast_good_ocv_uv3777000uV  
  5.   
  6.   
  7. <6>[ 7796.038543] calculate_soc_params: FCC = 3190000uAh batt_temp = 300cycles = 0  
  8.   
  9.   
  10. <6>[ 7796.038635] calculate_remaining_charge_uah: ocv = 3777000 pc = 35  
  11.   
  12.   
  13. <6>[ 7796.038665] calculate_soc_params: RC = 1116500uAh  
  14.   
  15.   
  16. <6>[ 7796.038726] calculate_soc_params: cc_uah = 394979uAh raw->cc = 5764312  
  17.   
  18.   
  19. <6>[ 7796.038818] calculate_state_of_charge: RUC(RC-CC-UUC) = 657721uAh RC = 1116500uAh CC394979uAh UUC63800uAh FCC3190000uAh SOC(RUC/FCC-UUC) =21  

adjust_soc方法:

  1. </pre><p class="pa-1" style="line-height: 18px; font-size: 14px; padding-top: 0px; padding-bottom: 0px; margin-top: 0px; margin-bottom: 10px; color: rgb(68, 68, 68); font-family: 宋体;"><pre name="code" class="html"> static int last_soc_est = -EINVAL;  
  2.   
  3.   
  4.   
  5. static int adjust_soc(struct pm8921_bms_chip *chip, int soc,  
  6.   
  7.  
  8.   int batt_temp, int chargecycles,  
  9.   int rbatt, int fcc_uah, int uuc_uah, int cc_uah)  
  10. {  
  11.  
  12.  int ibat_ua = 0vbat_uv = 0;  
  13.  
  14.  int ocv_est_uv = 0soc_est = 0pc_est = 0pc = 0;  
  15.  
  16.  int delta_ocv_uv = 0;  
  17.  int n = 0;  
  18.  int rc_new_uah = 0;  
  19.  int pc_new = 0;  
  20.  
  21.  int soc_new = 0;  
  22.   
  23.  int m = 0;  
  24.  
  25.  int rc = 0;  
  26.  int delta_ocv_uv_limit = 0;  
  27.  
  28.  int correction_limit_uv = 0;  
  29.  
  30.  rc = pm8921_bms_get_simultaneous_battery_voltage_and_current(  
  31.      &ibat_ua,  
  32.     &vbat_uv);  
  33.   
  34. if (rc < 0) {  
  35.    pr_err("simultaneous vbat ibat failed err = %d\n", rc);  
  36.  
  37.   goto out;  
  38.  }  
  39.    
  40.  very_low_voltage_check(chip, ibat_ua, vbat_uv);  
  41.    
  42.  if (chip->low_voltage_detect &&  wake_lock_active(&chip->low_voltage_wake_lock)) {  
  43.   if (is_voltage_below_cutoff_window(chip, ibat_ua, vbat_uv)) {  
  44.    soc = 0;  
  45.    pr_info("Voltage below cutoff, setting soc to 0\n");  
  46.    goto out;  
  47.   }  
  48.  }  
  49.   
  50.  delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);  
  51.  ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000;  
  52.  calc_current_max(chip, ocv_est_uv, rbatt);  
  53.  pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles);  
  54.  soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100,  
  55.       (s64)fcc_uah - uuc_uah);  
  56.  soc_est = bound_soc(soc_est);  
  57.  
  58.  /* never adjust during bms reset mode */  
  59.  
  60.  if (bms_reset) {  
  61.  
  62.   pr_debug("bms reset mode, SOC adjustment skipped\n");  
  63.   goto out;  
  64.  }  
  65.  
  66.  if (ibat_ua < 0 && pm8921_is_batfet_closed()) {  
  67.  
  68.   soc = charging_adjustments(chip, soc, vbat_uv, ibat_ua,  
  69.  
  70.     batt_temp, chargecycles,  
  71.   
  72.     fcc_uah, cc_uah, uuc_uah);  
  73.   
  74.   goto out;  
  75.  }  
  76.   
  77.  /*  
  78.   * do not adjust  
  79.   * if soc_est is same as what bms calculated  
  80.   * OR if soc_est > 15  
  81.   * OR if soc it is above 90 because we might pull it low  
  82.   * and  cause a bad user experience  
  83.   */  
  84.   
  85.  if (soc_est == soc  
  86.   || soc_est > 15  
  87.   || soc >= 90)  
  88.   goto out;  
  89.   
  90.  if (last_soc_est == -EINVAL)  
  91.   last_soc_est = soc;  
  92.   
  93.  n = min(200, max(1 , soc + soc_est + last_soc_est));  
  94.  /* remember the last soc_est in last_soc_est */  
  95.  last_soc_est = soc_est;  
  96.   
  97.  pc = calculate_pc(chip, chip->last_ocv_uv,  
  98.   
  99.    chip->last_ocv_temp_decidegc, last_chargecycles);  
  100.   
  101.  if (pc > 0) {  
  102.   
  103.   pc_new = calculate_pc(chip, chip->last_ocv_uv - (++m * 1000),  
  104.      chip->last_ocv_temp_decidegc,  
  105.      last_chargecycles);  
  106.   
  107.   while (pc_new == pc) {  
  108.    /* start taking 10mV steps */  
  109.    m = m + 10;  
  110.    pc_new = calculate_pc(chip,  
  111.       chip->last_ocv_uv - (m * 1000),  
  112.       chip->last_ocv_temp_decidegc,  
  113.   
  114.       last_chargecycles);  
  115.   }  
  116.  } else {  
  117.   
  118.   /*  
  119.    * pc is already at the lowest point,  
  120.    * assume 1 millivolt translates to 1% pc  
  121.    */  
  122.   
  123.   pc = 1;  
  124.   
  125.   pc_new = 0;  
  126.      
  127.   m = 1;   
  128.  }  
  129.     
  130.  delta_ocv_uv = div_s64((soc - soc_est) * (s64)m * 1000,  
  131.   
  132.        n * (pc - pc_new));  
  133.   
  134.  if (abs(delta_ocv_uv) > delta_ocv_uv_limit) {  
  135.   
  136.   pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv,  
  137.  
  138.     delta_ocv_uv_limit);  
  139.  
  140.   if (delta_ocv_uv > 0)  
  141.    delta_ocv_uv = delta_ocv_uv_limit;  
  142.   else  
  143.    delta_ocv_uv = -1 * delta_ocv_uv_limit;  
  144.   
  145.   pr_debug("new delta ocv = %d\n", delta_ocv_uv);   
  146.  }  
  147.  
  148.  if (wake_lock_active(&chip->low_voltage_wake_lock)) {  
  149.      
  150.   pr_debug("Low Voltage, apply only ibat limited corrections\n");  
  151.   
  152.   goto skip_limiting_corrections;  
  153.  }  
  154.   
  155.  if (chip->last_ocv_uv > 3800000)  
  156.   correction_limit_uv = the_chip->high_ocv_correction_limit_uv;  
  157.  else  
  158.   correction_limit_uv = the_chip->low_ocv_correction_limit_uv;  
  159.  
  160.  if (abs(delta_ocv_uv) > correction_limit_uv) {  
  161.    
  162.   pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv,  
  163.   
  164.     correction_limit_uv);  
  165.   
  166.   if (delta_ocv_uv > 0)   
  167.    delta_ocv_uv = correction_limit_uv;  
  168.   else   
  169.    delta_ocv_uv = -1 * correction_limit_uv;  
  170.      
  171.   pr_debug("new delta ocv = %d\n", delta_ocv_uv);  
  172.  
  173.  }  
  174.   
  175. skip_limiting_corrections: 

  176.  chip->last_ocv_uv -delta_ocv_uv;  
  177.  if (chip->last_ocv_uv >= chip->max_voltage_uv)  
  178.   chip->last_ocv_uv = chip->max_voltage_uv;  
  179.   
  180.  /* calculate the soc based on this new ocv */  
  181.    
  182.  pc_new = calculate_pc(chip, chip->last_ocv_uv,  
  183.   
  184.    chip->last_ocv_temp_decidegc, last_chargecycles);  
  185.   
  186.  rc_new_uah = (fcc_uah * pc_new) / 100;  
  187.   
  188.  soc_new = (rc_new_uah - cc_uah - uuc_uah)*100 / (fcc_uah - uuc_uah); 

  189.  soc_new = bound_soc(soc_new);  
  190.   
  191.  /*  
  192.   * if soc_new is ZERO force it higher so that phone doesnt report soc=0  
  193.   * soc = 0 should happen only when soc_est == 0  
  194.   */  
  195.  if (soc_new == 0 && soc_est >= the_chip->hold_soc_est)  
  196.   
  197.   soc_new = 1;  
  198.   
  199.  soc = soc_new;  
  200.   
  201. out:  
  202.  
  203.  pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, "  
  204.  
  205.   "soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, "  
  206.   
  207.   "pc_new = %d, soc_new = %d, rbatt = %d, m = %d\n", 

  208.   ibat_ua, vbat_uv, ocv_est_uv, pc_est, 

  209.   soc_est, n, delta_ocv_uv, chip->last_ocv_uv, 

  210.  pc_new, soc_new, rbatt, m);  
  211.   
  212.  return soc;  
  213. }
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值