嵌入式系统电源管理

1、电源管理

1.1 概述

目的:

      在系统可维持正常所期望工作状态的情况下,尽可能降低功耗。Linux电源管理涉及到系统待机,频率电压变换,系统空闲处理,运行时期电源管理等多个方面。

措施:

《Linux设备驱动开发详解》一书中对电源管理的总结:

1.2 单片机

对于51单片机的电源管理,提供了低速,空闲和掉电三种模式来省电。

  • 通过分频使系统降频工作
  • 空闲模式:

  • 掉电模式

1.3 linux电源管理模型

1.3.1 休眠模型

(1)电源状态描述

- On(on)                                                  S0 -  Working
- Standby (standby)                                S1 -  CPU and RAM are powered but not executed
- to RAM(mem)                                         S3 -  RAM is powered and the running content is saved to RAM【Suspend】
- Suspend to Disk,Hibernation(disk)    S4 -  All content is saved to Disk and power down 【Hibernate】

(2)cat /sys/power/state

查看支持的状态:

"suspend" 在嵌入式设备一般是指进入到suspend-to-RAM状态

1.3.2 Runtime模型

在系统运行阶段进入低功耗状态

1.4 Android电源管理

2、Linux电源管理

linux系统电源管理涉及多个子系统,如休眠唤醒,调频调压,充电等等。

2.1 Suspend to RAM

2.1.1 触发方式

查看支持的休眠方式:

cat /sys/power/state

休眠一般是由应用程序触发 -> 内核休眠处理 -> 硬件。最简单的触发方式:

echo mem > /sys/power/state

2.1.2 Suspend 流程

系统平台到驱动级挂起流程

 

https://blog.csdn.net/lixiaojie1012/article/details/23707681

https://blog.csdn.net/wenjin359/article/details/83056560

https://blog.csdn.net/u011006622/article/details/47417823

dev_pm_ops(include/linux/pm.h)

struct dev_pm_ops {
	int (*prepare)(struct device *dev);
	void (*complete)(struct device *dev);
	int (*suspend)(struct device *dev);
	int (*resume)(struct device *dev);
	int (*freeze)(struct device *dev);
	int (*thaw)(struct device *dev);
	int (*poweroff)(struct device *dev);
	int (*restore)(struct device *dev);
	int (*suspend_late)(struct device *dev);
	int (*resume_early)(struct device *dev);
	int (*freeze_late)(struct device *dev);
	int (*thaw_early)(struct device *dev);
	int (*poweroff_late)(struct device *dev);
	int (*restore_early)(struct device *dev);
	int (*suspend_noirq)(struct device *dev);
	int (*resume_noirq)(struct device *dev);
	int (*freeze_noirq)(struct device *dev);
	int (*thaw_noirq)(struct device *dev);
	int (*poweroff_noirq)(struct device *dev);
	int (*restore_noirq)(struct device *dev);
	int (*runtime_suspend)(struct device *dev);
	int (*runtime_resume)(struct device *dev);
	int (*runtime_idle)(struct device *dev);
};

2.1.3 如何在驱动中支持Suspend to RAM

(1)提供回调函数

定义PM相关回调函数结构体dev_pm_ops,并实现PM相关回调函数。

static struct dev_pm_ops lcd_pm = {
	.suspend = lcd_suspend,
	.resume  = lcd_resume,	
};

struct platform_driver lcd_drv = {
	.probe		= lcd_probe,
	.remove		= lcd_remove,
	.driver		= {
		.name	= "mylcd",
		.pm     = &lcd_pm,
	}
};

(2)如果有需要则注册notifie事件,在休眠唤醒的不同阶段执行准备工作。

static int lcd_suspend_notifier(struct notifier_block *nb,
                unsigned long event,
                void *dummy)
{

    switch (event) {
    case PM_SUSPEND_PREPARE:
        printk("lcd suspend notifiler test: PM_SUSPEND_PREPARE\n");
        return NOTIFY_OK;
    case PM_POST_SUSPEND:
        printk("lcd suspend notifiler test: PM_POST_SUSPEND\n");
        return NOTIFY_OK;

    default:
        return NOTIFY_DONE;
    }
}

static struct notifier_block lcd_pm_notif_block = {
    .notifier_call = lcd_suspend_notifier,
};
register_pm_notifier(&lcd_pm_notif_block);

(3)至少设置1个唤醒源,这样就可以完成系统的休眠,并唤醒。   

 /* 指定这些中断可以用于唤醒系统 */
    irq_set_irq_wake(irq, 1);

