低功耗 LEDC 开发指南
引言
在智能照明等低功耗应用中,设备通常需要保持 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 长连接,推荐使用以下低功耗配置:
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 模块工作,在未连接时让设备适度休眠,以节省能量并尝试重新建立连接。
具体使用方式如下:
在 Wi-Fi 初始化之后调用
esp_wifi_set_connectionless_wake_interval(uint16_t interval);设置唤醒间隔在 Wi-Fi 初始化之后调用
esp_now_set_wake_window(uint16_t window);API 设置唤醒工作窗口
例如,设置唤醒间隔为 200ms,唤醒工作窗口为 110ms,在未连接到路由器的情况下,RF 将周期性工作以降低功耗。