Edit online

设计说明

21 Nov 2024
Read time: 3 minute(s)

PM 驱动源码位于 bsp/artinchip/drv/pm/

PM 框架和功耗模式

Luban-Lite 的 PM 驱动完全遵循 RT-Thread 的 PM 框架。PM 框架将系统的功耗分为多个等级,每个等级对应一个功耗模式,实现对不同功耗模式的管理。

以下是 Luban-Lite 中各功耗模式的详细描述及其在 Luban-Lite 中的实现功能:

1. 功能模式及优先级描述
功耗模式 级别 描述 Luban-Lite 是否支持 Luban-Lite 实现功能的区别
PM_SLEEP_MODE_NONE 0 系统处于活跃状态

未做任何低功耗处理,系统处于全速运行状态。

PM_SLEEP_MODE_IDLE 1 空闲模式

执行 WFI(Wait For Interrupt)指令,使 CPU 进入空闲状态,等待中断唤醒。

PM_SLEEP_MODE_LIGHT 2 轻睡眠模式

执行 WFI 指令,同时将 CPU 及总线时钟切换到 24 MHz,并关闭 PLL 时钟。

PM_SLEEP_MODE_DEEP 3 深睡眠模式 执行 WFI 指令,同时将 CPU 及总线时钟切换到 24 MHz,并关闭 PLL 时钟,将 PSRAM 时钟降频。
PM_SLEEP_MODE_STANDBY 4 待机模式 -
PM_SLEEP_MODE_SHUTDOWN 5 关机模式 -

申请和释放功耗模式

API 接口
为了管理功耗模式,RT-Thread 提供了一组 API 接口,用于申请和释放不同的功耗模式。
注: PM 框架中功耗模式的申请和释放必须成对使用,否则可能导致系统无法正确管理功耗状态。
  • PM 框架提供的申请休眠模式的接口有:
    rt_pm_release(rt_uint8_t mode)
    rt_pm_module_request(uint8_t module_id, rt_uint8_t sleep_mode)
    rt_pm_sleep_request(rt_uint16_t module_id, rt_uint8_t mode)
    rt_pm_sleep_none_request(rt_uint16_t module_id)
    rt_pm_sleep_idle_request(rt_uint16_t module_id)
    rt_pm_sleep_light_request(rt_uint16_t module_id)
  • 释放休眠模式的接口有:
    rt_pm_release(rt_uint8_t sleep_mode)
    rt_pm_module_release(uint8_t module_id, rt_uint8_t sleep_mode)
    rt_pm_module_release_all(uint8_t module_id, rt_uint8_t sleep_mode)
    rt_pm_sleep_release(rt_uint16_t module_id, rt_uint8_t mode)
    rt_pm_sleep_none_release(rt_uint16_t module_id)
    rt_pm_sleep_idle_release(rt_uint16_t module_id)
    rt_pm_sleep_light_release(rt_uint16_t module_id)
  • module_id
    RT-Thread 官方推荐使用基于 module_id 的 API 接口,用于申请和释放功耗模式。使用这些接口可以查看申请功耗的模块,也方便调试。
    rt_pm_module_request(uint8_t module_id, rt_uint8_t sleep_mode) // 申请进入指定的功耗模式
    rt_pm_module_release(uint8_t module_id, rt_uint8_t sleep_mode)  // 释放指定的功耗模式

    module_id 是在 pm_cfg.h 文件中定义的,用于表示不同的模块。开发者可以根据实际需求自行定义 module_id,以便更好地管理和调试功耗模式。

