esp_wifi: When WiFi TSF is active, skip light sleep

* Add an API for peripherals to set callbacks to skip light sleep

* Make WiFi power save example work
This commit is contained in:
Xia Xiaotian 2020-05-07 16:15:56 +08:00
parent bd920d22b0
commit 526a3e49ed
12 changed files with 140 additions and 10 deletions

View file

@ -320,6 +320,11 @@ void esp_perip_clk_init(void)
/* Enable WiFi MAC and POWER clocks */
DPORT_SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN);
/* Set WiFi light sleep clock source to RTC slow clock */
DPORT_REG_SET_FIELD(DPORT_BT_LPCK_DIV_INT_REG, DPORT_BT_LPCK_DIV_NUM, 0);
DPORT_CLEAR_PERI_REG_MASK(DPORT_BT_LPCK_DIV_FRAC_REG, DPORT_LPCLK_SEL_8M);
DPORT_SET_PERI_REG_MASK(DPORT_BT_LPCK_DIV_FRAC_REG, DPORT_LPCLK_SEL_RTC_SLOW);
/* Enable RNG clock. */
periph_module_enable(PERIPH_RNG_MODULE);
}

View file

@ -64,6 +64,7 @@ typedef enum {
ESP_SLEEP_WAKEUP_ULP, //!< Wakeup caused by ULP program
ESP_SLEEP_WAKEUP_GPIO, //!< Wakeup caused by GPIO (light sleep only)
ESP_SLEEP_WAKEUP_UART, //!< Wakeup caused by UART (light sleep only)
ESP_SLEEP_WAKEUP_WIFI, //!< Wakeup caused by WIFI (light sleep only)
} esp_sleep_source_t;
/* Leave this type define for compatibility */
@ -239,6 +240,13 @@ esp_err_t esp_sleep_enable_uart_wakeup(int uart_num);
*/
uint64_t esp_sleep_get_ext1_wakeup_status(void);
/**
* @brief Enable wakeup by WiFi MAC
* @return
* - ESP_OK on success
*/
esp_err_t esp_sleep_enable_wifi_wakeup(void);
/**
* @brief Set power down mode for an RTC power domain in sleep mode
*
@ -349,7 +357,6 @@ esp_deep_sleep_wake_stub_fn_t esp_get_deep_sleep_wake_stub(void);
*/
void esp_default_wake_deep_sleep(void);
#ifdef __cplusplus
}
#endif

View file

