一、低功耗概述:NuttX 低功耗架构简介

大家好,我是你们的老朋友。今天咱们开始聊 NuttX 的低功耗设计。说实话,我做了这么多年嵌入式,低功耗这块儿踩过的坑真不少。记得有一次,一个物联网项目,电池续航死活达不到客户要求的三个月,最后发现是休眠模式没用好——嗯,这事儿后面会细说。

先说说 NuttX 的低功耗架构。它不像 Linux 那样庞大,也不像 FreeRTOS 那样简单。NuttX 走的是中间路线——灵活,但又不失深度。我个人觉得,它的设计哲学是「给你工具,但不替你做决定」。

1.1 低功耗模式分类

NuttX 把低功耗模式分成三大类。你想想看,这其实跟人的休息状态很像:

  • IDLE(空闲模式):就像你坐在沙发上发呆,随时能站起来干活。CPU 停了,但外设还在跑。唤醒延迟极低,微秒级。
  • Sleep(睡眠模式):好比晚上浅睡,闹钟一响就醒。CPU 和部分外设都停了,但 RAM 保持。唤醒延迟在毫秒级。
  • Deep Sleep(深度睡眠):这就是深度睡眠了,几乎一切停止。RAM 可能掉电,唤醒后要重新初始化。延迟最长,但功耗最低。

重要提醒: 这三种模式不是所有芯片都支持。我在项目中遇到过,某款国产 MCU 号称支持 Deep Sleep,结果唤醒后 GPIO 状态全乱了。所以,选型时一定要看芯片手册的「低功耗模式特性表」。

下面这张表是我自己整理的,方便大家对比:

模式 CPU 状态 RAM 状态 外设状态 唤醒延迟 典型功耗
IDLE 停止 保持 运行 ~1 µs ~10 mA
Sleep 停止 保持 部分停止 ~100 µs ~1 mA
Deep Sleep 掉电 可选保持 全部停止 ~10 ms ~10 µA

1.2 功耗管理框架(PM Framework)整体介绍

NuttX 的 PM Framework,说白了就是一个「协调员」。它负责告诉系统:「现在该省电了」或者「该醒过来了」。

这个框架的核心思想是 状态机 + 回调机制。我刚开始看源码时,觉得有点绕。后来画了个图才明白——其实就三部分:

  1. PM 状态机:管理着从 NORMAL 到 IDLE、SLEEP、DEEP SLEEP 的切换逻辑。
  2. 通知回调:每个驱动可以注册一个回调函数。当状态变化时,PM 框架会通知它们。
  3. 唤醒源管理:谁可以唤醒系统?定时器、GPIO 中断、RTC 闹钟……这些都要注册。

我的小技巧: 调试 PM 框架时,可以在每个回调函数里加个 GPIO 翻转。用逻辑分析仪一看,就知道状态切换的时序对不对。我曾经靠这招抓到一个驱动没及时释放锁的问题。

来看一个最简单的 PM 注册示例:

#include <nuttx/power/pm.h>

static int my_driver_pm_callback(FAR struct pm_callback_s *cb,
                                 int domain,
                                 enum pm_state_e pmstate)
{
    switch (pmstate) {
    case PM_NORMAL:
        /* 正常工作,打开外设 */
        break;
    case PM_IDLE:
        /* 可以关闭一些不用的外设 */
        break;
    case PM_SLEEP:
        /* 保存上下文,准备休眠 */
        break;
    case PM_DEEP_SLEEP:
        /* 保存关键数据,进入深度睡眠 */
        break;
    }
    return OK;
}

/* 注册回调 */
static struct pm_callback_s g_my_cb = {
    .notify = my_driver_pm_callback,
};

pm_register(&g_my_cb);

这里要注意一点:回调函数里不能做耗时操作。我曾经犯过这个错——在 PM_SLEEP 回调里打印日志,结果串口还没发完,系统就休眠了。嗯,从那以后我学乖了,回调里只做寄存器操作,日志什么的放到外面去。

1.3 为什么需要 PM Framework?

你可能会问:「我自己在 main 循环里判断一下,然后调用芯片的休眠指令不行吗?」

行,当然行。但问题在于——谁来决定什么时候休眠?

在一个多任务系统里,可能有 5 个线程在跑。线程 A 在等传感器数据,线程 B 在刷 LED,线程 C 在发网络包。如果每个线程都自己决定休眠,那系统就乱套了。

PM Framework 的作用就是:收集所有线程的「休眠意愿」,然后统一决策。只有所有线程都同意,系统才会进入更深的睡眠模式。

避坑指南: 我曾经遇到一个 bug——某个驱动在初始化时注册了 PM 回调,但卸载时忘了注销。结果系统进入 Deep Sleep 后,那个回调还在,但驱动已经没了,直接导致唤醒后崩溃。所以,注册和注销一定要成对出现

1.4 小结

这一章咱们把 NuttX 低功耗的骨架搭起来了。你记住三点就行:

  • 三种模式:IDLE、Sleep、Deep Sleep,功耗和延迟是 trade-off
  • PM Framework 是个协调员,用状态机 + 回调来管理
  • 所有驱动都要注册回调,统一决策才能休眠

下一章,我会带大家深入 PM 状态机的实现细节。到时候咱们手撕源码,看看 NuttX 到底是怎么做状态切换的。准备好了吗?


公众号:蓝海资料掘金营,微信deep3321