mirror of
https://github.com/espressif/esp-idf.git
synced 2024-09-21 06:56:11 -04:00
feat(PowerManagement/lightsleep): Support PM_LIGHT_SLEEP_CALLBACKS
This commit is contained in:
parent
dce3b64411
commit
15f66392d3
@ -138,4 +138,18 @@ menu "Power Management"
|
|||||||
bool
|
bool
|
||||||
default y if PM_ENABLE && BTDM_CTRL_HLI
|
default y if PM_ENABLE && BTDM_CTRL_HLI
|
||||||
|
|
||||||
|
config PM_LIGHT_SLEEP_CALLBACKS
|
||||||
|
bool "Enable registration of auto light sleep callbacks"
|
||||||
|
depends on FREERTOS_USE_TICKLESS_IDLE
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
If enabled, it allows user to register entry and exit callbacks which are called before and after
|
||||||
|
entering auto light sleep.
|
||||||
|
|
||||||
|
NOTE: These callbacks are executed from the IDLE task context hence you cannot have any blocking calls
|
||||||
|
in your callbacks.
|
||||||
|
|
||||||
|
NOTE: Enabling these callbacks may change sleep duration calculations based on time spent in callback and
|
||||||
|
hence it is highly recommended to keep them as short as possible
|
||||||
|
|
||||||
endmenu # "Power Management"
|
endmenu # "Power Management"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2016-2022 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2016-2023 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -192,6 +192,62 @@ esp_err_t esp_pm_lock_delete(esp_pm_lock_handle_t handle);
|
|||||||
*/
|
*/
|
||||||
esp_err_t esp_pm_dump_locks(FILE* stream);
|
esp_err_t esp_pm_dump_locks(FILE* stream);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Function prototype for light sleep callback functions (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
|
||||||
|
*
|
||||||
|
* @param sleep_time_us supplied by the power management framework.
|
||||||
|
* For entry callback, sleep_time_us indicates the expected sleep time in us
|
||||||
|
* For exit callback, sleep_time_us indicates the actual sleep time in us
|
||||||
|
* @param arg is the user provided argument while registering callbacks
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - ESP_OK allow entry light sleep mode
|
||||||
|
*/
|
||||||
|
typedef esp_err_t (*esp_pm_light_sleep_cb_t)(int64_t sleep_time_us, void *arg);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/**
|
||||||
|
* Callback function defined by internal developers.
|
||||||
|
*/
|
||||||
|
esp_pm_light_sleep_cb_t enter_cb;
|
||||||
|
esp_pm_light_sleep_cb_t exit_cb;
|
||||||
|
/**
|
||||||
|
* Input parameters of callback function defined by internal developers.
|
||||||
|
*/
|
||||||
|
void *enter_cb_user_arg;
|
||||||
|
void *exit_cb_user_arg;
|
||||||
|
/**
|
||||||
|
* Execution priority of callback function defined by internal developers.
|
||||||
|
* The smaller the priority, the earlier it executes when call esp_sleep_execute_event_callbacks.
|
||||||
|
* If functions have the same priority, the function registered first will be executed first.
|
||||||
|
*/
|
||||||
|
uint32_t enter_cb_prior;
|
||||||
|
uint32_t exit_cb_prior;
|
||||||
|
} esp_pm_sleep_cbs_register_config_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register entry or exit callbacks for light sleep (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
|
||||||
|
* @param cbs_conf Config struct containing entry or exit callbacks function and corresponding argument
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG if the input parameter enter_cb and exit_cb in cbs_conf are NULL
|
||||||
|
* - ESP_ERR_NO_MEM if the remaining memory is insufficient to support malloc
|
||||||
|
* - ESP_FAIL if register the same function repeatedly
|
||||||
|
*
|
||||||
|
* @note These callback functions are called from IDLE task context hence they cannot call any blocking functions
|
||||||
|
*/
|
||||||
|
esp_err_t esp_pm_light_sleep_register_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unregister entry or exit callbacks for light sleep (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
|
||||||
|
* @param cbs_conf Config struct containing entry or exit callbacks function and corresponding argument
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_INVALID_ARG if the input parameter enter_cb and exit_cb in cbs_conf are NULL
|
||||||
|
*/
|
||||||
|
esp_err_t esp_pm_light_sleep_unregister_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#include "esp_private/sleep_gpio.h"
|
#include "esp_private/sleep_gpio.h"
|
||||||
#include "esp_private/sleep_modem.h"
|
#include "esp_private/sleep_modem.h"
|
||||||
#include "esp_sleep.h"
|
#include "esp_sleep.h"
|
||||||
|
#include "esp_memory_utils.h"
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
@ -201,6 +202,146 @@ pm_mode_t esp_pm_impl_get_mode(esp_pm_lock_type_t type, int arg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
|
||||||
|
/**
|
||||||
|
* @brief Function entry parameter types for light sleep callback functions (if CONFIG_FREERTOS_USE_TICKLESS_IDLE)
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
/**
|
||||||
|
* Callback function defined by user.
|
||||||
|
*/
|
||||||
|
esp_pm_light_sleep_cb_t cb;
|
||||||
|
/**
|
||||||
|
* Input parameters of callback function defined by user.
|
||||||
|
*/
|
||||||
|
void *arg;
|
||||||
|
/**
|
||||||
|
* Execution priority of callback function defined by user.
|
||||||
|
*/
|
||||||
|
uint32_t prior;
|
||||||
|
/**
|
||||||
|
* Next callback function defined by user.
|
||||||
|
*/
|
||||||
|
struct _esp_pm_sleep_cb_config_t *next;
|
||||||
|
} esp_pm_sleep_cb_config_t;
|
||||||
|
|
||||||
|
static esp_pm_sleep_cb_config_t *s_light_sleep_enter_cb_config;
|
||||||
|
static esp_pm_sleep_cb_config_t *s_light_sleep_exit_cb_config;
|
||||||
|
static portMUX_TYPE s_sleep_pm_cb_mutex = portMUX_INITIALIZER_UNLOCKED;
|
||||||
|
|
||||||
|
esp_err_t esp_pm_light_sleep_register_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf)
|
||||||
|
{
|
||||||
|
if (cbs_conf->enter_cb == NULL && cbs_conf->exit_cb == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
portENTER_CRITICAL(&s_sleep_pm_cb_mutex);
|
||||||
|
if (cbs_conf->enter_cb != NULL) {
|
||||||
|
esp_pm_sleep_cb_config_t **current_enter_ptr = &(s_light_sleep_enter_cb_config);
|
||||||
|
while (*current_enter_ptr != NULL) {
|
||||||
|
if (((*current_enter_ptr)->cb) == (cbs_conf->enter_cb)) {
|
||||||
|
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
current_enter_ptr = &((*current_enter_ptr)->next);
|
||||||
|
}
|
||||||
|
esp_pm_sleep_cb_config_t *new_enter_config = (esp_pm_sleep_cb_config_t *)heap_caps_malloc(sizeof(esp_pm_sleep_cb_config_t), MALLOC_CAP_INTERNAL);
|
||||||
|
if (new_enter_config == NULL) {
|
||||||
|
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
|
||||||
|
return ESP_ERR_NO_MEM; /* Memory allocation failed */
|
||||||
|
}
|
||||||
|
new_enter_config->cb = cbs_conf->enter_cb;
|
||||||
|
new_enter_config->arg = cbs_conf->enter_cb_user_arg;
|
||||||
|
new_enter_config->prior = cbs_conf->enter_cb_prior;
|
||||||
|
while (*current_enter_ptr != NULL && (*current_enter_ptr)->prior <= new_enter_config->prior) {
|
||||||
|
current_enter_ptr = &((*current_enter_ptr)->next);
|
||||||
|
}
|
||||||
|
new_enter_config->next = *current_enter_ptr;
|
||||||
|
*current_enter_ptr = new_enter_config;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbs_conf->exit_cb != NULL) {
|
||||||
|
esp_pm_sleep_cb_config_t **current_exit_ptr = &(s_light_sleep_exit_cb_config);
|
||||||
|
while (*current_exit_ptr != NULL) {
|
||||||
|
if (((*current_exit_ptr)->cb) == (cbs_conf->exit_cb)) {
|
||||||
|
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
current_exit_ptr = &((*current_exit_ptr)->next);
|
||||||
|
}
|
||||||
|
esp_pm_sleep_cb_config_t *new_exit_config = (esp_pm_sleep_cb_config_t *)heap_caps_malloc(sizeof(esp_pm_sleep_cb_config_t), MALLOC_CAP_INTERNAL);
|
||||||
|
if (new_exit_config == NULL) {
|
||||||
|
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
|
||||||
|
return ESP_ERR_NO_MEM; /* Memory allocation failed */
|
||||||
|
}
|
||||||
|
new_exit_config->cb = cbs_conf->exit_cb;
|
||||||
|
new_exit_config->arg = cbs_conf->exit_cb_user_arg;
|
||||||
|
new_exit_config->prior = cbs_conf->exit_cb_prior;
|
||||||
|
while (*current_exit_ptr != NULL && (*current_exit_ptr)->prior <= new_exit_config->prior) {
|
||||||
|
current_exit_ptr = &((*current_exit_ptr)->next);
|
||||||
|
}
|
||||||
|
new_exit_config->next = *current_exit_ptr;
|
||||||
|
*current_exit_ptr = new_exit_config;
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t esp_pm_light_sleep_unregister_cbs(esp_pm_sleep_cbs_register_config_t *cbs_conf)
|
||||||
|
{
|
||||||
|
if (cbs_conf->enter_cb == NULL && cbs_conf->exit_cb == NULL) {
|
||||||
|
return ESP_ERR_INVALID_ARG;
|
||||||
|
}
|
||||||
|
portENTER_CRITICAL(&s_sleep_pm_cb_mutex);
|
||||||
|
if (cbs_conf->enter_cb != NULL) {
|
||||||
|
esp_pm_sleep_cb_config_t **current_enter_ptr = &(s_light_sleep_enter_cb_config);
|
||||||
|
while (*current_enter_ptr != NULL) {
|
||||||
|
if ((*current_enter_ptr)->cb == cbs_conf->enter_cb) {
|
||||||
|
esp_pm_sleep_cb_config_t *temp = *current_enter_ptr;
|
||||||
|
*current_enter_ptr = (*current_enter_ptr)->next;
|
||||||
|
free(temp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current_enter_ptr = &((*current_enter_ptr)->next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cbs_conf->exit_cb != NULL) {
|
||||||
|
esp_pm_sleep_cb_config_t **current_exit_ptr = &(s_light_sleep_exit_cb_config);
|
||||||
|
while (*current_exit_ptr != NULL) {
|
||||||
|
if ((*current_exit_ptr)->cb == cbs_conf->exit_cb) {
|
||||||
|
esp_pm_sleep_cb_config_t *temp = *current_exit_ptr;
|
||||||
|
*current_exit_ptr = (*current_exit_ptr)->next;
|
||||||
|
free(temp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current_exit_ptr = &((*current_exit_ptr)->next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&s_sleep_pm_cb_mutex);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t IRAM_ATTR esp_pm_execute_enter_sleep_callbacks(int64_t sleep_time_us)
|
||||||
|
{
|
||||||
|
esp_pm_sleep_cb_config_t *enter_current = s_light_sleep_enter_cb_config;
|
||||||
|
while (enter_current != NULL) {
|
||||||
|
enter_current->cb(sleep_time_us, enter_current->arg);
|
||||||
|
enter_current = enter_current->next;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t IRAM_ATTR esp_pm_execute_exit_sleep_callbacks(int64_t sleep_time_us)
|
||||||
|
{
|
||||||
|
esp_pm_sleep_cb_config_t *exit_current = s_light_sleep_exit_cb_config;
|
||||||
|
while (exit_current != NULL) {
|
||||||
|
exit_current->cb(sleep_time_us, exit_current->arg);
|
||||||
|
exit_current = exit_current->next;
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static esp_err_t esp_pm_sleep_configure(const void *vconfig)
|
static esp_err_t esp_pm_sleep_configure(const void *vconfig)
|
||||||
{
|
{
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
@ -628,6 +769,20 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
|
|||||||
int64_t wakeup_delay_us = portTICK_PERIOD_MS * 1000LL * xExpectedIdleTime;
|
int64_t wakeup_delay_us = portTICK_PERIOD_MS * 1000LL * xExpectedIdleTime;
|
||||||
int64_t sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm);
|
int64_t sleep_time_us = MIN(wakeup_delay_us, time_until_next_alarm);
|
||||||
if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL) {
|
if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL) {
|
||||||
|
int64_t slept_us = 0;
|
||||||
|
#if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
|
||||||
|
if (s_light_sleep_enter_cb_config != NULL && s_light_sleep_enter_cb_config->cb) {
|
||||||
|
uint32_t cycle = esp_cpu_get_cycle_count();
|
||||||
|
esp_err_t err = esp_pm_execute_enter_sleep_callbacks(sleep_time_us);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
portEXIT_CRITICAL(&s_switch_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
sleep_time_us -= (esp_cpu_get_cycle_count() - cycle) / (esp_clk_cpu_freq() / 1000000ULL);
|
||||||
|
}
|
||||||
|
if (sleep_time_us >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP * portTICK_PERIOD_MS * 1000LL)
|
||||||
|
{
|
||||||
|
#endif
|
||||||
esp_sleep_enable_timer_wakeup(sleep_time_us - LIGHT_SLEEP_EARLY_WAKEUP_US);
|
esp_sleep_enable_timer_wakeup(sleep_time_us - LIGHT_SLEEP_EARLY_WAKEUP_US);
|
||||||
#if CONFIG_PM_TRACE && SOC_PM_SUPPORT_RTC_PERIPH_PD
|
#if CONFIG_PM_TRACE && SOC_PM_SUPPORT_RTC_PERIPH_PD
|
||||||
/* to force tracing GPIOs to keep state */
|
/* to force tracing GPIOs to keep state */
|
||||||
@ -643,7 +798,7 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
|
|||||||
s_light_sleep_counts++;
|
s_light_sleep_counts++;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
int64_t slept_us = esp_timer_get_time() - sleep_start;
|
slept_us = esp_timer_get_time() - sleep_start;
|
||||||
ESP_PM_TRACE_EXIT(SLEEP, core_id);
|
ESP_PM_TRACE_EXIT(SLEEP, core_id);
|
||||||
|
|
||||||
uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL);
|
uint32_t slept_ticks = slept_us / (portTICK_PERIOD_MS * 1000LL);
|
||||||
@ -666,6 +821,12 @@ void IRAM_ATTR vApplicationSleep( TickType_t xExpectedIdleTime )
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
other_core_should_skip_light_sleep(core_id);
|
other_core_should_skip_light_sleep(core_id);
|
||||||
|
#if CONFIG_PM_LIGHT_SLEEP_CALLBACKS
|
||||||
|
}
|
||||||
|
if (s_light_sleep_exit_cb_config != NULL && s_light_sleep_exit_cb_config->cb) {
|
||||||
|
esp_pm_execute_exit_sleep_callbacks(slept_us);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL(&s_switch_lock);
|
portEXIT_CRITICAL(&s_switch_lock);
|
||||||
|
Loading…
Reference in New Issue
Block a user