本文转载自:http://blog.csdn.net/xubin341719/article/details/8970363

android充电这块,有的电源管理芯片内部包含充电管理,如s5pv210上常用的AT8937。我们这次用的max77686没有充电控制这块,所以我们加入一个充电IC来控制,选用PM2301.

一、PM2301和主控、电池的逻辑

如下图所示:

1、蓝色部分:IIC控制接口,这个说得太多了,好多外围器件都是通过IIC控制的,这个一定要熟悉、熟悉、熟烂了,然后可以完成比较多的工作。

2、黄色部分:中断、使能控制脚,CHG_STATUS(IRQ)、 DC_IN_INT(WAKE_UP) 、 PM2301_LP(LPN)、CHARGER_EN(ENN)控制引脚;

IRQ:充电IC的状态,如果有动作通知主控;

WAKE_UP:如果有DC插入,产生中断通知主控;

LPN:

ENN:充电IC使能;

3、PM2301 、电池、系统电压的大致逻辑

标号1:系统电压有PM2301提供;

标号2:PM2301给电池充电;

标号3:系统电压有电池提供;

标号:1和标号:3不同时提供电压给系统,中间有一个MOS管切换;分两种情况:

(1)、不插充电器时,有电池提供电压给系统,走通道标号:3给系统供电;

(2)、插入DC后,系统侦测到DC插入,把3的通道关闭,打开1给系统供电,同时有2给电池充电;

二、PM2301硬件电路

如下所示:

Q5这个MOS管,就是控制系统供电的,没有充电时,VBATT有VBAT+提供,充电时,VBATT有SENSE_COMM提供。

控制脚对应主控的引脚:

IIC

IIC ID 为2

CHG_STATUS(IRQ)

EXYNOS4_GPX1(3)

DC_IN_INT(WAKE_UP)

EXYNOS4_GPX0(7)

PM2301_LP(LPN)

EXYNOS4_GPX1(7)

CHARGER_EN(ENN)

EXYNOS4_GPL2(0)

下图为PM2301的参考电路解法,同样看到P1控制VSYSTEM电源部分的切换控制。

下图为整个电池充电的过程控制:

Trickle mode、Constant current mode (CC mode or fast charge mode)、Constant voltage mode (CV mode) 、End of charge feature

三、PL2301驱动部分

PL2301的硬件、工作原理做简单的解释,接下来我们分析驱动程序:

驱动用到知识点:

IIC的注册;

任务初始化宏(在上一篇我们简单提过);

中断线程化;

1、IIC的注册

这个和上一篇所说的电量计相似;

(1)、pm2301驱动部分

  1. static const struct i2c_device_id pm2301_id[] = {
  2. { "pm2301", 0 },
  3. { }
  4. };
  5. MODULE_DEVICE_TABLE(i2c, pm2301_id);
  6. static struct i2c_driver pm2301_i2c_driver = {
  7. .driver = {
  8. .name   = "pm2301",
  9. },
  10. .probe      = pm2301_probe,
  11. .remove     = __devexit_p(pm2301_remove),
  12. .suspend    = pm2301_suspend,
  13. .resume     = pm2301_resume,
  14. .id_table   = pm2301_id,
  15. };
  16. static int __init pm2301_init(void)
  17. {
  18. printk(KERN_INFO "pm2301_init !!\n");
  19. return i2c_add_driver(&pm2301_i2c_driver);
  20. }
  21. module_init(pm2301_init);

(2)、平台驱动部分

arch/arm/mach-exynos/mach-smdk4x12.c

  1. static struct i2c_board_info i2c_devs1[] __initdata = {
  2. …………
  3. #ifdef CONFIG_CHARGER_PM2301
  4. {
  5. I2C_BOARD_INFO("pm2301", 0x2c),
  6. .platform_data  = &pm2301_platform_data,
  7. },
  8. #endif
  9. …………
  10. };

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

/sys/bus/i2c/drivers/pm2301

2、关于:pm2301_platform_data这个结构体

  1. static struct pm2301_platform_data pm2301_platform_data = {
  2. .hw_init = pm2301_hw_init,//(1)、硬件接口初始化化;
  3. .gpio_lpn = GPIO_PM2301_LP,//(2)、结构体初始化;
  4. .gpio_irq = GPIO_CHARGER_STATUS,
  5. .gpio_enn = GPIO_CHARGER_ENABLE,
  6. .gpio_wakeup = GPIO_CHARGER_ONLINE,
  7. };

arch/arm/mach-exynos/mach-smdk4x12.c

(1)、硬件接口初始化

  1. static int pm2301_hw_init(void)
  2. {
  3. printk("pm2301_hw_init !!\n");
  4. if (gpio_request(GPIO_CHARGER_ONLINE, "GPIO_CHARGER_ONLINE"))   {
  5. printk(KERN_ERR "%s :GPIO_CHARGER_ONLINE request port error!\n", __func__);
  6. goto err_gpio_failed;
  7. } else {
  8. s3c_gpio_setpull(GPIO_CHARGER_ONLINE, S3C_GPIO_PULL_NONE);
  9. s3c_gpio_cfgpin(GPIO_CHARGER_ONLINE, S3C_GPIO_SFN(0));
  10. gpio_direction_input(GPIO_CHARGER_ONLINE);
  11. gpio_free(GPIO_CHARGER_ONLINE);
  12. }
  13. if (gpio_request(GPIO_CHARGER_STATUS, "GPIO_CHARGER_STATUS"))   {
  14. printk(KERN_ERR "%s :GPIO_CHARGER_STATUS request port error!\n", __func__);
  15. goto err_gpio_failed;
  16. } else {
  17. s3c_gpio_setpull(GPIO_CHARGER_STATUS, S3C_GPIO_PULL_NONE);
  18. s3c_gpio_cfgpin(GPIO_CHARGER_STATUS, S3C_GPIO_SFN(0));
  19. gpio_direction_input(GPIO_CHARGER_STATUS);
  20. gpio_free(GPIO_CHARGER_STATUS);
  21. }
  22. if (gpio_request(GPIO_CHARGER_ENABLE, "GPIO_CHARGER_ENABLE"))   {
  23. printk(KERN_ERR "%s :GPIO_CHARGER_ENABLE request port error!\n", __func__);
  24. goto err_gpio_failed;
  25. } else {
  26. s3c_gpio_setpull(GPIO_CHARGER_ENABLE, S3C_GPIO_PULL_NONE);
  27. s3c_gpio_cfgpin(GPIO_CHARGER_ENABLE, S3C_GPIO_SFN(1));
  28. gpio_direction_output(GPIO_CHARGER_ENABLE, 0);
  29. gpio_free(GPIO_CHARGER_ENABLE);
  30. }
  31. if (gpio_request(GPIO_PM2301_LP, "GPIO_PM2301_LP")) {
  32. printk(KERN_ERR "%s :GPIO_PM2301_LP request port error!\n", __func__);
  33. goto err_gpio_failed;
  34. } else {
  35. s3c_gpio_setpull(GPIO_PM2301_LP, S3C_GPIO_PULL_NONE);
  36. s3c_gpio_cfgpin(GPIO_PM2301_LP, S3C_GPIO_SFN(1));
  37. gpio_direction_output(GPIO_PM2301_LP, 1);
  38. gpio_free(GPIO_PM2301_LP);
  39. }
  40. return 1;
  41. err_gpio_failed:
  42. return 0;
  43. }

(2)、结构体初始化

Include/Linux/pm2301_charger.h

  1. #define GPIO_CHARGER_ONLINE     EXYNOS4_GPX0(7)//对应控制脚的主控接口
  2. #define GPIO_CHARGER_STATUS     EXYNOS4_GPX1(3)
  3. #define GPIO_CHARGER_ENABLE     EXYNOS4_GPL2(0)
  4. #define GPIO_PM2301_LP          EXYNOS4_GPX1(7)
  5. struct pm2301_platform_data {
  6. int (*hw_init)(void);
  7. int gpio_enn;
  8. int gpio_wakeup;
  9. int gpio_irq;
  10. int gpio_lpn;
  11. };
  12. extern int pm2301_get_online(void);
  13. extern int pm2301_get_status(void);

3、probe函数分析

如果你是初学者,建议多看程序,你会发现,其实驱动程序的格式大多都是相同的,如这个IIC 器件的, 队列、定时器之类的东西。

  1. static int __devinit pm2301_probe(struct i2c_client *client,
  2. const struct i2c_device_id *id)
  3. {
  4. struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
  5. struct pm2301_chip *chip;
  6. int ret;
  7. printk(KERN_INFO "PM2301 probe !!\n");
  8. //(1)、前面这部分是对IIC的初始化;
  9. if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
  10. return -EIO;
  11. chip = kzalloc(sizeof(*chip), GFP_KERNEL);
  12. if (!chip)
  13. return -ENOMEM;
  14. g_chip = chip;
  15. chip->client = client;
  16. chip->pdata = client->dev.platform_data;
  17. i2c_set_clientdata(client, chip);
  18. /* Hardware Init for PM2301 */
  19. if (chip->pdata->hw_init && !(chip->pdata->hw_init())) {
  20. dev_err(&client->dev, "hardware initial failed.\n");
  21. goto err_hw_failed;
  22. }
  23. mutex_init(&i2c_lock);
  24. //(2)、初始化两个队列
  25. INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online, pm2301_online_work);
  26. INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status, pm2301_ststus_work);
  27. //(3)、中断线程化
  28. chip->irq_online = gpio_to_irq(chip->pdata->gpio_wakeup);
  29. chip->irq_status = gpio_to_irq(chip->pdata->gpio_irq);
  30. /* Request IRQ for PM2301 */
  31. ret = request_threaded_irq(chip->irq_online,
  32. NULL, pm2301_dcin,
  33. IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
  34. "PM2301 DC IN", chip);
  35. if (ret) {
  36. printk(KERN_ERR "Cannot request irq %d for DC (%d)\n",
  37. chip->irq_online, ret);
  38. goto err_hw_failed;
  39. }
  40. #ifdef PM2301_REPORT_STATUS_BY_IRQ
  41. ret = request_threaded_irq(chip->irq_status,
  42. NULL, pm2301_status,
  43. IRQF_TRIGGER_FALLING,
  44. "PM2301 STATUS", chip);
  45. if (ret) {
  46. printk(KERN_ERR "Cannot request irq %d for CHARGE STATUS (%d)\n",
  47. chip->irq_status, ret);
  48. goto err_hw_failed;
  49. }
  50. #endif
  51. charger_initial = 1;
  52. g_has_charged = 0;
  53. g_has_charging_full_or_stop = 0;
  54. #ifdef PM2301_REPORT_STATUS_BY_IRQ
  55. /* Set wakeup source for online pin*/
  56. irq_set_irq_wake(chip->irq_status, 1);
  57. #endif
  58. /* Set wakeup source for online pin*/
  59. irq_set_irq_wake(chip->irq_online, 1);
  60. /* Init default interrupt route for PM2301 */
  61. pm2301_reg_init(chip->client);
  62. /* Init online & status value */
  63. chip->online = pm2301_charger_online(chip);
  64. g_pm2301_online = chip->online;  /* Sync to global */
  65. pm2301_charger_enable(chip->client, chip->online);
  66. pm2301_charger_status(chip);
  67. printk(KERN_INFO "PM2301 probe success!!\n");
  68. return 0;
  69. err_hw_failed:
  70. dev_err(&client->dev, "failed: power supply register\n");
  71. i2c_set_clientdata(client, NULL);
  72. kfree(chip);
  73. return ret;
  74. }

(1)、前面这部分是对IIC的初始化

这部分就不再多说了,搞来搞去都是这个老样子;

(2)、任务初始化宏

  1. INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online, pm2301_online_work);
  2. INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status, pm2301_ststus_work);

把pm2301_online_work加入队列chip->work_online, pm2301_ststus_work加入chip->work_status队列。

(3)、中断线程化  request_threaded_irq

为什么要提出中断线程化?
在 Linux 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。但是,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。

看下我们程序中如何把中断线程化的:

  1. chip->irq_online = gpio_to_irq(chip->pdata->gpio_wakeup);
  2. chip->irq_status = gpio_to_irq(chip->pdata->gpio_irq);

看到这里是否想起:

  1. static struct pm2301_platform_data pm2301_platform_data = {
  2.     ………………
  3. .gpio_lpn = GPIO_PM2301_LP,
  4. .gpio_irq = GPIO_CHARGER_STATUS,
  5. .gpio_enn = GPIO_CHARGER_ENABLE,
  6. .gpio_wakeup = GPIO_CHARGER_ONLINE,
  7. };
  8. #define GPIO_CHARGER_ONLINE     EXYNOS4_GPX0(7)
  9. #define GPIO_CHARGER_STATUS     EXYNOS4_GPX1(3)
  10. #define GPIO_CHARGER_ENABLE         EXYNOS4_GPL2(0)
  11. #define GPIO_PM2301_LP          EXYNOS4_GPX1(7)

感觉申请个中断脚,这样有点费劲呀;

中断线程化:

  1. /* Request IRQ for PM2301 */
  2. ret = request_threaded_irq(chip->irq_online,
  3. NULL, pm2301_dcin,
  4. IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
  5. "PM2301 DC IN", chip);

当有插入DC中断出发时调用:

  1. static irqreturn_t pm2301_dcin(int irq, void *_data)
  2. {
  3. struct pm2301_chip *chip = _data;
  4. schedule_delayed_work(&chip->work_online, PM2301_DELAY);
  5. return IRQ_HANDLED;
  6. }

Pm2301_dcin调度队列:chip->work_online执行:pm2301_online_work函数

  1. static void pm2301_online_work(struct work_struct *work)
  2. {
  3. struct pm2301_chip *chip;
  4. chip = container_of(work, struct pm2301_chip, work_online.work);
  5. int new_online = pm2301_charger_online(chip);
  6. if (chip->online != new_online) {
  7. chip->online = new_online;
  8. g_pm2301_online = chip->online;  /* Sync to global */
  9. pm2301_charger_enable(chip->client, chip->online);//①、初始化充电IC;
  10. #ifdef PM2301_REPORT_STATUS_BY_IRQ
  11. /*To avoid status pin keep low*/
  12. schedule_delayed_work(&chip->work_status, 1000);
  13. #endif
  14. #if defined(CONFIG_BATTERY_MAX17040)
  15. TriggerGasgaugeUpdate();//②、把DC状态更新到max17040;
  16. #endif
  17. }
  18. }

①、初始化电IC

这里面主要是写一些寄存器

  1. static void pm2301_charger_enable(struct i2c_client *client, int online)
  2. {
  3. if (online) {   /* Enabled Charging*/
  4. int batt_capacity = 0;
  5. batt_capacity = GetGasgaugeCapacity();
  6. /* Don't start charging if battery capacity above 95% when DC plug in*/
  7. if(0) {
  8. //if( batt_capacity >= 95 ) {
  9. pm2301_write_reg(client, 0x01, 0x02);
  10. pm2301_write_reg(client, 0x26, 0x00);   /* always keep the register to 0 */
  11. } else {
  12. pm2301_write_reg(client, 0x00, 0x01);   /* force resume of charging */
  13. pm2301_write_reg(client, 0x01, 0x06);   /* ChEn=1, AutoResume=1 */
  14. pm2301_write_reg(client, 0x05, 0x7A);   /* ChEoccurrentLevel:150mA, ChPrechcurrentLevel:100mA, ChCCcurrentLevel:1000mA/2000mA */
  15. pm2301_write_reg(client, 0x06, 0x0A);   /* ChVersumeVot:3.6V ChPrechVoltLevel:2.9V */
  16. pm2301_write_reg(client, 0x07, 0x1E);   /* ChVoltLevel:4.25V */
  17. pm2301_write_reg(client, 0x26, 0x00);   /* always keep the register to 0 */
  18. }
  19. g_has_charged = 1;
  20. } else {    /* Disable Charging*/
  21. pm2301_write_reg(client, 0x01, 0x02);
  22. pm2301_write_reg(client, 0x26, 0x00);   /* always keep the register to 0 */
  23. g_has_charged = 0;
  24. }
  25. }

②、把DC状态更新到max17040

  1. TriggerGasgaugeUpdate()

插入DC这部流程如下:

android电池(五):电池 充电IC(PM2301)驱动分析篇【转】的更多相关文章

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

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

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

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

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

    本文转载自:http://blog.csdn.net/xubin341719/article/details/8969369 电池电量计,库仑计,用max17040这颗电量IC去计量电池电量,这种方法 ...

  4. 新款iPad Pro4的电池续航和充电速度对比

    3月18日晚苹果官网上架了两款新iPad Pro,两款iPad Pro 2020外观大小分别为11英寸和12.9英寸,搭载苹果A12Z仿生芯片,起售价分别为6288元和7899元.那么两款iPad P ...

  5. 【转】android 电容屏(三):驱动调试之驱动程序分析篇

    关键词:android  电容屏 tp 工作队列 中断 坐点计算  电容屏主要参数平台信息:内核:linux2.6/linux3.0系统:android/android4.0  平台:S5PV310( ...

  6. Android 8.1 关机充电动画(三)Android模式

    system:Android 8.1 platform:RK3326/PX30 uboot kernel system/core/healthd Android 8.1 关机充电动画(一)模式选择 A ...

  7. 【转】android 电容屏(二):驱动调试之基本概念篇

    关键词:android  电容屏 tp 工作队列 中断 多点触摸协议平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台:S5PV310(samsung ...

  8. 充电 IC 对 0V 电池充电问题

    只讨论锂聚合物电池,由于设置漏电流原因,有些锂电池会放电到 0V(正常是不应该的). 但是在时候就是会发电到 0V,这里就要考虑充电 IC 是否有对 0V 电池充电的功能,还有保护 IC 是否允许 0 ...

  9. 【转】Android LCD(四):LCD驱动调试篇

    关键词:android LCD TFTSN75LVDS83B  TTL-LVDS LCD电压背光电压 平台信息:内核:linux2.6/linux3.0系统:android/android4.0 平台 ...

随机推荐

  1. Android XMPP 即时通讯

    0 http://blog.csdn.net/lnb333666/article/details/7471292 0.1 http://www.cnblogs.com/charley_yang/arc ...

  2. JQuery加载并解析XML

    转自http://blog.csdn.net/pan_junbiao/article/details/7441003,致谢! 1.简述 XML(eXtensible Markup Language)即 ...

  3. logstash5安装并实现mariadb数据写入到elasticsearch

    java环境这里默认安装了 ,一般源码安装,这里就不说了 一.安装logstash 安装logstash可以用yum安装,也可以用源码安装: yum安装: 1.导入GPG: rpm --import ...

  4. 一个可以模拟GET,POST,PUT,DELET请求的HTTP在线工具

    一个简陋的HTTP请求工具,UI比较丑陋.0.0,可以用于接口调试. 之前在调试公司的远程接口的时候用的是curl,后来也在网上找到几种Http请求模拟的客户端程序.当时后来发现google app ...

  5. java编写socket使用bufferedReader.readLine()问题研究

    不写java代码好久,临时写个socket通讯竟然失败,郁闷之下仔细研究了下. 客户端使用BufferedReader来读取数据,在while中调用BufferedReader.readLine()函 ...

  6. use-svn-cmd(Linux)

    SVN是Subversion的简称,是一个开放源代码的版本控制系统,它采用了分支管理系统,是一个跨平台的软件,支持大多数常见的操作系统. svn命令行使用: 1.查看svn所支持的全部命令 $ svn ...

  7. 《TomCat与Java Web开发技术详解》(第二版) 第三章节的学习总结--利用Context元素来自定义web应用的存储位置

    在学习完第三章后(第三章提供的web应用是helloaapp,我将其放到了tomcat/webapps中),对Context元素的作用理解不深:但是当进入第四章后,发现第四章提供的源码包中也有一个叫h ...

  8. 【cogs182】【USACO Jan07】均衡队形【st表】

    题目描写叙述 农夫约翰的 N (1 ≤ N ≤ 50,000) 头奶牛,每天挤奶时总会按相同的顺序站好. 一日.农夫约翰决定为奶牛们举行一个"终极飞盘"比赛.为简化问题.他将从奶牛 ...

  9. zip文件压缩

    zip文件结构            上面中的每一行都是一个条目,zip文件就是由一个或者多个条目组成.      条目在Java中对应ZipEntry类       创建zip压缩文件      知 ...

  10. 【Mac系统 + Git】之上传项目代码到github上以及删除某个文件夹

    之前做开发的时候,用过一段时间git代码管理工具,用命令行操作感觉十分高大上,今天我想从头总结一篇Mac系统下如何利用git上传代码到github上的学习. 目录 一.安装Git 二.创建.ssh文件 ...