hrtimer:The count is = 26
hrtimer:The count is = 27
echo mem > /sys/power/state
PM: Syncing filesystems ... done.
Freezing user space processes ... (elapsed 0.001 seconds) done.
Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
Suspending console(s) (use no_console_suspend to debug)
PM: suspend of devices complete after 6.982 msecs
PM: suspend devices took 0.010 seconds
PM: late suspend of devices complete after 2.199 msecs
PM: noirq suspend of devices complete after 2.082 msecs
Disabling non-boot CPUs ...
PM: noirq resume of devices complete after 1.463 msecs
PM: early resume of devices complete after 1.646 msecs
gpmi-nand 1806000.gpmi-nand: enable the asynchronous EDO mode 5
PM: resume of devices complete after 73.038 msecs
PM: resume devices took 0.080 seconds
Restarting tasks ... done.
root@mys6ul14x14:/mnt# hrtimer:The count is = 28
hrtimer:The count is = 29
hrtimer:The count is = 30
hrtimer:The count is = 31
hrtimer:The count is = 32

2.2 Runtime Suspend

运行时期的电源管理机制,有些时候,并不希望直接让整个系统Suspend to RAM,那么对于一些独立的设备希望做到电源管理,内核提供Runtim PM,应用程序根据需要,动态的管理某个设备的休眠唤醒。做到对子模块在运行时期的电源管理。将Suspend to RAM与Runtime PM结合会更有效的实现节能降耗。

2.2.1 如何使驱动支持Runtime Suspend

  1. 驱动初始化中使能rpm
  2. 提供rpm相关回调函数
  3. 根据需要在合适的时候调用相关API对设备进行休眠或者唤醒操作
  4. 销毁函数中禁止掉相关资源

简之,一个驱动想要实现rpm,在dev_pm_ops 增加回调函数,在驱动合适的时候调用rpm相关API,实现对设备的在线时期电源管理。

2.2.2 Runtime API

  • 使能和关闭

void pm_runtime_enable(struct device *dev)

static inline void pm_runtime_disable(struct device *dev)

如下:pm_runtime_enable主要是对变量disable_depth进行-1操作。反之pm_runtime_disable对disable_depth进行+1操作。disable_depth在初始化的时候初始值为1。

void pm_runtime_enable(struct device *dev)
{
	unsigned long flags;

	spin_lock_irqsave(&dev->power.lock, flags);

	if (dev->power.disable_depth > 0)
		dev->power.disable_depth--;
	else
		dev_warn(dev, "Unbalanced %s!\n", __func__);

	spin_unlock_irqrestore(&dev->power.lock, flags);
}
  • 休眠与唤醒
static inline int pm_runtime_get_sync(struct device *dev)

static inline int pm_runtime_put_sync(struct device *dev)

pm_runtime_get_sync与pm_runtime_put_sync供给驱动在合适的时候调用,从而导致驱动提供的休眠唤醒回调函数被调用。其大致调用流程如下:

pm_runtime_get_sync流程

(1)增加引用计数

(2)判断是否使能,是否处于休眠等,不满足条件直接返回

(3)检测是否设置timer_autosuspends,决定是否延迟进行操作

(4)如果设备处于RPM_RESUMING或者RPM_SUSPENDING,则等待

(5)如果有父设备的话,进行父设备的相关唤醒操作

(6) 调用子系统及驱动提供的相关回调函数,执行最终目的操作 

子系统级别回调函数         

            callback = dev->pm_domain->ops.runtime_resume; 或
            callback = dev->type->pm->runtime_resume;      或
            callback = dev->class->pm->runtime_resume;     或
            callback = dev->bus->pm->runtime_resume;       或

设备级别回调函数
            callback = dev->driver->pm->runtime_resume;

2.2.3 LCD Runtime Suspend例子

1、在probe的时候使能,在remove的时候禁止

static int lcd_probe(struct platform_device *pdev)
{
	pm_runtime_set_active(&pdev->dev); //默认状态是运行
	pm_runtime_enable(&pdev->dev);
	return 0;
}
static int lcd_remove(struct platform_device *pdev)
{
	pm_runtime_disable(&pdev->dev);
	return 0;
}

2、提供相关回调函数

struct platform_driver lcd_drv = {
	.probe		= lcd_probe,
	.remove		= lcd_remove,
	.driver		= {
		.name	= "mylcd",
		.pm     = &lcd_pm,
	}
};

