点击打开链接

高通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. ocv = raw->last_good_ocv_uv;
  8. batt_temp_decidegc = chip->last_ocv_temp_decidegc;
  9. pc = calculate_pc(chip, ocv, batt_temp_decidegc, chargecycles);
  10. pr_info("ocv = %d pc = %d\n", ocv, pc);
  11. return (fcc_uah * pc) / 100;
  12. }

但是通常情况下开机使用计算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. cc_voltage_uv = cc;
  15. pr_debug("cc = %d\n", cc);
  16. cc_voltage_uv = cc_to_microvolt(chip, cc_voltage_uv);
  17. cc_voltage_uv = pm8xxx_cc_adjust_for_gain(cc_voltage_uv);
  18. pr_debug("cc_voltage_uv = %lld microvolts\n", cc_voltage_uv);
  19. cc_pvh = ccmicrovolt_to_pvh(cc_voltage_uv);
  20. pr_debug("cc_pvh = %lld pico_volt_hour\n", cc_pvh);
  21. cc_uah = div_s64(cc_pvh, chip->r_sense_uohm);
  22. *val = cc_uah;
  23. }

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. for (i = 0; i <= 100; i++) {
  17. ocv_mv = interpolate_ocv(chip->pc_temp_ocv_lut,
  18. batt_temp_degc, i);
  19. rbatt_mohm = get_rbatt(chip, i, batt_temp);
  20. unusable_uv = (rbatt_mohm * i_ma) + (chip->v_cutoff * 1000);
  21. delta_uv = ocv_mv * 1000 - unusable_uv;
  22. pr_debug("soc = %d ocv = %d rbat = %d u_uv = %d delta_v = %d\n",
  23. i, ocv_mv, rbatt_mohm, unusable_uv, delta_uv);
  24. if (delta_uv > 0)
  25. break;
  26. prev_delta_uv = delta_uv;
  27. prev_rbatt_mohm = rbatt_mohm;
  28. prev_ocv_mv = ocv_mv;
  29. }
  30. uuc_rbatt_uv = linear_interpolate(rbatt_mohm, delta_uv,
  31. prev_rbatt_mohm, prev_delta_uv,
  32. 0);
  33. unusable_uv = (uuc_rbatt_uv * i_ma) + (chip->v_cutoff * 1000);
  34. pc_unusable = calculate_pc(chip, unusable_uv, batt_temp, chargecycles);
  35. uuc = (fcc_uah * pc_unusable) / 100;
  36. pr_debug("For i_ma = %d, unusable_rbatt = %d unusable_uv = %d unusable_pc = %d uuc = %d\n",
  37. i_ma, uuc_rbatt_uv, unusable_uv,
  38. pc_unusable, uuc);
  39. *ret_pc_unusable = pc_unusable;
  40. return uuc;
  41. }

