2. 低功耗状态基础
大家好,我是你们的低功耗设计讲师。这节课咱们聊聊 L1.1 和 L1.2 子状态。说白了,这两个东西就是 CPU 在浅睡眠和深睡眠之间的两个“中间档”。你在做低功耗产品时,不可能总是一下子跳到最深休眠,得给系统留点喘气的空间。
我刚开始做嵌入式低功耗时,总想着“要么全睡,要么全醒”。结果发现电池撑不住,系统响应也慢。后来才明白,L1.1 和 L1.2 这两个子状态,才是真正省电又灵活的妙招。咱们一个个来看。
L1.1 子状态:浅度睡眠,快速唤醒
官方定义: L1.1 是 ACPI 规范中定义的处理器电源状态下的一个子状态。在这个状态下,CPU 核心时钟和大多数 PLL(锁相环)都关了,但缓存(L1/L2)仍然保持供电和上下文。换句话说,芯片内部的关键状态还留着,只是不跑指令了。
注意: 我曾经在某个项目中把 L1.1 误当成完全睡眠,结果唤醒后缓存内容全丢了。后来翻手册才发现,L1.1 下缓存是自保活的,但前提是供电没断。如果你在硬件设计时把缓存的电源域也切了,那就不是 L1.1 了,直接跳到 L2 了。
进入条件(硬性要求):
- 所有 CPU 核心执行完 HLT 指令或类似的空转指令。
- 中断控制器(如 APIC)处于活动状态,能响应唤醒事件。
- 系统软件(比如 OS 的 idle 线程)确认功耗策略允许 L1.1。
- 缓存一致性协议处于洁净状态,没有未完成的缓存行写回。
退出条件(如何醒来):
- 任意使能的中断(包括定时器、外设中断、IPI等)到达。
- 从收到中断到恢复第一条指令,典型延迟在几十微秒到几百微秒之间。
- 我一般建议客户:如果你的中断响应要求 < 100µs,用 L1.1 很合适;要是 < 10µs,那你得考虑 C0 状态或者特殊硬件加速。
典型场景举例:
- USB 设备轮询: 键盘、鼠标这类设备,大部分时间空闲,但一旦用户操作就要立刻响应。L1.1 下功耗比 C0 降低 70% 以上,唤醒延迟又足够短。
- 周期性传感器采集: 例如温度传感器,每 100ms 采集一次。期间 CPU 可在 L1.1 里等着,中断来了再唤醒处理数据。
- 音频播放的空闲空隙: 播放流媒体时,解码器处理完一个数据块后短暂等待下一个 DMA 中断,这时进入 L1.1 能省不少电。
L1.2 子状态:更深一层,门控更狠
L1.2 比 L1.1 多做了两件事:关闭了 L1 缓存的部分电压域(有些设计连 L2 也关),并且 把 PLL 和大多数内部时钟全关了。这意味着唤醒时需要重新建立时钟稳定,延迟增加不少。
| 对比项 | L1.1 | L1.2 |
|---|---|---|
| 缓存供电 | 全保持 | 部分保持/丢失 |
| PLL | 关闭 | 关闭 |
| 内部时钟 | 大部分关闭 | 全部关闭 |
| 唤醒延迟(典型) | 50~200 µs | 200~1000 µs |
| 功耗节约 | 约 60-80% 相比 C0 | 约 85-95% 相比 C0 |
| 退出后恢复复杂度 | 低(直接执行) | 中(需重建上下文) |
进入条件: 除了 L1.1 的条件外,额外要求:
- 系统软件确认没有正在进行的 DMA 操作会访问缓存区域。
- 缓存内容可以被安全清空或写回(如果硬件不负责自恢复)。
- 唤醒事件必须能够通过低功耗唤醒控制器传递,而不是直接由中断控制器发。
退出过程:
- 唤醒信号唤醒电源管理控制器。
- 重新建立 PLL 锁相,稳定时钟(需要几十到几百微秒)。
- 恢复缓存状态(如果硬件不支持自保活,则需要软件从内存重新加载)。
- 跳转至中断服务程序。
避坑指南: 我曾经在一个物联网网关项目里,把 L1.2 的阈值设得太低,结果每次中断都触发退出再进入,反而比一直跑 C0 还耗电。后来加了迟滞时间——进入 L1.2 前必须空闲至少 5ms,功耗才降下来。记住:频繁进出更费电。
典型场景举例:
- 可穿戴设备的深层空闲: 手表不亮屏、无操作时,可以长时间待在 L1.2,只保留 RTC 或加速度传感器中断。我的上一个可穿戴方案就是靠这个子状态把待机电流从 2mA 降到 150µA。
- 物联网传感器节点(每分钟上报一次): 大多数时间处于睡眠,每隔 60 秒由定时器唤醒采集数据并上报。L1.2 非常适合这种长周期场景。
- SoC 内部非实时模块的空闲: 比如 GPU 没任务时,可以进入 L1.2;而 CPU 自己还在 L1.1 等待用户输入。不同类型核心可以用不同子状态。
进入/退出流程中的一个关键细节:仲裁与锁定
你想想看,如果多个核心都想进入不同的子状态,怎么协调?我记得有一次调试多核 SoC 的低功耗,一个核要进 L1.1,另一个要进 L1.2,结果整个系统因为电源域不一致死锁了。后来加上一个仲裁器,强制所有核心必须协商一致才能切换电源域。这就是业界常说的 「Last Core」原则:最后一个申请睡眠的核心负责正式下电。
实用忠告: 在你写低功耗驱动时,一定要检查硬件是否支持「异步退出」。如果某个核还在 L1.2 时另一个核被中断唤醒了,电源控制器能否独立恢复那个核的缓存?有些老芯片不行,只能全系统退出。这一点在项目选型阶段就要确认。
再补两句避坑
- 定时器配置: 如果你用 L1.2,确保唤醒定时器用的是独立运行的实时时钟(RTC),而不是依赖 PLL 的普通定时器。PLL 都关了,定时器也就停了。我曾经犯过这错误,醒来后发现时间慢了半秒。
- 中断极性: L1.1/L1.2 下中断控制器可能处于低功耗模式,有些中断(比如边沿触发)可能会丢失。建议统一用电平触发,并且保持至少几个时钟周期的宽度。嗯,这点生产测试时最容易忽略。
- 测量功耗时的陷阱: 你想测量 L1.2 的实际电流,不能只测 1 秒。因为唤醒瞬间电流会飙高,你的万用表可能取到平均值。正确的做法:让系统在 L1.2 里待至少 10 秒,然后用示波器抓电流波形,取平稳区间的值。我每次都这样干,从没出过偏差。