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

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

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

欢迎指正错误,共同学习、共同进步!!

转自: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", 0 },

{ }

};

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

#include

#define MAX17040_DELAY          msecs_to_jiffies(5000)

3、任务初始化宏

INIT_WORK(work,func);

INTI_DELAYED_WORK(work,func);

INIT_DELAYED_WORK_DEFERRABLE(work,func);

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

INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);

调度函数 max17040_work加入chip->work队列;

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

static void max17040_work(struct work_struct *work)

{

struct max17040_chip *chip;

int old_usb_online, old_online, old_vcell, old_soc;

chip = container_of(work, struct max17040_chip, work.work);

#ifdef MAX17040_SUPPORT_CURVE

/* The module need to be update per hour (60*60)/3 = 1200 */

if (g_TimeCount >= 1200) {

handle_model(0);

g_TimeCount = 0;

}

g_TimeCount++;

#endif

old_online = chip->online;//(1)、保存老的电池信息,如电量、AC、USB是否插入;

old_usb_online = chip->usb_online;

old_vcell = chip->vcell;

old_soc = chip->soc;

max17040_get_online(chip->client);//(2)、读取电池新的状态信息

max17040_get_vcell(chip->client);

max17040_get_soc(chip->client);

max17040_get_status(chip->client);

if ((old_vcell != chip->vcell) || (old_soc != chip->soc)) {//(3)、如果电池信息有变化,就上报系统;

/* printk(KERN_DEBUG "power_supply_changed for battery\n"); */

power_supply_changed(&chip->battery);

}

#if !defined(CONFIG_CHARGER_PM2301)//(4)、如果用PM2301充电IC,USB充电功能不用;

if (old_usb_online != chip->usb_online) {

/* printk(KERN_DEBUG "power_supply_changed for usb\n"); */

power_supply_changed(&chip->usb);

}

#endif

if (old_online != chip->online) {//(5)、如果有DC插入,则更新充电状态;

/* printk(KERN_DEBUG "power_supply_changed for AC\n"); */

power_supply_changed(&chip->ac);

}

schedule_delayed_work(&chip->work, MAX17040_DELAY);

}

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

old_online = chip->online;

old_usb_online = chip->usb_online;

old_vcell = chip->vcell;

old_soc = chip->soc;

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

max17040_get_online(chip->client);//读取是否有AC插入;

max17040_get_vcell(chip->client);//读取电池电压;(这个地方原来写错,多谢细心网友,更正!!)

max17040_get_soc(chip->client);//读取电池电量;

max17040_get_status(chip->client);//读取状态;

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

if ((old_vcell != chip->vcell) || (old_soc != chip->soc)) {

/* printk(KERN_DEBUG "power_supply_changed for battery\n"); */

power_supply_changed(&chip->battery);

}

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

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

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

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

power_supply_changed(&chip->ac);

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 = 0

FullAdjustment= 100

RCOMP0=161

TempCoUp =0

TempCoDown = -2

OCVTest = 56224

SOCCheckA = 113

SOCCheckB = 115

bits= 18

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[65] = {

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; //56224

unsigned char INI_OCVTest_Low_Byte = 0xA0;

unsigned char INI_SOCCheckA = 0x71;// 113

unsigned char INI_SOCCheckB = 0x73;//115

unsigned char INI_RCOMP = 0xa1;//161

unsigned char INI_bits = 18;

unsigned char original_OCV_1;

unsigned char original_OCV_2;

#elseunsigned char INI_RCOMP = 0x64;

unsigned char INI_bits = 19;

unsigned char original_OCV_1;

unsigned char original_OCV_2;

四、驱动分析

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 = 0;

handle_model(0);

#endif

max17040_get_version(client);

battery_initial = 1;

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 0;

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)、获取电池信息;

通过传递下来的参数,来读取结构体中相应的状态,这个函数实现比较简单。

static int max17040_get_property(struct power_supply *psy,

enum power_supply_property psp,

union power_supply_propval *val)

{

struct max17040_chip *chip = container_of(psy,

struct max17040_chip, battery);

switch (psp) {

case POWER_SUPPLY_PROP_STATUS:

val->intval = chip->status;

break;

case POWER_SUPPLY_PROP_ONLINE:

val->intval = chip->online;

break;

case POWER_SUPPLY_PROP_VOLTAGE_NOW:

case POWER_SUPPLY_PROP_PRESENT:

val->intval = chip->vcell;

if (psp  == POWER_SUPPLY_PROP_PRESENT)

val->intval = 1; /* You must never run Odrioid1 without Battery. */

break;

case POWER_SUPPLY_PROP_CAPACITY:

val->intval = chip->soc;

break;

case POWER_SUPPLY_PROP_TECHNOLOGY:

val->intval = POWER_SUPPLY_TECHNOLOGY_LION;

break;

case POWER_SUPPLY_PROP_HEALTH:

if (chip->vcell

val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;

else

val->intval = POWER_SUPPLY_HEALTH_GOOD;

break;

case POWER_SUPPLY_PROP_TEMP:

val->intval = 365;

break;

default:

return -EINVAL;

}

return 0;

}

(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上报。

power_supply_changed(&chip->battery);

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);

}

