http://www.cnblogs.com/to7str/archive/2013/03/24/2978699.html
1. 引言
1.1. 編寫目的
對TF303充電部分做一個整理,內容涉及到電池的基礎知識、8607中斷、power supply子系統、 電池和充電電器驅動、充電流程和電量計算等。
1.2. 環境
硬件環境:TF303
軟件環境:svn://172.16.0.70/svn_android/PXA920/branch/Kernel/W1225.03_8390_Kernel 版本8538
2. 充電基礎知識
2.1. 名詞解釋
恆流充電:保持恆定電流對電池進行充電。
恆壓充電:保持恆定電壓對電池進行充電。
熱敏電阻:對熱敏感的半導體電阻,其阻值隨溫度變化的曲線呈非線性或線性。
記憶效應:電池的記憶效應是指在下一次充電時所能充電的百分比。爲了消除電池的記憶效應,在下一次充電之前,必須先完全放電,然後再充電。只有這樣,才能百分之百的充滿電池。
涓流充電: 以一微小的電流對電池充電, 涓流充電用來先對完全放電的電池單元進行預充(恢復性充電)。。
電池容量:電池容量的國標單位爲庫侖,常用單位爲mAh 。1mAh=0.001安培*3600秒=3.6安培秒=3.6庫侖,比如一顆900mAh的電池可以提供300mA恆流的持續3小時的供電能力.
2.2. 電池參數
電池的五個主要參數爲:電池的容量、標稱電壓、內阻、放電終止電壓和充電終止電壓。
電池的容量:通常用mAh(毫安時)表示,1000mAh就是能在1000mA的電流下放電 1小時。
標稱電壓:通常指的是開路輸出電壓,也就是不接任何負載,沒有電流輸出的電壓值。
2.3. 鋰電池原理
電池由正極鋰化合物、中間的電解質膜及負極碳組成。
當電池充電時,鋰離子從正極中脫嵌,在負極中嵌入,放電時反之。一般採用嵌鋰過渡金屬氧化物做正極,如LiCoO2、LiNiO2、LiMn2O4。
2.4. 電量計算
常見的方法:ADC和庫侖計
2.5. ADC法
鋰離子電池有一個對電量計量很有用的特性,就是在放電的時候,電池電壓隨電量的流逝會逐漸降低,並且有相當大的斜率.這就提供給我們另外一種近似的電量計量途徑.
用電壓來估計電池的剩餘容量有以下幾個不穩定性:
1.同一個電池,在同等剩餘容量的情況下,電壓值因放電電流的大小而變化. 放電電流越大,電壓越低.在沒有電流的情況下,電壓最高.
2.環境溫度對電池電壓的影響, 溫度越低,同等容量電池電壓越低.
3.循環對電池放電平臺的影響, 隨着循環的進行,鋰離子電池的放電平臺趨於惡化.放電平臺降低.所以相同電壓所代表的容量也相應變化了.
4.不同廠家,不同容量的鋰離子電池,其放電的平臺略有差異.
5.不同類型的電極材料的鋰離子電池,放電平臺有較大差異.鈷鋰和錳鋰的放電平臺就完全不同.
2.6. 庫侖計法
通過統計流入和流出電池的電荷數,使用庫侖計時需要通過其他方法獲得電池在使用前的電量。
2.7. 鋰電池充電
鋰電池的充電過程:涓充---恆流---恆壓---停止
充電曲線
2.8. TF303充電電路
(1)8067、8606和電池的連接圖
(2)8607中斷引腳
8607的PMIC_INTN引腳接到AP的PMIC_INT引腳
(3)i2c通信
8606和8607的SCL引腳連接到AP的GPIO_53,SDA連接到GPIO_53
3. Android 電池管理
在kernel中,驅動會在文件系統中生成battery相關的文件,包括電池電量、狀態等,驅動更新硬件信息時會調用power_supply提供的接口,power_supply收到這個事件後通過uevent機制利用netlink將這個event上報給app層,app層收event後再去讀sysfs中的相關文件獲取數據。
3.1. 相關代碼
(1)java代碼:
frameworks/base/services/java/com/android/server/BatteryService.java
frameworks/base/services/java/com/android/server/ SystemServer.java
frameworks/base/core/java/android/os/UEventObserver.java
(2)JNI代碼:
frameworks/base/services/jni/com_android_server_BatteryService.cpp
hardware/libhardware_legacy/uevent/uevent.c
android底層代碼
hardware/libhardware_legacy/uevent/uevent.c
(3)krenel代碼
與平臺無關文件
kernel/drivers/power/power_supply_core.c
kernel/drivers/power/power_supply_sysfs.c
kernel/drivers/usb/gadget/mv_gadget.c
平臺相關代碼
kernel/drivers/power/88pm860x_battery.c
kernel/drivers/power/88pm860x_charger.c
kernel/drivers/usb/misc/88pm860x_vbus.c
kernel/drivers/mfd/88pm860x-core.c
kernel/drivers/mfd/88pm860x-i2c.c
3.2. power系統文件
/sys/class/power_supply/battery/
/sys/class/power_supply/ac/
/sys/class/power_supply/usb/
3.3. power supply子系統
3.3.1. 數據結構體
struct power_supply結構體,如下圖
此結構體用於描述供電模塊,系統中總過註冊了三個power_supply,分別是:AC、USB host、battery
下面以battery的power_supply初始化對該結構體成員做簡單的說明
如上圖所示,其中的info->battery爲一個struct power_supply結構體
const char *name;用於描述模塊的名稱
enum power_supply_type type;用於描述類型,類型如下
enum power_supply_property *properties;用於描述該供電模塊有哪些屬性
battery的屬性如下
char **supplied_to; 供電給那個模塊
int (*get_property)(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); 此函數指針將指向獲得該模塊說屬性的接口函數
struct work_struct changed_work;工作隊列的初始化是在power_supply註冊函數中初始化的(詳細見後)。
3.3.2. 系統api
3.3.3. power_supply_class_init
初始化函數原型如下
Power驅動創建了一個叫struct class *power_supply_class的類容器, 並且初始化函數指針power_supply_class->dev_uevent 指向power_supply_uevent函數,供電模塊的uevent環境變量的添加都是此函數中是實現的。
3.3.4. power_supply_register
此函數是對外提供的API,作用是註冊power_supply供電模塊,系統中需要調用此函數來註冊Battery、AC、USB三個供電模塊。函數的具體實現如下
此函數的主要操作有:
(1)把該設備的class指向power_supply_class
(2)調用device_add(dev);
(3)綁定函數power_supply_changed_work到 psy- >changed_work 工作隊列。備註:BATTERY AC USB這三個供電模塊的change_work都與power_supply_changed_work 函數綁定。
在調用device_add(dev)(定義在:/kernel/drivers/base/core.c中)時,會執行這一句代碼:klist_add_tail(&dev- >knode_class, &dev->class- >p- >class_devices); 因爲在上面的函數中dev->class = power_supply_class; ,所以device_add(dev) 會把dev- >knode_class 加入到power_supply_class 的klist鏈表中。也就是說會把usb、ac、charger三個的dev- >knode_class (備註:是個struct klist_node結構體)註冊到這個power_supply_class 鏈表中,到時候可以通過class_for_each_device 這個函數通過這個類來找到這三個device,然後通過device在找到power_supply。
3.3.5. power_supply_uevent
power_supply_uevent 此函數的作用是通過調用uevent的接口函數add_uevent_var添加環境變量,爲Battery、 AC、 USB準備好環境變量。
4. 中斷
4.1. 8607中斷
中斷相關寄存器
8607中斷狀態寄存器0x03、0x04 、05; 中斷使能寄存器 0x06、0x07、0x08
8607的PMIC_INTN引腳接到AP的PMIC_INT引腳
4.2. 8607總中斷註冊
kernel/arch/arm/mach-mmp/raho_tf302_hwv0.c
其中pm860x_irq爲8607總中斷的處理函數
4.3. 8607子中斷註冊
8607子中斷的資源定義
舉例88pm860x_battery.c
4.4. 中斷的執行流程
當AP的PMIC_INTN引腳中斷信號發生時,則調用8607的總中斷處理函數pm860x_irq, 在此函數中通過i2c來讀8607中斷狀態寄存器,遍歷860中斷狀態寄存器各個bit爲,判斷出具體哪個中斷髮生,pm860x_irq函數具體實現如下:
handle_nested_irq函數說明:該函數用於實現一種中斷共享機制,當多箇中斷共享某一根中斷線時,我們可以把這個中斷線作爲父中斷,共享該中斷的各個設備作爲子中斷,在父中斷的中斷線程中決定和分發響應哪個設備的請求,在得出真正發出請求的子設備後,調用handle_nested_irq來響應子中斷。
5. battery
5.1. 數據結構
電池d信息的數據結構
5.2. Battery init相關
5.2.1. Probe函數
函數的主要操作有:
電池初始化,將在後面介紹
初始化並註冊電池的struct power_supply結構體
註冊8607子中斷PM8607_IRQ_CC的處理函數爲pm860x_coulomb_handler
註冊8607子中斷PM8607_IRQ_BAT的處理函數爲pm860x_batt_handler
初始化monitor_work和changed_work兩個工作 ,其中monitor_work主要用於每隔30s刷新電池信息到用戶空間,changed_work用於更新的電池的狀態。
5.2.2. pm860x_init_battery
5.3. Battery measure api
5.3.1. 測量vbatt
int measure_vbatt(struct pm860x_battery_info *info, int state, int *data);
此函數通過measure_12bit_voltage 函數去讀8607的vbatt寄存器PM8607_VBAT_MEAS1 (0x6D)。然後通過校驗算法計算出vbatt的值。
5.3.2. 測量ibatt
int measure_current(struct pm860x_battery_info *info, int *data);
通過8607的0x68 0x6c計算出ibatt
5.3.3. 計算開路電壓
static int calc_ocv(struct pm860x_battery_info *info, int *ocv);
開路電壓值是通過測出的電壓、電流和內阻計算而來
5.3.4. 庫侖計計數
(1)數據結構
struct ccnt {
unsigned long long int pos;
unsigned long long int neg;
unsigned int spos;
unsigned int sneg;
int total_chg; /*充電統計*/
int total_dischg; /*放電統計*/
};
(2)庫侖計計算
static int calc_ccnt(struct pm860x_battery_info *info, struct ccnt *ccnt);
此函數通過8607的0x47和0x95寄存器算出total_chg和total_dischg
5.3.5. 計算電量
接口函數:static int calc_capacity(struct pm860x_battery_info *info, int *cap);
函數的內部實現如下圖
5.3.6. ADC計算電量
接口函數:static int calc_soc(struct pm860x_battery_info *info, int state, int *soc);
函數的流程爲:通過ADC測出電壓,然後再查array_soc 電量與電壓的對應關係表,從而得出電量值
5.3.7. 充電完成時更新soc
接口函數:int pm860x_battery_update_soc(void)
函數功能:在充電徹底終止後,將庫侖計清零,並將start_soc設爲100
5.4. GET電池屬性API
接口函數:static int pm860x_batt_get_prop(struct power_supply *psy, enum power_supply_property psp,
union power_supply_propval *val);
文件系統中,所有電池相關的設備文件都是通過此API來獲取數據,此函數在pm860x_battery_probe 中註冊info->battery.get_property =pm860x_batt_get_prop; 此函數會調用電池相關的測量函數。
5.5. Battery work
Power supply子系統通過uevent機制把信息傳輸到用戶空間上去,當battery的狀態發生改變的時候會向用戶空間上報一個uevent,這樣的話用戶空間就可以知道什麼時候去抓信息
5.5.1. Driver定時更新battery信息機制
在battery driver使用工作隊列來定時更新電池信息,工作隊列定義在struct pm860x_battery_info 的struct delayed_work monitor_work;成員。monitor_work初始化在如下函數中,相關代碼如下:
static __devinit int pm860x_battery_probe(struct platform_device *pdev){
...
INIT_DELAYED_WORK_DEFERRABLE(&info->monitor_work, pm860x_battery_work);
queue_delayed_work(chip->monitor_wqueue, &info->monitor_work, MONITOR_INTERVAL);
...
}
綁定pm860x_battery_work 函數到monitor_work 工作上,延時30s後將 monitor_work 加入到 monitor_wqueue 工作隊列中。
static void pm860x_battery_work(struct work_struct *work)
{
struct pm860x_battery_info *info = container_of(work,
struct pm860x_battery_info, monitor_work.work);
int cap, v, ocv, i, temp;
power_supply_changed(&info->battery);
queue_delayed_work(info->chip->monitor_wqueue, &info->monitor_work, MONITOR_INTERVAL);
}
函數說明:先調用power_supply_changed 函數,延時30s後將 monitor_work 再加入到 monitor_wqueue 工作隊列中。 操作的結果就是每隔30s會調用一次pm860x_battery_work和power_supply_changed函數
5.5.2. Power supply子系統更新battery信息機制
void power_supply_changed(struct power_supply *psy)函數中通過schedule_work(&psy- >changed_work); 去執行changed_work綁定的函數, 此工作隊是在如下函數中初始化。
int power_supply_register(struct device *parent, struct power_supply *psy){
INIT_WORK(&psy- >changed_work, power_supply_changed_work);
....
}
static void power_supply_changed_work(struct work_struct *work){
struct power_supply *psy = container_of(work, struct power_supply, changed_work);
kobject_uevent(&psy- >dev- >kobj, KOBJ_CHANGE);
…..
}
kobject_uevent 將調用kobject_uevent_env 函數將power_supply_uevent 函數中準備好的環境變量通過netlink發送的app層。
6. charger
6.1. 數據結構
6.2. 中斷
中斷 |
處理函數 |
功能 |
PM8607_IRQ_CHG |
pm860x_charger_handler |
charger detect |
PM8607_IRQ_CHG_DONE |
pm860x_done_handler |
charging done |
PM8607_IRQ_CHG_FAIL |
pm860x_exception_handler |
charging timeout |
PM8607_IRQ_CHG_FAULT |
pm860x_exception_handler |
charging fault |
PM8607_IRQ_GPADC1 |
pm860x_temp_handler |
battery temperature |
PM8607_IRQ_VBAT |
pm860x_vbattery_handler |
battery voltage |
PM8607_IRQ_VCHG |
pm860x_vchg_handler |
vchg voltage |
6.2.1. pm860x_charger_handler
插入充電器會執行此中斷
6.2.2. pm860x_done_handler
當充電徹底終止後,此中斷會發生。
6.2.3. pm860x_vbattery_handler
VBATT_INT中斷處理函數
6.2.4. pm860x_vchg_handler
VCHG_INT中斷處理函數
6.3. charger init
6.3.1. probe函數
6.3.2. pm860x_init_charger
6.4. charger api
6.4.1. pm860x_set_charger_type
接口函數:void pm860x_set_charger_type(enum enum_charger_type type);
函數功能:設置充電器類型
函數入參爲要設置的充電器的類型有:
USB_CHARGER
AC_STANDARD_CHARGER
AC_OTHER_CHARGER
函數的內部實現如下圖
6.4.2. set_vchg_threshold
函數原型:static void set_vchg_threshold(struct pm860x_charger_info *info, int min, int max)
函數功能:設置VCHG_INT中斷觸發條件,即pm860x_vchg_handler中斷函數的觸發條件。
6.4.3. set_vbatt_threshold
函數原型:static void set_vbatt_threshold(struct pm860x_charger_info *info,
int min, int max)
函數功能:設置VBAT_INT中斷觸發條件, 即pm860x_vbattery_handler中斷函數的觸發條件。
內部實現如下:
6.4.4. set_charging_fsm
函數原型:static int set_charging_fsm(struct pm860x_charger_info *info)
函數功能:維護充電狀態機
函數的內部實現如下圖:
將程序流程圖整理成fsm如下圖
6.5. 充電的步驟
6.5.1. 預充
當電池的電壓太低,比如過放保護的電池,需要通過預充一段時間後,再轉入快充。
預充的實現函數是:static int start_precharge(struct pm860x_charger_info *info)
函數的內部實現如下圖:
6.6. 快充
快充主要有兩個過程先恆流再恆壓,通過設置8606和8607相關寄存器來控制恆流和恆壓的過程。函數實現:int start_fastcharge(struct pm860x_charger_info *info)
函數的內部實現如下圖:
6.7. 停充
接口函數是:static ssize_t stop_charging(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
7. 關於電量計算