从零开始手把手教你为嵌入式设备编写一个简单的Power Supply驱动基于Linux 4.19.111在嵌入式Linux开发中电源管理是一个至关重要的环节。无论是智能家居设备、工业控制器还是便携式医疗设备稳定可靠的电源供应都是系统正常运行的基础。本文将带你深入Linux内核的Power Supply子系统从零开始构建一个完整的电池驱动。1. 理解Linux Power Supply子系统Linux内核中的Power Supply子系统为各类电源设备提供了统一的抽象框架。这个框架的核心思想是将电源状态信息抽象为属性properties并通过sysfs接口向用户空间暴露这些信息。1.1 子系统架构Power Supply子系统主要由三个部分组成Core位于drivers/power/power_supply_core.c提供核心数据结构和公共逻辑Sysfs位于drivers/power/power_supply_sysfs.c实现sysfs接口和uevent功能LEDs位于drivers/power/power_supply_leds.c提供电源状态LED指示功能典型的电源管理硬件通常包含两个主要组件Fuel Gauge电量计监测电池电量、健康状态等信息Charger充电管理处理电源插拔检测和充放电过程1.2 关键数据结构开发Power Supply驱动需要理解三个核心数据结构struct power_supply_config { struct device_node *of_node; struct fwnode_handle *fwnode; void *drv_data; char **supplied_to; size_t num_supplicants; }; struct power_supply_desc { const char *name; enum power_supply_type type; enum power_supply_property *properties; size_t num_properties; int (*get_property)(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val); int (*set_property)(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val); /* 其他成员省略 */ }; struct power_supply { const struct power_supply_desc *desc; struct device dev; /* 其他成员省略 */ };2. 硬件准备与数据手册分析假设我们基于i.MX6UL平台开发使用TI的BQ27425电量计芯片通过I2C接口通信。2.1 硬件连接典型的连接方式如下i.MX6UL引脚BQ27425引脚功能描述I2C1_SCLSCLI2C时钟I2C1_SDASDAI2C数据GPIO1_IO02INT中断输出VDD_3V3VCC电源(3.3V)GNDGND地线2.2 关键寄存器分析从BQ27425数据手册中我们需要关注以下寄存器寄存器地址名称描述访问权限0x02Voltage电池电压(mV)只读0x04AverageCurrent平均电流(mA)只读0x06RelativeStateOfCharge剩余电量百分比(%)只读0x08InternalTemperature内部温度(0.1°K)只读0x0AFullChargeCapacity满充容量(mAh)只读3. 驱动开发实战3.1 初始化与注册首先定义驱动所需的数据结构struct bq27425_data { struct i2c_client *client; struct power_supply *psy; struct delayed_work monitor_work; int voltage; int current; int capacity; int temp; };然后实现驱动的probe函数static int bq27425_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct bq27425_data *data; struct power_supply_config psy_cfg {}; int ret; /* 分配并初始化数据结构 */ data devm_kzalloc(client-dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; >static enum power_supply_property bq27425_props[] { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TECHNOLOGY, }; static const struct power_supply_desc bq27425_psy_desc { .name bq27425-battery, .type POWER_SUPPLY_TYPE_BATTERY, .properties bq27425_props, .num_properties ARRAY_SIZE(bq27425_props), .get_property bq27425_get_property, }; static int bq27425_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct bq27425_data *data power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: val-intval POWER_SUPPLY_STATUS_DISCHARGING; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: val-intval >static int bq27425_read_word(struct i2c_client *client, u8 reg) { int ret; ret i2c_smbus_read_word_data(client, reg); if (ret 0) return ret; return le16_to_cpu(ret); } static void bq27425_update_data(struct bq27425_data *data) { >static void bq27425_monitor_work(struct work_struct *work) { struct bq27425_data *data container_of(work, struct bq27425_data, monitor_work.work); /* 更新数据 */ bq27425_update_data(data); /* 通知子系统数据变化 */ power_supply_changed(data-psy); /* 重新调度工作队列每5秒更新一次 */ schedule_delayed_work(data-monitor_work, 5 * HZ); }4. 设备树配置在设备树中添加节点描述硬件连接i2c1 { status okay; bq27425: fuel-gauge55 { compatible ti,bq27425; reg 0x55; monitored-battery battery; }; }; battery: battery { compatible simple-battery; voltage-min-design-microvolt 3000000; voltage-max-design-microvolt 4200000; energy-full-design-microwatt-hours 10000000; charge-full-design-microamp-hours 2500000; };5. 测试与验证驱动加载后可以通过sysfs接口验证功能# 查看电源设备列表 ls /sys/class/power_supply/ # 查看电池属性 cat /sys/class/power_supply/bq27425-battery/voltage_now cat /sys/class/power_supply/bq27425-battery/capacity也可以通过内核日志查看驱动运行状态dmesg | grep bq274256. 高级功能扩展6.1 实现充电状态检测扩展驱动以支持充电状态检测/* 在属性数组中添加 */ POWER_SUPPLY_PROP_STATUS, /* 在get_property中添加处理 */ case POWER_SUPPLY_PROP_STATUS: if (data-current 0) /* 正电流表示充电 */ val-intval POWER_SUPPLY_STATUS_CHARGING; else if (data-capacity 100) val-intval POWER_SUPPLY_STATUS_FULL; else val-intval POWER_SUPPLY_STATUS_DISCHARGING; break;6.2 添加温度监控实现温度监控和过热保护/* 在监控工作函数中添加检查 */ if (data-temp 500) { /* 50°C */ dev_warn(data-client-dev, Battery temperature too high: %d\n, >/* 在监控工作函数中添加检查 */ if (data-capacity 15) { power_supply_changed(data-psy); /* 触发uevent */ dev_info(data-client-dev, Low battery: %d%%\n,>