手把手教你为嵌入式设备编写一个简单的Power Supply驱动(基于Linux 4.19内核)

张开发
2026/4/21 19:13:29 15 分钟阅读

分享文章

手把手教你为嵌入式设备编写一个简单的Power Supply驱动(基于Linux 4.19内核)
嵌入式Linux电源管理实战从零构建Power Supply驱动在嵌入式系统开发中电源管理是一个至关重要的环节。无论是智能家居设备、工业控制器还是便携式医疗设备高效的电源管理都能显著提升产品的续航能力和稳定性。本文将带你深入Linux内核的Power Supply子系统通过实战演练教你如何为嵌入式设备开发一个完整的电源驱动。1. Power Supply子系统架构解析Linux内核的Power Supply子系统为各类电源设备提供了统一的管理框架。这个精妙的抽象层将五花八门的电源硬件电池、USB充电器、AC适配器等转换为标准化的软件接口让上层应用可以一致地访问电源状态信息。核心组件包括power_supply_core实现基础框架和公共逻辑power_supply_sysfs提供sysfs接口和uevent通知机制power_supply_leds实现电源状态LED指示功能在实际项目中我们最常打交道的是以下几个关键数据结构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_config { struct device_node *of_node; char **supplied_to; size_t num_supplicants; void *drv_data; };提示在嵌入式开发中90%的电源驱动工作都围绕这两个结构体展开。理解它们的每个字段含义是开发可靠驱动的关键。2. 驱动开发环境搭建在开始编码前我们需要准备适当的开发环境。假设我们基于Linux 4.19内核进行开发这是一个在嵌入式领域广泛使用的LTS版本。开发工具准备清单交叉编译工具链如arm-linux-gnueabihfQEMU模拟器或真实硬件开发板Linux内核源码含对应版本的.config文件必要的内核头文件对于我们的虚拟电池设备我们可以创建一个简单的Makefileobj-m virtual_battery.o KDIR ? /lib/modules/$(shell uname -r)/build all: make -C $(KDIR) M$(PWD) modules clean: make -C $(KDIR) M$(PWD) clean关键开发技巧使用modprobe -r和modprobe命令快速重载驱动模块通过dmesg -w实时查看内核日志利用sysfs接口快速验证驱动功能3. 虚拟电池驱动实现让我们实现一个模拟电池设备驱动它将展示以下特性电量百分比随时间递减充电状态可切换电池健康状态温度模拟首先定义驱动模块的基本结构#include linux/module.h #include linux/power_supply.h #include linux/slab.h #include linux/timer.h struct virtual_battery { struct power_supply *psy; struct timer_list timer; int capacity; bool charging; int health; };接下来实现核心的回调函数static int virtual_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct virtual_battery *bat power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_CAPACITY: val-intval bat-capacity; break; case POWER_SUPPLY_PROP_STATUS: val-intval bat-charging ? POWER_SUPPLY_STATUS_CHARGING : POWER_SUPPLY_STATUS_DISCHARGING; break; case POWER_SUPPLY_PROP_HEALTH: val-intval bat-health; break; case POWER_SUPPLY_PROP_TEMP: val-intval 250; // 模拟25.0°C break; default: return -EINVAL; } return 0; }注意get_property回调必须快速返回避免阻塞。复杂操作应该使用工作队列或定时器异步处理。4. 驱动注册与初始化完整的驱动初始化流程包括以下几个关键步骤填充power_supply_desc结构体配置power_supply_config注册power_supply设备设置定时器或工作队列static int virtual_battery_probe(struct platform_device *pdev) { struct virtual_battery *bat; struct power_supply_config psy_cfg {}; static enum power_supply_property props[] { POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_TEMP, }; bat devm_kzalloc(pdev-dev, sizeof(*bat), GFP_KERNEL); if (!bat) return -ENOMEM; psy_cfg.drv_data bat; psy_cfg.of_node pdev-dev.of_node; bat-psy devm_power_supply_register(pdev-dev, (struct power_supply_desc){ .name virtual_battery, .type POWER_SUPPLY_TYPE_BATTERY, .properties props, .num_properties ARRAY_SIZE(props), .get_property virtual_battery_get_property, }, psy_cfg); if (IS_ERR(bat-psy)) return PTR_ERR(bat-psy); // 初始化模拟电池状态 bat-capacity 100; bat-charging false; bat-health POWER_SUPPLY_HEALTH_GOOD; // 设置定时器模拟电量消耗 timer_setup(bat-timer, virtual_battery_timer, 0); mod_timer(bat-timer, jiffies msecs_to_jiffies(5000)); return 0; }关键点解析使用devm_系列函数自动管理资源定时器间隔设置为5秒模拟电量消耗通过power_supply_changed()通知内核状态变化5. Sysfs接口与用户空间交互成功注册驱动后内核会自动在/sys/class/power_supply/下创建对应目录。我们的虚拟电池设备将暴露以下属性/sys/class/power_supply/virtual_battery/ ├── capacity - 当前电量百分比 ├── health - 电池健康状态 ├── status - 充电状态 ├── temp - 电池温度 └── type - 设备类型我们可以通过简单的shell命令测试驱动功能# 查看当前电量 cat /sys/class/power_supply/virtual_battery/capacity # 监控电量变化 watch -n 1 cat /sys/class/power_supply/virtual_battery/capacity高级技巧要实现更复杂的用户空间交互可以考虑添加uevent通知支持实现ioctl控制接口创建debugfs节点用于调试6. 电源事件处理与状态机一个健壮的电源驱动需要妥善处理各种状态变化。以下是典型的电池状态机实现static void virtual_battery_timer(struct timer_list *t) { struct virtual_battery *bat from_timer(bat, t, timer); // 模拟电量变化 if (bat-charging) { bat-capacity min(100, bat-capacity 5); } else { bat-capacity max(0, bat-capacity - 2); } // 模拟充电完成 if (bat-charging bat-capacity 95) { bat-charging false; } // 通知内核状态变化 power_supply_changed(bat-psy); // 重新设置定时器 mod_timer(bat-timer, jiffies msecs_to_jiffies(5000)); }状态转换表当前状态触发条件新状态动作放电中电量15%低电量发送通知低电量接入充电充电中开始充电充电中电量95%充满停止充电充满断开电源放电中开始放电7. 高级功能扩展基础功能实现后我们可以考虑添加更多实用特性温度监控实现case POWER_SUPPLY_PROP_TEMP_ALERT_MIN: val-intval 0; // 最低温度告警阈值 break; case POWER_SUPPLY_PROP_TEMP_ALERT_MAX: val-intval 600; // 最高温度告警阈值(60°C) break;充电控制static int virtual_battery_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { struct virtual_battery *bat power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: bat-charging (val-intval POWER_SUPPLY_STATUS_CHARGING); power_supply_changed(bat-psy); break; default: return -EINVAL; } return 0; }电源路径管理static void virtual_battery_external_power_changed(struct power_supply *psy) { // 当外部电源状态变化时的处理逻辑 struct virtual_battery *bat power_supply_get_drvdata(psy); bool ac_online power_supply_is_system_supplied(); if (ac_online bat-capacity 90) { bat-charging true; power_supply_changed(bat-psy); } }8. 调试与性能优化开发过程中有效的调试手段能极大提高效率常用调试技巧使用dynamic_debug动态控制日志级别通过tracepoints跟踪电源状态变化利用perf工具分析驱动性能常见问题排查表问题现象可能原因解决方案属性不显示未在properties数组中声明检查power_supply_desc状态变化无通知忘记调用power_supply_changed添加状态变化通知用户空间读不到值get_property返回错误检查回调函数实现驱动加载失败资源分配问题检查dmesg输出性能优化建议减少get_property调用频率使用延迟工作队列处理耗时操作合理设置轮询间隔平衡响应速度和功耗9. 实战集成到真实项目将我们的驱动集成到真实嵌入式系统时需要考虑以下方面设备树配置示例virtual_battery: battery { compatible linux,virtual-battery; monitored-battery bat; power-supplies charger; };与充电器IC协同工作static int virtual_battery_charger_event(struct notifier_block *nb, unsigned long event, void *data) { struct virtual_battery *bat container_of(nb, struct virtual_battery, charger_nb); if (event CHARGER_CONNECTED) { bat-charging true; power_supply_changed(bat-psy); } return NOTIFY_OK; }电源管理策略低电量时进入省电模式高温环境下限制充电电流多电源自动切换管理10. 测试与验证完善的测试方案是驱动稳定性的保障单元测试用例import unittest import os class TestVirtualBattery(unittest.TestCase): classmethod def setUpClass(cls): cls.sysfs_path /sys/class/power_supply/virtual_battery def test_capacity_decrease(self): initial int(open(f{self.sysfs_path}/capacity).read()) time.sleep(6) # 等待定时器触发 current int(open(f{self.sysfs_path}/capacity).read()) self.assertLess(current, initial)自动化测试框架集成使用LTPLinux Test Project进行内核测试编写Shell脚本验证sysfs接口使用Python自动化测试用例真实场景测试项目连续充放电循环测试极端温度条件下的行为验证长时间运行的稳定性测试11. 进阶主题掌握了基础驱动开发后可以进一步探索以下高级主题与PMIC的深度集成static int virtual_battery_set_charge_current(struct virtual_battery *bat, int current_ua) { // 实际项目中这里会通过I2C/SPI配置PMIC bat-charge_current current_ua; return 0; }Fuel Gauge校准学习曲线补偿温度补偿算法老化因子计算低功耗优化技巧合理使用中断代替轮询动态调整更新频率电源状态感知的延迟处理12. 总结与最佳实践通过本教程我们完整实现了一个功能齐全的虚拟电池驱动。在实际项目开发中建议遵循以下最佳实践代码组织建议将硬件相关和平台相关代码分离使用模块化设计便于功能扩展保持与内核编码风格一致维护技巧详细记录硬件特性和限制为每个回调函数添加注释说明维护变更日志和兼容性说明性能考量关键路径避免内存分配合理使用缓存减少I/O操作考虑多核环境下的并发安全电源驱动作为连接硬件和系统的桥梁其稳定性和可靠性直接影响用户体验。希望本教程能帮助你在未来的嵌入式项目中构建出更加优秀的电源管理解决方案。

更多文章