android电池(四):电池 电量计(MAX17040)驱动分析篇【转】
本文转载自:http://blog.csdn.net/xubin341719/article/details/8969369
电池电量计,库仑计,用max17040这颗电量IC去计量电池电量,这种方法比较合理。想起比较遥远的年代,做samsung s5pc110/sp5v210的时候,计量电量用一个AD口加两个分压电阻就做了,低电量的时候系统一直判断不准确,“低电关机”提示一会有,一会没有,客户那个郁闷呀,“到底是有电还是没电?”。
如下图,通过两个分压电阻,和一个AD脚去侦测VCC(电池)电压。
一、MAX17040的工作原理
电量计MAX17040,他通过芯片去测量电池电量,芯片本身集成的电路比较复杂,同时可以通过软件上的一些算法去实现一些处理,是测量出的电量更加准确。还有一个好处,就是他之接输出数字量,通过IIC直接读取,我们在电路设计、程序处理上更加的统一化。
如下图所示,MAX17040和电池盒主控的关系,一个AD脚接到电池VBAT+,检测到的电量信息,通过IIC传到主控。
下面是电路图,电路接口比较简单,VBAT+,接到max17040的CELL,IIC接到主控的IIC2接口,这个我们在程序中要配置。看这个器件比较简单吧。
看下max17040的内部结构,其实这也是一个AD转换的过程,单独一颗芯片去实现,这样看起来比较专业些。CELL接口,其实就是一个ADC转换的引脚,我们可以看到芯片内部有自己的时钟(time base),IIC控制器之类的,通过CELL采集到的模拟量,转换成数字量,传输给主控。
通过上面的介绍Max17040的硬件、原理我们基本上都了解了,比较简单,下面我们就重点去分析下驱动程序。
二、MAX17040 总体流程
电量计的工作流程比较简单,max17040通过CELL ADC转换引脚,把电池的相关信息,实时读取,存入max17040相应的寄存器,驱动申请一个定时器,记时结束,通过IIC去读取电池状态信息,和老的电池信息对比,如果用变化上报,然后重新计时;这样循环操作,流程如下所示:
三、MAX17040这个电量计驱动,我们主要用到以下知识点
1、IIC的注册(这个在TP、CAMERA中都有分析);
2、Linux 中定时器的使用;
3、任务初始化宏;
4、linux定时器调度队列;
5、max17040测到电量后如何上传到系统(这个电池系统中有简要的分析);
6、AC、USB充电状态的上报,这个和电池电量是一种方法。
7、电池曲线的测量与加入;
1、IIC的注册
IIC这个总线,在工作中用的比较多,TP、CAMERA、电量计、充电IC、音频芯片、电源管理芯片、基本所有的传感器,所以这大家要仔细看下,后面有时间的话单独列一片介绍下IIC,从单片机时代都用的比较多,看来条总线的生命力很强,像C语言一样,很难被同类的东西替代到,至少现在应该是这样的。
看下他结构体的初始化与驱动的申请,这个比较统一,这里就不想想解释了。
(1)、IIC驱动的注册:
static const struct i2c_device_id max17040_id[] = {
{ "max17040", },
{ }
};
MODULE_DEVICE_TABLE(i2c, max17040_id); static struct i2c_driver max17040_i2c_driver = {
.driver = {
.name = "max17040",
},
.probe = max17040_probe,
.remove = __devexit_p(max17040_remove),
.suspend = max17040_suspend,
.resume = max17040_resume,
.id_table = max17040_id,
}; static int __init max17040_init(void)
{
printk("MAX17040 max17040_init !!\n");
wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present");
return i2c_add_driver(&max17040_i2c_driver);
}
module_init(max17040_init);
(2)在arch/arm/mach-exynos/mach-smdk4x12.c中,IC平台驱动的注册:
static struct i2c_board_info i2c_devs2[] __initdata = {
#if defined(CONFIG_BATTERY_MAX17040)
{
I2C_BOARD_INFO("max17040", 0x36),//IIC地址;
.platform_data = &max17040_platform_data,
},
#endif
……………………
};
下图就是我们IIC驱动注册生成的文件;
/sys/bus/i2c/drivers/max17040
2、linux 中定时器的使用
定时器,就是定一个时间, 比如:申请一个10秒定时器,linux系统开始计时,到10秒,请示器清零重新计时并发出信号告知系统计时完成,系统接到这个信号,做相应的处理;
3、任务初始化宏
任务结构体的初始化完成后,接下来要将任务安排进工作队列。 可采用多种方法来完成这一操作。 首先,利用 queue_work 简单地将任务安排进工作队列(这将任务绑定到当前的 CPU)。 或者,可以通过 queue_work_on 来指定处理程序在哪个 CPU 上运行。 两个附加的函数为延迟任务提供相同的功能(其结构体装入结构体 work_struct 之中,并有一个 计时器用于任务延迟 )。
4、linux定时器调度队列
INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);
schedule_delayed_work(&chip->work, MAX17040_DELAY);
通过定时器调度队列;
5、max17040测到电量后如何上传到系统(这个电池系统中有简要的分析);
4中的定时器记时完成,就可以调度队列,chip->work执行:max17040_work函数,把改读取的信息上传,我们看下max17040_work函数的实现:
(1)、保存老的电池信息,如电量、AC、USB是否插入
(2)、读取电池新的状态信息
(3)、如果电池信息有变化,就上报系统
power_supply_changed这个函数比较重要, 我们后面分析;
(4)、如果用PM2301充电IC,USB充电功能不用
这个是由于我们的系统耗电比较大,用USB充电时,电流过小,所以出现越充越少的现象,所以这个功能给去掉了。
(5)、如果有DC插入,则跟新充电状态
6、AC、USB充电状态怎么更新到应用
如上面所说,通过power_supply_changed上报;
7、电池曲线的测量与加入
电池曲线,就是电池的冲放电信息,就是用专业的设备,对电池连续充放电几天,测出一个比较平均的值。然后转换成针对电量IC(如我们用的max17040)的数字量,填入一个数组中,如下图所示:
下面数据时针对电池曲线的数字量,和相关参数。如上图所示,为160小时的电池信息,包括:不同颜色分别代表不同的曲线:如temperature ,reference SOC ,fuel gauge SOC,Vcell,Empty Voltage
数据表格如下:
Device=MAX17040
Title = 1055_2_113012
EmptyAdjustment =
FullAdjustment=
RCOMP0=
TempCoUp =
TempCoDown = -
OCVTest =
SOCCheckA =
SOCCheckB =
bits=
0xC2 0xE8 0x0D 0x37 0x51 0x5B 0x5E 0x62
0x6A 0x88 0xA6 0xCB 0xF1 0x3C 0x99 0x1A
0x60 0x0D 0x80 0x0D 0xA0 0x01 0xC0 0x0C
0xF0 0x0F 0x30 0x0F 0x90 0x06 0x10 0x06 0xAC 0x20 0xAE 0x80 0xB0 0xD0 0xB3 0x70
0xB5 0x10 0xB5 0xB0 0xB5 0xE0 0xB6 0x20
0xB6 0xA0 0xB8 0x80 0xBA 0x60 0xBC 0xB0
0xBF 0x10 0xC3 0xC0 0xC9 0x90 0xD1 0xA0
0x02 0x90 0x0E 0x00 0x0C 0x10 0x0E 0x20
0x2C 0x60 0x4C 0xB0 0x39 0x80 0x39 0x80
0x0C 0xD0 0x0C 0xD0 0x0A 0x10 0x09 0xC0
0x08 0xF0 0x07 0xF0 0x05 0x60 0x05 0x60 0xC0 0x09 0xE0 0x00 0x00 0x01 0x30 0x02
0x52 0x06 0x54 0x0B 0x53 0x080x63 0x08
0x29 0xE0 0xC1 0xE2 0xC6 0xCB 0x98 0x98
0xCD 0xCD 0xA1 0x9C 0x8F 0x7F 0x56 0x56
加入驱动中的值:
/driver/power/max17040_common.c中
unsigned char model_data[] = {
0x40, /* 1st field is start reg address, others are model parameters */
0xAC, 0x20,0xAE, 0x80, 0xB0, 0xD0, 0xB3, 0x70,
0xB5, 0x10, 0xB5, 0xB0, 0xB5, 0xE0,0xB6, 0x20,
0xB6, 0xA0, 0xB8, 0x80, 0xBA, 0x60, 0xBC, 0xB0,
0xBF, 0x10, 0xC3, 0xC0, 0xC9, 0x90, 0xD1, 0xA0,
0x02, 0x90, 0x0E, 0x00, 0x0C, 0x10,0x0E, 0x20,
0x2C, 0x60,0x4C, 0xB0, 0x39, 0x80, 0x39, 0x80,
0x0C, 0xD0,0x0C, 0xD0, 0x0A, 0x10,0x09, 0xC0,
0x08, 0xF0, 0x07, 0xF0, 0x05, 0x60, 0x05, 0x60,
}; unsigned char INI_OCVTest_High_Byte = 0xDB; //
unsigned char INI_OCVTest_Low_Byte = 0xA0;
unsigned char INI_SOCCheckA = 0x71;//
unsigned char INI_SOCCheckB = 0x73;//
unsigned char INI_RCOMP = 0xa1;//
unsigned char INI_bits = ;
unsigned char original_OCV_1;
unsigned char original_OCV_2;
#elseunsigned char INI_RCOMP = 0x64;
unsigned char INI_bits = ;
unsigned char original_OCV_1;
<strong>unsigned char original_OCV_2;</strong>
四、驱动分析
1、Probe函数分析
上面我们简单了解驱动中用到的主要知识点,后面我们把这些点串起来,驱动还是从probe说起;
static int __devinit max17040_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct max17040_chip *chip;
int ret;
printk("MAX17040 probe !!\n"); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
return -EIO; chip = kzalloc(sizeof(*chip), GFP_KERNEL); if (!chip)
return -ENOMEM; g_chip = chip;
g_i2c_client = client;//(1)、IIC 驱动部分client 申请; chip->client = client;
chip->pdata = client->dev.platform_data;
i2c_set_clientdata(client, chip);
chip->battery.name = "battery";//(2)、chip name;
chip->battery.type = POWER_SUPPLY_TYPE_BATTERY;
chip->battery.get_property = max17040_get_property;//(3)、获取电池信息;
chip->battery.properties = max17040_battery_props;//(4)、电池各种信息;
chip->battery.num_properties = ARRAY_SIZE(max17040_battery_props);
chip->battery.external_power_changed = NULL;
ret = power_supply_register(&client->dev, &chip->battery);//(5)、battery加入power_supply
if (ret)
goto err_battery_failed; chip->ac.name = "ac"
chip->ac.type = POWER_SUPPLY_TYPE_MAINS;
chip->ac.get_property = adapter_get_property;
chip->ac.properties = adapter_get_props;
chip->ac.num_properties = ARRAY_SIZE(adapter_get_props);
chip->ac.external_power_changed = NULL;
ret = power_supply_register(&client->dev, &chip->ac);//(6)、和battery相似,把ac加入power_supply
if (ret)
goto err_ac_failed; #if !defined(CONFIG_CHARGER_PM2301)
chip->usb.name = "usb";
chip->usb.type = POWER_SUPPLY_TYPE_USB;
chip->usb.get_property = usb_get_property;
chip->usb.properties = usb_get_props;
chip->usb.num_properties = ARRAY_SIZE(usb_get_props);
chip->usb.external_power_changed = NULL;
ret = power_supply_register(&client->dev, &chip->usb);//(7)、和battery相似,把usb加入power_supply
if (ret)
goto err_usb_failed; if (chip->pdata->hw_init && !(chip->pdata->hw_init())) {
dev_err(&client->dev, "hardware initial failed.\n");
goto err_hw_init_failed;
}
#endif #ifdef MAX17040_SUPPORT_CURVE
g_TimeCount = ;
handle_model();
#endif
max17040_get_version(client);
battery_initial = ; INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);//(8)、任务宏初始化,max17040加入chip->work队列;
schedule_delayed_work(&chip->work, MAX17040_DELAY);//(9)、通过定时器调度队列; printk("MAX17040 probe success!!\n");
return ; err_hw_init_failed:
power_supply_unregister(&chip->usb);
err_usb_failed:
power_supply_unregister(&chip->ac);
err_ac_failed:
power_supply_unregister(&chip->battery);
err_battery_failed:
dev_err(&client->dev, "failed: power supply register\n");
i2c_set_clientdata(client, NULL);
kfree(chip);
return ret;
}
(1)、IIC 驱动部分client 申请;
(2)、chip name;
(3)、获取电池信息;
通过传递下来的参数,来读取结构体中相应的状态,这个函数实现比较简单。
(4)电池各种信息
static enum power_supply_property max17040_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_STATUS,
/*POWER_SUPPLY_PROP_ONLINE,*/
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_TEMP,
};
(5)、battery加入power_supply;
(6)、和battery相似,把ac加入power_supply;
(7)、和battery相似,把usb加入power_supply;
(8)、max17040加入chip->work队列;
前面已经分析;
(9)、通过定时器调度队列;
前面已经分析;
2、power_supply_changed简要分析
如:把电池电量信息上报:我们在max17040_work队列调度函数中, 如果有电池信息、状态变化,则上用power_supply_changed上报。
Kernel/drivers/power/power_supply_core.c中:
void power_supply_changed(struct power_supply *psy)
{
unsigned long flags; dev_dbg(psy->dev, "%s\n", __func__); spin_lock_irqsave(&psy->changed_lock, flags);
psy->changed = true;
wake_lock(&psy->work_wake_lock);
spin_unlock_irqrestore(&psy->changed_lock, flags);
schedule_work(&psy->changed_work);//调度psy->changed_work
}
Psy->changed_work的执行函数:
static void power_supply_changed_work(struct work_struct *work)
{
unsigned long flags;
struct power_supply *psy = container_of(work, struct power_supply,
changed_work); dev_dbg(psy->dev, "%s\n", __func__); spin_lock_irqsave(&psy->changed_lock, flags);
if (psy->changed) {
psy->changed = false;
spin_unlock_irqrestore(&psy->changed_lock, flags); class_for_each_device(power_supply_class, NULL, psy,
__power_supply_changed_work); power_supply_update_leds(psy); kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);//uevent状态
spin_lock_irqsave(&psy->changed_lock, flags);
}
if (!psy->changed)
wake_unlock(&psy->work_wake_lock);
spin_unlock_irqrestore(&psy->changed_lock, flags);
}
android电池(四):电池 电量计(MAX17040)驱动分析篇【转】的更多相关文章
- 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇
关键词:android 电池 电量计 MAX17040 任务初始化宏 power_supply 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台: ...
- 【转】android电池(五):电池 充电IC(PM2301)驱动分析篇
关键词:android 电池 电量计 PL2301任务初始化宏 power_supply 中断线程化 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 ...
- android电池(五):电池 充电IC(PM2301)驱动分析篇【转】
本文转载自:http://blog.csdn.net/xubin341719/article/details/8970363 android充电这块,有的电源管理芯片内部包含充电管理,如s5pv210 ...
- 【转】android 电池(一):锂电池基本原理篇
关键词:android 电池关机充电 androidboot.mode charger 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV3 ...
- 【转】android 电容屏(三):驱动调试之驱动程序分析篇
关键词:android 电容屏 tp 工作队列 中断 坐点计算 电容屏主要参数平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310( ...
- [Android6.0][RK3399] 电池系统(三)电量计 CW2015 驱动流程分析【转】
本文转载自:http://blog.csdn.net/dearsq/article/details/72770295 Platform: RK3399 OS: Android 6.0 Kernel: ...
- 【转】Android LCD(四):LCD驱动调试篇
关键词:android LCD TFTSN75LVDS83B TTL-LVDS LCD电压背光电压 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台 ...
- 【转】android camera(四):camera 驱动 GT2005
关键词:android camera CMM 模组 camera参数 GT2005 摄像头常见问题 平台信息: 内核:linux系统:android 平台:S5PV310(samsung exyn ...
- Android : 跟我学Binder --- (4) 驱动情景分析
目录: Android : 跟我学Binder --- (1) 什么是Binder IPC?为何要使用Binder机制? Android : 跟我学Binder --- (2) AIDL分析及手动实现 ...
随机推荐
- 转 【MQTT】在Windows下搭建MQTT服务器
MQTT简介 MQ 遥测传输 (MQTT) 是轻量级基于代理的发布/订阅的消息传输协议,设计思想是开放.简单.轻量.易于实现.这些特点使它适用于受限环境.该协议的特点有: 使用发布/订阅消息模式,提供 ...
- Android_Fragment_Fragment详解
Android_Fragment_Fragment详解 分类: Android基础2013-10-03 08:23 92人阅读 评论(0) 收藏 举报 AndroidFragmentFragmen ...
- Struts2学习之拦截器栈
© 版权声明:本文为博主原创文章,转载请注明出处 拦截器栈: - 从结构上看:拦截器栈相当于多个拦截器的组合 - 从功能上看:拦截器栈也是拦截器 默认拦截器栈: - 在struts-core.jar中 ...
- CSRF介绍与应对以及Java代码示例
详细信息看这里:https://www.ibm.com/developerworks/cn/web/1102_niugang_csrf/ 简介 CSRF(Cross Site Request Forg ...
- 各种类型的电影排行榜-movie路线
[最费脑力的14部电影]<盗梦空间>.<记忆裂痕>.<生死停留>.<死亡幻觉>.<禁闭岛>.<穆赫兰道>.<蝴蝶效应> ...
- 多媒体开发之--- live555 vs2010/vs2013下编译,使用,测试
Ⅰ live555简介 Live555 是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了对标准流媒体传输协议如RTP/RTCP.RTSP.SIP等的支持.Live555实现了对多种音视频编 ...
- MyEclipse中Save could not be completed
在MyEclipse下编程时,保存的时候,假设出现例如以下图所看到的错误: - 刘立 - 707903908的博客" src="http://img0.ph.126.net/9y4 ...
- 安装Hadoop 1.1.2 (一 安装JDK)
1 下载jdk1.7 xxx .rpm 2 以Root权限登陆 3 修改文件权限 chmod +x jdk-7u25-linux-x64.rpm 4 安装 JDK rpm -ivh jdk-7u2 ...
- -[ASIDataCompressor compressBytes:length:error:shouldFinish:] in ASIDataCompressor.o
本文转载至 http://blog.csdn.net/m372897500/article/details/38293973 -[ASIDataCompressor compressBytes:len ...
- redis 集群 搭建
环境: centos6.5 192.168.16.11 centos6.5 192.168.16.12 centos6.5 192.168.16.13 三台虚拟机模拟9个节点,一台机器3个节点,创建出 ...