• 针对高通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 ", 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 ", 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 ", cc_voltage_uv);  
    21.  cc_pvh = ccmicrovolt_to_pvh(cc_voltage_uv);  
    22.  pr_debug("cc_pvh = %lld pico_volt_hour ", 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 ",  
    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 ",  
    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.   
    9.   int batt_temp, int chargecycles,  
    10.   
    11.   
    12.   
    13.   int rbatt, int fcc_uah, int uuc_uah, int cc_uah)  
    14.   
    15.   
    16.   
    17. {  
    18.   
    19.   
    20.   
    21.  int ibat_ua = 0vbat_uv = 0;  
    22.   
    23.   
    24.   
    25.  int ocv_est_uv = 0soc_est = 0pc_est = 0pc = 0;  
    26.   
    27.   
    28.   
    29.  int delta_ocv_uv = 0;  
    30.   
    31.   
    32.   
    33.  int n = 0;  
    34.   
    35.   
    36.   
    37.  int rc_new_uah = 0;  
    38.   
    39.   
    40.   
    41.  int pc_new = 0;  
    42.   
    43.   
    44.   
    45.  int soc_new = 0;  
    46.   
    47.   
    48.   
    49.  int m = 0;  
    50.   
    51.   
    52.   
    53.  int rc = 0;  
    54.   
    55.   
    56.   
    57.  int delta_ocv_uv_limit = 0;  
    58.   
    59.   
    60.   
    61.  int correction_limit_uv = 0;  
    62.   
    63.   
    64.   
    65.   
    66.   
    67.   
    68.   
    69.  rc = pm8921_bms_get_simultaneous_battery_voltage_and_current(  
    70.   
    71.   
    72.   
    73.        &ibat_ua,  
    74.   
    75.   
    76.   
    77.        &vbat_uv);  
    78.   
    79.   
    80.   
    81.  if (rc < 0) {  
    82.   
    83.   
    84.   
    85.   pr_err("simultaneous vbat ibat failed err = %d ", rc);  
    86.   
    87.   
    88.   
    89.   goto out;  
    90.   
    91.   
    92.   
    93.  }  
    94.   
    95.   
    96.   
    97.   
    98.   
    99.   
    100.   
    101.  very_low_voltage_check(chip, ibat_ua, vbat_uv);  
    102.   
    103.   
    104.   
    105.   
    106.   
    107.   
    108.   
    109.  if (chip->low_voltage_detect &&  
    110.   
    111.   
    112.   
    113.   wake_lock_active(&chip->low_voltage_wake_lock)) {  
    114.   
    115.   
    116.   
    117.   if (is_voltage_below_cutoff_window(chip, ibat_ua, vbat_uv)) {  
    118.   
    119.   
    120.   
    121.    soc = 0;  
    122.   
    123.   
    124.   
    125.    pr_info("Voltage below cutoff, setting soc to 0 ");  
    126.   
    127.   
    128.   
    129.    goto out;  
    130.   
    131.   
    132.   
    133.   }  
    134.   
    135.   
    136.   
    137.  }  
    138.   
    139.   
    140.   
    141.   
    142.   
    143.   
    144.   
    145.  delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);  
    146.   
    147.   
    148.   
    149.   
    150.   
    151.   
    152.   
    153.  ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000;  
    154.   
    155.   
    156.   
    157.  calc_current_max(chip, ocv_est_uv, rbatt);  
    158.   
    159.   
    160.   
    161.  pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles);  
    162.   
    163.   
    164.   
    165.  soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100,  
    166.   
    167.   
    168.   
    169.       (s64)fcc_uah - uuc_uah);  
    170.   
    171.   
    172.   
    173.  soc_est = bound_soc(soc_est);  
    174.   
    175.   
    176.   
    177.   
    178.   
    179.   
    180.   
    181.  /* never adjust during bms reset mode */  
    182.   
    183.   
    184.   
    185.  if (bms_reset) {  
    186.   
    187.   
    188.   
    189.   pr_debug("bms reset mode, SOC adjustment skipped ");  
    190.   
    191.   
    192.   
    193.   goto out;  
    194.   
    195.   
    196.   
    197.  }  
    198.   
    199.   
    200.   
    201.   
    202.   
    203.   
    204.   
    205.  if (ibat_ua < 0 && pm8921_is_batfet_closed()) {  
    206.   
    207.   
    208.   
    209.   soc = charging_adjustments(chip, soc, vbat_uv, ibat_ua,  
    210.   
    211.   
    212.   
    213.     batt_temp, chargecycles,  
    214.   
    215.   
    216.   
    217.     fcc_uah, cc_uah, uuc_uah);  
    218.   
    219.   
    220.   
    221.   goto out;  
    222.   
    223.   
    224.   
    225.  }  
    226.   
    227.   
    228.   
    229.   
    230.   
    231.   
    232.   
    233.  /*  
    234.   
    235.   
    236.   
    237.   * do not adjust  
    238.   
    239.   
    240.   
    241.   * if soc_est is same as what bms calculated  
    242.   
    243.   
    244.   
    245.   * OR if soc_est > 15  
    246.   
    247.   
    248.   
    249.   * OR if soc it is above 90 because we might pull it low  
    250.   
    251.   
    252.   
    253.   * and  cause a bad user experience  
    254.   
    255.   
    256.   
    257.   */  
    258.   
    259.   
    260.   
    261.  if (soc_est == soc  
    262.   
    263.   
    264.   
    265.   || soc_est > 15  
    266.   
    267.   
    268.   
    269.   || soc >= 90)  
    270.   
    271.   
    272.   
    273.   goto out;  
    274.   
    275.   
    276.   
    277.   
    278.   
    279.   
    280.   
    281.  if (last_soc_est == -EINVAL)  
    282.   
    283.   
    284.   
    285.   last_soc_est = soc;  
    286.   
    287.   
    288.   
    289.   
    290.   
    291.   
    292.   
    293.  n = min(200, max(1 , soc + soc_est + last_soc_est));  
    294.   
    295.   
    296.   
    297.  /* remember the last soc_est in last_soc_est */  
    298.   
    299.   
    300.   
    301.  last_soc_est = soc_est;  
    302.   
    303.   
    304.   
    305.   
    306.   
    307.   
    308.   
    309.  pc = calculate_pc(chip, chip->last_ocv_uv,  
    310.   
    311.   
    312.   
    313.    chip->last_ocv_temp_decidegc, last_chargecycles);  
    314.   
    315.   
    316.   
    317.  if (pc > 0) {  
    318.   
    319.   
    320.   
    321.   pc_new = calculate_pc(chip, chip->last_ocv_uv - (++m * 1000),  
    322.   
    323.   
    324.   
    325.      chip->last_ocv_temp_decidegc,  
    326.   
    327.   
    328.   
    329.      last_chargecycles);  
    330.   
    331.   
    332.   
    333.   while (pc_new == pc) {  
    334.   
    335.   
    336.   
    337.    /* start taking 10mV steps */  
    338.   
    339.   
    340.   
    341.    m = m + 10;  
    342.   
    343.   
    344.   
    345.    pc_new = calculate_pc(chip,  
    346.   
    347.   
    348.   
    349.       chip->last_ocv_uv - (m * 1000),  
    350.   
    351.   
    352.   
    353.       chip->last_ocv_temp_decidegc,  
    354.   
    355.   
    356.   
    357.       last_chargecycles);  
    358.   
    359.   
    360.   
    361.   }  
    362.   
    363.   
    364.   
    365.  } else {  
    366.   
    367.   
    368.   
    369.   /*  
    370.   
    371.   
    372.   
    373.    * pc is already at the lowest point,  
    374.   
    375.   
    376.   
    377.    * assume 1 millivolt translates to 1% pc  
    378.   
    379.   
    380.   
    381.    */  
    382.   
    383.   
    384.   
    385.   pc = 1;  
    386.   
    387.   
    388.   
    389.   pc_new = 0;  
    390.   
    391.   
    392.   
    393.   m = 1;  
    394.   
    395.   
    396.   
    397.  }  
    398.   
    399.   
    400.   
    401.   
    402.   
    403.   
    404.   
    405.  delta_ocv_uv = div_s64((soc - soc_est) * (s64)m * 1000,  
    406.   
    407.   
    408.   
    409.        n * (pc - pc_new));  
    410.   
    411.   
    412.   
    413.   
    414.   
    415.   
    416.   
    417.  if (abs(delta_ocv_uv) > delta_ocv_uv_limit) {  
    418.   
    419.   
    420.   
    421.   pr_debug("limiting delta ocv %d limit = %d ", delta_ocv_uv,  
    422.   
    423.   
    424.   
    425.     delta_ocv_uv_limit);  
    426.   
    427.   
    428.   
    429.   
    430.   
    431.   
    432.   
    433.   if (delta_ocv_uv > 0)  
    434.   
    435.   
    436.   
    437.    delta_ocv_uv = delta_ocv_uv_limit;  
    438.   
    439.   
    440.   
    441.   else  
    442.   
    443.   
    444.   
    445.    delta_ocv_uv = -1 * delta_ocv_uv_limit;  
    446.   
    447.   
    448.   
    449.   pr_debug("new delta ocv = %d ", delta_ocv_uv);  
    450.   
    451.   
    452.   
    453.  }  
    454.   
    455.   
    456.   
    457.   
    458.   
    459.   
    460.   
    461.  if (wake_lock_active(&chip->low_voltage_wake_lock)) {  
    462.   
    463.   
    464.   
    465.   pr_debug("Low Voltage, apply only ibat limited corrections ");  
    466.   
    467.   
    468.   
    469.   goto skip_limiting_corrections;  
    470.   
    471.   
    472.   
    473.  }  
    474.   
    475.   
    476.   
    477.   
    478.   
    479.   
    480.   
    481.  if (chip->last_ocv_uv > 3800000)  
    482.   
    483.   
    484.   
    485.   correction_limit_uv = the_chip->high_ocv_correction_limit_uv;  
    486.   
    487.   
    488.   
    489.  else  
    490.   
    491.   
    492.   
    493.   correction_limit_uv = the_chip->low_ocv_correction_limit_uv;  
    494.   
    495.   
    496.   
    497.   
    498.   
    499.   
    500.   
    501.  if (abs(delta_ocv_uv) > correction_limit_uv) {  
    502.   
    503.   
    504.   
    505.   pr_debug("limiting delta ocv %d limit = %d ", delta_ocv_uv,  
    506.   
    507.   
    508.   
    509.     correction_limit_uv);  
    510.   
    511.   
    512.   
    513.   
    514.   
    515.   
    516.   
    517.   if (delta_ocv_uv > 0)  
    518.   
    519.   
    520.   
    521.    delta_ocv_uv = correction_limit_uv;  
    522.   
    523.   
    524.   
    525.   else  
    526.   
    527.   
    528.   
    529.    delta_ocv_uv = -1 * correction_limit_uv;  
    530.   
    531.   
    532.   
    533.   pr_debug("new delta ocv = %d ", delta_ocv_uv);  
    534.   
    535.   
    536.   
    537.  }  
    538.   
    539.   
    540.   
    541.   
    542.   
    543.   
    544.   
    545. skip_limiting_corrections:  
    546.   
    547.   
    548.   
    549.  chip->last_ocv_uv -delta_ocv_uv;  
    550.   
    551.   
    552.   
    553.   
    554.   
    555.   
    556.   
    557.  if (chip->last_ocv_uv >= chip->max_voltage_uv)  
    558.   
    559.   
    560.   
    561.   chip->last_ocv_uv = chip->max_voltage_uv;  
    562.   
    563.   
    564.   
    565.   
    566.   
    567.   
    568.   
    569.  /* calculate the soc based on this new ocv */  
    570.   
    571.   
    572.   
    573.  pc_new = calculate_pc(chip, chip->last_ocv_uv,  
    574.   
    575.   
    576.   
    577.    chip->last_ocv_temp_decidegc, last_chargecycles);  
    578.   
    579.   
    580.   
    581.  rc_new_uah = (fcc_uah * pc_new) / 100;  
    582.   
    583.   
    584.   
    585.  soc_new = (rc_new_uah - cc_uah - uuc_uah)*100 / (fcc_uah - uuc_uah);  
    586.   
    587.   
    588.   
    589.  soc_new = bound_soc(soc_new);  
    590.   
    591.   
    592.   
    593.   
    594.   
    595.   
    596.   
    597.  /*  
    598.   
    599.   
    600.   
    601.   * if soc_new is ZERO force it higher so that phone doesnt report soc=0  
    602.   
    603.   
    604.   
    605.   * soc = 0 should happen only when soc_est == 0  
    606.   
    607.   
    608.   
    609.   */  
    610.   
    611.   
    612.   
    613.  if (soc_new == 0 && soc_est >= the_chip->hold_soc_est)  
    614.   
    615.   
    616.   
    617.   soc_new = 1;  
    618.   
    619.   
    620.   
    621.   
    622.   
    623.   
    624.   
    625.  soc = soc_new;  
    626.   
    627.   
    628.   
    629.   
    630.   
    631.   
    632.   
    633. out:  
    634.   
    635.   
    636.   
    637.  pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, "  
    638.   
    639.   
    640.   
    641.   "soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, "  
    642.   
    643.   
    644.   
    645.   "pc_new = %d, soc_new = %d, rbatt = %d, m = %d ",  
    646.   
    647.   
    648.   
    649.   ibat_ua, vbat_uv, ocv_est_uv, pc_est,  
    650.   
    651.   
    652.   
    653.   soc_est, n, delta_ocv_uv, chip->last_ocv_uv,  
    654.   
    655.   
    656.   
    657.   pc_new, soc_new, rbatt, m);  
    658.   
    659.   
    660.   
    661.   
    662.   
    663.   
    664.   
    665.  return soc;  
    666.   
    667.   
    668.   
    669. }  

  • 相关阅读:
    .net中AjaxPro的简单使用
    关于 Activity 中 startActivityForResult 和 onActivityResult
    产品经理值得读的12本书
    存储过程中使用事务与try catch
    扁平化数据解构转化成树形结构
    JavaScript编程精解
    泛型
    OATS:Orthogonal Array Testing Strategy
    等价类划分
    C# homework
  • 原文地址:https://www.cnblogs.com/LoongEmbedded/p/5298183.html
Copyright © 2020-2023  润新知