4、电源管理策略配置:PM策略文件详解、策略优先级、策略切换规则
好,咱们接着聊电源管理。前面几章我们把QNX的电源管理框架、硬件抽象层都过了一遍。今天要聊的,是真正落地的东西——PM策略文件。
说白了,策略文件就是告诉系统:什么时候该睡,什么时候该醒,睡多深,醒多快。我刚开始接触QNX电源管理时,觉得这玩意儿不就是个配置文件嘛,随便写写就行。结果第一次上车实测,屏幕闪得跟迪斯科似的……嗯,从那以后我再也不敢小看这个文件了。
4.1 PM策略文件(pm_policy.conf)详解
策略文件默认路径是 /etc/system/pm_policy.conf。当然,你也可以指定其他位置。我个人习惯把它放在 /etc/ 下,方便统一管理。
先看一个典型的策略文件长什么样:
# PM Policy Configuration
# 策略名称: 每个策略对应一个电源状态
policy {
name = "active"
latency = 0
power = 100
wakeup_sources = "all"
}
policy {
name = "idle"
latency = 50
power = 60
wakeup_sources = "touch,key"
}
policy {
name = "sleep"
latency = 200
power = 10
wakeup_sources = "power_key,alarm"
}
policy {
name = "deep_sleep"
latency = 500
power = 1
wakeup_sources = "power_key"
}
每个 policy 块定义了一个电源状态。这里有四个关键字段:
- name:策略名称,你自己起名。但建议跟硬件支持的电源状态对应起来。
- latency:从该状态恢复到完全可用的时间,单位是毫秒。这个值越小,恢复越快。
- power:该状态下的功耗百分比。100表示全功耗,0表示完全断电。
- wakeup_sources:哪些外设可以唤醒系统。比如触摸屏、按键、RTC闹钟等。
我的经验:latency 和 power 是一对冤家。你想省电,就得接受更长的唤醒时间。我在做某款车机时,客户要求待机功耗低于5mA,但唤醒时间不能超过300ms。折腾了好久,最后在硬件上加了快速唤醒电路才搞定。
4.2 策略优先级
策略之间是有优先级的。QNX PM框架怎么决定用哪个策略?看两个东西:
- 策略在文件中的顺序:越靠前优先级越高。
- 系统当前的约束条件:比如某个驱动要求最低功耗不能低于某个值。
举个例子:
# 优先级从高到低
policy { name = "active"; priority = 100 }
policy { name = "idle"; priority = 80 }
policy { name = "sleep"; priority = 50 }
policy { name = "deep_sleep"; priority = 10 }
优先级数值越大,越优先被考虑。但注意,不是优先级高的就一定会被选中。系统会综合所有约束条件,选一个最省电且满足所有约束的策略。
核心原则:策略选择是「约束满足」问题,不是「优先级排序」问题。优先级只是参考,最终决策权在PM框架手里。
我记得有一次,某个音频驱动注册了一个约束,要求系统不能进入deep_sleep。结果车机在播放音乐时,明明屏幕关了,系统却一直停留在idle状态,功耗下不去。查了半天才发现是驱动忘了释放约束。嗯,这种坑踩过一次就记住了。
4.3 策略切换规则
策略切换不是你想切就能切的。QNX有一套严格的切换规则:
4.3.1 切换触发条件
- 用户态请求:应用程序调用
pm_set_policy()主动切换。 - 内核态触发:比如CPU空闲时间超过阈值,内核自动发起切换。
- 外设事件:比如按键按下、触摸事件,系统从低功耗状态唤醒。
- 定时器超时:比如屏幕无操作5分钟后,自动进入sleep。
4.3.2 切换流程
系统切换策略时,会走这么几步:
- 检查约束:当前所有驱动是否允许切换到目标策略。
- 通知驱动:调用驱动的
pm_policy_change()回调,让驱动做好准备。 - 硬件操作:PM框架执行具体的硬件操作,比如关闭时钟、降低电压。
- 确认状态:等待所有驱动确认切换完成。
- 更新状态:系统正式进入新策略。
注意:如果某个驱动在步骤2中返回错误,整个切换会被取消。我曾经遇到一个网卡驱动,在进入sleep前没有正确关闭DMA,导致切换失败,系统一直卡在idle状态。所以驱动开发时,一定要把pm_policy_change()回调写完整。
4.3.3 切换延迟与超时
切换不是瞬间完成的。每个策略都有一个 switch_latency 参数:
policy {
name = "sleep"
switch_latency = 100 # 切换过程最多允许100ms
...
}
如果切换超时,PM框架会怎么做?
- 默认行为:强制切换,忽略超时的驱动。
- 安全行为:回滚到之前的策略。
我个人建议用安全行为。强制切换可能导致驱动状态不一致,下次唤醒时出问题。你想想看,车机在高速上突然黑屏了,多吓人。
4.4 实战配置建议
说了这么多理论,来点实际的。我总结了几条配置策略文件的经验:
| 场景 | 推荐策略 | latency | power | 说明 |
|---|---|---|---|---|
| 用户正在操作 | active | 0 | 100 | 全速运行,不省电 |
| 屏幕亮但无操作 | idle | 50 | 60 | 降低CPU频率,关闭部分外设 |
| 屏幕灭,音乐播放 | audio_sleep | 100 | 30 | 保留音频通路,其他都关 |
| 熄火停车 | deep_sleep | 500 | 1 | 只保留RTC和CAN唤醒 |
避坑指南:我曾经在某个项目中,把idle策略的latency设成了10ms。结果系统频繁在active和idle之间切换,功耗反而比一直active还高。后来把latency调到50ms,加了个滞回时间,问题就解决了。说白了,切换本身也是有代价的。
4.5 策略文件的调试技巧
配置写好了,怎么验证对不对?我常用的方法:
- 查看当前策略:
pm status命令可以看系统当前在哪个策略。 - 跟踪策略切换:
pm trace -e policy可以实时看到切换事件。 - 检查约束:
pm constraint list列出所有驱动注册的约束。 - 模拟切换:
pm set_policy sleep手动强制切换,看驱动反应。
嗯,调试时最好连上串口或者网络,不然系统睡死了你都不知道。我有一次在实验室里,车机进入deep_sleep后怎么都唤不醒,最后发现是唤醒源的GPIO配置错了。从那以后,我每次改策略文件都会先做一次完整的唤醒测试。
好了,策略配置这块就聊到这儿。下一章我们讲具体的驱动开发,看看怎么让驱动乖乖听PM框架的话。