高通的这套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_uv= 3777000uV
  2. <6>[ 7796.038360] read_soc_params_raw: last_good_ocv_raw= 0x943f, last_good_ocv_uv= 3777000uV
  3. <6>[ 7796.038543] calculate_soc_params: FCC = 3190000uAh batt_temp = 300, cycles = 0
  4. <6>[ 7796.038635] calculate_remaining_charge_uah: ocv = 3777000 pc = 35
  5. <6>[ 7796.038665] calculate_soc_params: RC = 1116500uAh
  6. <6>[ 7796.038726] calculate_soc_params: cc_uah = 394979uAh raw->cc = 5764312
  7. <6>[ 7796.038818] calculate_state_of_charge: RUC(RC-CC-UUC) = 657721uAh RC = 1116500uAh CC= 394979uAh UUC= 63800uAh FCC= 3190000uAh 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. static int adjust_soc(struct pm8921_bms_chip *chip, int soc,
  3. int batt_temp, int chargecycles,
  4. int rbatt, int fcc_uah, int uuc_uah, int cc_uah)
  5. {
  6. int ibat_ua = 0, vbat_uv = 0;
  7. int ocv_est_uv = 0, soc_est = 0, pc_est = 0, pc = 0;
  8. int delta_ocv_uv = 0;
  9. int n = 0;
  10. int rc_new_uah = 0;
  11. int pc_new = 0;
  12. int soc_new = 0;
  13. int m = 0;
  14. int rc = 0;
  15. int delta_ocv_uv_limit = 0;
  16. int correction_limit_uv = 0;
  17. rc = pm8921_bms_get_simultaneous_battery_voltage_and_current(
  18. &ibat_ua,
  19. &vbat_uv);
  20. if (rc < 0) {
  21. pr_err("simultaneous vbat ibat failed err = %d\n", rc);
  22. goto out;
  23. }
  24. very_low_voltage_check(chip, ibat_ua, vbat_uv);
  25. if (chip->low_voltage_detect &&
  26. wake_lock_active(&chip->low_voltage_wake_lock)) {
  27. if (is_voltage_below_cutoff_window(chip, ibat_ua, vbat_uv)) {
  28. soc = 0;
  29. pr_info("Voltage below cutoff, setting soc to 0\n");
  30. goto out;
  31. }
  32. }
  33. delta_ocv_uv_limit = DIV_ROUND_CLOSEST(ibat_ua, 1000);
  34. ocv_est_uv = vbat_uv + (ibat_ua * rbatt)/1000;
  35. calc_current_max(chip, ocv_est_uv, rbatt);
  36. pc_est = calculate_pc(chip, ocv_est_uv, batt_temp, last_chargecycles);
  37. soc_est = div_s64((s64)fcc_uah * pc_est - uuc_uah*100,
  38. (s64)fcc_uah - uuc_uah);
  39. soc_est = bound_soc(soc_est);
  40. /* never adjust during bms reset mode */
  41. if (bms_reset) {
  42. pr_debug("bms reset mode, SOC adjustment skipped\n");
  43. goto out;
  44. }
  45. if (ibat_ua < 0 && pm8921_is_batfet_closed()) {
  46. soc = charging_adjustments(chip, soc, vbat_uv, ibat_ua,
  47. batt_temp, chargecycles,
  48. fcc_uah, cc_uah, uuc_uah);
  49. goto out;
  50. }
  51. /*
  52. * do not adjust
  53. * if soc_est is same as what bms calculated
  54. * OR if soc_est > 15
  55. * OR if soc it is above 90 because we might pull it low
  56. * and  cause a bad user experience
  57. */
  58. if (soc_est == soc
  59. || soc_est > 15
  60. || soc >= 90)
  61. goto out;
  62. if (last_soc_est == -EINVAL)
  63. last_soc_est = soc;
  64. n = min(200, max(1 , soc + soc_est + last_soc_est));
  65. /* remember the last soc_est in last_soc_est */
  66. last_soc_est = soc_est;
  67. pc = calculate_pc(chip, chip->last_ocv_uv,
  68. chip->last_ocv_temp_decidegc, last_chargecycles);
  69. if (pc > 0) {
  70. pc_new = calculate_pc(chip, chip->last_ocv_uv - (++m * 1000),
  71. chip->last_ocv_temp_decidegc,
  72. last_chargecycles);
  73. while (pc_new == pc) {
  74. /* start taking 10mV steps */
  75. m = m + 10;
  76. pc_new = calculate_pc(chip,
  77. chip->last_ocv_uv - (m * 1000),
  78. chip->last_ocv_temp_decidegc,
  79. last_chargecycles);
  80. }
  81. } else {
  82. /*
  83. * pc is already at the lowest point,
  84. * assume 1 millivolt translates to 1% pc
  85. */
  86. pc = 1;
  87. pc_new = 0;
  88. m = 1;
  89. }
  90. delta_ocv_uv = div_s64((soc - soc_est) * (s64)m * 1000,
  91. n * (pc - pc_new));
  92. if (abs(delta_ocv_uv) > delta_ocv_uv_limit) {
  93. pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv,
  94. delta_ocv_uv_limit);
  95. if (delta_ocv_uv > 0)
  96. delta_ocv_uv = delta_ocv_uv_limit;
  97. else
  98. delta_ocv_uv = -1 * delta_ocv_uv_limit;
  99. pr_debug("new delta ocv = %d\n", delta_ocv_uv);
  100. }
  101. if (wake_lock_active(&chip->low_voltage_wake_lock)) {
  102. pr_debug("Low Voltage, apply only ibat limited corrections\n");
  103. goto skip_limiting_corrections;
  104. }
  105. if (chip->last_ocv_uv > 3800000)
  106. correction_limit_uv = the_chip->high_ocv_correction_limit_uv;
  107. else
  108. correction_limit_uv = the_chip->low_ocv_correction_limit_uv;
  109. if (abs(delta_ocv_uv) > correction_limit_uv) {
  110. pr_debug("limiting delta ocv %d limit = %d\n", delta_ocv_uv,
  111. correction_limit_uv);
  112. if (delta_ocv_uv > 0)
  113. delta_ocv_uv = correction_limit_uv;
  114. else
  115. delta_ocv_uv = -1 * correction_limit_uv;
  116. pr_debug("new delta ocv = %d\n", delta_ocv_uv);
  117. }
  118. skip_limiting_corrections:
  119. chip->last_ocv_uv -= delta_ocv_uv;
  120. if (chip->last_ocv_uv >= chip->max_voltage_uv)
  121. chip->last_ocv_uv = chip->max_voltage_uv;
  122. /* calculate the soc based on this new ocv */
  123. pc_new = calculate_pc(chip, chip->last_ocv_uv,
  124. chip->last_ocv_temp_decidegc, last_chargecycles);
  125. rc_new_uah = (fcc_uah * pc_new) / 100;
  126. soc_new = (rc_new_uah - cc_uah - uuc_uah)*100 / (fcc_uah - uuc_uah);
  127. soc_new = bound_soc(soc_new);
  128. /*
  129. * if soc_new is ZERO force it higher so that phone doesnt report soc=0
  130. * soc = 0 should happen only when soc_est == 0
  131. */
  132. if (soc_new == 0 && soc_est >= the_chip->hold_soc_est)
  133. soc_new = 1;
  134. soc = soc_new;
  135. out:
  136. pr_debug("ibat_ua = %d, vbat_uv = %d, ocv_est_uv = %d, pc_est = %d, "
  137. "soc_est = %d, n = %d, delta_ocv_uv = %d, last_ocv_uv = %d, "
  138. "pc_new = %d, soc_new = %d, rbatt = %d, m = %d\n",
  139. ibat_ua, vbat_uv, ocv_est_uv, pc_est,
  140. soc_est, n, delta_ocv_uv, chip->last_ocv_uv,
  141. pc_new, soc_new, rbatt, m);
  142. return soc;
  143. }

