本文轉(zhuǎn)載自:http://blog.csdn.net/xubin341719/article/details/8970363
android充電這塊,有的電源管理芯片內(nèi)部包含充電管理,如s5pv210上常用的AT8937。我們這次用的max77686沒有充電控制這塊,所以我們加入一個(gè)充電IC來控制,選用PM2301.
一、PM2301和主控、電池的邏輯
如下圖所示:
?
1、藍(lán)色部分:IIC控制接口,這個(gè)說得太多了,好多外圍器件都是通過IIC控制的,這個(gè)一定要熟悉、熟悉、熟爛了,然后可以完成比較多的工作。
2、黃色部分:中斷、使能控制腳,CHG_STATUS(IRQ)、?DC_IN_INT(WAKE_UP)?、?PM2301_LP(LPN)、CHARGER_EN(ENN)控制引腳;
IRQ:充電IC的狀態(tài),如果有動(dòng)作通知主控;
WAKE_UP:如果有DC插入,產(chǎn)生中斷通知主控;
LPN:
ENN:充電IC使能;
3、PM2301?、電池、系統(tǒng)電壓的大致邏輯
標(biāo)號1:系統(tǒng)電壓有PM2301提供;
標(biāo)號2:PM2301給電池充電;
標(biāo)號3:系統(tǒng)電壓有電池提供;
標(biāo)號:1和標(biāo)號:3不同時(shí)提供電壓給系統(tǒng),中間有一個(gè)MOS管切換;分兩種情況:
(1)、不插充電器時(shí),有電池提供電壓給系統(tǒng),走通道標(biāo)號:3給系統(tǒng)供電;
(2)、插入DC后,系統(tǒng)偵測到DC插入,把3的通道關(guān)閉,打開1給系統(tǒng)供電,同時(shí)有2給電池充電;
二、PM2301硬件電路
如下所示:
?
Q5這個(gè)MOS管,就是控制系統(tǒng)供電的,沒有充電時(shí),VBATT有VBAT+提供,充電時(shí),VBATT有SENSE_COMM提供。
控制腳對應(yīng)主控的引腳:
| 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電源部分的切換控制。
?
下圖為整個(gè)電池充電的過程控制:
Trickle?mode、Constant?current?mode?(CC?mode?or?fast?charge?mode)、Constant?voltage?mode?(CV?mode)?、End?of?charge?feature
?
三、PL2301驅(qū)動(dòng)部分
PL2301的硬件、工作原理做簡單的解釋,接下來我們分析驅(qū)動(dòng)程序:
驅(qū)動(dòng)用到知識點(diǎn):
IIC的注冊;
??????任務(wù)初始化宏(在上一篇我們簡單提過);
中斷線程化;
1、IIC的注冊
這個(gè)和上一篇所說的電量計(jì)相似;
(1)、pm2301驅(qū)動(dòng)部分
[cpp]?view plaincopy
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)、平臺(tái)驅(qū)動(dòng)部分
arch/arm/mach-exynos/mach-smdk4x12.c
[cpp]?view plaincopy
static?struct?i2c_board_info?i2c_devs1[]?__initdata?=?{????…………??#ifdef?CONFIG_CHARGER_PM2301??????{??????????I2C_BOARD_INFO("pm2301",?0x2c),??????????.platform_data??=?&pm2301_platform_data,??????},??#endif??…………??};?? 下圖就是我們IIC驅(qū)動(dòng)注冊生成的文件;
/sys/bus/i2c/drivers/pm2301
?
2、關(guān)于:pm2301_platform_data這個(gè)結(jié)構(gòu)體
[cpp]?view plaincopy
static?struct?pm2301_platform_data?pm2301_platform_data?=?{??????.hw_init?=?pm2301_hw_init,????.gpio_lpn?=?GPIO_PM2301_LP,????.gpio_irq?=?GPIO_CHARGER_STATUS,??????.gpio_enn?=?GPIO_CHARGER_ENABLE,??????.gpio_wakeup?=?GPIO_CHARGER_ONLINE,??};?? arch/arm/mach-exynos/mach-smdk4x12.c
(1)、硬件接口初始化
[cpp]?view plaincopy
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)、結(jié)構(gòu)體初始化
Include/Linux/pm2301_charger.h
[cpp]?view plaincopy
#define?GPIO_CHARGER_ONLINE?????EXYNOS4_GPX0(7)//對應(yīng)控制腳的主控接口??#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函數(shù)分析
如果你是初學(xué)者,建議多看程序,你會(huì)發(fā)現(xiàn),其實(shí)驅(qū)動(dòng)程序的格式大多都是相同的,如這個(gè)IIC?器件的,?隊(duì)列、定時(shí)器之類的東西。
[cpp]?view plaincopy
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");??????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);????????????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);??????INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online,?pm2301_online_work);??????INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status,?pm2301_ststus_work);??????chip->irq_online?=?gpio_to_irq(chip->pdata->gpio_wakeup);??????chip->irq_status?=?gpio_to_irq(chip->pdata->gpio_irq);??????????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??????????irq_set_irq_wake(chip->irq_status,?1);??#endif??????????irq_set_irq_wake(chip->irq_online,?1);????????????pm2301_reg_init(chip->client);??????????chip->online?=?pm2301_charger_online(chip);??????g_pm2301_online?=?chip->online;??????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的初始化
這部分就不再多說了,搞來搞去都是這個(gè)老樣子;
(2)、任務(wù)初始化宏
[cpp]?view plaincopy
INIT_DELAYED_WORK_DEFERRABLE(&chip->work_online,?pm2301_online_work);??INIT_DELAYED_WORK_DEFERRABLE(&chip->work_status,?pm2301_ststus_work);?? 把pm2301_online_work加入隊(duì)列chip->work_online,?pm2301_ststus_work加入chip->work_status隊(duì)列。
(3)、中斷線程化??request_threaded_irq
為什么要提出中斷線程化?
在?Linux?中,中斷具有最高的優(yōu)先級。不論在任何時(shí)刻,只要產(chǎn)生中斷事件,內(nèi)核將立即執(zhí)行相應(yīng)的中斷處理程序,等到所有掛起的中斷和軟中斷處理完畢后才能執(zhí)行正常的任務(wù),因此有可能造成實(shí)時(shí)任務(wù)得不到及時(shí)的處理。中斷線程化之后,中斷將作為內(nèi)核線程運(yùn)行而且被賦予不同的實(shí)時(shí)優(yōu)先級,實(shí)時(shí)任務(wù)可以有比中斷線程更高的優(yōu)先級。這樣,具有最高優(yōu)先級的實(shí)時(shí)任務(wù)就能得到優(yōu)先處理,即使在嚴(yán)重負(fù)載下仍有實(shí)時(shí)性保證。但是,并不是所有的中斷都可以被線程化,比如時(shí)鐘中斷,主要用來維護(hù)系統(tǒng)時(shí)間以及定時(shí)器等,其中定時(shí)器是操作系統(tǒng)的脈搏,一旦被線程化,就有可能被掛起,這樣后果將不堪設(shè)想,所以不應(yīng)當(dāng)被線程化。?
看下我們程序中如何把中斷線程化的:
[cpp]?view plaincopy
chip->irq_online?=?gpio_to_irq(chip->pdata->gpio_wakeup);??chip->irq_status?=?gpio_to_irq(chip->pdata->gpio_irq);?? 看到這里是否想起:
[cpp]?view plaincopy
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)?? 感覺申請個(gè)中斷腳,這樣有點(diǎn)費(fèi)勁呀;
中斷線程化:
[cpp]?view plaincopy
ret?=?request_threaded_irq(chip->irq_online,?????????????????????????????NULL,?pm2301_dcin,?????????????????????????????IRQF_TRIGGER_FALLING?|?IRQF_TRIGGER_RISING,?????????????????????????????"PM2301?DC?IN",?chip);?? 當(dāng)有插入DC中斷出發(fā)時(shí)調(diào)用:
[cpp]?view plaincopy
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調(diào)度隊(duì)列:chip->work_online執(zhí)行:pm2301_online_work函數(shù)
[cpp]?view plaincopy
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;??????????pm2301_charger_enable(chip->client,?chip->online);#ifdef?PM2301_REPORT_STATUS_BY_IRQ????????????????????schedule_delayed_work(&chip->work_status,?1000);??#endif??#if?defined(CONFIG_BATTERY_MAX17040)??????????TriggerGasgaugeUpdate();#endif??????}??}?? ①、初始化電IC
這里面主要是寫一些寄存器
[cpp]?view plaincopy
static?void?pm2301_charger_enable(struct?i2c_client?*client,?int?online)??{??????if?(online)?{???????????int?batt_capacity?=?0;??????????batt_capacity?=?GetGasgaugeCapacity();??????????????????if(0)?{??????????????????????pm2301_write_reg(client,?0x01,?0x02);??????????????pm2301_write_reg(client,?0x26,?0x00);???????????}?else?{??????????????pm2301_write_reg(client,?0x00,?0x01);???????????????pm2301_write_reg(client,?0x01,?0x06);???????????????pm2301_write_reg(client,?0x05,?0x7A);???????????????pm2301_write_reg(client,?0x06,?0x0A);???????????????pm2301_write_reg(client,?0x07,?0x1E);???????????????pm2301_write_reg(client,?0x26,?0x00);???????????}??????????g_has_charged?=?1;??????}?else?{????????????pm2301_write_reg(client,?0x01,?0x02);??????????pm2301_write_reg(client,?0x26,?0x00);???????????g_has_charged?=?0;??????}??}?? ②、把DC狀態(tài)更新到max17040
?
[cpp]?view plaincopy
TriggerGasgaugeUpdate()?? 插入DC這部流程如下:
?
總結(jié)
以上是生活随笔為你收集整理的android电池(五):电池 充电IC(PM2301)驱动分析篇【转】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。