# 电源管理

[English]

## 概述

ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求，调整外围总线 (APB) 频率和 CPU 频率，并使芯片进入 Light-sleep 模式，尽可能减少运行应用程序的功耗。

应用程序组件可以通过创建和获取电源管理锁来控制功耗。

例如：

- 对于从 APB 获得时钟频率的外设，其驱动可以要求在使用该外设时，将 APB 频率设置为 80 MHz。

- RTOS 可以要求 CPU 在有任务准备开始运行时以最高配置频率工作。

- 一些外设可能需要中断才能启用，因此其驱动也会要求禁用 Light-sleep 模式。

请求较高的 APB 频率或 CPU 频率以及禁用 Light-sleep 模式会增加功耗，因此请将组件使用的电源管理锁降到最少。

## 电源管理配置

编译时可使用 CONFIG_PM_ENABLE 选项启用电源管理功能。

启用电源管理功能将会增加中断延迟。额外延迟与多个因素有关，例如：CPU 频率、单/双核模式、是否需要进行频率切换等。CPU 频率为 240 MHz 且未启用频率调节时，最小额外延迟为 0.2 us；如果启用频率调节，且在中断入口将频率由 40 MHz 调节至 80 MHz，则最大额外延迟为 40 us。

通过调用 `esp_pm_configure()` 函数可以在应用程序中启用动态调频 (DFS) 功能和自动 Light-sleep 模式。此函数的参数 `esp_pm_config_t` 定义了频率调节的相关设置。在此参数结构中，需要初始化以下三个字段：

> - `max_freq_mhz`：最大 CPU 频率 (MHz)，即获取 `ESP_PM_CPU_FREQ_MAX` 锁后所使用的频率。该字段通常设置为 CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ。

- `min_freq_mhz`：最小 CPU 频率 (MHz)，即未持有电源管理锁时所使用的频率。

  - `light_sleep_enable`：没有获取任何管理锁时，决定系统是否需要自动进入 Light-sleep 状态 (`true`/`false`)。

如果在 menuconfig 中启用了 CONFIG_PM_DFS_INIT_AUTO 选项，最大 CPU 频率将由 CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ 设置决定，最小 CPU 频率将锁定为 XTAL 频率。

备注

自动 Light-sleep 模式基于 FreeRTOS Tickless Idle 功能，因此如果在 menuconfig 中没有启用 CONFIG_FREERTOS_USE_TICKLESS_IDLE 选项，在请求自动 Light-sleep 时，`esp_pm_configure()` 将会返回 ESP_ERR_NOT_SUPPORTED 错误。

备注

Light-sleep 状态下，外设设有时钟门控，不会产生来自 GPIO 和内部外设的中断。睡眠模式 文档中所提到的唤醒源可用于从 Light-sleep 状态触发唤醒。

例如，EXT0 和 EXT1 唤醒源可以通过 GPIO 唤醒芯片。

## 电源管理锁

应用程序可以通过获取或释放管理锁来控制电源管理算法。应用程序获取电源管理锁后，电源管理算法的操作将受到下面的限制。释放电源管理锁后，限制解除。

电源管理锁设有获取/释放计数器，如果已多次获取电源管理锁，则需要将电源管理锁释放相同次数以解除限制。

ESP32-C6 支持下表中三种电源管理锁。

电源管理锁 | 描述
--- | ---
`ESP_PM_CPU_FREQ_MAX` | 请求使用 `esp_pm_configure()` 将 CPU 频率设置为最大值。ESP32-C6 可以将该值设置为 80 MHz 或 160 MHz。
`ESP_PM_APB_FREQ_MAX` | 请求将 APB 频率设置为最大值，ESP32-C6 支持的最大频率为 80 MHz。
`ESP_PM_NO_LIGHT_SLEEP` | 禁止自动切换至 Light-sleep 模式。

## ESP32-C6 电源管理算法

下表列出了启用动态调频时如何切换 CPU 频率和 APB 频率。可以使用 `esp_pm_configure()` 或 CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ 指定 CPU 最大频率。