@ -80,6 +80,11 @@ static uint32_t s_ccount_div;
static uint32_t s_ccount_mul;
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
#define PERIPH_SKIP_LIGHT_SLEEP_NO 1
/* Indicates if light sleep shoule be skipped by peripherals. */
static skip_light_sleep_cb_t s_periph_skip_light_sleep_cb[PERIPH_SKIP_LIGHT_SLEEP_NO];
/* Indicates if light sleep entry was skipped in vApplicationSleep for given CPU.
* This in turn gets used in IDLE hook to decide if `waiti` needs
* to be invoked or not.
@ -477,6 +482,42 @@ void esp_pm_impl_waiti(void)
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
esp_err_t esp_pm_register_skip_light_sleep_callback(skip_light_sleep_cb_t cb)
{
for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) {
if (s_periph_skip_light_sleep_cb[i] == cb) {
return ESP_OK;
} else if (s_periph_skip_light_sleep_cb[i] == NULL) {
s_periph_skip_light_sleep_cb[i] = cb;
return ESP_OK;
}
}
return ESP_ERR_NO_MEM;
}
esp_err_t esp_pm_unregister_skip_light_sleep_callback(skip_light_sleep_cb_t cb)
{
for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) {
if (s_periph_skip_light_sleep_cb[i] == cb) {
s_periph_skip_light_sleep_cb[i] = NULL;
return ESP_OK;
}
}
return ESP_ERR_INVALID_STATE;
}
static inline bool IRAM_ATTR periph_should_skip_light_sleep(void)
{
for (int i = 0; i < PERIPH_SKIP_LIGHT_SLEEP_NO; i++) {
if (s_periph_skip_light_sleep_cb[i]) {
if (s_periph_skip_light_sleep_cb[i]() == true) {
return true;
}
}
}
return false;
}
static inline bool IRAM_ATTR should_skip_light_sleep(int core_id)
{
#if portNUM_PROCESSORS == 2
@ -486,7 +527,7 @@ static inline bool IRAM_ATTR should_skip_light_sleep(int core_id)
return true;
}
#endif // portNUM_PROCESSORS == 2
if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching) {
if (s_mode != PM_MODE_LIGHT_SLEEP || s_is_switching || periph_should_skip_light_sleep()) {
s_skipped_light_sleep[core_id] = true;
} else {
s_skipped_light_sleep[core_id] = false;

View file

@ -582,6 +582,12 @@ esp_err_t esp_sleep_enable_uart_wakeup(int uart_num)
return ESP_OK;
}
esp_err_t esp_sleep_enable_wifi_wakeup(void)
{
s_config.wakeup_triggers |= RTC_WIFI_TRIG_EN;
return ESP_OK;
}
esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void)
{
if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET && !s_light_sleep_wakeup) {
@ -603,6 +609,8 @@ esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void)
return ESP_SLEEP_WAKEUP_GPIO;
} else if (wakeup_cause & (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN)) {
return ESP_SLEEP_WAKEUP_UART;
} else if (wakeup_cause & RTC_WIFI_TRIG_EN) {
return ESP_SLEEP_WAKEUP_WIFI;
} else {
return ESP_SLEEP_WAKEUP_UNDEFINED;
}

View file

@ -109,6 +109,38 @@ void esp_pm_impl_dump_stats(FILE* out);
*/
void esp_pm_impl_waiti(void);
#if CONFIG_IDF_TARGET_ESP32S2
/**
* @brief Callback function type for peripherals to skip light sleep.
*
*/
typedef bool (* skip_light_sleep_cb_t)(void);
/**
* @brief Register peripherals skip light sleep callback
*
* This function allows you to register a callback that gets the result
* that if light sleep should be skipped by peripherals.
* @param cb function to get the result
* @return
* - ESP_OK on success
* - ESP_ERR_NO_MEM if no more callback slots are available
*/
esp_err_t esp_pm_register_skip_light_sleep_callback(skip_light_sleep_cb_t cb);
/**
* @brief Unregisterperipherals skip light sleep callback
*
* This function allows you to unregister a callback which was previously
* registered using esp_register_skip_light_sleep_callback.
* @param cb function to get the result
* @return
* - ESP_OK on success
* - ESP_ERR_INVALID_STATE if the given callback hasn't been registered before
*/
esp_err_t esp_pm_unregister_skip_light_sleep_callback(skip_light_sleep_cb_t cb);
#endif
#ifdef CONFIG_PM_PROFILING
#define WITH_PROFILING
#endif

View file