c语言 库仑计_android电池(四):电池 电量计(MAX17040)驱动分析篇相关推荐

  1. 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇

    关键词:android 电池  电量计  MAX17040 任务初始化宏 power_supply 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0  ...

  2. max17040C语言,电池 电量计(MAX17040)驱动分析篇

    关键词:android 电池  电量计  MAX17040 任务初始化宏power_supply 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0 平台 ...

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

    android电池(五):电池 充电IC(PM2301)驱动分析篇 关键词:android 电池  电量计  PL2301任务初始化宏 power_supply 中断线程化 平台信息: 内核:linu ...

  4. rk3399平台电量计cw2015驱动分析

    rk3399平台电量计cw2015驱动分析 文章目录 rk3399平台电量计cw2015驱动分析 cw2015电路设计 cw2015 2芯电池设计参考 电路设计参考注意事项 板级配置 dts配置实例 ...

  5. px4+vins+ego单机鲁棒飞行四(PX4飞控日志分析篇)

    px4+vins+ego单机鲁棒飞行四(PX4飞控日志分析篇) 一.FlightPlot安装 二.记录日志 二.取出日志 三.分析日志 一.FlightPlot安装 参考博客 参考视频 二.记录日志 ...

  6. android电池(五):电池 充电IC(PM2301)驱动分析篇【转】

    本文转载自:http://blog.csdn.net/xubin341719/article/details/8970363 android充电这块,有的电源管理芯片内部包含充电管理,如s5pv210 ...

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

    http://blog.csdn.net/xubin341719/article/details/8497830 关键词:android  电池关机充电 androidboot.mode charge ...

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

    关键词:android  电池关机充电 androidboot.mode charger 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0  平台:S5 ...

  9. Android Battery(四) 电池管理

    欢迎转载,转载请注明:http://blog.csdn.net/zhgxhuaa 说明 本篇将介绍省电管理篇,主要介绍一下Android的耗电情况和目前市面上<电池助手>类应用涉及到的一些 ...

  10. 【RGB手持补光棒调光照明方案】 单节双节电池LED升压恒流驱动调光芯片FP7208,PWM内部转模拟调光,无频闪顾虑低亮无抖动

    一:方案名称: [RGB手持补光棒调光照明方案] 单节双节电池LED升压恒流驱动调光芯片FP7208,PWM内部转模拟调光,无频闪顾虑低亮无抖动 二:方案描述: FP7208是一颗异步升压LED驱动I ...

最新文章

  1. centos7下使用yum安装mysql并创建用户,数据库以及设置远程访问
  2. 生产者跟消费者问题(C++实现)
  3. 二进制代码查看器Binary Viewer下载教程
  4. 爬虫-性能相关- twisted-tornado
  5. 数据结构名词解释(考试没有,供参考)
  6. 2.TCP/IP 详解卷1 --- 链路层
  7. 2022Android设备唯一标识(AndroidID,OAID等 )
  8. apkg格式怎么打开_干货:pdf转换器简单、好用,还能在线互转文件格式!
  9. 唐福林《新浪微博开放平台中的Redis实践》演讲视频
  10. Firefox插件Xmarks的使用方法
  11. 为什么中国难以诞生像SAP、甲骨文一样的企业级服务公司?
  12. python:在指定范围内按学号随机生成座位顺序,并分行输出
  13. ACAD DWG to PDF Converter 9.8.2.4版本更新啦
  14. 智能创新引导工具----项目分析与设计
  15. 好看的皮囊千篇一律,有趣的灵魂万里挑一
  16. 基于jsp+java+ssm的农产品购物商城系统-计算机毕业设计
  17. 《k3s 源码解析4 ---- k3s重要数据结构》
  18. 云主机的优势有哪些?
  19. 计算机网络期末大题汇总
  20. 用Cocos2d-JS制作类神经猫的游戏《你是我的小羊驼》 转载

热门文章

  1. 深入浅出统计学 笔记 总结 学习心得
  2. 计算机病毒黑色星期五制造者,计算机病毒复习资料
  3. 服务器修改开机启动项,启动项设置_服务器开机启动项
  4. 【C语言编程】实现猜数字游戏
  5. 21天学通JAVA——学习笔记
  6. 微软影子系统EWF软件用法及参数描述
  7. WiFi图标在任务栏里不见了,提示:适配器Qualcomn Atheros QCA9377 Wireless Network Adapter遇到驱动程序或硬件相关的问题
  8. DSS的Windows版本如何安装呢?
  9. 中文版通用工程师软件DPS 别克雪佛兰编程改装
  10. 数据库网页搭建教程(二)——数据库网页设计