CPU 最高频率 | 电源管理锁获取情况 | APB 频率和 CPU 频率
160 | 获取 `ESP_PM_CPU_FREQ_MAX` | CPU: 160 MHz APB: 80 MHz
获取 `ESP_PM_APB_FREQ_MAX`，未获得 `ESP_PM_CPU_FREQ_MAX` | CPU: 80 MHz APB: 80 MHz
无 | 使用 `esp_pm_configure()` 为二者设置最小值
80 | 获取 `ESP_PM_CPU_FREQ_MAX` 或 `ESP_PM_APB_FREQ_MAX` | CPU: 80 MHz APB: 80 MHz
无 | 使用 `esp_pm_configure()` 为二者设置最小值

如果没有获取任何管理锁，调用 `esp_pm_configure()` 将启动 Light-sleep 模式。Light-sleep 模式持续时间由以下因素决定：

- 处于阻塞状态的 FreeRTOS 任务数（有限超时）

- 高分辨率定时器 API 注册的计数器数量

也可以设置 Light-sleep 模式在最近事件（任务解除阻塞，或计时器超时）之前的持续时间，在持续时间结束后再唤醒芯片。

为了跳过不必要的唤醒，可以将 `skip_unhandled_events` 选项设置为 `true` 来初始化 `esp_timer`。带有此标志的定时器不会唤醒系统，有助于减少功耗。

## 动态调频和外设驱动

启用动态调频后，APB 频率可在一个 RTOS 滴答周期内多次更改。有些外设不受 APB 频率变更的影响，但有些外设可能会出现问题。例如，Timer Group 外设定时器会继续计数，但定时器计数的速度将随 APB 频率的变更而变更。

时钟频率不受 APB 频率影响的外设时钟源通常有 `REF_TICK`, `XTAL`, `RC_FAST` (i.e., `RTC_8M`)。因此，为了保证外设在 DFS 期间的所有行为一致，建议在上述时钟中选择其一作为外设的时钟源。如果想要了解更多详情可以浏览每个外设 ”API 参考 > 外设 API“ 页面的 “电源管理” 章节。

目前以下外设驱动程序可感知动态调频，并在调频期间使用 `ESP_PM_APB_FREQ_MAX` 锁：

- SPI master

- I2C

- I2S（如果 APLL 锁在使用中，I2S 则会启用 `ESP_PM_NO_LIGHT_SLEEP` 锁）

- SDMMC

启用以下驱动程序时，将占用 `ESP_PM_APB_FREQ_MAX` 锁：

- SPI slave：从调用 `spi_slave_initialize()` 至 `spi_slave_free()` 期间。

- GPTimer：从调用 `gptimer_enable()` 至 `gptimer_disable()` 期间。

- Ethernet：从调用 `esp_eth_driver_install()` 至 `esp_eth_driver_uninstall()` 期间。

- WiFi：从调用 `esp_wifi_start()` 至 `esp_wifi_stop()` 期间。如果启用了调制解调器睡眠模式，广播关闭时将释放此管理锁。

- TWAI：从调用 `twai_driver_install()` 至 `twai_driver_uninstall()` 期间 (只有在 TWAI 时钟源选择为 `TWAI_CLK_SRC_APB` 的时候生效)。

- Bluetooth：从调用 `esp_bt_controller_enable()` 至 `esp_bt_controller_disable()` 期间。如果启用了蓝牙调制解调器，广播关闭时将释放此管理锁。但依然占用 `ESP_PM_NO_LIGHT_SLEEP` 锁。

- PCNT：从调用 `pcnt_unit_enable()` 至 `pcnt_unit_disable()` 期间。

- Sigma-delta：从调用 `sdm_channel_enable()` 至 `sdm_channel_disable()` 期间。

- MCPWM: 从调用 `mcpwm_timer_enable()` 至 `mcpwm_timer_disable()` 期间，以及调用 `mcpwm_capture_timer_enable()` 至 `mcpwm_capture_timer_disable()` 期间。

以下外设驱动程序无法感知动态调频，应用程序需自己获取/释放管理锁：

- 旧版 PCNT 驱动

- 旧版 Sigma-delta 驱动

- 旧版定时器驱动 (Timer Group)

- 旧版 MCPWM 驱动

## Light-sleep 外设下电

> ESP32-C6 支持在 Light-sleep 时掉电外设的电源域.

