电量计–问题和功能记录 切换CV相关问题 充电过充中切cv 需求描述:(NPC660项目)在充电过充中Host发送SBS73命令切换截止电压,从默认的4.35v切到4.1v,同时Charger充电器也设置截止电压4.15v以保证能充满到4.1v。期望充电到4.1v时rsoc能报100.
问题分析:在充电过充中切换截止电压,之前电量计累计的数据没有清0,导致充满到4.1v时电量还是按4.35v计算,只有70%
解决方案:收到SBS73就复位电量计数据(清0),注意清0会导致rsoc跳变。
放电过充中切cv 需求描述:(NPC660项目)按4.35v充满后,如果长时间没拔下充电器,充电器会判断为长时间过充,Host发送SBS73命令切换截止电压,从默认的4.35v切到4.1v,之后放电到4.1v。期望放电到4.1v过充中rsoc能保持100,4.1v以下按正常电量消耗显示,电量无跳变。
问题分析:
(1)切换后Host有时读到soc是0值,可能是正好读到了复位数据。
(2)初始方案是判断电压在4.35~4.1时直接sbsif强制上报100,但4.1v时soc会有跳变。
(3)Host在4.35v时发送cv切换时GGMEM0看到soc是100,但4.35放电到4.1v过充中,soc一直下降,放电到4.1v按真实soc显示会跳变到89。
解决方案:
(1)测试发现放电中每次发SBS73切cv到4.1v时,GGMEM0 soc都复位到100,因此解决方案是4.1v附近再复位一次电量计数据。
(2)不能在刚刚4.1v时复位,算法来不及刷新数据,会导致soc跳变。在4.1V + 20mV时提前复位。
(3)为了防止跳变,预设了平滑下降数据,平滑追赶跳变soc值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 uint8_t cv_adjusted_flag = 0; static int16_t adjusted_cv_value = 0; #define FAKE_SOC_TABLE_SIZE 40 static uint8_t fake_soc[FAKE_SOC_TABLE_SIZE] = { 99, 99, 98, 98, 97, 97, 96, 96, 95, 95, 94, 94, 93, 93, 92, 92, 91, 91, 90, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, }; //main loop每秒调用一次 void check_and_force_soc_if_needed(void) { // 获取当前电池电压 int16_t current_voltage = ((SBSIF_T *)psbsifHandle)->sbs_buff[SBS09_BATTVOLT]; // 判断是否需要处理CV调整后的特殊情况 if (cv_adjusted_flag) { // 如果电压仍高于调整后的CV值,保持SOC为100% if (current_voltage > adjusted_cv_value) { // 检查是否是放电状态 if (((SBSIF_T *)psbsifHandle)->sbs_buff[SBS0A_BATTCURR] < 0) { //4.1V以上时直接上报100,避免误读到SBS73的reset soc值0. ((SBSIF_T *)psbsifHandle)->sbs_buff[SBS0D_RSOC] = 100; ((SBSIF_T *)psbsifHandle)->sbs_buff[SBS0E_ASOC] = 100; //接近4.1V时提前reset数据,这样降到4.1v时真实soc能接近100 //如果不reset,从4.35放电到4.1的电量会被计算到soc,造成4.1v的soc只有90% if(current_voltage - adjusted_cv_value < 20) reset_gauge_parameters(); } } else { // 电压已降至CV值以下,按真实soc报 if (((SBSIF_T *)psbsifHandle)->sbs_buff[SBS0A_BATTCURR] < 0) { // 不要在这里复位,不然客户会看到soc跳变 // reset_gauge_parameters(); // 备选: 如果用真实rsoc有跳变(5~15%左右),做平滑处理,每秒降低soc直到匹配真实soc static int idrop = 0; if((((SBSIF_T *)psbsifHandle)->sbs_buff[SBS0D_RSOC] < fake_soc[idrop]) && (idrop < FAKE_SOC_TABLE_SIZE)) { ((SBSIF_T *)psbsifHandle)->sbs_buff[SBS0D_RSOC] = fake_soc[idrop]; idrop++; return; //这里退出,下次继续,直到匹配或者超出fake table } } // 清除全局标志 cv_adjusted_flag = 0; } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void smart_charge_func(void) { // SBS73: 超长时间充电切CV值功能 int16_t host_update = (((SBSIF_T *)psbsifHandle)->sbs_buff[SBS73_UPDATE_CV] >> 16) & 0xFFFF; int16_t update_cv = ((SBSIF_T *)psbsifHandle)->sbs_buff[SBS73_UPDATE_CV] & 0xFFFF // 如果收到Host发送的CV调整命令 if (host_update && update_cv) { // 记录新的CV值 adjusted_cv_value = update_cv; // 获取当前电池电压 int16_t current_voltage = ((SBSIF_T *)psbsifHandle)->sbs_buff[SBS09_BATTVOLT]; // 如果当前电压高于新设定的CV值,设置标志且只设置一次 if ((cv_adjusted_flag == 0) && (current_voltage > adjusted_cv_value)) { cv_adjusted_flag = 1; } } ....
充电到99停充,自动报百 FW有优化处理,当Charger提前截止充电了,电池处于idle状态且当前电量在99,Host发送81命令0x3数据通知电量计,电量计idle_process检测此标志位,持续30s后电量计自动报100,避免充电器插着但长时间停充场景的电量到不了100。
GGMEM数据使用SBS命令导出 客户环境下可能没有多的I2C接口能接Cobra上位机去读GGMEM,而问题调试必须依赖于GGMEM数据。
客户的Host代码可以使用SBS D0 ~ D8命令获取GGMEM0 ~8
Host乱切cv导致跳0和满充75问题
Host侧代码可通过SBS D0~D8读GGMEM去分析这种跳0问题:
注意Cobra轮询时间比较长最短1s,有时不一定能观测到soc跳0的值,但GGMEM0第一个DWORD复位为DESIGN FCC = 13ec是可以作为判断的。
最终原因就是Host Charger逻辑问题,一直在发SBS73,而FW没加防呆,频繁复位libfg数据,导致最终4.1V时接近OCV查表值的电量75,而不是CV追赶值100.
Cycle-count放电循环次数 SBS17 cycle-count = 总放电量/电池容量,每秒更新
不接i2c多次循环再读cycle count可以读到更新,因为sleep机制下会有timer每60s唤醒一次持续1s,更新libfg信息,包括累计discharge ccmah,计算cycle count
USB模式和AC Charger模式的eocma调整接口 USB口只有500mA电流,应该保证USB充电的eocma小于500mA,典型值是200mA。参数的满充截止电流如果和USB电流相等都是500mA,host必须发SBS74切eocma到200mA,否则充电尾端加速过快,容易读到电量跳变。
NTC表的两种形式 SD77561原版使用电压-电阻查表;SD77428A3使用电阻-温度查表,支持6uA/48uA两种电流源切换,大电流源保证高温时R*I = V有更大的值,这样电压ADC有更高精度。现在需要将SD77561的电压-电阻查表改成电阻-温度查表,支持6uA/72uA两种电流源切换:
(1)首先将SD77428代码里已经有的R-T表,手动创建一个77561 PRJ的NTC.txt格式表:
x轴是16个采样点,total length是cobra解析此表的参数总和,即16*2 (x和y) + 6(header) = 38
(2)修改ADC查表方式,并添加高温切换大电流源
代码分支:HaoPeng-I0917A
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 // Copy raw thm voltage to buffer ((SBSIF_T *)psbsifHandle)->sbs_val = sbs_cal_lsb_factor((int32_t)(dacq_p->raw_buff[((SBSIF_T *)psbsifHandle)->sbs_val]), ADC_EXTMPLSB, ADC_EXTMPLSB_FACTOR); //暂存,后面切换电流源用到此raw电压作为条件 temp2 = ((SBSIF_T *)psbsifHandle)->sbs_val; /* find Re, with 20uA source */ // we modify thermal table as Voltage V.S. Temperature, so don't need to multiple THM_OHM //((SBSIF_T *)psbsifHandle)->sbs_val *= (THM_OHM); //Re = 1000uV / 20uA ==> Ohm //转换电压成电阻:电压*1000精度/电流源 = NTC R,支持72ua和6ua电流源,72uA为了高温下高精度测量 if(EXTNTCSRC_72UA == Chip_ADC_GetNTCSrc()) ((SBSIF_T *)psbsifHandle)->sbs_val *= (1000/72); else ((SBSIF_T *)psbsifHandle)->sbs_val *= (1000/6); //查表,改成电阻-温度表 lut_one_latitude(TEMPERATURE_DATA_NUM, (one_latitude_data_t *)GDM_FLASH_THML_TABLE, ((SBSIF_T *)psbsifHandle)->sbs_val, &temp1); temp1 += DK_BASE; //记录温度 ((SBSIF_T *)psbsifHandle)->sbs_buff[SBS61_ETDK1] = temp1 - (int32_t)(param_board_cfg[PARM_BCFG_EXTTHMOFFSET]); //((SBSIF_T *)psbsifHandle)->sbs_val; //根据ntc电压(温度状况)决定电流源切换提高精度: //高温ntc电压小于60mV切换72uA,常温低温ntc电压大于900mV切换6uA if((temp2 < 60) && (EXTNTCSRC_6UA == Chip_ADC_GetNTCSrc())){ Chip_ADC_SetNTCSrc(EXTNTCSRC_72UA); }else if ((temp2 > 900) && (EXTNTCSRC_72UA == Chip_ADC_GetNTCSrc())){ Chip_ADC_SetNTCSrc(EXTNTCSRC_6UA); }
在线升级bin和参数对照 终端在线升级出问题一般只会回传bin,需要对照bin的参数是怎么配置的,注意bin没有8K的bootloader offset即可找到参数
缺失的参数定义在C:\git-834\COBRA\COBRA Documents\SD77226SBS_X_20250315\Project\Parameter查看
SOH更新原理
77428 Host驱动使用硬件I2C判断通信问题 i2c sbs接口持续错误可用此方式判断是硬件问题还是软件问题
注释函数大幅减少Code段占用 一般注释掉函数的调用处,但不注释函数体本身,只会减少运行时堆栈,并不会减少Code代码段,;但是561 KEIL项目注释main的libfg_update后,Code段从36K减到20K
原因是编译器优化:未被调用的函数被从Image中移除;只有被调用的函数体才存放在Image。
如下图分别是不调用和调用libfg_update时的.map信息:
SD77428 power supply框架获取电量信息 框架参考:https://www.kernel.org/doc/html/latest/power/power_supply_class.html
代码见<include/linux/power_supply.h>
基本原理是dev设备,注册power supply属性,其中的desc描述结构包含get_property回调函数接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static int32_t sd77428_power_supply_init(struct sd77428_data *chip) { chip->bat_cfg.drv_data = chip; chip->bat_cfg.of_node = chip->client->dev.of_node; chip->bat_desc.name = "sd77428"; chip->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; chip->bat_desc.properties = sd77428_battery_props; chip->bat_desc.num_properties = ARRAY_SIZE(sd77428_battery_props); chip->bat_desc.get_property = sd77428_battery_get_property; //这里是get_property回调 chip->bat_desc.no_thermal = 1; chip->bat_desc.external_power_changed = sd77428_external_power_changed; chip->bat = devm_power_supply_register(chip->dev, &chip->bat_desc, &chip->bat_cfg); ...
驱动实现get_property回调,关联电量信息和上报的状态,有的信息需要映射,例如SOH不能直接上报值只能报状态:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = chip->batt_info.batt_voltage; //mV break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = chip->batt_info.batt_current; //mA break; case POWER_SUPPLY_PROP_CAPACITY: //capacity in percents(soc), from 0 to 100 val->intval = chip->batt_info.batt_rsoc; break; case POWER_SUPPLY_PROP_TEMP: val->intval = chip->batt_info.batt_temp; //单位:摄氏度 break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: //design charge capacity val->intval = chip->batt_info.batt_capacity; break; case POWER_SUPPLY_PROP_CHARGE_FULL: //full-charge-capacity(FCC) val->intval = chip->batt_info.batt_fcc; break; case POWER_SUPPLY_PROP_CHARGE_NOW: //charge capacity now val->intval = chip->batt_info.batt_rc; break; case POWER_SUPPLY_PROP_HEALTH: //val->intval = chip->batt_info.batt_soh; //不能直接返回SOH值 val->intval = POWER_SUPPLY_HEALTH_GOOD; //根据状态返回POWER_SUPPLY_HEALTH定义的状态 break;
然后linux应用层通过sysfs访问power_supply路径下的节点,设备名称是前面注册的.name名称(sd77428),路径下是各属性都像文件一样访问。
用uevent查看节点的所有实时信息,power_supply节点的任何信息的更新都会反映到默认属性uevent(power_supply属于uevent子类,基于uevent通知机制实现);如果只查看某个信息如capacity,charge_now,都可以分开cat查看
如下是终端输出,sysfs查看uevent和驱动打印的电池信息是一致的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 root@phytiumpi:~# ls /sys/class/power_supply sd77428 root@phytiumpi:~# ls /sys/class/power_supply/sd77428 capacity charge_now device power status temp uevent charge_full_design current_now health present subsystem type voltage_now root@phytiumpi:~# cat /sys/class/power_supply/sd77428/uevent POWER_SUPPLY_NAME=sd77428 POWER_SUPPLY_STATUS=Discharging POWER_SUPPLY_PRESENT=1 POWER_SUPPLY_VOLTAGE_NOW=4089 POWER_SUPPLY_CURRENT_NOW=0 POWER_SUPPLY_CAPACITY=98 POWER_SUPPLY_TEMP=25 POWER_SUPPLY_CHARGE_FULL_DESIGN=3400 POWER_SUPPLY_CHARGE_FULL=3400 POWER_SUPPLY_CHARGE_NOW=3318 POWER_SUPPLY_HEALTH=Good root@phytiumpi:~# [ 23.297385] [bmt]sd77428_get_batt_info: vbat:4089, ibat:00000, tbat:25, rsoc:098, fcc:3400, dcap:3400, soh:100, cycle:0, rc:3318, dfcc:3798, cc:8242, ext_chg -1
561 cobra导出EEPROM 64K数据
从左到右依次read all,每次export一个bin,得到3个bin,然后用hex editor合并成一个64KB的bin,按EEPROM+Sytem+Information合并
561 SBS改成428格式,交叉测试 LK阶段问题怀疑是428使用轮询I2C造成slave持续拉低clock/data,561使用中断处理I2C,为了快速验证,不改host 428驱动情况下,使用561修改部分命令成428格式测试。
改两处即可:I2C slave地址,SBS命令的值和长度。注意Slave地址设置原代码有问题,应该直接赋值SADDR1,不能或等,否则COM通信失败
561 使用轮询方式处理I2C 需求同上,为了对比测试428问题,561使用轮询i2c方式
修改两处:
1.i2c slave handle加到while1 ms轮询
2.关闭NVIC I2C中断开关 (561基于ARM,428的riscv是另外一套PLIC关闭)
注意自定义的数字逻辑也有中断使能和中断状态,这个和NVIC中断是无关的,属于内部状态,所以即使关了外部NVIC中断开关,也只是切断了ISR回调处理,轮询方式调用i2c slave handle仍可以读内部i2c register中断状态判断是读是写是传输完成,只要保证清除状态,不要重复处理即可。
77428A3不更新FCC问题 A3驱动下载参数后需要发SBS8F带非0参数,reset_lib去更新FCC=Design Capacity