@ -422,6 +422,14 @@ static int get_time_wrapper(void *t)
return os_get_time(t);
}
static uint32_t esp_clk_slowclk_cal_get_wrapper(void)
{
/* The bit width of WiFi light sleep clock calibration is 12 while the one of
* system is 19. It should shift 19 - 12 = 7.
*/
return (esp_clk_slowclk_cal_get() >> 7);
}
static void * IRAM_ATTR malloc_internal_wrapper(size_t size)
{
return heap_caps_malloc(size, MALLOC_CAP_8BIT|MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL);
@ -604,7 +612,7 @@ wifi_osi_funcs_t g_wifi_osi_funcs = {
._get_time = get_time_wrapper,
._random = os_random,
#if CONFIG_IDF_TARGET_ESP32S2
._slowclk_cal_get = esp_clk_slowclk_cal_get,
._slowclk_cal_get = esp_clk_slowclk_cal_get_wrapper,
#endif
._log_write = esp_log_write,
._log_writev = esp_log_writev,

View file

@ -414,6 +414,17 @@ esp_err_t esp_wifi_internal_get_negotiated_channel(wifi_interface_t ifx, uint8_t
*/
esp_err_t esp_wifi_internal_get_negotiated_bandwidth(wifi_interface_t ifx, uint8_t aid, uint8_t *bw);
#if CONFIG_IDF_TARGET_ESP32S2
/**
* @brief Check if WiFi TSF is active
*
* @return
* - true: Active
* - false: Not active
*/
bool esp_wifi_internal_is_tsf_active(void);
#endif
#ifdef __cplusplus
}
#endif

View file

@ -17,6 +17,8 @@
#include "esp_log.h"
#include "esp_private/wifi.h"
#include "esp_pm.h"
#include "esp_sleep.h"
#include "esp_private/pm_impl.h"
#include "soc/rtc.h"
#include "esp_wpa.h"
#include "esp_netif.h"
@ -121,6 +123,11 @@ esp_err_t esp_wifi_deinit(void)
#if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER
tcpip_adapter_clear_default_wifi_handlers();
#endif
#if CONFIG_IDF_TARGET_ESP32S2
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
esp_pm_unregister_skip_light_sleep_callback(esp_wifi_internal_is_tsf_active);
#endif
#endif
return err;
@ -137,6 +144,16 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config)
}
}
#endif
#if CONFIG_IDF_TARGET_ESP32S2
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
esp_err_t ret = esp_pm_register_skip_light_sleep_callback(esp_wifi_internal_is_tsf_active);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to register skip light sleep callback (0x%x)", ret);
return ret;
}
esp_sleep_enable_wifi_wakeup();
#endif
#endif
#if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER
esp_err_t err = tcpip_adapter_set_default_wifi_handlers();
if (err != ESP_OK) {

View file

@ -700,7 +700,7 @@ void rtc_sleep_set_wakeup_time(uint64_t t);
#define RTC_GPIO_TRIG_EN BIT(2) //!< GPIO wakeup (light sleep only)
#define RTC_TIMER_TRIG_EN BIT(3) //!< Timer wakeup
#define RTC_SDIO_TRIG_EN BIT(4) //!< SDIO wakeup (light sleep only)
#define RTC_MAC_TRIG_EN BIT(5) //!< MAC wakeup (light sleep only)
#define RTC_WIFI_TRIG_EN BIT(5) //!< WIFI wakeup (light sleep only)
#define RTC_UART0_TRIG_EN BIT(6) //!< UART0 wakeup (light sleep only)
#define RTC_UART1_TRIG_EN BIT(7) //!< UART1 wakeup (light sleep only)
#define RTC_TOUCH_TRIG_EN BIT(8) //!< Touch wakeup

View file

@ -1,6 +1,3 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# Wifi Power Save Example
This example shows how to use power save mode of wifi.

View file

@ -70,13 +70,13 @@ menu "Example Configuration"
config EXAMPLE_MIN_CPU_FREQ_40M
bool "40 MHz (use with 40MHz XTAL)"
depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_20M
bool "20 MHz (use with 40MHz XTAL)"
depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_10M
bool "10 MHz (use with 40MHz XTAL)"
depends on ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
depends on IDF_TARGET_ESP32S2 || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO
config EXAMPLE_MIN_CPU_FREQ_26M
bool "26 MHz (use with 26MHz XTAL)"
depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO

View file

@ -95,7 +95,11 @@ void app_main(void)
// Configure dynamic frequency scaling:
// maximum and minimum frequencies are set in sdkconfig,
// automatic light sleep is enabled if tickless idle support is enabled.
#if CONFIG_IDF_TARGET_ESP32
esp_pm_config_esp32_t pm_config = {
#elif CONFIG_IDF_TARGET_ESP32S2
esp_pm_config_esp32s2_t pm_config = {
#endif
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE