android电池(五):电池 充电IC(PM2301)驱动分析篇【转】
本文转载自: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驱动部分
- static const struct i2c_device_id pm2301_id[] = {
- { "pm2301", 0 },
- { }
- };
- MODULE_DEVICE_TABLE(i2c, pm2301_id);
- static struct i2c_driver pm2301_i2c_driver = {
- .driver = {
- .name = "pm2301",
- },
- .probe = pm2301_probe,
- .remove = __devexit_p(pm2301_remove),
- .suspend = pm2301_suspend,
- .resume = pm2301_resume,
- .id_table = pm2301_id,
- };
- static int __init pm2301_init(void)
- {
- printk(KERN_INFO "pm2301_init !!\n");
- return i2c_add_driver(&pm2301_i2c_driver);
- }
- module_init(pm2301_init);
(2)、平台驱动部分
arch/arm/mach-exynos/mach-smdk4x12.c
- static struct i2c_board_info i2c_devs1[] __initdata = {
- …………
- #ifdef CONFIG_CHARGER_PM2301
- {
- I2C_BOARD_INFO("pm2301", 0x2c),
- .platform_data = &pm2301_platform_data,
- },
- #endif
- …………
- };
下图就是我们IIC驱动注册生成的文件;
/sys/bus/i2c/drivers/pm2301
2、关于:pm2301_platform_data这个结构体
- static struct pm2301_platform_data pm2301_platform_data = {
- .hw_init = pm2301_hw_init,//(1)、硬件接口初始化化;
- .gpio_lpn = GPIO_PM2301_LP,//(2)、结构体初始化;
- .gpio_irq = GPIO_CHARGER_STATUS,
- .gpio_enn = GPIO_CHARGER_ENABLE,
- .gpio_wakeup = GPIO_CHARGER_ONLINE,
- };
arch/arm/mach-exynos/mach-smdk4x12.c
(1)、硬件接口初始化
- static int pm2301_hw_init(void)
- {
- printk("pm2301_hw_init !!\n");
- if (gpio_request(GPIO_CHARGER_ONLINE, "GPIO_CHARGER_ONLINE")) {
- printk(KERN_ERR "%s :GPIO_CHARGER_ONLINE request port error!\n", __func__);
- goto err_gpio_failed;
- } else {
- s3c_gpio_setpull(GPIO_CHARGER_ONLINE, S3C_GPIO_PULL_NONE);
- s3c_gpio_cfgpin(GPIO_CHARGER_ONLINE, S3C_GPIO_SFN(0));
- gpio_direction_input(GPIO_CHARGER_ONLINE);
- gpio_free(GPIO_CHARGER_ONLINE);
- }
- if (gpio_request(GPIO_CHARGER_STATUS, "GPIO_CHARGER_STATUS")) {
- printk(KERN_ERR "%s :GPIO_CHARGER_STATUS request port error!\n", __func__);
- goto err_gpio_failed;
- } else {
- s3c_gpio_setpull(GPIO_CHARGER_STATUS, S3C_GPIO_PULL_NONE);
- s3c_gpio_cfgpin(GPIO_CHARGER_STATUS, S3C_GPIO_SFN(0));
- gpio_direction_input(GPIO_CHARGER_STATUS);
- gpio_free(GPIO_CHARGER_STATUS);
- }
- if (gpio_request(GPIO_CHARGER_ENABLE, "GPIO_CHARGER_ENABLE")) {
- printk(KERN_ERR "%s :GPIO_CHARGER_ENABLE request port error!\n", __func__);
- goto err_gpio_failed;
- } else {
- s3c_gpio_setpull(GPIO_CHARGER_ENABLE, S3C_GPIO_PULL_NONE);
- s3c_gpio_cfgpin(GPIO_CHARGER_ENABLE, S3C_GPIO_SFN(1));
- gpio_direction_output(GPIO_CHARGER_ENABLE, 0);
- gpio_free(GPIO_CHARGER_ENABLE);
- }
- if (gpio_request(GPIO_PM2301_LP, "GPIO_PM2301_LP")) {
- printk(KERN_ERR "%s :GPIO_PM2301_LP request port error!\n", __func__);
- goto err_gpio_failed;
- } else {
- s3c_gpio_setpull(GPIO_PM2301_LP, S3C_GPIO_PULL_NONE);
- s3c_gpio_cfgpin(GPIO_PM2301_LP, S3C_GPIO_SFN(1));
- gpio_direction_output(GPIO_PM2301_LP, 1);
- gpio_free(GPIO_PM2301_LP);
- }
- return 1;
- err_gpio_failed:
- return 0;
- }
(2)、结构体初始化
Include/Linux/pm2301_charger.h
- #define GPIO_CHARGER_ONLINE EXYNOS4_GPX0(7)//对应控制脚的主控接口
- #define GPIO_CHARGER_STATUS EXYNOS4_GPX1(3)
- #define GPIO_CHARGER_ENABLE EXYNOS4_GPL2(0)
- #define GPIO_PM2301_LP EXYNOS4_GPX1(7)
- struct pm2301_platform_data {
- int (*hw_init)(void);
- int gpio_enn;
- int gpio_wakeup;
- int gpio_irq;
- int gpio_lpn;
- };
- extern int pm2301_get_online(void);
- extern int pm2301_get_status(void);
3、probe函数分析
如果你是初学者,建议多看程序,你会发现,其实驱动程序的格式大多都是相同的,如这个IIC 器件的, 队列、定时器之类的东西。
- static int __devinit pm2301_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
- {
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
- struct pm2301_chip *chip;
- int ret;
- printk(KERN_INFO "PM2301 probe !!\n");
- //(1)、前面这部分是对IIC的初始化;
- if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
- return -EIO;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (!chip)
- return -ENOMEM;
- g_chip = chip;
- chip->client = client;
- chip->pdata = client->dev.platform_data;
- i2c_set_clientdata(client, chip);
- /* Hardware Init for PM2301 */
- if (chip->pdata->hw_init && !(chip->pdata->hw_init())) {
- dev_err(&client->dev, "hardware initial failed.\n");
- goto err_hw_failed;
- }
- mutex_init(&i2c_lock);
- //(2)、初始化两个队列
- INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online, pm2301_online_work);
- INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status, pm2301_ststus_work);
- //(3)、中断线程化
- chip->irq_online = gpio_to_irq(chip->pdata->gpio_wakeup);
- chip->irq_status = gpio_to_irq(chip->pdata->gpio_irq);
- /* Request IRQ for PM2301 */
- ret = request_threaded_irq(chip->irq_online,
- NULL, pm2301_dcin,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "PM2301 DC IN", chip);
- if (ret) {
- printk(KERN_ERR "Cannot request irq %d for DC (%d)\n",
- chip->irq_online, ret);
- goto err_hw_failed;
- }
- #ifdef PM2301_REPORT_STATUS_BY_IRQ
- ret = request_threaded_irq(chip->irq_status,
- NULL, pm2301_status,
- IRQF_TRIGGER_FALLING,
- "PM2301 STATUS", chip);
- if (ret) {
- printk(KERN_ERR "Cannot request irq %d for CHARGE STATUS (%d)\n",
- chip->irq_status, ret);
- goto err_hw_failed;
- }
- #endif
- charger_initial = 1;
- g_has_charged = 0;
- g_has_charging_full_or_stop = 0;
- #ifdef PM2301_REPORT_STATUS_BY_IRQ
- /* Set wakeup source for online pin*/
- irq_set_irq_wake(chip->irq_status, 1);
- #endif
- /* Set wakeup source for online pin*/
- irq_set_irq_wake(chip->irq_online, 1);
- /* Init default interrupt route for PM2301 */
- pm2301_reg_init(chip->client);
- /* Init online & status value */
- chip->online = pm2301_charger_online(chip);
- g_pm2301_online = chip->online; /* Sync to global */
- pm2301_charger_enable(chip->client, chip->online);
- pm2301_charger_status(chip);
- printk(KERN_INFO "PM2301 probe success!!\n");
- return 0;
- err_hw_failed:
- dev_err(&client->dev, "failed: power supply register\n");
- i2c_set_clientdata(client, NULL);
- kfree(chip);
- return ret;
- }
(1)、前面这部分是对IIC的初始化
这部分就不再多说了,搞来搞去都是这个老样子;
(2)、任务初始化宏
- INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online, pm2301_online_work);
- 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 中,中断具有最高的优先级。不论在任何时刻,只要产生中断事件,内核将立即执行相应的中断处理程序,等到所有挂起的中断和软中断处理完毕后才能执行正常的任务,因此有可能造成实时任务得不到及时的处理。中断线程化之后,中断将作为内核线程运行而且被赋予不同的实时优先级,实时任务可以有比中断线程更高的优先级。这样,具有最高优先级的实时任务就能得到优先处理,即使在严重负载下仍有实时性保证。但是,并不是所有的中断都可以被线程化,比如时钟中断,主要用来维护系统时间以及定时器等,其中定时器是操作系统的脉搏,一旦被线程化,就有可能被挂起,这样后果将不堪设想,所以不应当被线程化。
看下我们程序中如何把中断线程化的:
- chip->irq_online = gpio_to_irq(chip->pdata->gpio_wakeup);
- chip->irq_status = gpio_to_irq(chip->pdata->gpio_irq);
看到这里是否想起:
- static struct pm2301_platform_data pm2301_platform_data = {
- ………………
- .gpio_lpn = GPIO_PM2301_LP,
- .gpio_irq = GPIO_CHARGER_STATUS,
- .gpio_enn = GPIO_CHARGER_ENABLE,
- .gpio_wakeup = GPIO_CHARGER_ONLINE,
- };
- #define GPIO_CHARGER_ONLINE EXYNOS4_GPX0(7)
- #define GPIO_CHARGER_STATUS EXYNOS4_GPX1(3)
- #define GPIO_CHARGER_ENABLE EXYNOS4_GPL2(0)
- #define GPIO_PM2301_LP EXYNOS4_GPX1(7)
感觉申请个中断脚,这样有点费劲呀;
中断线程化:
- /* Request IRQ for PM2301 */
- ret = request_threaded_irq(chip->irq_online,
- NULL, pm2301_dcin,
- IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
- "PM2301 DC IN", chip);
当有插入DC中断出发时调用:
- static irqreturn_t pm2301_dcin(int irq, void *_data)
- {
- struct pm2301_chip *chip = _data;
- schedule_delayed_work(&chip->work_online, PM2301_DELAY);
- return IRQ_HANDLED;
- }
Pm2301_dcin调度队列:chip->work_online执行:pm2301_online_work函数
- static void pm2301_online_work(struct work_struct *work)
- {
- struct pm2301_chip *chip;
- chip = container_of(work, struct pm2301_chip, work_online.work);
- int new_online = pm2301_charger_online(chip);
- if (chip->online != new_online) {
- chip->online = new_online;
- g_pm2301_online = chip->online; /* Sync to global */
- pm2301_charger_enable(chip->client, chip->online);//①、初始化充电IC;
- #ifdef PM2301_REPORT_STATUS_BY_IRQ
- /*To avoid status pin keep low*/
- schedule_delayed_work(&chip->work_status, 1000);
- #endif
- #if defined(CONFIG_BATTERY_MAX17040)
- TriggerGasgaugeUpdate();//②、把DC状态更新到max17040;
- #endif
- }
- }
①、初始化电IC
这里面主要是写一些寄存器
- static void pm2301_charger_enable(struct i2c_client *client, int online)
- {
- if (online) { /* Enabled Charging*/
- int batt_capacity = 0;
- batt_capacity = GetGasgaugeCapacity();
- /* Don't start charging if battery capacity above 95% when DC plug in*/
- if(0) {
- //if( batt_capacity >= 95 ) {
- pm2301_write_reg(client, 0x01, 0x02);
- pm2301_write_reg(client, 0x26, 0x00); /* always keep the register to 0 */
- } else {
- pm2301_write_reg(client, 0x00, 0x01); /* force resume of charging */
- pm2301_write_reg(client, 0x01, 0x06); /* ChEn=1, AutoResume=1 */
- pm2301_write_reg(client, 0x05, 0x7A); /* ChEoccurrentLevel:150mA, ChPrechcurrentLevel:100mA, ChCCcurrentLevel:1000mA/2000mA */
- pm2301_write_reg(client, 0x06, 0x0A); /* ChVersumeVot:3.6V ChPrechVoltLevel:2.9V */
- pm2301_write_reg(client, 0x07, 0x1E); /* ChVoltLevel:4.25V */
- pm2301_write_reg(client, 0x26, 0x00); /* always keep the register to 0 */
- }
- g_has_charged = 1;
- } else { /* Disable Charging*/
- pm2301_write_reg(client, 0x01, 0x02);
- pm2301_write_reg(client, 0x26, 0x00); /* always keep the register to 0 */
- g_has_charged = 0;
- }
- }
②、把DC状态更新到max17040
- TriggerGasgaugeUpdate()
插入DC这部流程如下:
android电池(五):电池 充电IC(PM2301)驱动分析篇【转】相关推荐
- android电池(五):电池 充电IC(PM2301)驱动分析篇
android电池(五):电池 充电IC(PM2301)驱动分析篇 关键词:android 电池 电量计 PL2301任务初始化宏 power_supply 中断线程化 平台信息: 内核:linu ...
- 【转】android电池(四):电池 电量计(MAX17040)驱动分析篇
关键词:android 电池 电量计 MAX17040 任务初始化宏 power_supply 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0 ...
- 高通平台power_supply 框架下添加第三方充电IC的驱动方法
1.power_supply电源框架介绍: power supply framework在kernel/drivers/power/下.内核抽象出来power supply子系统为驱动提供了统一的框架 ...
- max17040C语言,电池 电量计(MAX17040)驱动分析篇
关键词:android 电池 电量计 MAX17040 任务初始化宏power_supply 平台信息: 内核:linux2.6/linux3.0 系统:android/android4.0 平台 ...
- android 6.0 电池曲线,Android6.0 RK3399 电池系统(一)BQ25700 IC 驱动分析 · Younix’s Studio...
8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? Platform: RK3399 OS: Android 6.0 Kernel: 4.4 Version: v2017. ...
- Android上电到现实充电画面,android 电池(二):android关机充电流程、充电画面显示(一)...
上一篇我们讲了锂电池的充放电的流程和电池的一些特性,这一节我们重点说一下android关机充电是怎么.充电画面显示是怎么实现的,这个在工作中也比较有用,我们开始做这一块的时候也走了不少的弯路.我记得我 ...
- MTK 关机充电时充电IC正常,电池正常充电,但是充电动画一直显示0%
MTK 关机充电时充电IC正常,电池正常充电,但是充电动画一直显示0% 平台 mt8168+mt6357+chargerIC 问题 MTK 关机充电时充电IC正常,电池正常充电,但是充电动画一直显示0 ...
- Android 优化电池使用时间 ——监控电池电量和充电状态
时常看Android官网开发文档,最近突然发现Android官网提供了一些中文翻译文档,真心感觉不错.考虑很大一部分童鞋都生活在大中华局域网里,不能畅游世界,特转载相关系列文档,以供大家一起学习,原文 ...
- Android官方开发文档Training系列课程中文版:电池续航时间优化之监测电池电量及充电状态
原文地址:http://android.xsoftlab.net/training/monitoring-device-state/index.html 引言 作为一款优秀的APP应用,应该总是想方设 ...
最新文章
- [css] 用css实现饼图效果
- php5.2 array,详解php 5.2.x 数组操作实例
- jar包中的类如何读取包内和包外的配置文件
- 简单理解L0、L1与L2范数
- jupyter-notebook设置⽀持远程访问
- 潘多拉固件设置ipv6_Phicomm-k2+pandorabox固件+PPPOE拨号+IPV6
- win10多合一原版系统_如何制作Win10多合一系统安装盘
- vue 加headers_vue上传图片设置headers表头信息
- 一个女留学生在美国的七年(转载)
- 华为路由器联动_华为移动路由体验报告:你的随身WiFi伴侣
- 《数论概论》读书笔记(第二章)勾股数组
- Android拦截黑名单(简易版)
- 链路聚合(eth-trunk)
- MSSQL 如何删除字段的所有约束和索引
- CVPR 2022 | QueryDet:使用级联稀疏query加速高分辨率下的小目标检测
- matlab 线性回归 参数显著性,matlab建立多元线性回归模型并进行显著性检验及预测问题...
- 《植物大战僵尸》开发商PopCap或10亿美元出售
- 小米手机5完美卡刷开发版获得ROOT超级权限的教程
- 为什么计算机处理打不开,为什么电脑ps安装成功打不开怎么办
- Sutherland-Hodgeman 逐次裁剪法(多边形裁剪)
热门文章
- mysql查询09软件技术1班_MySQL查询语句的45道练习(2019.09最新版)
- 自己做网站翻译服务器 - 添加网站,猎场seo视频教程:站群之间应该如何进行链接-专业...
- linux配置服务器心得体会,linux 学习 linux上搭建dhcp服务器
- cmd查看当前python安装路径_NotePad++上配置Python
- win怎么打开微软更新服务器,怎样打开win10的更新服务器地址
- cad转shp 奥维_CAD转换工具程序包(附下载)
- matlab如何将两张图画在一起,如何在MATLAB里面将两个图画在一起
- java date不要秒_java – 比较日期忽略Joda中DateTime的秒和毫秒时刻
- cass高程点内插插件_聊聊CASS土方计算那些事-DTM法
- java的3个初始化_通过实例解析Java类初始化和实例初始化