> 如果在 menuconfig 中启用了 CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP，在初始化外设时，驱动会将外设工作的寄存器上下文注册到休眠备份链表中，在进入休眠前，`REG_DMA` 外设会读取休眠备份链表中的配置，根据链表中的配置将外设的寄存器上下文备份至内存，`REG_DMA` 也会在唤醒时将上下文从内存恢复到外设寄存中。

> 目前 IDF 支持以下外设的 Light-sleep 上下文备份，它们的上下文会自动恢复，或者提供了相关的选项允许用户进入外设下电模式：

> - INT_MTX

> - TEE/APM

> - IO_MUX / GPIO

> - MSPI (SPI0/1)

> - SYSTIMER

> - GPTimer

> - RMT

> - LEDC

> - I2C

> - I2S

> - ETM

> - MCPWM

> - All UARTs

> - Temperature Sensor

> - All TWAIs

> - PARL_IO

> - All GPSPIs

> 一些外设尚未支持睡眠上下文恢复，或者寄存器丢失后根本无法恢复。即使外设下电功能被启用，它们也会阻止外设下电的发生：

> - SDIO Slave

> - PCNT

> 以下外设（以及一些未在本章节任意一组中列出的外设）尚未支持外设下电功能。如果您的应用使用了这些外设，它们可能无法在从睡眠中醒来后仍然正常工作：

> - ASSIST_DEBUG

> - Trace

> - Crypto: AES/ECC/HMAC/RSA/SHA/DS/XTA_AES/ECDSA

> - USB-Serial-JTAG

> - SARADC

> 备注

> 当外设电源域在睡眠期间断电时，IO_MUX 和 GPIO 模块都处于下电状态，这意味着芯片引脚的状态不会受这些模块控制。要在休眠期间保持 IO 的状态，需要在配置 GPIO 状态前后调用 `gpio_hold_dis()` 和 `gpio_hold_en()`。此操作可确保 IO 配置被锁存，防止 IO 在睡眠期间浮空。

## API 参考

### Header File

- components/esp_pm/include/esp_pm.h

- This header file can be included with:

  >     #include "esp_pm.h"

- This header file is a part of the API provided by the `esp_pm` component. To declare that your component depends on `esp_pm`, add the following to your CMakeLists.txt:

  >     REQUIRES esp_pm

  > or

  >     PRIV_REQUIRES esp_pm

### Functions

esp_err_t esp_pm_configure(const void *config)

  Set implementation-specific power management configuration.

  参数:

    config -- pointer to implementation-specific configuration structure (e.g. esp_pm_config_esp32)

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if the configuration values are not correct

    - ESP_ERR_NOT_SUPPORTED if certain combination of values is not supported, or if CONFIG_PM_ENABLE is not enabled in sdkconfig

esp_err_t esp_pm_get_configuration(void *config)

  Get implementation-specific power management configuration.

  参数:

    config -- pointer to implementation-specific configuration structure (e.g. esp_pm_config_esp32)

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if the pointer is null

esp_err_t esp_pm_lock_create(esp_pm_lock_type_t lock_type, int arg, const char *name, esp_pm_lock_handle_t *out_handle)

  Initialize a lock handle for certain power management parameter.

  When lock is created, initially it is not taken. Call esp_pm_lock_acquire to take the lock.

  This function must not be called from an ISR.

  备注

  If the lock_type argument is not valid, it will cause an abort.

  参数:

    - lock_type -- Power management constraint which the lock should control

    - arg -- argument, value depends on lock_type, see esp_pm_lock_type_t

    - name -- arbitrary string identifying the lock (e.g. "wifi" or "spi"). Used by the esp_pm_dump_locks function to list existing locks. May be set to NULL. If not set to NULL, must point to a string which is valid for the lifetime of the lock.

    - out_handle -- [out] handle returned from this function. Use this handle when calling esp_pm_lock_delete, esp_pm_lock_acquire, esp_pm_lock_release. Must not be NULL.

  返回:

    - ESP_OK on success

    - ESP_ERR_NO_MEM if the lock structure can not be allocated

    - ESP_ERR_INVALID_ARG if out_handle is NULL

    - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig

esp_err_t esp_pm_lock_acquire(esp_pm_lock_handle_t handle)

  Take a power management lock.

  Once the lock is taken, power management algorithm will not switch to the mode specified in a call to esp_pm_lock_create, or any of the lower power modes (higher numeric values of 'mode').

  The lock is recursive, in the sense that if esp_pm_lock_acquire is called a number of times, esp_pm_lock_release has to be called the same number of times in order to release the lock.

  This function may be called from an ISR.

  This function is not thread-safe w.r.t. calls to other esp_pm_lock_* functions for the same handle.

  参数:

    handle -- handle obtained from esp_pm_lock_create function

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if the handle is invalid

    - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig

esp_err_t esp_pm_lock_release(esp_pm_lock_handle_t handle)

  Release the lock taken using esp_pm_lock_acquire.

  Call to this functions removes power management restrictions placed when taking the lock.

  Locks are recursive, so if esp_pm_lock_acquire is called a number of times, esp_pm_lock_release has to be called the same number of times in order to actually release the lock.

  This function may be called from an ISR.

  This function is not thread-safe w.r.t. calls to other esp_pm_lock_* functions for the same handle.

  参数:

    handle -- handle obtained from esp_pm_lock_create function

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if the handle is invalid

    - ESP_ERR_INVALID_STATE if lock is not acquired

    - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig

esp_err_t esp_pm_lock_delete(esp_pm_lock_handle_t handle)

  Delete a lock created using esp_pm_lock.

  The lock must be released before calling this function.

  This function must not be called from an ISR.

  参数:

    handle -- handle obtained from esp_pm_lock_create function

  返回:

    - ESP_OK on success

    - ESP_ERR_INVALID_ARG if the handle argument is NULL

    - ESP_ERR_INVALID_STATE if the lock is still acquired

    - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig

esp_err_t esp_pm_dump_locks(FILE *stream)

  Dump the list of all locks to stderr

  This function dumps debugging information about locks created using esp_pm_lock_create to an output stream.

  This function must not be called from an ISR. If esp_pm_lock_acquire/release are called while this function is running, inconsistent results may be reported.

  参数:

    stream -- stream to print information to; use stdout or stderr to print to the console; use fmemopen/open_memstream to print to a string buffer.

  返回:

    - ESP_OK on success

    - ESP_ERR_NOT_SUPPORTED if CONFIG_PM_ENABLE is not enabled in sdkconfig

### Structures

struct esp_pm_config_t

  Power management config.

  Pass a pointer to this structure as an argument to esp_pm_configure function.

  Public Members

  int max_freq_mhz

    Maximum CPU frequency, in MHz

  int min_freq_mhz

    Minimum CPU frequency to use when no locks are taken, in MHz

  bool light_sleep_enable

    Enter light sleep when no locks are taken

### Type Definitions

typedef esp_pm_config_t esp_pm_config_esp32_t

  backward compatibility newer chips no longer require this typedef

typedef esp_pm_config_t esp_pm_config_esp32s2_t

typedef esp_pm_config_t esp_pm_config_esp32s3_t

typedef esp_pm_config_t esp_pm_config_esp32c3_t

typedef esp_pm_config_t esp_pm_config_esp32c2_t

typedef esp_pm_config_t esp_pm_config_esp32c6_t

typedef struct esp_pm_lock *esp_pm_lock_handle_t

  Opaque handle to the power management lock.

### Enumerations

enum esp_pm_lock_type_t

  Power management constraints.

  Values:

  enumerator ESP_PM_CPU_FREQ_MAX

    Require CPU frequency to be at the maximum value set via esp_pm_configure. Argument is unused and should be set to 0.

  enumerator ESP_PM_APB_FREQ_MAX

    Require APB frequency to be at the maximum value supported by the chip. Argument is unused and should be set to 0.

  enumerator ESP_PM_NO_LIGHT_SLEEP

    Prevent the system from going into light sleep. Argument is unused and should be set to 0.

  enumerator ESP_PM_LOCK_MAX

此文档对您有帮助吗？

- 反馈已收到，谢谢！
- 如果您有其他意见，欢迎填写乐鑫文档反馈表。

- 我们重视您的反馈。
- 您可以填写乐鑫文档反馈表告诉我们如何改进该文档。