点击打开链接

高通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. PG, Pool之间的一些数量关系

    先说一下我的环境: Ceph cluster中包含6台OSD节点 (osd.0 - 5), 一共有10个Pool (0 - 9), 这些Pool共享了144个PG (这个数字是所有Pool的PG_SI ...

  2. Codeforces Round #375 (Div. 2) A B C D F

    A 数轴上有三个人要到一个点去过年 使三个人走路距离的和最小  让两边的人都走到中间那个点即可 B 给出一个字符串 其中有_ ( ) 三种字符和英文字母 连续的英文字母是一个单词 括号对中不会套括号对 ...

  3. springmvc Failed to load resource: the server responded with a status of 404 (Not Found)

    jsp页面导入css.js提示上述问题. Spring对静态资源的请求做专门处理 <!-- 对静态资源的请求 --><mvc:resources location="/js ...

  4. C#编程利器之一:类(Class)【转】

    C#编程利器之一:类(Class) 面向对象的程序设计(Object-Oriented Programming,简记为OOP)是一种功能非常强大的编程方法,立意于创建软件重用代码,以类为基础去思考编程 ...

  5. 针对ajax执行后swiper特效无法执行解决方案

    ajax执行后重新绑定swiper事件.

  6. [转]Android Studio系列教程六--Gradle多渠道打包

    转自:http://www.stormzhang.com/devtools/2015/01/15/android-studio-tutorial6/ Android Studio系列教程六--Grad ...

  7. cocos2dx 3.x(在Mac平台下利用Eclipse打包安卓apk安装包详细教程)

    最近在学习cocos2dx在MAC上如何打包apk,今天先把安装JDK和ANT的过程记来. 首先,打开终端,输入"java -version" 点击回车后,出现如下提示: 我们的M ...

  8. JQuery 支持 hide 和 show 事件的方法与分析

    问题提出  JQuery不支持hide和show作为事件形式出现, 实际上这两个仅仅是JQuery对象的一个方法(fn): 有一类UI交互需求,根据一个DOM对象的或者显示对附属的DOM对象做相同操作 ...

  9. Hadoop学习(5)-- Hadoop2

    在Hadoop1(版本<=0.22)中,由于NameNode和JobTracker存在单点中,这制约了hadoop的发展,当集群规模超过2000台时,NameNode和JobTracker已经不 ...

  10. Java接口回调

    public class A { private D d; private C c; public A (C c) { this.c = c; } public void setD (D d) { t ...