申请和释放规则
当有多个线程或任务同时申请不同的功耗模式时,RT-Thread PM 框架会根据以下规则决定当前系统应运行在哪个功耗模式下:
  1. 最高优先级的模式优先:如果某个线程申请了 PM_SLEEP_MODE_NONE 模式,而另一个线程申请了 PM_SLEEP_MODE_DEEP 模式,则系统会以 PM_SLEEP_MODE_NONE 模式运行,直到所有申请 PM_SLEEP_MODE_NONE 模式的任务都释放了这个模式,系统才会切换到 PM_SLEEP_MODE_DEEP 模式,进入 deep sleep mode。

    RT-Thread 的功耗模式需要在代码中主动申请和释放,为了保证系统的正常运行,功耗越大的模式优先级越高。

  2. 默认休眠模式:在 PM 框架初始化时,将 PM_SLEEP_MODE_DEEP 设置为默认休眠模式。如果没有比 PM_SLEEP_MODE_DEEP 更高优先级的请求,系统会立刻进入 deep sleep mode。
  3. 初始化时的默认模式:PM 框架初始化时会申请 PM_SLEEP_MODE_NONE 模式,以确保在 PM 初始化完成后,系统可以正常运行,而不会立即进入 deep sleep mode。因此,在应用代码中,想要进入低功耗模式,需要主动调用 release 相关的 API 接口释放 PM_SLEEP_MODE_NONE 模式。

休眠唤醒流程

PM 框架休眠唤醒流程如下:

suspend_resume_flow

1. 休眠唤醒流程

如上图所示,PM 框架及驱动使能后,RT-Thread 会在 idle 线程进入休眠唤醒流程。在休眠唤醒流程中,PM 框架会检查所有正在运行的任务和线程,确定当前系统中申请的最高优先级的功耗模式,并以最高优先级的功耗模式运行。例如,如果最高优先级是 PM_SLEEP_MODE_DEEP,系统将进入 deep sleep mode。

注: 功耗越高,优先级越高。

在低功耗模式 ( deep sleep mode) 下,系统会等待外部中断或特定事件来触发唤醒。当外部中断或事件触发时,系统会退出低功耗模式,恢复到工作状态,继续执行被中断的任务或线程。

代码段保护

在 RT-Thread 的 PM 框架中,通过申请和释放功耗模式来保护代码段,确保在特定阶段不允许系统进入低功耗模式。例如,如果在读取数据过程中不允许进入休眠,则执行代码段保护的流程如下:
注: PM 框架中模式的申请和释放必须成对使用。
  1. 通过 rt_pm_module_request 函数申请不进入任何低功耗模式。
    rt_pm_module_request(PM_MAIN_ID, PM_SLEEP_MODE_NONE);
  2. 等待执行读取数据的操作。
  3. 通过 rt_pm_module_release 函数释放不进入任何低功耗模式的请求。
    rt_pm_module_release(PM_MAIN_ID, PM_SLEEP_MODE_NONE);

设置唤醒源

Luban-Lite SDK 在 PM 框架基础上增加了设置唤醒源的接口,中断唤醒源可以唤醒系统。通过以下接口,可以设置对应的唤醒源:
  • rt_device_wakeup_enable(rt_device_t dev, rt_bool_t enable):用来设置普通外设作为唤醒源,包括 RTC、I2C 和 SPI 等。这类设备在 RR-Thread 中采用 rt_device 描述设备。
  • rt_pm_set_pin_wakeup_source(rt_base_t pin):用来设置某个 GPIO 引脚作为唤醒源。

    在 RT-Thread 中,采用 rt_base_t 描述一个具体的 GPIO 口,而没有采用 rt_device 描述,因此需要一个单独的接口来设置 GPIO 的唤醒源。

低功耗定时器

系统中没有专用的低功耗定时器,Luban-Lite SDK 通过利用 CPU 的计时器 MTIME 来实现低功耗定时器。详细的实现原理和步骤如下所示:

  1. 修改 MTIME 的比较值寄存器,减少中断的周期性触发,从而实现 tickless 的目的。

  2. 获取低功耗定时器的下一次超时时间,然后加上当前的 MTIME 的计数值,再将结果写入 MTIME 的比较值寄存器。

  3. 当系统从低功耗模式唤醒时,需要计算 MTIME 当前计数值和休眠前计数值的差值,并将该差值补偿到 rt_tick 中。

  4. 在系统唤醒后,需要恢复 MTIME 的周期性触发计数。