低功耗 LEDC 开发指南

[English]

引言

在智能照明等低功耗应用中,设备通常需要保持 Wi-Fi 长连接,同时尽可能降低功耗。ESP-IDF 提供了自动 Light Sleep、Modem Sleep 以及动态调频(DFS)等电源管理机制,可根据应用需求调节 CPU 和 APB 频率,实现系统低功耗运行。

启用动态频率调节(DFS)后,部分外设的时钟会随 APB 频率变化而改变。例如,在使用 PWM 调光(LEDC)时,动态调频可能导致 LED 灯闪烁,需要结合电源管理策略保证亮度稳定。同时,当 Wi-Fi 断开连接时,RF 模块持续工作也会增加功耗,需要合理配置断连状态下的功耗优化策略。

本文档将以 LEDC 为例,介绍如何在启用动态调频(DFS)和低功耗模式下,保证 PWM 调光稳定,同时管理 Wi-Fi 断连状态下的功耗。类似原理也适用于其他依赖 APB 或固定时钟的外设。

保持 Wi-Fi 长连接

针对智能照明类产品,通常需要保持 Wi-Fi 长连接,推荐使用以下低功耗配置:

  1. 使能 自动 Light sleep

  2. 使能 BLE Modem sleep

  3. 使能 Wi-Fi Modem sleep

LEDC 时钟源选择

启用 动态调频(DFS) 后,APB 频率可能在单个 RTOS 周期内多次变化,部分依赖 APB 时钟的外设通信可能受到影响,具体说明见 动态调频和外设驱动

例如,对于调光操作,使用 I2C 外部调光芯片时,通信不受动态调频影响;使用 LEDC 时,如果时钟源为 APB,则可能会出现闪烁现象。

为保证调光稳定,需要根据使用场景选择合适的时钟源。LEDC 支持两种时钟源:APB 和 RTC 8M。两者在时钟精度、受动态调频影响的敏感性以及温度稳定度上各有特点,选择时需要权衡精度与稳定性。

APB 时钟源

RTC 8M 时钟源

时钟频率

80 MHz

8 MHz

时钟精度

±10 ppm

动态调频影响

需要使用 PM Lock 保证 LEDC 正常工作

不受影响

温度稳定度

低(±2%)

时钟抖动

稳定

较稳定

使用 APB 作为时钟源

通常,LEDC 默认使用 APB 作为时钟源。在启用动态调频(DFS)后,APB 频率会动态调整,但 LEDC 本身不会自动适应 APB 的变化。这会导致开灯时 PWM 输出闪烁,影响调光稳定性。

