前段时间比较烦躁,各种不想学习不想工作,于是休息了几天。这几天又下来任务了--调试充电电路和电池电量检测电路,于是又开始工作,顺便把调试过程记录下来。
平台: cpu 飞思卡尔imx6q 4核
充电芯片 MAX8903
电量检测芯片 MAX11801
android版本 android4.0
一、电量检测
我们用的电池电量检测芯片MAX11801其实是一款电阻触摸屏的驱动芯片,它外带一个AD采集引脚,因此我们用这个引脚来检测电池电压。MAX11801电源为3.3V而电池电压范围可能是0~4.2V,因此我们需要给电池电压分压。我们所用的电路如下
知道了硬件电路下面来 添加这个芯片的驱动,这是一个i2c的芯片,因此首先在board文件中添加i2c设备
I2C_BOARD_INFO("max11801", 0x48),
.platform_data = (void *)&max11801_mode,
.irq = gpio_to_irq(SABRESD_TS_INT),
},
然后添加这个芯片的驱动文件放在/drivers/input/touchiscreen/max11801_ts.c
对于这个驱动文件我们只要读取出AD的值就可以了,对于触摸屏部分我们并不需要,因此主要是下面几个函数
static u32 max11801_dcm_sample_aux(struct i2c_client *client)
{
u8 temp_buf;
int ret;
int aux = 0;
u32 sample_data = 0;
/* AUX_measurement*/
max11801_dcm_write_command(client, AUX_measurement);//发送AD采集命令
mdelay(5);
ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_MSB, //读取高字节数据
1, &temp_buf);
if (ret < 1)
printk(KERN_DEBUG "FIFO_RD_AUX_MSB read fails\n");
else
aux_buf[0] = temp_buf;
mdelay(5);
ret = i2c_smbus_read_i2c_block_data(client, FIFO_RD_AUX_LSB, //读取低字节数据
1, &temp_buf);
if (ret < 1)
printk(KERN_DEBUG "FIFO_RD_AUX_LSB read fails\n");
else
aux_buf[1] = temp_buf;
aux = (aux_buf[0] << 4) + //视最低4位无效并去掉
(aux_buf[1] >> 4);
/*
10k和18.7k并联后电阻
R=18.7*10/(18.7+10)=6.516
V(aux) = V(bat)*6.516/(6.516+18.7)
V(aux) = aux*3300/0xfff
V(bat) = aux*1386880/444717
*/
sample_data = (aux*1386880)/444717; //计算出电池电压
return sample_data;
}
u32 max11801_read_adc(void)
{
u32 adc_data;
adc_data = max11801_dcm_sample_aux(max11801_client);
// printk("----%s %d\n",__func__,adc_data); //lijianzhang
return adc_data;
}
EXPORT_SYMBOL_GPL(max11801_read_adc);
由于电池电量检测的驱动非常简单,而且和充电驱动关系非常密切,因此一般都卸载充电驱动里面,我们也是这么做的。下面的代码都是从充电驱动中摘出来的,因此当大家看到,一些设备文件和函数参数类型 都是充电驱动中的 时候不要太奇怪。
通过上面的max11801_read_adc函数我们已经得到了理论计算的电池的电压,但实际应用中由于分压电阻误差,焊接问题等,这个电压会有一定的误差因此需要一个校正函数
u32 calibration_voltage(struct max8903_data *data)
{
int volt[ADC_SAMPLE_COUNT];
u32 voltage_data;
int i;
for (i = 0; i < ADC_SAMPLE_COUNT; i++) { //多次采样,防止AD误差
if (data->charger_online == 0 && data->usb_charger_online == 0) {
/* ADC offset when battery is discharger*/
volt[i] = max11801_read_adc()-offset_discharger; //没有充电情况下 电压误差
} else {
if (data->charger_online == 1)
volt[i] = max11801_read_adc()-offset_charger;//DC充电式 电压误差
else if (data->usb_charger_online == 1)
volt[i] = max11801_read_adc()-offset_usb_charger;//usb充电 电压误差
else if (data->charger_online == 1 && data->usb_charger_online == 1)
volt[i] = max11801_read_adc()-offset_charger;
}
}
sort(volt, i, 4, cmp_func, NULL);//对电压排序
for (i = 0; i < ADC_SAMPLE_COUNT; i++)
pr_debug("volt_sorted[%2d]: %d\n", i, volt[i]);
/* get the average of second max/min of remained. */
voltage_data = (volt[2] + volt[ADC_SAMPLE_COUNT - 3]) / 2;//去掉最大值最小值 并对剩余数据求平均
return voltage_data;
}
从上面函数我们读取到了正确的电压值。电池电压是随时变化的,我们要检测电池电量,必须随时采集,因此用一个定时器来做这件事情,代码如下:
INIT_DELAYED_WORK(&data->work, max8903_battery_work);
schedule_delayed_work(&data->work, data->interval);
电压采集完成后就是将电压上报出去,上报的过程是:我们读取到电压变化->告诉android端电池电压变化了->android会通过power_supply设备文件来读取具体的电压值。
我们来看定时器回调函数
static void max8903_battery_wo