一种基于电池电压计算电池电量的算法的分析总结

这种算法应用在低成本的无界面的移动产品上,不依于赖库伦计,纯电压计算电压的方法。

首先,设置一些基本的变量:

struct capacity {
	int capacity;
	int min;
	int max;	
	int offset;
	int hysteresis;
};

struct battery_capacity {
	struct capacity *tables;
	int size;	
};

struct battery {
    ......
    struct device *dev;
    struct wake_lock wlock;
    struct power_supply psy;
    struct battery_monitor monitor;
    struct battery_property props;
    struct battery_parameter params;
    ......
};

再通过电池厂商提供的电池曲线制作成:

static struct capacity battery_capacity_tables[]= {
   /*percentage, min, max, hysteresis*/
	{0, 3306, 3426, 0, 10},
	{1, 3427, 3638, 0, 10},
	{10,3639, 3697, 0, 10},
	{20,3698, 3729, 0, 10},
	{30,3730, 3748, 0, 10},
	{40,3749, 3776, 0, 10},
	{50,3777, 3827, 0, 10},
	{60,3828, 3895, 0, 10},
	{70,3896, 3954, 0, 10},
	{80,3955, 4050, 0, 10},
	{90,4051, 4119, 0, 10},
	{100,4120,4240, 0, 10},
};

此处稍作解释一下:

在选取电压时,厂商提供的电压有OCV/CV两种,我选取的是CV作为上述表中的数据,这样做的目的是可以让电池尽早的充满,同时也保护电池,防止电压过高。但是MTK与高通的制作电池曲线中都是以OCV作为计算基础的。

定义两个全局变量:

......
static struct capacity *battery_capacity_tables = battery_capacity_tables;
static int cap_size = ARRAY_SIZE(battery_capacity_tables);
......
bat->params->cap.tables = battery_capacity_batbles;
bat->params->cap.size = cap_size;
......

有了参考数据,我们就可以开始计算电量了。

通过PMIC已有的采取电压函数进行电压采集。

static int battery_get_voltage(struct battery *bat)
{
    ......

    int vbat;
    int ret;
    int index;
    int vbat_sample[8] = {0};
    int count = ARRAY_SIZE(vbat_sample);
 
   ......

    for(index = 0; index < count; index++) {
        /*调用pmic的内部函数进行电压的读取,一般是通过ADC通道获取的*/
        ret = pmu_battery_get_vbat(bat, &(vbat_sample[index])); 
        if (!ret)
            break;
        mdelay(10);
    }

    ......

    vbat = battery_calcuate_average(vbat_sample, count);
    return vbat;
}

采集完了电压之后,就需要给采集到的电压进行计算。

static  int battery_calculate_average(int *battery_array, unsigned int num)
{
    int min = battery_array[0];
    int max = battery_array[0];
    int sum = 0;
    int index;

    for(index = 0; index < num; index++) {
        if (battery_array[index] > max)
           max = battery_array[index];
       else if (battery_array[index] < min) 
           min = battery_array[index];
       else
           ;
       sum += battery_array[index];
    }

    return ( num > 3 ? ((sum - max - min) / (num - 2)) : (sum / num));
}

这是对采集的电压进行初步计算,这还不够的,还要进行电压的平滑处理。计算电压的平均值。

#define MAX_CALCULATE_COUNT    48
#define BATTERY_VOLTAGE_ERROR_UP    (25 * 1000) /*uV*/
#define BATTERY_VOLTAGE_ERROR_DOWN    (-50 * 1000) /*uV*/
static int battery_calculate_average_voltage(struct battery *bat, int cur_voltage)
{
    static int vol_pre = 0;
    static int vol_error = 0;
    static int vol_recover = BATTERY_VOLTAGE_ERROR_UP;
    static unsigned char cur_index = 0;
    static int vol_array[MAX_CALCULATE_COUNT];
    int average = 0;
    static int pre_average = 0;
    int dv = 0;
    int index;

    if (!bat->props.persent) {
        vol_pre = 0;
        vol_error = 0;
        vol_recover = BATTERY_VOLTAGE_ERROR_UP;
        cur_index = 0
        max_index = 0;
        return cur_voltage;
    }

    /*对电池放电时电压进行检查并自动纠正*/
    do {
        if (vol_pre > 0) {
            dv = cur_voltage - vol_pre;
            if (dv <= BATTERY_VOLTAGE_ERROR_DOWN) { /*估算电池电压下降快,进行补偿*/
                vol_error = (dv * 9) / 10;
                vol_recover = - dv / 2;
            } else if (dv > vol_recover) { /*估算电池电压上升快,进行补偿*/
                vol_error = (dv * 9) / 10;
                vol_recover = dv / 2;
            } else {
                vol_error = 0;
                vol_recover = BATTERY_VOLTAGE_ERROR_UP;
            }
        }
        vol_pre = cur_voltage;
        cur_voltage -= vol_error;
    } while (0);
   
    vol_array[cur_index] = cur_voltage;
    cur_index++;
    if (cur_index >= ARRAY_SIZE(vol_array))
        cur_index = 0;
   
    if (max_index < ARRAY_SIZE(vol_array))
        max_index++;
 
    for (index = 0 ; index < max_index; index++) {
        average += vol_array[index];
    } 
    average = average / max_index;
   
    if (bat->props.state == POWER_SUPPLY_STATUS_DISCHARGING  \ 
        && pre_average > 0 && pre_average < average)
        return pre_average;
    
    pre_average = average;

    return average; 
}

电压计算好了,现在就要跟据电压结合电池曲线表进行电量的计算了。

static int battery_calculate_capacity(struct battery *bat, int battery_voltage)
{
    int vbat = battery_voltage;
    struct capacity *tables = bat->params->cap.tables;
    int high = bat->params->cap.size - 1;
    int low = 0;
    int offset = 0;
    int cap = 0;
    int v0 = 0;
    int c0 = 0;
    int v1 = 0;
    int c1 = 0;
    int deviation = 0;
    int compensated = bat->props.state == POWER_SUPPLY_STATUS_DISCHARGING ? false : true;

    if (!tables) {
        pr_err("capacity convert tables is not present!\n");
        return -EINVAL;
    }

    while (high >= low) {
        mid = (high + low) / 2;
        offset = (compensated ? tables[mid].offset : 0);
        if ((vbat + offset) < tables[mid].min)
            high = mid - 1;
        else if ((vbat + offset) > tables[mid].max)
            low = mid + 1;
        else
            break;
    }
   
    cap = tables[mid].capacity;
    if ( 0 < mid) && (mid < bat->params->cap.size - 1 ) {
        v0 = tables[mid].min;
        c0 = tables[mid].capacity;
        v1 = tables[mid + 1].min;
        c1 = tables[mid + 1].capacity;

        deviation = ((c1 - c0) * (vbat - v0) * 10) / (v1 - v0);
        cap = c0 + (deviation / 10 + ((deviation % 10 ) >= 5 ? 1 : 0));
        if (cap < c0)
            cap = c0;
        else if (cap > c1)
            cap = c1;
    }

    return cap;
}

到此,分析完毕。 

这是针对最近做的一个项目中发现这个依据电压计算电池电量算法整理的,感觉还不错,就分享出来。

 

 

 

你可能感兴趣的:(Linux学习总结,手机充电总结,Linux驱动总结)