提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
现在有部分机器用途于便携式设备,所以在硬件上改用了电池供电。battery管理是power_supply的重要组成部分。本章节介绍的电池充放电管理在硬件上引用了充电小板,通过ad转换读取电池电压值,模拟当前电池剩余的电量,从而实现电池冲放电管理。重点介绍一下power_supply子系统管理电池显示。
一、power_supply子系统介绍
power_supply子系统是kernel用来管理所有与电池有关的设备,包括充电放电,电池安全情况等等信息。话不多说,直接从宏观到微观的深入了解。
(1) 查看电池的注册信息
rk3399_all:/ # ls -al /sys/class/power_supply
total 0
drwxr-xr-x 2 root root 0 2013-01-18 17:17 .
drwxr-xr-x 77 root root 0 2013-01-18 17:17 ..
lrwxrwxrwx 1 root root 0 2013-01-18 17:17 test_ac -> ../../devices/virtual/power_supply/test_ac
lrwxrwxrwx 1 root root 0 2013-01-18 17:17 test_battery -> ../../devices/virtual/power_supply/test_battery
lrwxrwxrwx 1 root root 0 2013-01-18 17:17 test_usb -> ../../devices/virtual/power_supply/test_usb
可以发现当前注册了3个power_supply,那么它是怎么注册的呢?
调用函数:
struct power_supply *__must_check power_supply_register(struct device *parent,
const struct power_supply_desc *desc,
const struct power_supply_config *cfg)
或者:
devm_power_supply_register(struct device *parent,
const struct power_supply_desc *desc,
const struct power_supply_config *cfg)
两者都是注册power_supply子系统的,后者的注册表示不用的时候会自动释放。其中我们需要实现的参数有 struct power_supply_desc *desc 和 struct power_supply_config *cfg。第一个参数是设备的parent,直接传&pdev->dev即可。接下来分析一下后面两个参数。
第二个参数:
struct power_supply_desc {
const char *name; //注册的名字,会在 sys/class/power_supply/name 生成节点
enum power_supply_type type; 设备的类型 一般为 dc 或 usb 或 battery 我们选择battety 对应的宏 为 POWER_SUPPLY_TYPE_BATTERY
enum power_supply_property *properties; // psy 属性
size_t num_properties; //psy属性个数
// psy的回调函数用于获取psy属性的参数
int (*get_property)(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val);
// psy的回调函数用于设置psy属性的参数
int (*set_property)(struct power_supply *psy,
enum power_supply_property psp,
const union power_supply_propval *val);
int (*property_is_writeable)(struct power_supply *psy,
enum power_supply_property psp);
void (*external_power_changed)(struct power_supply *psy);
void (*set_charged)(struct power_supply *psy);
}
简单明显的在代码中描述,现在讲重点, psy属性。它其实就是一个枚举的参数,如果需要设置什么属性就枚举什么,由于枚举的参数很多就提几个重点讲一下。看代码注释。
static enum power_supply_property rk30_adc_battery_props[] = {
POWER_SUPPLY_PROP_STATUS, // psy 的状态
POWER_SUPPLY_PROP_HEALTH, // psy 的健康值
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW, //当前电压值
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CAPACITY, //当前电量
POWER_SUPPLY_PROP_MODEL_NAME, //模块名字
POWER_SUPPLY_PROP_MANUFACTURER, //电池制造商
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, //设置的最大电压值
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, //设置的最小电压值
};
说明:当你注册成功后,你可以在 /sys/class/power_supply/test_battery 目录下看到这个属性对应文件
属性的节点存在了,那么他是怎么上报到应用层的呢?当然是通过回调函数去设置(set_property)或者读取(get_property)属性值啦.是通过一个switch去选择不同属性的参数,至于上报的参数需要自己设置或者算法去获取。提供了get_property的示例。
static int rk30_adc_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
switch (psp) {
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = "KYLIN battery";
break;
case POWER_SUPPLY_PROP_MANUFACTURER:
val->strval = "ZYSJ_ADC_BATTERY";
break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = rk30_adc_battery_get_status(gBatteryData);
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = battery_health;
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = battery_present;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = battery_technology;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = rk30_adc_battery_get_capacity(gBatteryData);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
//val->intval = 100;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val ->intval = rk30_adc_battery_get_voltage(gBatteryData);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
val->intval = BATT_MAX_VOL_VALUE;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
val->intval = BATT_ZERO_VOL_VALUE;
break;
default:
pr_info("%s: some properties deliberately report errors.\n",
__func__);
return -EINVAL;
}
return 0;
}
回归到第一个注册函数的第三个参数 struct power_supply_config *cfg。
struct power_supply_config{
struct device_node *of_node;
struct fwnode_handle *fwnode;
/* Driver private data */
void *drv_data; //用于传递数据
chat ** supplied_to; //一个字符串数组,保存了由该PSY供电的PSY列表,以此可将PSY组成互相级联的PSY链表。
size_t num_supplicants; //supplicant的个数
}
power_supply注册的流程讲完了,顺便提一下它的卸载流程,不然会造成内存浪费。
void power_supply_unregister(struct power_supply *psy) //传递注册的psy参数即可
当 power_supply更新状态时,调用power_supply_changed函数通知应用层。届时等待应用层调用回调函数进行 power_supply的设置或者获取。
void power_supply_changed(struct power_supply *psy)
说明:属性变化时,调用power_supply_changed更新psy状态,并向应用层上报状态
一个电池充放电管理关键还是在于如何检测是否正在充电,是否充满电,这个需要配合硬件,以上的API函数可以控制电池的充电和放电上报,你可以通过AD脚检测电池的电压,设置一个定时器去模拟电池充放电管理的过程,并且通过两个IO口,读取IO口的电平值判断电池是否充满电,是否正在充电,这样可以达到充放电的管理过程。
这套函数同样使用与rk3288 和 rk3399 。
回顾一下调试手段:
1、查看电池是否注册了
cat /sys/class/power_supply/ xxx //xxx 为注册的名字
2、电池的属性
cat /sys/class/power_supply/test_battery //里面有子系统的属性值
文章到此结束啦,祝你生活愉快。