因此,在实际应用中需要结合 LEDC 工作状态使用 PM Lock,固定 LEDC 频率,避免 DFS 改变其频率导致出现闪烁现象:

  • 在开灯(LEDC 输出)的时候,获取 PM Lock,阻止 APB 时钟动态调整,同时阻止 CPU 进入 Light Sleep 模式;

  • 在关灯(LEDC 不输出)的时候,释放 PM Lock,允许 APB 时钟动态调整,同时允许 CPU 进入 Light Sleep 模式以节省功耗。

 static esp_pm_lock_handle_t s_pm_apb_lock = NULL;
 static esp_pm_lock_handle_t s_pm_light_lock = NULL;
 static esp_pm_lock_handle_t s_pm_cpu_lock = NULL;
 ledc_timer_config_t ledc_timer = {
           .duty_resolution = LEDC_TIMER_13_BIT, // resolution of PWM duty
           .freq_hz = 5000, // frequency of PWM signal
           .speed_mode = LEDC_LS_MODE, // timer mode
           .timer_num = LEDC_LS_TIMER, // timer index
           .clk_cfg = LEDC_USE_APB_CLK, /*!< LEDC timer select APB clock as source clock*/
 };

 if (s_pm_apb_lock == NULL) {
           if (esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "l_apb", &s_pm_apb_lock) != ESP_OK) {
                   ESP_LOGE(TAG, "esp pm lock create failed");
           }
           if (esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, "l_ls", &s_pm_light_lock) != ESP_OK) {
                   ESP_LOGE(TAG, "esp pm lock create failed");
           }
           if (esp_pm_lock_create(ESP_PM_CPU_FREQ_MAX, 0, "l_cpu", &s_pm_cpu_lock) != ESP_OK) {
                    ESP_LOGE(TAG, "esp pm lock create failed");
           }
 }

 while (1) {
           ESP_ERROR_CHECK(esp_pm_lock_acquire(s_pm_apb_lock));
           ESP_ERROR_CHECK(esp_pm_lock_acquire(s_pm_light_lock));
           ESP_ERROR_CHECK(esp_pm_lock_acquire(s_pm_cpu_lock));
           ESP_LOGI(TAG, "light turn on");
           for (ch = 0; ch < LEDC_CH_NUM; ch++) {
                   ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, LEDC_TEST_DUTY);
                   ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel);
           }
           vTaskDelay(pdMS_TO_TICKS(5 * 1000));
                   ESP_LOGI(TAG, "light turn off");
           for (ch = 0; ch < LEDC_CH_NUM; ch++) {
                   ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, 0);
                   ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel);
           }
           ESP_ERROR_CHECK(esp_pm_lock_release(s_pm_apb_lock));
           ESP_ERROR_CHECK(esp_pm_lock_release(s_pm_light_lock));
           ESP_ERROR_CHECK(esp_pm_lock_release(s_pm_cpu_lock));
           vTaskDelay(pdMS_TO_TICKS(5 * 1000));
}

使用 RTC 8M 作为时钟源

LEDC 使用 RTC 8M 作为时钟源时,在使能启用动态调频(DFS)的情况下,RTC 8M 时钟不会受到影响,在开灯的时候不会出现闪烁现象。因此,在实际应用中也不需要使用 PM lock。

ledc_timer_config_t ledc_timer = {
             .duty_resolution = LEDC_TIMER_10_BIT, // resolution of PWM duty
             .freq_hz = 5000, // frequency of PWM signal
             .speed_mode = LEDC_LS_MODE, // timer mode
             .timer_num = LEDC_LS_TIMER, // timer index
             .clk_cfg = LEDC_USE_RTC8M_CLK, /*!< LEDC timer select RTC8M_CLK as source clock*/
  };

  while (1) {
             ESP_LOGI(TAG, "light turn on");
             for (ch = 0; ch < LEDC_CH_NUM; ch++) {
                     ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, LEDC_TEST_DUTY);
                     ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel);
             }
             vTaskDelay(pdMS_TO_TICKS(5 * 1000));
             ESP_LOGI(TAG, "light turn off");
             for (ch = 0; ch < LEDC_CH_NUM; ch++) {
                     ledc_set_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel, 0);
                     ledc_update_duty(ledc_channel[ch].speed_mode, ledc_channel[ch].channel);
             }
             vTaskDelay(pdMS_TO_TICKS(5 * 1000));
  }

Wi‑Fi 未连接状态下的功耗优化

在正常情况下,智能设备保持与路由器的连接。但如果路由器出现故障或信号异常,设备将断开连接。此时,RF 模块可能持续工作,导致平均功耗显著升高。

为降低功耗,同时保持重新连接的能力,ESP-IDF 提供了针对断连状态的功耗优化方案。该方案通过周期性唤醒 RF 模块工作,在未连接时让设备适度休眠,以节省能量并尝试重新建立连接。

具体使用方式如下:

  1. 开启断连电源管理

  2. 在 Wi-Fi 初始化之后调用 esp_wifi_set_connectionless_wake_interval(uint16_t interval); 设置唤醒间隔

  3. 在 Wi-Fi 初始化之后调用 esp_now_set_wake_window(uint16_t window); API 设置唤醒工作窗口

例如,设置唤醒间隔为 200ms,唤醒工作窗口为 110ms,在未连接到路由器的情况下,RF 将周期性工作以降低功耗。