static struct dev_pm_ops lcd_pm = {
	.suspend = lcd_suspend,
	.resume  = lcd_resume,	
	.runtime_suspend = lcd_suspend,
	.runtime_resume  = lcd_resume,	
};

3、在open的时候唤醒,在关闭时候休眠设备

static int mylcd_open(struct fb_info *info, int user)
{
    pm_runtime_get_sync(&lcd_dev.dev);
    return 0;
}
static int mylcd_release(struct fb_info *info, int user)
{
    pm_runtime_put_sync(&lcd_dev.dev);
    return 0;
}

2.2.4 sys接口

上面通过驱动中提供接口的,APP可以在合适的时候触发RPM,系统sys接口也提供了相关接口

echo on >  /sys/devices/.../power/control  

control_store -> pm_runtime_forbid(dev); :
                atomic_inc(&dev->power.usage_count);
                rpm_resume(dev, 0);

echo auto >  /sys/devices/.../power/control

control_store -> pm_runtime_allow(dev); 
                atomic_dec_and_test(&dev->power.usage_count)
                rpm_idle(dev, RPM_AUTO);

2.3 USB休眠唤醒机制分析

2.3.1 USB PM框架

bus-》devices

2.3.2 USB回调函数

static const struct dev_pm_ops usb_device_pm_ops = {
	.prepare =	usb_dev_prepare,
	.complete =	usb_dev_complete,
	.suspend =	usb_dev_suspend,
	.resume =	usb_dev_resume,
	.freeze =	usb_dev_freeze,
	.thaw =		usb_dev_thaw,
	.poweroff =	usb_dev_poweroff,
	.restore =	usb_dev_restore,
#ifdef CONFIG_PM_RUNTIME
	.runtime_suspend =	usb_runtime_suspend,
	.runtime_resume =	usb_runtime_resume,
	.runtime_idle =		usb_runtime_idle,
#endif
};

struct device_type usb_device_type = {
	.name =		"usb_device",
	.release =	usb_release_dev,
	.uevent =	usb_dev_uevent,
	.devnode = 	usb_devnode,
#ifdef CONFIG_PM
	.pm =		&usb_device_pm_ops,
#endif
};

usb_alloc_dev
->dev->dev.bus = &usb_bus_type;

 

typedef struct pm_message {
    int event;
} pm_message_t;


 

#define PM_EVENT_INVALID	(-1)
#define PM_EVENT_ON		0x0000
#define PM_EVENT_FREEZE 	0x0001
#define PM_EVENT_SUSPEND	0x0002
#define PM_EVENT_HIBERNATE	0x0004
#define PM_EVENT_QUIESCE	0x0008
#define PM_EVENT_RESUME		0x0010
#define PM_EVENT_THAW		0x0020
#define PM_EVENT_RESTORE	0x0040
#define PM_EVENT_RECOVER	0x0080
#define PM_EVENT_USER		0x0100
#define PM_EVENT_REMOTE		0x0200
#define PM_EVENT_AUTO		0x0400

 

 

3、Andorid电源管理

 

嵌入式系统开发是一门涉及硬件和软件的综合学科,主要用于设计和开发嵌入式设备,如智能手机、汽车电子系统、家电等。以下是一个嵌入式系统开发的学习路线: 1. 学习C/C++编程语言:C/C++是嵌入式系统开发中最常用的编程语言,掌握它们是入门的基础。 2. 学习计算机体系结构和操作系统原理:了解计算机的基本原理和操作系统的工作原理,对于理解嵌入式系统的底层运行机制非常重要。 3. 学习嵌入式系统架构和硬件知识:了解常见的嵌入式系统架构,如ARM、MIPS等,并学习相关的硬件知识,如处理器、内存、外设等。 4. 学习嵌入式系统开发工具和平台:掌握常用的嵌入式开发工具和平台,如Keil、IAR Embedded Workbench、Arduino等,以及相关的调试和测试工具。 5. 学习嵌入式系统编程技术:学习嵌入式系统的编程技术,如裸机编程、驱动程序开发、RTOS(实时操作系统)等。 6. 学习嵌入式系统通信和网络技术:了解嵌入式系统中常用的通信和网络技术,如UART、SPI、I2C、CAN、Ethernet等。 7. 学习嵌入式系统电源管理和功耗优化:了解嵌入式系统电源管理技术和功耗优化方法,以提高系统的性能和续航能力。 8. 实践项目开发:通过参与实际的嵌入式系统项目开发,将所学知识应用到实际中,提升自己的实践能力和项目管理能力。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值