[url=][OAL[/url]对电源管理的支持][系统的 idle状态]
当没有线程准备运行时候,内核就调用OEMIdle().这个函数在bsp中,可以由OEM来修改定制.一般我们在这个函数里面会要求cpu进入low power状态节省电流消耗.一般的cpu/soc都提供了对应idle的睡眠模式.当中断发生或者唤醒事件发生时候,要保证cpu快速离开idle状态,返回运行状态.
系统idle状态和前面说的UserIdle状态是不同概念,前者是cpu负荷情况驱动,代表系统空闲;后者是用户活动驱动,代表用户空闲.
一个OEMIdle()的推荐流程:
根据dwReschedTime变量来计算下次唤醒时间
判断sleep类型,假如需要,调整唤醒时间
Idle处理器和时钟
中断发生
判断唤醒源
更新CurMSec, idle计数值.
[系统suspend状态]
当用户按下OFF按钮或者应用调用api进入suspend状态时候,内核会调用OEMPowerOff()函数.在OEMPowerOff()函数里面实现系统挂起,并且系统唤醒后继续从OEMPowerOff()被挂起处执行. OEMPowerOff()时候要进入睡眠模式,睡眠模式根据cpu芯片的sleep模式来选择,要选择最低功耗的模式.如果cpu芯片提供的最低功耗模式是PowerDown模式,处理工作比较复杂,因为唤醒后是从reset处开始执行,要恢复挂起时候的环境,使得应用程序不知道自己被挂起过.一般按照这样流程来处理:关屏,清framebuffer, 保存必须的寄存器到内存, 设置io, 保存通用寄存器, 保存wakeup地址, 静止中断,清除cache, 使能唤醒源中断, 设置sdram自刷新, cpu进入PowerDown. 唤醒后的流程相反即可. 对于PowerDown模式之外的其他模式,比如慢时钟模式, 处理则简单很多,最重要的是设置唤醒源(一般是任何中断可唤醒), sdram进入自刷新状态.
[SDRAM的控制]
SDRAM的耗电比较大,一般是系统里面除了lcd背光外,sdram是最大的电力消耗设备.常见有mobile sdram和normal sdram这2种,mobile sdram相对于normal sdram增加了温度补偿自刷新,局部阵列自刷新,深度休眠特性,更加适合功耗限制设备,(但mobile sdram工作在更低电压(1.8~2.5v),我想,对有些3.3v总线的cpu未必适合,因为总线会增加很多电平转换的电路.)
在OEMPowerOff()函数里面,保存好当前环境到sdram,然后使得sdram进入自刷新状态,cpu就可以进入最低功耗的sleep模式.唤醒后需要退出自刷新状态.
[url=][[/url]应用层于电源管理]电源管理模块也提供了应用层接口,使得应用程序也可以参与到电源管理.
应用层可以通过SetSystemPowerState()来设置系统电源状态,可以通过SetDevicePower来设置子设备电源状态,可以通过SetPowerRequirement通知电源管理模块将子设备设置在特殊电源状态下,不随系统电源改变.此外,电源管理还提供了消息队列,应用层还可以通过RequestPowerNotifications函数请求电源管理模块发送相关消息(PBT_RESUME, PBT_POWERSTATUSCHANGE, PBT_TRANSITION, PBT_POWERINFOCHANGE).
设计应用程序也许有几点值得考虑:不要无谓占用cpu,尽可能快的让出cpu.比如一个很小的动画,哪怕只占1%的cpu也会导致一些系统无法进入低功耗.这里是2点建议:(1)当应用不在foreground时候,停止占用cpu.(2)用户没有和应用交互时候,停止应用对cpu的占用.另外一些应用也许是相反情况的,播放媒体文件时候,当开始播放时候,不希望自动进入suspend模式.可以(1)每隔一些时间就reset一次定时器.(2)或者设置所有定时器为0,停止电源管理(tcpmp就是这样的).
[url=][[/url]电源管理的系统实现]电源管理模块实体是一个动态链接库pm.dll来实现的.可以在pb的catalog窗口中选择电源管理组件添加到os中.如下图,微软提供了2个选择(二选一).第一个代表完整功能,所有api全功能实现,第二个代表空实现(形式上提供接口,但空函数).
电源管理模块的代码结构是分层的,MDD+PDD.MDD是抽象公共库,不需要改动,PDD是平台相关,主要改动都在PDD.针对平台特性,微软提供了2种类型PDD示例.一种是default,另外一种是pda版本的.默认的情况,使用的是default.如果要使用pda版本的,需要在系统中指定环境变量SYSGEN_PM_PDA. default和pda版本的主要区别:
default版本定义了4种状态:On, UserIdle, SystemIdle, Suspend;
PDA版本定义了On, ScreenOff, Unattended, Resume, Suspend.
default版本的简单描述:UserIdle状态是描述用户在使用但没有操作,比如阅读.SystemIdle状态描述用户停止使用,但系统仍然工作,比如文件传输.
PDA版本简单描述:ScreenOff状态描述用户请求把屏幕背灯关闭.是用户主动关闭的情况,区别于UserIdle,UserIdle是自动的.Unattended状态表示后台工作,用户不会对其察觉的情景,比如ActiveSync每5分钟唤醒系统同步,然后继续suspend; Resume状态描述唤醒后情景,比如唤醒后在指定时间内决定转到哪个状态,否则继续suspend.
[url=][[/url]定制电源管理模块的方法]Pm.dll是由device.exe加载的,首先device.exe当然是必须的,在pb的catalog中检查Device Manager组件,或者检查SYSGEN_DEVICE变量.其次,仍旧应该选择上图的电源管理组件power management full.
方案一(推荐方案):在bsp的驱动目录中新建一个pm目录,在这里完成电源管理模块PDD部分的实现,并链接MDD最终生成一个pm.dll替代原来系统的pm.dll.
PDD参考微软提供的代码platform.cpp,主要修改是增加状态转换的动作执行单元.
方案二:完全不修改电源管理部分,因为默认的PDD在状态转换时候虽然没有动作,但是广播了PBT_TRANSITION消息,可以截获这个消息来进行状态转换.这样作法不如方案一直接.如果是进程实现,还浪费一个宝贵进程资源.
[url=][[/url]影响系统功耗各方面考虑]1.系统时钟周期
典型的WinCE系统时钟周期是1ms,增加时钟周期有助进一步降低设备功耗.在OEMInit()àOALTimerInit()修改系统时钟.
2.可变系统时钟节拍Variable Tick Scheduler
典型设计里wince每毫秒产生系统时钟中断,那么每隔1ms都会使得idle退出,如果发现没有线程就绪时候继续idle. 对有功耗限制的设计,可以考虑改变系统时钟节拍后进入idle状态.这样在预期的时间段里,idle状态不会被无谓的系统时钟中断唤醒.
3.LCD背灯的调节策略
早期的设计使用一个独立的驱动来实现背灯的控制和调节策略.简单介绍背灯驱动原理:背灯驱动启动一个监视工作线程,不停等待3个事件:
1. BackLightChangeEvent
2. PowerChangedEvent(供电电源发生变化,比如插手了AC电源,会获得了这个事件)
3. PowerManager/ActivityTimer/UserActivity(用户输入事件)
从注册表中读取超时值,当超时事件发生,则将系统背灯关闭.背灯关闭期间,用户重新活动时候,发生第3个事件,则打开背灯.注册表的超时值决定了背灯工作时间.类同pc上设置屏幕保护时间.此外,背灯驱动也需要提供对系统电源状态切换的支持.power down时候要关闭背灯,power up时候打开背灯.
电源管理模块可以定义一种系统电源状态来描述背灯关闭的情景(比如在UserIdle或者ScreenOff状态时候关闭背灯,On状态时候打开背灯)所以,背灯驱动可以被取消.
4.IO口的漏电流
空载IO避免设置成为输入口,考虑悬空输入导致门电路开关,造成电流消耗.负载IO依照情况设定,一般设置输出低.
5.电池驱动
电池驱动最主要的功能是监视系统电力.它提供了其他模块和应用对系统电源状态的查询,查询是AC,还是battary供电,查询电池电量等. |