现在的位置: 首页 > 综合 > 正文

电池 电量计(MAX17040)驱动分析篇

2012年03月13日 ⁄ 综合 ⁄ 共 4493字 ⁄ 字号 评论关闭

关键词:android 电池  电量计  MAX17040 任务初始化宏 power_supply

平台信息:
内核:linux2.6/linux3.0
系统:android/android4.0 
平台:samsung exynos 4210、exynos 4412 exynos 5250

作者:xubin341719(欢迎转载,请注明作者)


完整驱动代码&规格书下载:MAX17040_PL2301

android 电池(一):锂电池基本原理篇

android 电池(二):android关机充电流程、充电画面显示

android 电池(三):android电池系统

android电池(四):电池 电量计(MAX17040)驱动分析篇

android电池(五):电池 充电IC(PM2301)驱动分析篇 

电池电量计,库仑计,用max17040这颗电量IC去计量电池电量,这种方法比较合理。想起比较遥远的年代,做samsung s5pc110/sp5v210的时候,计量电量用一个AD口加两个分压电阻就做了,低电量的时候系统一直判断不准确,“低电关机”提示一会有,一会没有,客户那个郁闷呀,“到底是有电还是没电?”。

如下图,通过两个分压电阻,和一个AD脚去侦测VCC(电池)电压。


一、MAX17040的工作原理

电量计MAX17040,他通过芯片去测量电池电量,芯片本身集成的电路比较复杂,同时可以通过软件上的一些算法去实现一些处理,是测量出的电量更加准确。还有一个好处,就是他之接输出数字量,通过IIC直接读取,我们在电路设计、程序处理上更加的统一化。

如下图所示,MAX17040和电池盒主控的关系,一个AD脚接到电池VBAT+,检测到的电量信息,通过IIC传到主控。


下面是电路图,电路接口比较简单,VBAT+,接到max17040CELLIIC接到主控的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驱动的注册:

[csharp] view
plain
copy

  1. static const struct i2c_device_id max17040_id[] = {  
  2.     { "max17040", 0 },  
  3.     { }  
  4. };  
  5. MODULE_DEVICE_TABLE(i2c, max17040_id);  
  6.   
  7. static struct i2c_driver max17040_i2c_driver = {  
  8.     .driver = {  
  9.         .name   = "max17040",  
  10.     },  
  11.     .probe      = max17040_probe,  
  12.     .remove     = __devexit_p(max17040_remove),  
  13.     .suspend    = max17040_suspend,  
  14.     .resume     = max17040_resume,  
  15.     .id_table   = max17040_id,  
  16. };  
  17.   
  18. static int __init max17040_init(void)  
  19. {  
  20.     printk("MAX17040 max17040_init !!\n");  
  21.     wake_lock_init(&vbus_wake_lock, WAKE_LOCK_SUSPEND, "vbus_present");  
  22.     return i2c_add_driver(&max17040_i2c_driver);  
  23. }  
  24. module_init(max17040_init);  

2)在arch/arm/mach-exynos/mach-smdk4x12.c中,IC平台驱动的注册:

[csharp] view
plain
copy

  1. static struct i2c_board_info i2c_devs2[] __initdata = {  
  2. #if defined(CONFIG_BATTERY_MAX17040)  
  3.     {  
  4.         I2C_BOARD_INFO("max17040", 0x36),//IIC地址;  
  5.         .platform_data = &max17040_platform_data,  
  6.     },  
  7. #endif  
  8. ……………………  
  9. };  

下图就是我们IIC驱动注册生成的文件;

/sys/bus/i2c/drivers/max17040 

2、linux 中定时器的使用

定时器,就是定一个时间, 比如:申请一个10秒定时器,linux系统开始计时,到10秒,请示器清零重新计时并发出信号告知系统计时完成,系统接到这个信号,做相应的处理;

[csharp] view
plain
copy

  1. #include <linux/delay.h>  
  2. #define MAX17040_DELAY          msecs_to_jiffies(5000)  

3任务初始化宏

[csharp] view
plain
copy

  1. INIT_WORK(work,func);  
  2. INTI_DELAYED_WORK(work,func);  
  3. INIT_DELAYED_WORK_DEFERRABLE(work,func);  

任务结构体的初始化完成后,接下来要将任务安排进工作队列。 可采用多种方法来完成这一操作。 首先,利用 queue_work 简单地将任务安排进工作队列(这将任务绑定到当前的 CPU)。 或者,可以通过 queue_work_on 来指定处理程序在哪个 CPU 上运行。 两个附加的函数为延迟任务提供相同的功能(其结构体装入结构体 work_struct 之中,并有一个 计时器用于任务延迟 )。

[csharp] view
plain
copy

  1. INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);  
  2. 调度函数 max17040_work加入chip->work队列;  

4、linux定时器调度队列

[csharp] view
plain
copy

  1. INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);  
  2. schedule_delayed_work(&chip->work, MAX17040_DELAY);  
  3. 通过定时器调度队列;  

5、max17040测到电量后如何上传到系统(这个电池系统中有简要的分析);

       4中的定时器记时完成,就可以调度队列,chip->work执行:max17040_work函数,把改读取的信息上传,我们看下max17040_work函数的实现:

[csharp] view
plain
copy

  1. static void max17040_work(struct work_struct *work)  
  2. {  
  3.     struct max17040_chip *chip;  
  4.     int old_usb_online, old_online, old_vcell, old_soc;  
  5.     chip = container_of(work, struct max17040_chip, work.work);  
  6.  
  7. #ifdef MAX17040_SUPPORT_CURVE  
  8.     /* The module need to be update per hour (60*60)/3 = 1200 */  
  9.     if (g_TimeCount >= 1200) {  
  10.         handle_model(0);  
  11.         g_TimeCount = 0;  
  12.     }  
  13.     g_TimeCount++;  
  14. #endif  
  15.   
  16.     old_online = chip->online;//(1)、保存老的电池信息,如电量、AC、USB是否插入;  
  17.     old_usb_online = chip->usb_online;  
  18.     old_vcell = chip->vcell;  
  19.     old_soc = chip->soc;  
  20.     max17040_get_online(chip->client);//(2)、读取电池新的状态信息  
  21.     max17040_get_vcell(chip->client);  
  22.     max17040_get_soc(chip->client);  
  23.     max17040_get_status(chip->client);  
  24.   
  25.     if ((old_vcell != chip->vcell) || (old_soc != chip->soc)) {//(3)、如果电池信息有变化,就上报系统;  
  26.         /* printk(KERN_DEBUG "power_supply_changed for battery\n"); */  
  27.         power_supply_changed(&chip->battery);  
  28.     }  
  29.  
【上篇】
【下篇】

抱歉!评论已关闭.