本笔记学习 Zephyr 的设备级电源管理(Device Power Management),实现外设的动态电源控制。
zephyr/samples/subsys/pm/device_pm/ - 设备级电源管理示例
| 类型 | 作用范围 | 控制方式 |
|---|---|---|
| System PM | 整个系统 | 系统状态转换 |
| Device PM | 单个外设 | 使用计数 + 运行时控制 |
| 状态 | 说明 | 功耗特征 |
|---|---|---|
| PM_DEVICE_STATE_ACTIVE | 正常运行 | 相对较高 |
| PM_DEVICE_STATE_SUSPENDED | 挂起 | 相对较低 |
| PM_DEVICE_STATE_OFF | 关闭 | 取决于具体硬件实现 |
#include <zephyr/pm/device.h>
static int my_device_pm_action(const struct device *dev,
enum pm_device_action action)
{
switch (action) {
case PM_DEVICE_ACTION_RESUME:
printk("设备恢复: %s\n", dev->name);
/* 恢复外设状态,如重新初始化寄存器 */
break;
case PM_DEVICE_ACTION_SUSPEND:
printk("设备挂起: %s\n", dev->name);
/* 保存外设状态,如保存寄存器值 */
break;
case PM_DEVICE_ACTION_TURN_ON:
printk("设备上电: %s\n", dev->name);
break;
case PM_DEVICE_ACTION_TURN_OFF:
printk("设备断电: %s\n", dev->name);
break;
default:
return -ENOTSUP;
}
return 0;
}
PM_DEVICE_DEFINE(my_driver, my_device_pm_action);
DEVICE_DEFINE(my_driver, "my_driver", &my_init,
PM_DEVICE_GET(my_driver), NULL, NULL,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &api);
static int my_init(const struct device *dev)
{
/* 初始化外设硬件... */
/* 启用设备运行时电源管理 */
return pm_device_runtime_enable(dev);
}
const struct device *dev = DEVICE_DT_GET(DT_NODELABEL(my_device));
if (!device_is_ready(dev)) {
printk("设备未就绪\n");
return -ENODEV;
}
/* 设备自动恢复(如果之前被挂起) */
int ret = pm_device_runtime_get(dev);
if (ret < 0) {
printk("获取设备失败: %d\n", ret);
return ret;
}
/* 现在可以正常使用设备 */
/* 设备可能被自动挂起 */
int ret = pm_device_runtime_put(dev);
if (ret == 1) {
printk("异步挂起请求已排队\n");
}
/* 非阻塞释放,允许系统在其他线程处理挂起 */
pm_device_runtime_put_async(dev);
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/pm/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/drivers/gpio.h>
static int my_gpio_pm_action(const struct device *dev,
enum pm_device_action action)
{
switch (action) {
case PM_DEVICE_ACTION_RESUME:
printk("GPIO 恢复\n");
/* 恢复 GPIO 配置 */
break;
case PM_DEVICE_ACTION_SUSPEND:
printk("GPIO 挂起\n");
/* 保存 GPIO 状态 */
break;
default:
return -ENOTSUP;
}
return 0;
}
PM_DEVICE_DEFINE(my_gpio, my_gpio_pm_action);
static int my_gpio_init(const struct device *dev)
{
return pm_device_runtime_enable(dev);
}
DEVICE_DEFINE(my_gpio, "my_gpio", my_gpio_init,
PM_DEVICE_GET(my_gpio), NULL, NULL,
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE, NULL);
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/pm/device_runtime.h>
#include <zephyr/drivers/gpio.h>
#define MY_GPIO DT_NODELABEL(gpio0)
int main(void)
{
const struct device *gpio = DEVICE_DT_GET(MY_GPIO);
if (!device_is_ready(gpio)) {
printk("GPIO 未就绪\n");
return -ENODEV;
}
/* 使用 GPIO 前获取 */
pm_device_runtime_get(gpio);
/* 配置 LED 引脚 */
const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
/* 闪烁 LED */
gpio_pin_set(gpio, led.pin, 1);
k_msleep(100);
gpio_pin_set(gpio, led.pin, 0);
/* 使用完毕后释放(可能触发挂起) */
pm_device_runtime_put(gpio);
printk("完成\n");
return 0;
}
设备可以声明依赖关系,确保父设备先于子设备恢复。
static int child_open(const struct device *dev)
{
const struct device *parent;
int ret;
/* 确保父设备已恢复 */
ret = pm_device_runtime_get(parent);
if (ret < 0) return ret;
/* 获取自身 */
ret = pm_device_runtime_get(dev);
if (ret < 0) {
pm_device_runtime_put(parent);
return ret;
}
return 0;
}
static int child_close(const struct device *dev)
{
/* 释放自身 */
pm_device_runtime_put(dev);
/* 释放父设备 */
pm_device_runtime_put(parent);
return 0;
}
# prj.conf
CONFIG_PM=y
CONFIG_PM_DEVICE=y
CONFIG_PM_DEVICE_RUNTIME=y
# 启用设备运行时自动启用
CONFIG_PM_DEVICE_RUNTIME_AUTO=y
/* 启用设备运行时 PM */
&gpio0 {
status = "okay";
zephyr,pm-device-runtime-auto;
};
是否具备独立电源域、自动 runtime PM,以及具体挂起行为,要以当前 SoC 文档、外设驱动实现和设备树配置为准。
pm_device_runtime_put()pm_device_runtime_put_async() 避免阻塞# 查看设备电源状态
# 启用 CONFIG_PM_DEBUG 后可在日志中查看状态转换
| 函数 | 说明 |
|---|---|
pm_device_runtime_enable() | 启用运行时 PM |
pm_device_runtime_disable() | 禁用运行时 PM |
pm_device_runtime_get() | 获取设备(恢复) |
pm_device_runtime_put() | 释放设备(挂起) |
pm_device_runtime_put_async() | 异步释放设备 |
pm_device_state_get() | 获取设备状态 |
*小白 🤖 - 2026-03-17*