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 */ /* Enable WiFi MAC and POWER clocks */
DPORT_SET_PERI_REG_MASK(DPORT_WIFI_CLK_EN_REG, DPORT_WIFI_CLK_WIFI_EN); 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. */ /* Enable RNG clock. */
periph_module_enable(PERIPH_RNG_MODULE); 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_ULP, //!< Wakeup caused by ULP program
ESP_SLEEP_WAKEUP_GPIO, //!< Wakeup caused by GPIO (light sleep only) 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_UART, //!< Wakeup caused by UART (light sleep only)
ESP_SLEEP_WAKEUP_WIFI, //!< Wakeup caused by WIFI (light sleep only)
} esp_sleep_source_t; } esp_sleep_source_t;
/* Leave this type define for compatibility */ /* 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); 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 * @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); void esp_default_wake_deep_sleep(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View file

@ -80,6 +80,11 @@ static uint32_t s_ccount_div;
static uint32_t s_ccount_mul; static uint32_t s_ccount_mul;
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE #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. /* 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 * This in turn gets used in IDLE hook to decide if `waiti` needs
* to be invoked or not. * to be invoked or not.
@ -477,6 +482,42 @@ void esp_pm_impl_waiti(void)
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE #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) static inline bool IRAM_ATTR should_skip_light_sleep(int core_id)
{ {
#if portNUM_PROCESSORS == 2 #if portNUM_PROCESSORS == 2
@ -486,7 +527,7 @@ static inline bool IRAM_ATTR should_skip_light_sleep(int core_id)
return true; return true;
} }
#endif // portNUM_PROCESSORS == 2 #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; s_skipped_light_sleep[core_id] = true;
} else { } else {
s_skipped_light_sleep[core_id] = false; 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; 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) esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void)
{ {
if (rtc_get_reset_reason(0) != DEEPSLEEP_RESET && !s_light_sleep_wakeup) { 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; return ESP_SLEEP_WAKEUP_GPIO;
} else if (wakeup_cause & (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN)) { } else if (wakeup_cause & (RTC_UART0_TRIG_EN | RTC_UART1_TRIG_EN)) {
return ESP_SLEEP_WAKEUP_UART; return ESP_SLEEP_WAKEUP_UART;
} else if (wakeup_cause & RTC_WIFI_TRIG_EN) {
return ESP_SLEEP_WAKEUP_WIFI;
} else { } else {
return ESP_SLEEP_WAKEUP_UNDEFINED; 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); 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 #ifdef CONFIG_PM_PROFILING
#define WITH_PROFILING #define WITH_PROFILING
#endif #endif

View file

@ -422,6 +422,14 @@ static int get_time_wrapper(void *t)
return os_get_time(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) static void * IRAM_ATTR malloc_internal_wrapper(size_t size)
{ {
return heap_caps_malloc(size, MALLOC_CAP_8BIT|MALLOC_CAP_DMA|MALLOC_CAP_INTERNAL); 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, ._get_time = get_time_wrapper,
._random = os_random, ._random = os_random,
#if CONFIG_IDF_TARGET_ESP32S2 #if CONFIG_IDF_TARGET_ESP32S2
._slowclk_cal_get = esp_clk_slowclk_cal_get, ._slowclk_cal_get = esp_clk_slowclk_cal_get_wrapper,
#endif #endif
._log_write = esp_log_write, ._log_write = esp_log_write,
._log_writev = esp_log_writev, ._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); 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 #ifdef __cplusplus
} }
#endif #endif

View file

@ -17,6 +17,8 @@
#include "esp_log.h" #include "esp_log.h"
#include "esp_private/wifi.h" #include "esp_private/wifi.h"
#include "esp_pm.h" #include "esp_pm.h"
#include "esp_sleep.h"
#include "esp_private/pm_impl.h"
#include "soc/rtc.h" #include "soc/rtc.h"
#include "esp_wpa.h" #include "esp_wpa.h"
#include "esp_netif.h" #include "esp_netif.h"
@ -121,6 +123,11 @@ esp_err_t esp_wifi_deinit(void)
#if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER #if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER
tcpip_adapter_clear_default_wifi_handlers(); 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 #endif
return err; return err;
@ -137,6 +144,16 @@ esp_err_t esp_wifi_init(const wifi_init_config_t *config)
} }
} }
#endif #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 #if CONFIG_ESP_NETIF_TCPIP_ADAPTER_COMPATIBLE_LAYER
esp_err_t err = tcpip_adapter_set_default_wifi_handlers(); esp_err_t err = tcpip_adapter_set_default_wifi_handlers();
if (err != ESP_OK) { 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_GPIO_TRIG_EN BIT(2) //!< GPIO wakeup (light sleep only)
#define RTC_TIMER_TRIG_EN BIT(3) //!< Timer wakeup #define RTC_TIMER_TRIG_EN BIT(3) //!< Timer wakeup
#define RTC_SDIO_TRIG_EN BIT(4) //!< SDIO wakeup (light sleep only) #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_UART0_TRIG_EN BIT(6) //!< UART0 wakeup (light sleep only)
#define RTC_UART1_TRIG_EN BIT(7) //!< UART1 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 #define RTC_TOUCH_TRIG_EN BIT(8) //!< Touch wakeup

View file

@ -1,6 +1,3 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# Wifi Power Save Example # Wifi Power Save Example
This example shows how to use power save mode of wifi. 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 config EXAMPLE_MIN_CPU_FREQ_40M
bool "40 MHz (use with 40MHz XTAL)" 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 config EXAMPLE_MIN_CPU_FREQ_20M
bool "20 MHz (use with 40MHz XTAL)" 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 config EXAMPLE_MIN_CPU_FREQ_10M
bool "10 MHz (use with 40MHz XTAL)" 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 config EXAMPLE_MIN_CPU_FREQ_26M
bool "26 MHz (use with 26MHz XTAL)" bool "26 MHz (use with 26MHz XTAL)"
depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO depends on ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO

View file

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