针对高通BMS的研究 高通电量计的更多相关文章

  1. 高通spi 屏幕 -lk代码分析

    lk SPI驱动 1. 初始化时钟 在lk中,我们是从kmain开始执行下来的,而执行顺序则是先初始化时钟,也就是在platform_early_init函数中开始执行的: 在这里我们需要修改这个函数 ...

  2. 高通8X16电池BMS算法(一)【转】

    本文转载自:http://www.voidcn.com/blog/yanleizhouqing/article/p-6037399.html 最近一直在搞电源管理相关内容,之前是8610的bms,现在 ...

  3. (转)Unity导出Android在高通骁龙800以上CPU概率性崩溃解决方法研究

    Android上的奇葩问题真的是太多了,开始测试反馈说游戏在某些Android手机上随机crash,后来经过详细的测试发现随机闪退的手机都是搭载了高通骁龙800以上的CPU.然后连上真机当crash的 ...

  4. 【转】高通平台android 环境配置编译及开发经验总结

    原文网址:http://blog.csdn.net/dongwuming/article/details/12784535 1.高通平台android开发总结 1.1 搭建高通平台环境开发环境 在高通 ...

  5. 高通开发笔记---Yangtze worknote

    点击打开链接 1. repo init -u git://review.sonyericsson.net/platform/manifest -b volatile-jb-mr1-yangtze 2. ...

  6. 高通 MSM8K bootloader : SBL1 .

    一. MSM8K Boot Flow 图1: 高通MSM8K平台bootloader启动流程基本类似,但具体各平台,比如MSM8974.MSM8916.MSM8994等,会有微小区别. 从上图,可以看 ...

  7. 高通android开发摘要

    一部分是开源的,可以从codeaurora.org上下载,还有一部分是高通产权的,需要从高通的网站上下载. 将高通产权的代码放到:vendor/qcom/proprietary 1. 设置bms一些参 ...

  8. 高通Android display架构分析

    目录(?)[-] Kernel Space Display架构介绍 函数和数据结构介绍 函数和数据结构介绍 函数和数据结构介绍 数据流分析 初始化过程分析 User Space display接口 K ...

  9. 高通 MSM8K bootloader之一: SBL1

    MSM8K Boot Flow  转自:http://www.cnblogs.com/liang123/p/6325257.html            http://blog.csdn.net/F ...

随机推荐

  1. 连连看bug记录

    1.bug统计 bug1: bug现象:消除两个相同图案之间的连线跨越了其他图片. 期待现象:应该改成绕过图案呈折线形. 二者差异:折现有没有穿过其他图片. bug原因猜测:小组能力原因. 复现步骤或 ...

  2. JavaScript 类式继承与原型继承

    交叉着写Java和Javascript都有2年多了,今天来总结下自己所了解的Javascript类与继承. Javascript本身没有类似Java的面向对象的类与继承术语,但其基于原型对象的思想却可 ...

  3. nginx反向代理(proxy_pass)tomcat的过程中,session失效的问题解决

    Nginx反向代理tomcat,很是方便,但是也有些细节的问题需要注意:今天遇到了这样一个问题,tomcat中路径“host/web1”,nginx中直接“host/”代理,这时候session就无法 ...

  4. Java输入流之BufferReader和Scanner的用法!

    在Java中,我们都知道Java的标准输入串是System.in.但是我们却很少在Java中看到谁使用它,这是因为我们平时输入的都是一个字符串或者是一个数字等等.而System.in提供的read方法 ...

  5. (IOS)BaiduFM 程序分析

    本文主要分享下楼主在学习Swift编程过程中,对GitHub上的一个开源app BaiduFM的研究心得. 项目地址:https://github.com/belm/BaiduFM-Swift 一.项 ...

  6. How to prevent SQL injection attacks?

    In our earlier tutorial on SQL Injection, one way to have prevented the SQL injection attack was by ...

  7. OC-数组排序-NSSortDescriptor使用

    OC-数组排序-NSSortDescriptor使用 在Object-c中,当有一个班级类MyClass,还有一个学生类Student.在班级类MyClass中通过一个可变数组NSMutableArr ...

  8. JSP中的隐式对象(implicit object)

  9. Salted hash password

    参考文档 http://www.cnblogs.com/richardlee/articles/2511321.html https://en.wikipedia.org/wiki/Salt_%28c ...

  10. 面向对象php基本格式、构造、析构、访问修饰符

    <?php//面向对象//1.类//由众多的对象抽象出来的 //2.对象//一切皆对象//由类实例化出来的 //基本形式class 类名{ 成员变量 成员方法 } $对象名 =new 类名 $对 ...