http://www.cnblogs.com/to7str/archive/2013/03/24/2978699.html
对TF303充电部分做一个整理,内容涉及到电池的基础知识、8607中断、power supply子系统、 电池和充电电器驱动、充电流程和电量计算等。
硬件环境:TF303
软件环境:svn://172.16.0.70/svn_android/PXA920/branch/Kernel/W1225.03_8390_Kernel 版本8538
恒流充电:保持恒定电流对电池进行充电。
恒压充电:保持恒定电压对电池进行充电。
热敏电阻:对热敏感的半导体电阻,其阻值随温度变化的曲线呈非线性或线性。
记忆效应:电池的记忆效应是指在下一次充电时所能充电的百分比。为了消除电池的记忆效应,在下一次充电之前,必须先完全放电,然后再充电。只有这样,才能百分之百的充满电池。
涓流充电: 以一微小的电流对电池充电, 涓流充电用来先对完全放电的电池单元进行预充(恢复性充电)。。
电池容量:电池容量的国标单位为库仑,常用单位为mAh 。1mAh=0.001安培*3600秒=3.6安培秒=3.6库仑,比如一颗900mAh的电池可以提供300mA恒流的持续3小时的供电能力.
电池的五个主要参数为:电池的容量、标称电压、内阻、放电终止电压和充电终止电压。
电池的容量:通常用mAh(毫安时)表示,1000mAh就是能在1000mA的电流下放电 1小时。
标称电压:通常指的是开路输出电压,也就是不接任何负载,没有电流输出的电压值。
电池由正极锂化合物、中间的电解质膜及负极碳组成。
当电池充电时,锂离子从正极中脱嵌,在负极中嵌入,放电时反之。一般采用嵌锂过渡金属氧化物做正极,如LiCoO2、LiNiO2、LiMn2O4。
常见的方法:ADC和库仑计
锂离子电池有一个对电量计量很有用的特性,就是在放电的时候,电池电压随电量的流逝会逐渐降低,并且有相当大的斜率.这就提供给我们另外一种近似的电量计量途径.
用电压来估计电池的剩余容量有以下几个不稳定性:
1.同一个电池,在同等剩余容量的情况下,电压值因放电电流的大小而变化. 放电电流越大,电压越低.在没有电流的情况下,电压最高.
2.环境温度对电池电压的影响, 温度越低,同等容量电池电压越低.
3.循环对电池放电平台的影响, 随着循环的进行,锂离子电池的放电平台趋于恶化.放电平台降低.所以相同电压所代表的容量也相应变化了.
4.不同厂家,不同容量的锂离子电池,其放电的平台略有差异.
5.不同类型的电极材料的锂离子电池,放电平台有较大差异.钴锂和锰锂的放电平台就完全不同.
通过统计流入和流出电池的电荷数,使用库仑计时需要通过其他方法获得电池在使用前的电量。
锂电池的充电过程:涓充---恒流---恒压---停止
充电曲线
(1)8067、8606和电池的连接图
(2)8607中断引脚
8607的PMIC_INTN引脚接到AP的PMIC_INT引脚
(3)i2c通信
8606和8607的SCL引脚连接到AP的GPIO_53,SDA连接到GPIO_53
在kernel中,驱动会在文件系统中生成battery相关的文件,包括电池电量、状态等,驱动更新硬件信息时会调用power_supply提供的接口,power_supply收到这个事件后通过uevent机制利用netlink将这个event上报给app层,app层收event后再去读sysfs中的相关文件获取数据。
(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
/sys/class/power_supply/battery/
/sys/class/power_supply/ac/
/sys/class/power_supply/usb/
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注册函数中初始化的(详细见后)。
初始化函数原型如下
Power驱动创建了一个叫struct class *power_supply_class的类容器, 并且初始化函数指针power_supply_class->dev_uevent 指向power_supply_uevent函数,供电模块的uevent环境变量的添加都是此函数中是实现的。
此函数是对外提供的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。
power_supply_uevent 此函数的作用是通过调用uevent的接口函数add_uevent_var添加环境变量,为Battery、 AC、 USB准备好环境变量。
中断相关寄存器
8607中断状态寄存器0x03、0x04 、05; 中断使能寄存器 0x06、0x07、0x08
8607的PMIC_INTN引脚接到AP的PMIC_INT引脚
kernel/arch/arm/mach-mmp/raho_tf302_hwv0.c
其中pm860x_irq为8607总中断的处理函数
8607子中断的资源定义
举例88pm860x_battery.c
当AP的PMIC_INTN引脚中断信号发生时,则调用8607的总中断处理函数pm860x_irq, 在此函数中通过i2c来读8607中断状态寄存器,遍历860中断状态寄存器各个bit为,判断出具体哪个中断发生,pm860x_irq函数具体实现如下:
handle_nested_irq函数说明:该函数用于实现一种中断共享机制,当多个中断共享某一根中断线时,我们可以把这个中断线作为父中断,共享该中断的各个设备作为子中断,在父中断的中断线程中决定和分发响应哪个设备的请求,在得出真正发出请求的子设备后,调用handle_nested_irq来响应子中断。
电池d信息的数据结构
函数的主要操作有:
电池初始化,将在后面介绍
初始化并注册电池的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用于更新的电池的状态。
int measure_vbatt(struct pm860x_battery_info *info, int state, int *data);
此函数通过measure_12bit_voltage 函数去读8607的vbatt寄存器PM8607_VBAT_MEAS1 (0x6D)。然后通过校验算法计算出vbatt的值。
int measure_current(struct pm860x_battery_info *info, int *data);
通过8607的0x68 0x6c计算出ibatt
static int calc_ocv(struct pm860x_battery_info *info, int *ocv);
开路电压值是通过测出的电压、电流和内阻计算而来
(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
接口函数:static int calc_capacity(struct pm860x_battery_info *info, int *cap);
函数的内部实现如下图
接口函数:static int calc_soc(struct pm860x_battery_info *info, int state, int *soc);
函数的流程为:通过ADC测出电压,然后再查array_soc 电量与电压的对应关系表,从而得出电量值
接口函数:int pm860x_battery_update_soc(void)
函数功能:在充电彻底终止后,将库仑计清零,并将start_soc设为100
接口函数: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; 此函数会调用电池相关的测量函数。
Power supply子系统通过uevent机制把信息传输到用户空间上去,当battery的状态发生改变的时候会向用户空间上报一个uevent,这样的话用户空间就可以知道什么时候去抓信息
在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函数
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层。
中断 |
处理函数 |
功能 |
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 |
插入充电器会执行此中断
当充电彻底终止后,此中断会发生。
VBATT_INT中断处理函数
VCHG_INT中断处理函数
接口函数:void pm860x_set_charger_type(enum enum_charger_type type);
函数功能:设置充电器类型
函数入参为要设置的充电器的类型有:
USB_CHARGER
AC_STANDARD_CHARGER
AC_OTHER_CHARGER
函数的内部实现如下图
函数原型:static void set_vchg_threshold(struct pm860x_charger_info *info, int min, int max)
函数功能:设置VCHG_INT中断触发条件,即pm860x_vchg_handler中断函数的触发条件。
函数原型:static void set_vbatt_threshold(struct pm860x_charger_info *info,
int min, int max)
函数功能:设置VBAT_INT中断触发条件, 即pm860x_vbattery_handler中断函数的触发条件。
内部实现如下:
函数原型:static int set_charging_fsm(struct pm860x_charger_info *info)
函数功能:维护充电状态机
函数的内部实现如下图:
将程序流程图整理成fsm如下图
当电池的电压太低,比如过放保护的电池,需要通过预充一段时间后,再转入快充。
预充的实现函数是:static int start_precharge(struct pm860x_charger_info *info)
函数的内部实现如下图:
快充主要有两个过程先恒流再恒压,通过设置8606和8607相关寄存器来控制恒流和恒压的过程。函数实现:int start_fastcharge(struct pm860x_charger_info *info)
函数的内部实现如下图:
接口函数是:static ssize_t stop_charging(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)