zoukankan      html  css  js  c++  java
  • android电池(四):电池 电量计(MAX17040)驱动分析篇


    电池电量计,库仑计,用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驱动的注册:

    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 !! ");  
    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平台驱动的注册:

    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秒,定时器 清零重新计时并发出信号告知系统计时完成,系统接到这个信号,做相应的处理;

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



    3任务初始化宏

    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 之中,并有一个 计时器用于任务延迟 )。

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

    4、linux定时器调度队列

    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函数的实现:

    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 "); */  
    27.         power_supply_changed(&chip->battery);  
    28.     }  
    29.  
    30. #if !defined(CONFIG_CHARGER_PM2301)//(4)、如果用PM2301充电IC,USB充电功能不用;  
    31.     if (old_usb_online != chip->usb_online) {  
    32.         /* printk(KERN_DEBUG "power_supply_changed for usb "); */  
    33.   
    34.         power_supply_changed(&chip->usb);  
    35.     }  
    36. #endif  
    37.   
    38.     if (old_online != chip->online) {//(5)、如果有DC插入,则更新充电状态;  
    39.         /* printk(KERN_DEBUG "power_supply_changed for AC "); */  
    40.         power_supply_changed(&chip->ac);  
    41.     }  
    42.   
    43.     schedule_delayed_work(&chip->work, MAX17040_DELAY);  


    1、保存老的电池信息,如电量、AC、USB是否插入

    1. old_online = chip->online;  
    2. old_usb_online = chip->usb_online;  
    3. old_vcell = chip->vcell;  
    4. old_soc = chip->soc;  

    2、读取电池新的状态信息

    1. max17040_get_online(chip->client);//读取是否有AC插入;  
    2. max17040_get_vcell(chip->client);//读取电池电压;(这个地方原来写错,多谢细心网友,更正!!)  
    3. max17040_get_soc(chip->client);//读取电池电量;  
    4. max17040_get_status(chip->client);//读取状态;  

    3、如果电池信息有变化,就上报系统

    1. if ((old_vcell != chip->vcell) || (old_soc != chip->soc)) {  
    2.     /* printk(KERN_DEBUG "power_supply_changed for battery "); */  
    3.     power_supply_changed(&chip->battery);  
    4. }  

    power_supply_changed这个函数比较重要, 我们后面分析;

    4、如果用PM2301充电IC,USB充电功能不用

    这个是由于我们的系统耗电比较大,用USB充电时,电流过小,所以出现越充越少的现象,所以这个功能给去掉了。

    5、如果有DC插入,则跟新充电状态

    1. power_supply_changed(&chip->ac); 

    6、AC、USB充电状态怎么更新到应用

    如上面所说,通过power_supply_changed上报;






















  • 相关阅读:
    windows C++ 获得一个进程的线程数目
    乌龟 SVN 在update后,如何知道 update前的版本号?
    C++ 类中的static对象貌似不能是类对象
    WinForms C#:html编辑器工程源码,含直接写WebBrowser的文件流、IPersistStreamInit接口的声明和一些相关的小方法
    用Remoting 实现一个文件传输组件
    [转载]Extreme Game Programming
    C#:10进制转2进制函数
    bindows 源码格式化工具(测试中的测试)C#源码
    明天又是周末,可以好好休息了!
    将文件加入到图形文件里;
  • 原文地址:https://www.cnblogs.com/liulaolaiu/p/11744582.html
Copyright © 2011-2022 走看看