zoukankan      html  css  js  c++  java
  • 高通8X16电池BMS算法(二)【转】

    本文转载自:http://www.voidcn.com/blog/yanleizhouqing/article/p-6051912.html

    上一篇主要讲电池相关的一些知识,上节忘记讲了,电池一般分为电量计电池和非电流计电池,电量计电池,就不需要用pmu8916的IC,当然这只是只,不需要BMS来计算soc,而jni层也需要读取电流计的电池相关属性。

    这一节主要是根据代码进行相关的分析。

    1. 先看probe的代码:

    static int qpnp_vm_bms_probe(struct spmi_device *spmi)
    {
    	...........
    	..........
    	...........
    	..........
    
    	//这里把电池的配置文件dtsi的读出来,并存到当前的结构体。
    	rc = set_battery_data(chip);
    	rc = config_battery_data(chip->batt_data);
    	..........
    	..........
    	//这个是核心的工作,一个线程,BMS的主要内容在此
    	INIT_DELAYED_WORK(&chip->monitor_soc_work, monitor_soc_work);
    	..........
    	..........
    
    	//电池一些常规的检测,主要从PMIC上读到的相关信息
    	battery_insertion_check(chip);
    	battery_status_check(chip);
    
    	/* character device to pass data to the userspace */
    	rc = register_bms_char_device(chip);
    	if (rc) {
    		pr_err("Unable to regiter '/dev/vm_bms' rc=%d
    ", rc);
    		goto fail_bms_device;
    	}
    
    	the_chip = chip;
    	//这个也很重要,我们从上节知道,初值last_ocv_soc是非常重要的,决定着后面的soc估值算法
    	calculate_initial_soc(chip);
    
    	//设置和注册电池的power supply
    	/* setup & register the battery power supply */
    	chip->bms_psy.name = "bms";
    	chip->bms_psy.type = POWER_SUPPLY_TYPE_BMS;
    	chip->bms_psy.properties = bms_power_props;
    	chip->bms_psy.num_properties = ARRAY_SIZE(bms_power_props);
    	chip->bms_psy.get_property = qpnp_vm_bms_power_get_property;
    	chip->bms_psy.set_property = qpnp_vm_bms_power_set_property;
    	chip->bms_psy.external_power_changed = qpnp_vm_bms_ext_power_changed;
    	chip->bms_psy.property_is_writeable = qpnp_vm_bms_property_is_writeable;
    	chip->bms_psy.supplied_to = qpnp_vm_bms_supplicants;
    	chip->bms_psy.num_supplicants = ARRAY_SIZE(qpnp_vm_bms_supplicants);
    
    	rc = power_supply_register(chip->dev, &chip->bms_psy);
    	if (rc < 0) {
    		pr_err("power_supply_register bms failed rc = %d
    ", rc);
    		goto fail_psy;
    	}
    	.....................
    	....................
    	....................
    	//这里启动工作线程
    	schedule_delayed_work(&chip->monitor_soc_work, 0);
    	..........................
    	.......................
    
    }
    

    2. 分析如何确定初始的last_ocv_uv:

    static int calculate_initial_soc(struct qpnp_bms_chip *chip)
    {
    	........
    	........
    	//读当前电池温度
    	rc = get_batt_therm(chip, &batt_temp);
    	............
    	//读PON OCV
    	rc = read_and_update_ocv(chip, batt_temp, true);
    	..........
    	//读关机保存的soc和last_soc_uv
    	
    	rc = read_shutdown_ocv_soc(chip);
    
    	//这里判断是使用估计soc还是估值soc。如果chip->warm_reset 为真
    	if (chip->warm_reset) {
    		if (chip->shutdown_soc_invalid) { //这个是dtsi的一个配置选项,若没有配置,
    						//则不使用关机soc
    			est_ocv = estimate_ocv(chip); //估值soc
    			chip->last_ocv_uv = est_ocv;
    		} else {
    			chip->last_ocv_uv = chip->shutdown_ocv;//使用关机的soc和ocv
    			pr_err("Hyan %d : set chip->last_ocv_uv = %d
    ", __LINE__, chip->last_ocv_uv);
    			chip->last_soc = chip->shutdown_soc;
    			chip->calculated_soc = lookup_soc_ocv(chip,
    						chip->shutdown_ocv, batt_temp);
    		}
    	} else {
    
    		if (chip->workaround_flag & WRKARND_PON_OCV_COMP)
    			adjust_pon_ocv(chip, batt_temp);
    
    		 /* !warm_reset use PON OCV only if shutdown SOC is invalid */
    		chip->calculated_soc = lookup_soc_ocv(chip,
    					chip->last_ocv_uv, batt_temp);
    		if (!chip->shutdown_soc_invalid &&
    			(abs(chip->shutdown_soc - chip->calculated_soc) <
    				chip->dt.cfg_shutdown_soc_valid_limit)) {
    			chip->last_ocv_uv = chip->shutdown_ocv; 
    			chip->last_soc = chip->shutdown_soc;
    			chip->calculated_soc = lookup_soc_ocv(chip,
    						chip->shutdown_ocv, batt_temp);//使用估值soc
    			
    		} else {
    			chip->shutdown_soc_invalid = true; //使用关机soc
    			
    		}
    	}
    	.............
    	............
    }
    
    	//得到PON OCV
    	rc = read_and_update_ocv(chip, batt_temp, true);
    		ocv_uv = convert_vbatt_raw_to_uv(chip, ocv_data, is_pon_ocv);
    				uv = vadc_reading_to_uv(reading, true); //读ADC值
    				uv = adjust_vbatt_reading(chip, uv);   //转化为soc_uv
    				rc = qpnp_vbat_sns_comp_result(chip->vadc_dev, &uv, is_pon_ocv); //根据IC的类型,进行温度补偿
    	//从寄存器中读到储存的soc和ocv
    	read_shutdown_ocv_soc
    		rc = qpnp_read_wrapper(chip, (u8 *)&stored_ocv,
    				chip->base + BMS_OCV_REG, 2);
    		rc = qpnp_read_wrapper(chip, &stored_soc, chip->base + BMS_SOC_REG, 1);
    
    	adjust_pon_ocv(struct qpnp_bms_chip *chip, int batt_temp)
    		rc = qpnp_vadc_read(chip->vadc_dev, DIE_TEMP, &result); 
    		pc = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
    					batt_temp, chip->last_ocv_uv / 1000); //根据ocv和temp,查表得PC(soc)。
    		rbatt_mohm = get_rbatt(chip, pc, batt_temp); //根据soc和temp,得电池内阻zhi
    		/* convert die_temp to DECIDEGC */
    		die_temp = (int)result.physical / 100;     
    		current_ma = interpolate_current_comp(die_temp);  //当前电流
    		delta_uv = rbatt_mohm * current_ma;
    		chip->last_ocv_uv += delta_uv;   //修正last_ocv_uv
    
    	//这个函数主要根据last_ocv_uv,计算出soc的
    	lookup_soc_ocv(struct qpnp_bms_chip *chip, int ocv_uv, int batt_temp)
    			//查表得到soc_ocv,soc_cutoff
    			soc_ocv = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
    					batt_temp, ocv_uv / 1000);
    			soc_cutoff = interpolate_pc(chip->batt_data->pc_temp_ocv_lut,
    				batt_temp, chip->dt.cfg_v_cutoff_uv / 1000);
    
    			soc_final = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_cutoff),
    							(100 - soc_cutoff));
    
    			if (batt_temp > chip->dt.cfg_low_temp_threshold)
    				iavg_ma = calculate_uuc_iavg(chip);
    			else
    				iavg_ma = chip->current_now / 1000;
    			//查表得到FCC,ACC
    			fcc = interpolate_fcc(chip->batt_data->fcc_temp_lut,
    								batt_temp);
    			acc = interpolate_acc(chip->batt_data->ibat_acc_lut,
    							batt_temp, iavg_ma);
    			//计算出UUC
    			soc_uuc = ((fcc - acc) * 100) / fcc;
    
    			if (batt_temp > chip->dt.cfg_low_temp_threshold)
    				soc_uuc = adjust_uuc(chip, soc_uuc);
    			//得到soc_acc
    			soc_acc = DIV_ROUND_CLOSEST(100 * (soc_ocv - soc_uuc),
    							(100 - soc_uuc));
    
    			soc_final = soc_acc;   //这个为上报的soc
    			chip->last_acc = acc;

    3. 看工作线程,monitor_soc_work(struct work_struct *work):

    static void monitor_soc_work(struct work_struct *work)
    	calculate_delta_time(&chip->tm_sec, &chip->delta_time_s);
    	rc = get_batt_therm(chip, &batt_temp);
    	new_soc = lookup_soc_ocv(chip, chip->last_ocv_uv,batt_temp);
    	new_soc = clamp_soc_based_on_voltage(chip, new_soc);
    	report_vm_bms_soc(chip);//上报事件,上层得到消息,调用qpnp_vm_bms_power_get_property,获取相关的属性,计算出
    				last_ocv_uv,并通过qpnp_vm_bms_power_set_property方法,设置last_ocv_uv,并启动monitor_soc_work。
    

    4. 待续

    .........

  • 相关阅读:
    .NET Core使用RabbitMQ
    微信openid和unionid区别
    .Net Core 中GC的工作原理
    .Net Core服务监控报警指标上报Prometheus+Grafana
    .Net Core基础的健康检查
    了解数据库的四种隔离级别
    IdentityServer使用ssl证书生成token
    IdentityServer客户端授权模式
    IdentityServer用户名密码模式
    IdentityServer4学习笔记汇总(实现传送门在底部)
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/6556046.html
Copyright © 2011-2022 走看看