forked from espressif/esp-idf
Merge branch 'feat/support_esp32p4_sleep_vbat' into 'master'
feat(esp_hw_support): support switch to VBAT power supply in deepsleep Closes IDF-10664 and IDF-10665 See merge request espressif/esp-idf!37251
This commit is contained in:
@ -191,6 +191,7 @@
|
||||
/examples/ethernet/ @esp-idf-codeowners/network
|
||||
/examples/get-started/ @esp-idf-codeowners/system
|
||||
/examples/ieee802154/ @esp-idf-codeowners/ieee802154
|
||||
/examples/lowpower/ @esp-idf-codeowners/power-management @esp-idf-codeowners/system
|
||||
/examples/mesh/ @esp-idf-codeowners/wifi
|
||||
/examples/network/ @esp-idf-codeowners/network @esp-idf-codeowners/wifi
|
||||
/examples/openthread/ @esp-idf-codeowners/ieee802154
|
||||
|
@ -157,7 +157,7 @@ if(NOT non_os_build)
|
||||
list(APPEND srcs "power_supply/brownout.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP_VBAT_INIT_AUTO)
|
||||
if(CONFIG_SOC_VBAT_SUPPORTED)
|
||||
list(APPEND srcs "power_supply/vbat.c")
|
||||
endif()
|
||||
|
||||
|
@ -47,6 +47,7 @@ typedef enum {
|
||||
#define RTC_SLEEP_PD_MODEM PMU_SLEEP_PD_MODEM //!< Power down modem(include wifi, ble and 15.4)
|
||||
|
||||
//These flags are not power domains, but will affect some sleep parameters
|
||||
#define RTC_SLEEP_POWER_BY_VBAT BIT(26)
|
||||
#define RTC_SLEEP_DIG_USE_8M BIT(27)
|
||||
#define RTC_SLEEP_USE_ADC_TESEN_MONITOR BIT(28)
|
||||
#define RTC_SLEEP_NO_ULTRA_LOW BIT(29) //!< Avoid using ultra low power in deep sleep, in which RTCIO cannot be used as input, and RTCMEM can't work under high temperature
|
||||
@ -123,6 +124,12 @@ typedef enum {
|
||||
#define RTC_LP_VAD_TRIG_EN 0
|
||||
#endif //SOC_LP_VAD_SUPPORTED
|
||||
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
#define RTC_VBAT_UNDER_VOLT_TRIG_EN PMU_VBAT_UNDERVOLT_WAKEUP_EN //!< VBAT under voltage wakeup
|
||||
#else
|
||||
#define RTC_VBAT_UNDER_VOLT_TRIG_EN 0
|
||||
#endif //SOC_VBAT_SUPPORTED
|
||||
|
||||
#define RTC_XTAL32K_DEAD_TRIG_EN 0 // TODO
|
||||
#define RTC_BROWNOUT_DET_TRIG_EN 0 // TODO
|
||||
|
||||
@ -143,6 +150,7 @@ typedef enum {
|
||||
RTC_XTAL32K_DEAD_TRIG_EN | \
|
||||
RTC_USB_TRIG_EN | \
|
||||
RTC_LP_VAD_TRIG_EN | \
|
||||
RTC_VBAT_UNDER_VOLT_TRIG_EN | \
|
||||
RTC_BROWNOUT_DET_TRIG_EN)
|
||||
|
||||
|
||||
|
@ -39,6 +39,7 @@ typedef enum {
|
||||
ESP_SLEEP_RTC_FAST_USE_XTAL_MODE, //!< The mode in which the crystal is used as the RTC_FAST clock source, need keep XTAL on in HP_SLEEP mode when ULP is working.
|
||||
ESP_SLEEP_DIG_USE_XTAL_MODE, //!< The mode requested by digital peripherals to keep XTAL clock on during sleep (both HP_SLEEP and LP_SLEEP mode). (!!! Only valid for lightsleep, will override the XTAL domain config by esp_sleep_pd_config)
|
||||
ESP_SLEEP_LP_USE_XTAL_MODE, //!< The mode requested by lp peripherals to keep XTAL clock on during sleep. Only valid for lightsleep.
|
||||
ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE, //!< The mode to switch power supply to VBAT during deep sleep.
|
||||
ESP_SLEEP_MODE_MAX,
|
||||
} esp_sleep_sub_mode_t;
|
||||
|
||||
|
@ -119,6 +119,7 @@ typedef enum {
|
||||
ESP_SLEEP_WAKEUP_COCPU_TRAP_TRIG, //!< Wakeup caused by COCPU crash
|
||||
ESP_SLEEP_WAKEUP_BT, //!< Wakeup caused by BT (light sleep only)
|
||||
ESP_SLEEP_WAKEUP_VAD, //!< Wakeup caused by VAD
|
||||
ESP_SLEEP_WAKEUP_VBAT_UNDER_VOLT, //!< Wakeup caused by VDD_BAT under voltage.
|
||||
} esp_sleep_source_t;
|
||||
|
||||
/**
|
||||
@ -194,6 +195,16 @@ esp_err_t esp_sleep_enable_timer_wakeup(uint64_t time_in_us);
|
||||
esp_err_t esp_sleep_enable_vad_wakeup(void);
|
||||
#endif
|
||||
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
/**
|
||||
* Wakeup chip is VBAT power voltage is lower than the configured brownout_threshold value.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_sleep_enable_vbat_under_volt_wakeup(void);
|
||||
#endif
|
||||
|
||||
#if SOC_TOUCH_SENSOR_SUPPORTED
|
||||
/**
|
||||
* @brief Enable wakeup by touch sensor
|
||||
@ -591,6 +602,7 @@ esp_err_t esp_sleep_pd_config(esp_sleep_pd_domain_t domain,
|
||||
*
|
||||
* @return
|
||||
* - No return - If the sleep is not rejected.
|
||||
* - ESP_ERR_INVALID_STATE VBAT power does not meet the requirements for entering deepsleep
|
||||
* - ESP_ERR_SLEEP_REJECT sleep request is rejected(wakeup source set before the sleep request)
|
||||
*/
|
||||
esp_err_t esp_deep_sleep_try_to_start(void);
|
||||
|
@ -68,6 +68,11 @@ entries:
|
||||
sar_periph_ctrl (noflash)
|
||||
elif PM_SLP_IRAM_OPT = y:
|
||||
sar_periph_ctrl: sar_periph_ctrl_power_enable (noflash)
|
||||
if ESP_VBAT_ISR_CACHE_SAFE = y:
|
||||
sleep_modes: esp_sleep_disable_wakeup_source (noflash)
|
||||
sleep_modes: esp_sleep_enable_vbat_under_volt_wakeup (noflash)
|
||||
sleep_modes: esp_sleep_sub_mode_config (noflash)
|
||||
sleep_modes: esp_sleep_sub_mode_force_disable (noflash)
|
||||
|
||||
[mapping:soc_pm]
|
||||
archive: libsoc.a
|
||||
|
@ -140,11 +140,6 @@ const pmu_sleep_config_t* pmu_sleep_config_default(
|
||||
)
|
||||
{
|
||||
pmu_sleep_power_config_t power_default = PMU_SLEEP_POWER_CONFIG_DEFAULT(sleep_flags);
|
||||
config->power = power_default;
|
||||
|
||||
pmu_sleep_param_config_t param_default = PMU_SLEEP_PARAM_CONFIG_DEFAULT(sleep_flags);
|
||||
config->param = *pmu_sleep_param_config_default(¶m_default, &power_default, sleep_flags, adjustment, slowclk_period, fastclk_period);
|
||||
|
||||
if (dslp) {
|
||||
pmu_sleep_digital_config_t digital_default = PMU_SLEEP_DIGITAL_DSLP_CONFIG_DEFAULT(sleep_flags, clk_flags);
|
||||
config->digital = digital_default;
|
||||
@ -152,6 +147,11 @@ const pmu_sleep_config_t* pmu_sleep_config_default(
|
||||
pmu_sleep_analog_config_t analog_default = PMU_SLEEP_ANALOG_DSLP_CONFIG_DEFAULT(sleep_flags);
|
||||
analog_default.lp_sys[LP(SLEEP)].analog.dbias = get_slp_lp_dbias();
|
||||
config->analog = analog_default;
|
||||
|
||||
if (sleep_flags & RTC_SLEEP_POWER_BY_VBAT) {
|
||||
power_default.lp_sys[PMU_MODE_LP_SLEEP].dig_power.vddbat_mode = 1;
|
||||
power_default.lp_sys[PMU_MODE_LP_SLEEP].dig_power.bod_source_sel = 1;
|
||||
}
|
||||
} else {
|
||||
pmu_sleep_digital_config_t digital_default = PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT(sleep_flags, clk_flags);
|
||||
config->digital = digital_default;
|
||||
@ -174,6 +174,9 @@ const pmu_sleep_config_t* pmu_sleep_config_default(
|
||||
}
|
||||
config->analog = analog_default;
|
||||
}
|
||||
config->power = power_default;
|
||||
pmu_sleep_param_config_t param_default = PMU_SLEEP_PARAM_CONFIG_DEFAULT(sleep_flags);
|
||||
config->param = *pmu_sleep_param_config_default(¶m_default, &power_default, sleep_flags, adjustment, slowclk_period, fastclk_period);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@ extern "C" {
|
||||
#define PMU_UART1_WAKEUP_EN BIT(7)
|
||||
#define PMU_BLE_SOC_WAKEUP_EN BIT(10)
|
||||
#define PMU_USB_WAKEUP_EN BIT(14)
|
||||
#define PMU_VBAT_UNDERVOLT_WAKEUP_EN BIT(15)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
@ -139,7 +139,9 @@ typedef union {
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t reserved0 : 30;
|
||||
uint32_t reserved0 : 27;
|
||||
uint32_t bod_source_sel : 1;
|
||||
uint32_t vddbat_mode : 2;
|
||||
uint32_t mem_dslp : 1;
|
||||
uint32_t peri_pd_en: 1;
|
||||
};
|
||||
|
@ -169,6 +169,11 @@ const pmu_sleep_config_t* pmu_sleep_config_default(
|
||||
pmu_sleep_analog_config_t analog_default = PMU_SLEEP_ANALOG_DSLP_CONFIG_DEFAULT(sleep_flags);
|
||||
analog_default.hp_sys.analog.xpd_0p1a = 0;
|
||||
config->analog = analog_default;
|
||||
|
||||
if (sleep_flags & RTC_SLEEP_POWER_BY_VBAT) {
|
||||
power_default.lp_sys[PMU_MODE_LP_SLEEP].dig_power.vddbat_mode = 1;
|
||||
power_default.lp_sys[PMU_MODE_LP_SLEEP].dig_power.bod_source_sel = 1;
|
||||
}
|
||||
} else {
|
||||
// Get light sleep digital_default
|
||||
pmu_sleep_digital_config_t digital_default = PMU_SLEEP_DIGITAL_LSLP_CONFIG_DEFAULT(sleep_flags);
|
||||
|
@ -25,7 +25,7 @@ extern "C" {
|
||||
#define PMU_EXT1_WAKEUP_EN BIT(12)
|
||||
#define PMU_LP_TIMER_WAKEUP_EN BIT(13)
|
||||
#define PMU_BOD_WAKEUP_EN BIT(14)
|
||||
#define PMU_VDDBAT_UNDERVOLT_WAKEUP_EN BIT(15)
|
||||
#define PMU_VBAT_UNDERVOLT_WAKEUP_EN BIT(15)
|
||||
#define PMU_LP_CORE_TRAP_WAKEUP_EN BIT(16)
|
||||
#define PMU_ETM_WAKEUP_EN BIT(17)
|
||||
#define PMU_LP_TIMER1_WAKEUP_EN BIT(18)
|
||||
|
49
components/esp_hw_support/power_supply/include/esp_vbat.h
Normal file
49
components/esp_hw_support/power_supply/include/esp_vbat.h
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
ESP_VBAT_STATE_NORMAL, /*!< Normal working state, not charging */
|
||||
ESP_VBAT_STATE_CHARGING, /*!< Battery is charging by VDDA, only for chargeable battery. */
|
||||
ESP_VBAT_STATE_LOWBATTERY, /*!< Battery is under voltage, battery replacement required */
|
||||
} esp_vbat_state_t;
|
||||
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
/**
|
||||
* @brief Wait until battery charging done.
|
||||
*
|
||||
* @note This function is not allowed to be called in ISR context.
|
||||
*
|
||||
* @param checking_period Time in ticks to wait until timeout or succeed
|
||||
* @return
|
||||
* - ESP_OK: Battery charging completed
|
||||
* - ESP_FAIL: Called in ISR context
|
||||
* - ESP_ERR_TIMEOUT: Timeout waiting for battery charging to complete
|
||||
*/
|
||||
esp_err_t esp_vbat_wait_battery_charge_done(TickType_t checking_period);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get battery status.
|
||||
* @return
|
||||
* - DISCHARGING Battery is discharging.
|
||||
* - CHARGING Battery is charing (ESP_VBAT_USE_RECHARGEABLE_BATTERY only).
|
||||
* - LOWBATTERY Battery is under voltage.
|
||||
*/
|
||||
esp_vbat_state_t esp_vbat_get_battery_state(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -78,6 +78,14 @@ menu "Power Supplier"
|
||||
during deep sleep.
|
||||
- When this option is disabled, the RTC battery input (VBAT) must not be left floating.
|
||||
|
||||
config ESP_VBAT_ISR_CACHE_SAFE
|
||||
bool "VBAT ISR IRAM-Safe"
|
||||
default y
|
||||
depends on SOC_VBAT_SUPPORTED && ESP_VBAT_INIT_AUTO
|
||||
help
|
||||
Ensure the VBAT interrupt is IRAM-Safe by allowing the interrupt handler to be
|
||||
executable when the cache is disabled (e.g. SPI Flash write).
|
||||
|
||||
config ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
bool "The battery for RTC battery is a rechargeable battery"
|
||||
default n
|
||||
@ -167,6 +175,13 @@ menu "Power Supplier"
|
||||
bool "2.57V"
|
||||
endchoice
|
||||
|
||||
config ESP_VBAT_WAKEUP_CHIP_ON_VBAT_BROWNOUT
|
||||
bool "Wake up chip in deepsleep if VBAT brownout"
|
||||
depends on !ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
default n
|
||||
help
|
||||
Wake up the chip if the vbat voltage drops below the brownout voltage during deepsleep.
|
||||
|
||||
config ESP_VBAT_DET_LVL_LOW
|
||||
int
|
||||
depends on ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
|
@ -67,6 +67,14 @@ menu "Power Supplier"
|
||||
during deep sleep.
|
||||
- When this option is disabled, the RTC battery input (VBAT) must not be left floating.
|
||||
|
||||
config ESP_VBAT_ISR_CACHE_SAFE
|
||||
bool "VBAT ISR IRAM-Safe"
|
||||
default y
|
||||
depends on SOC_VBAT_SUPPORTED && ESP_VBAT_INIT_AUTO
|
||||
help
|
||||
Ensure the VBAT interrupt is IRAM-Safe by allowing the interrupt handler to be
|
||||
executable when the cache is disabled (e.g. SPI Flash write).
|
||||
|
||||
config ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
bool "The battery for RTC battery is a rechargeable battery"
|
||||
default n
|
||||
@ -126,6 +134,13 @@ menu "Power Supplier"
|
||||
bool "2.42V"
|
||||
endchoice
|
||||
|
||||
config ESP_VBAT_WAKEUP_CHIP_ON_VBAT_BROWNOUT
|
||||
bool "Wake up chip in deepsleep if VBAT brownout"
|
||||
depends on !ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
default n
|
||||
help
|
||||
Wake up the chip if the vbat voltage drops below the brownout voltage during deepsleep.
|
||||
|
||||
config ESP_VBAT_DET_LVL_LOW
|
||||
int
|
||||
depends on ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
|
@ -15,17 +15,38 @@
|
||||
#include "hal/vbat_hal.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_private/esp_sleep_internal.h"
|
||||
#include "esp_private/startup_internal.h"
|
||||
#include "esp_private/rtc_ctrl.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_vbat.h"
|
||||
#include "esp_check.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "soc/clk_tree_defs.h"
|
||||
#include "soc/power_supply_periph.h"
|
||||
|
||||
#if CONFIG_ESP_VBAT_INIT_AUTO
|
||||
#if CONFIG_ESP_VBAT_ISR_CACHE_SAFE
|
||||
#define VBAT_INTR_ALLOC_FLAG (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED)
|
||||
#else
|
||||
#define VBAT_INTR_ALLOC_FLAG (ESP_INTR_FLAG_SHARED)
|
||||
#endif // CONFIG_ESP_VBAT_ISR_CACHE_SAFE
|
||||
|
||||
#define VBAT_BROWNOUT_DET_LVL CONFIG_ESP_VBAT_BROWNOUT_DET_LVL
|
||||
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
#define VBAT_CHARGE_DET_LVL_LOW CONFIG_ESP_VBAT_DET_LVL_LOW
|
||||
#define VBAT_CHARGE_DET_LVL_HIGH CONFIG_ESP_VBAT_DET_LVL_HIGH
|
||||
#define VBAT_CHARGER_RESISTOR_VALUE CONFIG_ESP_VBAT_CHARGER_CIRCUIT_RESISTOR_VAL
|
||||
#define VBAT_CHARGE_DET_LVL_LOW CONFIG_ESP_VBAT_DET_LVL_LOW
|
||||
#define VBAT_CHARGE_DET_LVL_HIGH CONFIG_ESP_VBAT_DET_LVL_HIGH
|
||||
#define VBAT_CHARGER_RESISTOR_VALUE CONFIG_ESP_VBAT_CHARGER_CIRCUIT_RESISTOR_VAL
|
||||
#endif
|
||||
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY || CONFIG_ESP_VBAT_WAKEUP_CHIP_ON_VBAT_BROWNOUT
|
||||
#define VBAT_CHARGER_FILTER_TIME_US 50
|
||||
#define VBAT_CHARGER_HYSTERESIS_THRESHOLD_LOW 100
|
||||
#define VBAT_CHARGER_HYSTERESIS_THRESHOLD_HIGH (VBAT_CHARGER_HYSTERESIS_THRESHOLD_LOW + (VBAT_CHARGER_FILTER_TIME_US * SOC_CLK_RC_FAST_FREQ_APPROX) / MHZ)
|
||||
#endif
|
||||
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
#if (VBAT_CHARGER_RESISTOR_VALUE < 1000 || VBAT_CHARGER_RESISTOR_VALUE > 4500 || VBAT_CHARGER_RESISTOR_VALUE % 500 != 0)
|
||||
#error "vbat charger resistor (ESP_VBAT_CHARGER_CIRCUIT_RESISTOR_VAL) must be between 1000 and 4500 ohms and must be a multiple of 500."
|
||||
#endif
|
||||
@ -33,60 +54,123 @@
|
||||
#if (VBAT_BROWNOUT_DET_LVL >= VBAT_CHARGE_DET_LVL_LOW)
|
||||
#error "vbat charger low threshold is equal or lower than vbat brownout threshold, please put vbat brownout threshold lower than vbat charger low threshold"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
static const char TAG[] = "VBAT";
|
||||
#endif
|
||||
|
||||
static struct {
|
||||
esp_vbat_state_t state;
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
SemaphoreHandle_t charging_done_sem;
|
||||
#endif
|
||||
} vbat_status = {
|
||||
.state = ESP_VBAT_STATE_NORMAL
|
||||
};
|
||||
|
||||
#if CONFIG_ESP_VBAT_INIT_AUTO
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
static StaticSemaphore_t charging_done_semphr_buffer;
|
||||
#endif
|
||||
|
||||
IRAM_ATTR static void vbat_isr_handler(void *arg)
|
||||
{
|
||||
portBASE_TYPE HPTaskAwoken = pdFALSE;
|
||||
uint32_t int_status;
|
||||
vbat_ll_get_interrupt_status(&int_status);
|
||||
vbat_ll_clear_intr_mask(int_status);
|
||||
|
||||
if (int_status & VBAT_LL_CHARGER_UNDERVOLTAGE_INTR) {
|
||||
ESP_DRAM_LOGW(TAG, "RTC battery voltage low, start charging...");
|
||||
vbat_ll_start_battery_charge(true);
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
if (int_status & VBAT_LL_CHARGER_UPVOLTAGE_INTR) {
|
||||
ESP_DRAM_LOGW(TAG, "RTC battery voltage reaches high limit , stop charging...");
|
||||
vbat_status.state = ESP_VBAT_STATE_NORMAL;
|
||||
vbat_ll_start_battery_charge(false);
|
||||
vbat_ll_enable_intr_mask(VBAT_LL_CHARGER_UNDERVOLTAGE_INTR | VBAT_LL_BROWNOUT_INTR, true);
|
||||
vbat_ll_enable_intr_mask(VBAT_LL_CHARGER_UPVOLTAGE_INTR, false);
|
||||
vbat_ll_clear_intr_mask(VBAT_LL_CHARGER_UPVOLTAGE_INTR);
|
||||
esp_sleep_enable_vbat_under_volt_wakeup();
|
||||
esp_sleep_sub_mode_config(ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE, true);
|
||||
xSemaphoreGiveFromISR(vbat_status.charging_done_sem, &HPTaskAwoken);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (int_status & (VBAT_LL_CHARGER_UNDERVOLTAGE_INTR)) {
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
vbat_status.state = ESP_VBAT_STATE_CHARGING;
|
||||
ESP_DRAM_LOGW(TAG, "RTC battery voltage low, start charging...");
|
||||
vbat_ll_start_battery_charge(true);
|
||||
vbat_ll_enable_intr_mask(VBAT_LL_CHARGER_UPVOLTAGE_INTR, true);
|
||||
vbat_ll_enable_intr_mask(VBAT_LL_CHARGER_UNDERVOLTAGE_INTR, false);
|
||||
#endif
|
||||
vbat_ll_clear_intr_mask(VBAT_LL_CHARGER_UNDERVOLTAGE_INTR);
|
||||
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_VBAT_UNDER_VOLT);
|
||||
esp_sleep_sub_mode_config(ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE, false);
|
||||
}
|
||||
|
||||
if (int_status & VBAT_LL_BROWNOUT_INTR) {
|
||||
// TODO: A callback may needed here to inform an under voltage event.
|
||||
vbat_status.state = ESP_VBAT_STATE_LOWBATTERY;
|
||||
esp_sleep_sub_mode_force_disable(ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE);
|
||||
ESP_DRAM_LOGW(TAG, "RTC battery voltage low, please change battery...");
|
||||
vbat_ll_clear_intr_mask(VBAT_LL_BROWNOUT_INTR);
|
||||
}
|
||||
|
||||
if (HPTaskAwoken) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
esp_err_t esp_vbat_wait_battery_charge_done(TickType_t checking_period)
|
||||
{
|
||||
BaseType_t ret;
|
||||
if (!xPortInIsrContext()) {
|
||||
ret = xSemaphoreTake(vbat_status.charging_done_sem, checking_period);
|
||||
} else {
|
||||
return ESP_FAIL;
|
||||
}
|
||||
return (ret == pdPASS) ? ESP_OK : ESP_ERR_TIMEOUT;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
esp_vbat_state_t esp_vbat_get_battery_state(void)
|
||||
{
|
||||
return vbat_status.state;
|
||||
}
|
||||
|
||||
#if CONFIG_ESP_VBAT_INIT_AUTO
|
||||
esp_err_t esp_vbat_init(void)
|
||||
{
|
||||
intr_handle_t vbat_intr;
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
vbat_status.charging_done_sem = xSemaphoreCreateBinaryStatic(&charging_done_semphr_buffer);
|
||||
vbat_hal_config_t vbat_cfg = {
|
||||
.enable_vbat_charger = true,
|
||||
.charger_resistor_value = VBAT_CHARGER_RESISTOR_VALUE,
|
||||
.low_threshold = VBAT_CHARGE_DET_LVL_LOW,
|
||||
.high_threshold = VBAT_CHARGE_DET_LVL_HIGH,
|
||||
.brownout_threshold = VBAT_BROWNOUT_DET_LVL,
|
||||
.undervoltage_filter_time = 20,
|
||||
.upvoltage_filter_time = 10,
|
||||
.undervoltage_filter_time = VBAT_CHARGER_HYSTERESIS_THRESHOLD_HIGH,
|
||||
.upvoltage_filter_time = VBAT_CHARGER_HYSTERESIS_THRESHOLD_LOW,
|
||||
.interrupt_mask = (VBAT_LL_CHARGER_MASK | VBAT_LL_DETECT_MASK),
|
||||
};
|
||||
|
||||
#else
|
||||
vbat_hal_config_t vbat_cfg = {
|
||||
.enable_vbat_charger = false,
|
||||
#if CONFIG_ESP_VBAT_WAKEUP_CHIP_ON_VBAT_BROWNOUT
|
||||
.enable_vbat_charger = true,
|
||||
.low_threshold = VBAT_BROWNOUT_DET_LVL,
|
||||
.undervoltage_filter_time = VBAT_CHARGER_HYSTERESIS_THRESHOLD_HIGH,
|
||||
.upvoltage_filter_time = VBAT_CHARGER_HYSTERESIS_THRESHOLD_LOW,
|
||||
#endif
|
||||
.brownout_threshold = VBAT_BROWNOUT_DET_LVL,
|
||||
.interrupt_mask = VBAT_LL_DETECT_MASK,
|
||||
};
|
||||
#endif
|
||||
|
||||
vbat_hal_config(&vbat_cfg);
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(power_supply_periph_signal.irq, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_SHARED, (uint32_t)brownout_ll_intr_get_status_reg(), VBAT_LL_CHARGER_MASK | VBAT_LL_DETECT_MASK, &vbat_isr_handler, NULL, &vbat_intr), TAG, "Allocate vbat isr failed");
|
||||
|
||||
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(power_supply_periph_signal.irq, VBAT_INTR_ALLOC_FLAG, (uint32_t)brownout_ll_intr_get_status_reg(), vbat_cfg.interrupt_mask, &vbat_isr_handler, NULL, &vbat_intr), TAG, "Allocate vbat isr failed");
|
||||
esp_sleep_sub_mode_force_disable(ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE);
|
||||
esp_sleep_sub_mode_config(ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE, true);
|
||||
esp_sleep_enable_vbat_under_volt_wakeup();
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
@ -127,6 +127,11 @@
|
||||
#include "hal/lp_timer_hal.h"
|
||||
#endif
|
||||
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
#include "esp_vbat.h"
|
||||
#include "hal/vbat_ll.h"
|
||||
#endif
|
||||
|
||||
#if SOC_PMU_SUPPORTED
|
||||
#include "esp_private/esp_pmu.h"
|
||||
#include "esp_private/sleep_sys_periph.h"
|
||||
@ -339,6 +344,9 @@ static esp_err_t timer_wakeup_prepare(int64_t sleep_duration);
|
||||
#if SOC_TOUCH_SENSOR_SUPPORTED
|
||||
static void touch_wakeup_prepare(void);
|
||||
#endif
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
static void vbat_under_volt_wakeup_prepare(void);
|
||||
#endif
|
||||
#if SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP && SOC_DEEP_SLEEP_SUPPORTED
|
||||
static void gpio_deep_sleep_wakeup_prepare(void);
|
||||
#endif
|
||||
@ -1044,13 +1052,6 @@ static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t cl
|
||||
* The configuration change will change the reading of the sleep pad, which will cause the touch wake-up sensor to trigger falsely.
|
||||
*/
|
||||
keep_rtc_power_on = true;
|
||||
#elif CONFIG_IDF_TARGET_ESP32P4
|
||||
/* Due to esp32p4 eco0 hardware bug, if LP peripheral power domain is powerdowned in sleep, there will be a possibility of
|
||||
* triggering the EFUSE_CRC reset, so disable the power-down of this power domain on lightsleep for ECO0 version.
|
||||
*/
|
||||
if (!ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 1)) {
|
||||
keep_rtc_power_on = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
@ -1065,6 +1066,12 @@ static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t cl
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
if (deep_sleep && (s_config.wakeup_triggers & RTC_VBAT_UNDER_VOLT_TRIG_EN)) {
|
||||
vbat_under_volt_wakeup_prepare();
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t reject_triggers = allow_sleep_rejection ? (s_config.wakeup_triggers & RTC_SLEEP_REJECT_MASK) : 0;
|
||||
|
||||
if (!deep_sleep) {
|
||||
@ -1191,6 +1198,20 @@ inline static uint32_t IRAM_ATTR call_rtc_sleep_start(uint32_t reject_triggers,
|
||||
|
||||
static esp_err_t IRAM_ATTR deep_sleep_start(bool allow_sleep_rejection)
|
||||
{
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE]) {
|
||||
esp_vbat_state_t battery_state = esp_vbat_get_battery_state();
|
||||
if (battery_state == ESP_VBAT_STATE_CHARGING) {
|
||||
ESP_LOGW(TAG, "Battery is charging, should wait until charging is complete before entering deep sleep!");
|
||||
} else if (battery_state == ESP_VBAT_STATE_LOWBATTERY) {
|
||||
ESP_LOGE(TAG, "Battery is in low battery state, chip may lose power during deepsleep!");
|
||||
}
|
||||
if ((battery_state != ESP_VBAT_STATE_NORMAL) && allow_sleep_rejection) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
/* Due to hardware limitations, on S2 the brownout detector sometimes trigger during deep sleep
|
||||
to circumvent this we disable the brownout detector before sleeping */
|
||||
@ -1667,6 +1688,16 @@ esp_err_t esp_sleep_disable_wakeup_source(esp_sleep_source_t source)
|
||||
else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_ULP, RTC_ULP_TRIG_EN)) {
|
||||
s_config.wakeup_triggers &= ~RTC_ULP_TRIG_EN;
|
||||
}
|
||||
#endif
|
||||
#if SOC_LP_VAD_SUPPORTED
|
||||
else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_VAD, RTC_LP_VAD_TRIG_EN)) {
|
||||
s_config.wakeup_triggers &= ~RTC_LP_VAD_TRIG_EN;
|
||||
}
|
||||
#endif
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
else if (CHECK_SOURCE(source, ESP_SLEEP_WAKEUP_VBAT_UNDER_VOLT, RTC_VBAT_UNDER_VOLT_TRIG_EN)) {
|
||||
s_config.wakeup_triggers &= ~RTC_VBAT_UNDER_VOLT_TRIG_EN;
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
ESP_LOGE(TAG, "Incorrect wakeup source (%d) to disable.", (int) source);
|
||||
@ -1748,6 +1779,14 @@ esp_err_t esp_sleep_enable_vad_wakeup(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
esp_err_t esp_sleep_enable_vbat_under_volt_wakeup(void)
|
||||
{
|
||||
s_config.wakeup_triggers |= RTC_VBAT_UNDER_VOLT_TRIG_EN;
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SLEEP_FN_ATTR esp_err_t timer_wakeup_prepare(int64_t sleep_duration)
|
||||
{
|
||||
if (sleep_duration < 0) {
|
||||
@ -1775,6 +1814,13 @@ static SLEEP_FN_ATTR esp_err_t timer_wakeup_prepare(int64_t sleep_duration)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
static void vbat_under_volt_wakeup_prepare(void)
|
||||
{
|
||||
vbat_ll_clear_intr_mask(VBAT_LL_CHARGER_UNDERVOLTAGE_INTR);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_TOUCH_SENSOR_SUPPORTED
|
||||
static void touch_wakeup_prepare(void)
|
||||
{
|
||||
@ -2274,6 +2320,10 @@ esp_sleep_wakeup_cause_t esp_sleep_get_wakeup_cause(void)
|
||||
#if SOC_LP_VAD_SUPPORTED
|
||||
} else if (wakeup_cause & RTC_LP_VAD_TRIG_EN) {
|
||||
return ESP_SLEEP_WAKEUP_VAD;
|
||||
#endif
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
} else if (wakeup_cause & RTC_VBAT_UNDER_VOLT_TRIG_EN) {
|
||||
return ESP_SLEEP_WAKEUP_VBAT_UNDER_VOLT;
|
||||
#endif
|
||||
} else {
|
||||
return ESP_SLEEP_WAKEUP_UNDEFINED;
|
||||
@ -2339,6 +2389,7 @@ int32_t* esp_sleep_sub_mode_dump_config(FILE *stream) {
|
||||
[ESP_SLEEP_RTC_FAST_USE_XTAL_MODE] = "ESP_SLEEP_RTC_FAST_USE_XTAL_MODE",
|
||||
[ESP_SLEEP_DIG_USE_XTAL_MODE] = "ESP_SLEEP_DIG_USE_XTAL_MODE",
|
||||
[ESP_SLEEP_LP_USE_XTAL_MODE] = "ESP_SLEEP_LP_USE_XTAL_MODE",
|
||||
[ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE] = "ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE",
|
||||
}[mode],
|
||||
s_sleep_sub_mode_ref_cnt[mode] ? "ENABLED" : "DISABLED",
|
||||
s_sleep_sub_mode_ref_cnt[mode]);
|
||||
@ -2607,6 +2658,12 @@ static SLEEP_FN_ATTR uint32_t get_sleep_flags(uint32_t sleep_flags, bool deepsle
|
||||
}
|
||||
#endif
|
||||
|
||||
#if SOC_VBAT_SUPPORTED
|
||||
if (s_sleep_sub_mode_ref_cnt[ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE] && deepsleep) {
|
||||
sleep_flags |= RTC_SLEEP_POWER_BY_VBAT;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ESP_SLEEP_RTC_BUS_ISO_WORKAROUND
|
||||
if (!deepsleep) {
|
||||
sleep_flags &= ~RTC_SLEEP_PD_RTC_PERIPH;
|
||||
|
@ -80,6 +80,10 @@ ESP_SYSTEM_INIT_FN(init_brownout, CORE, BIT(0), 104)
|
||||
// [refactor-todo] leads to call chain rtc_is_register (driver) -> esp_intr_alloc (esp32/esp32s2) ->
|
||||
// malloc (newlib) -> heap_caps_malloc (heap), so heap must be at least initialized
|
||||
esp_err_t ret = ESP_OK;
|
||||
// BOD and VBAT share the same interrupt number. To avoid blocking the system in an intermediate state
|
||||
// where an interrupt occurs and the interrupt number is enabled, but the ISR is not configured, enable
|
||||
// the interrupt after configuring both ISRs.
|
||||
portDISABLE_INTERRUPTS();
|
||||
#if CONFIG_ESP_BROWNOUT_DET
|
||||
esp_brownout_init();
|
||||
#else
|
||||
@ -91,7 +95,7 @@ ESP_SYSTEM_INIT_FN(init_brownout, CORE, BIT(0), 104)
|
||||
#if CONFIG_ESP_VBAT_INIT_AUTO
|
||||
ret = esp_vbat_init();
|
||||
#endif
|
||||
|
||||
portENABLE_INTERRUPTS();
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
@ -102,6 +102,7 @@ static inline void vbat_ll_set_charger_resistor(uint32_t resistor)
|
||||
*/
|
||||
static inline void vbat_ll_start_battery_charge(bool start)
|
||||
{
|
||||
LP_ANA_PERI.vddbat_charge_cntl.vddbat_charge_charger = start;
|
||||
LP_ANA_PERI.vddbat_bod_cntl.vddbat_charger = start;
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <stdbool.h>
|
||||
#include "esp_bit_defs.h"
|
||||
#include "soc/lp_analog_peri_struct.h"
|
||||
#include "hal/assert.h"
|
||||
#include "hal/regi2c_ctrl.h"
|
||||
#include "soc/regi2c_brownout.h"
|
||||
|
||||
@ -72,6 +73,7 @@ static inline void vbat_ll_enable_charger_comparator(bool enable)
|
||||
*/
|
||||
static inline void vbat_ll_set_undervoltage_filter_time(uint32_t time_tick)
|
||||
{
|
||||
HAL_ASSERT(time_tick < (2<<10));
|
||||
LP_ANA_PERI.vddbat_charge_cntl.vddbat_charge_undervoltage_target = time_tick;
|
||||
}
|
||||
|
||||
@ -82,6 +84,7 @@ static inline void vbat_ll_set_undervoltage_filter_time(uint32_t time_tick)
|
||||
*/
|
||||
static inline void vbat_ll_set_upvoltage_filter_time(uint32_t time_tick)
|
||||
{
|
||||
HAL_ASSERT(time_tick < (2<<10));
|
||||
LP_ANA_PERI.vddbat_charge_cntl.vddbat_charge_upvoltage_target = time_tick;
|
||||
}
|
||||
|
||||
@ -102,6 +105,7 @@ static inline void vbat_ll_set_charger_resistor(uint32_t resistor)
|
||||
*/
|
||||
static inline void vbat_ll_start_battery_charge(bool start)
|
||||
{
|
||||
LP_ANA_PERI.vddbat_charge_cntl.vddbat_charge_charger = start;
|
||||
LP_ANA_PERI.vddbat_bod_cntl.vddbat_charger = start;
|
||||
}
|
||||
|
||||
|
@ -21,8 +21,8 @@ typedef struct {
|
||||
uint8_t low_threshold; // low voltage threshold
|
||||
uint8_t high_threshold; // high voltage threshold
|
||||
uint8_t brownout_threshold; // brownout threshold
|
||||
uint8_t undervoltage_filter_time; // under voltage filter time
|
||||
uint8_t upvoltage_filter_time; // up voltage filter time
|
||||
uint16_t undervoltage_filter_time; // under voltage filter time
|
||||
uint16_t upvoltage_filter_time; // up voltage filter time
|
||||
} vbat_hal_config_t;
|
||||
|
||||
/**
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "hal/assert.h"
|
||||
#include "hal/vbat_ll.h"
|
||||
#include "hal/vbat_hal.h"
|
||||
|
||||
@ -15,9 +15,19 @@ void vbat_hal_config(const vbat_hal_config_t *cfg)
|
||||
uint8_t resistor_reg = (cfg->charger_resistor_value - 1000) / 500;
|
||||
vbat_ll_set_charger_resistor(resistor_reg);
|
||||
vbat_ll_set_charger_threshold(cfg->low_threshold, cfg->high_threshold);
|
||||
HAL_ASSERT(cfg->undervoltage_filter_time > cfg->upvoltage_filter_time);
|
||||
vbat_ll_set_undervoltage_filter_time(cfg->undervoltage_filter_time);
|
||||
vbat_ll_set_upvoltage_filter_time(cfg->upvoltage_filter_time);
|
||||
}
|
||||
vbat_ll_set_brownout_threshold(cfg->brownout_threshold);
|
||||
|
||||
uint32_t int_status;
|
||||
vbat_ll_get_interrupt_status(&int_status);
|
||||
// TODO: When the chip wakes up from vbat under voltage, the upvoltage interrupt will
|
||||
// be triggered at the same time, workaround by software here.
|
||||
if (int_status & VBAT_LL_CHARGER_UNDERVOLTAGE_INTR) {
|
||||
vbat_ll_clear_intr_mask(VBAT_LL_CHARGER_UPVOLTAGE_INTR);
|
||||
}
|
||||
|
||||
vbat_ll_enable_intr_mask(cfg->interrupt_mask, true);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -769,9 +769,9 @@ typedef union {
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t module : 2;
|
||||
uint32_t reserved1 : 29;
|
||||
uint32_t sw_update : 1;
|
||||
uint32_t ana_vddbat_mode : 2;
|
||||
uint32_t reserved1 : 29;
|
||||
uint32_t sw_update : 1;
|
||||
};
|
||||
uint32_t val;
|
||||
} pmu_vddbat_cfg_t;
|
||||
|
9
examples/lowpower/.build-test-rules.yml
Normal file
9
examples/lowpower/.build-test-rules.yml
Normal file
@ -0,0 +1,9 @@
|
||||
# Documentation: .gitlab/ci/README.md#manifest-file-to-control-the-buildtest-apps
|
||||
|
||||
examples/lowpower/vbat:
|
||||
enable:
|
||||
- if: SOC_VBAT_SUPPORTED == 1
|
||||
disable_test:
|
||||
- if: IDF_TARGET in ["esp32h2"]
|
||||
temporary: true
|
||||
reason: not supported yet
|
8
examples/lowpower/README.md
Normal file
8
examples/lowpower/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-C61 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# System Examples
|
||||
|
||||
Configuration and management of ESP chips lowpower related features.
|
||||
|
||||
See the [README.md](../README.md) file in the upper level [examples](../) directory for more information about examples.
|
8
examples/lowpower/vbat/CMakeLists.txt
Normal file
8
examples/lowpower/vbat/CMakeLists.txt
Normal file
@ -0,0 +1,8 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(vbat)
|
100
examples/lowpower/vbat/README.md
Normal file
100
examples/lowpower/vbat/README.md
Normal file
@ -0,0 +1,100 @@
|
||||
| Supported Targets | ESP32-H2 | ESP32-P4 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
# VBAT Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
When the application is idle and the chip enters deepsleep, you may want to turn off the main power to save power (because the main power usually also drives other power-consuming devices on the board), but you also want the ESP chip to maintain the RTC timing and wake-up functions. Or you want to enter deepsleep mode that only maintains RTC timing but with another power supply when the main power is about to undervoltage. In these cases, you can use this feature, which allows the chip to switch to backup battery power when entering deepsleep.
|
||||
|
||||
This example demonstrates the ESP backup battery power supply solution, which has the following features:
|
||||
|
||||
- Supports using backup power (VBAT) during deepsleep, and RTC Timer can keep timing after the main power is lost.
|
||||
- Supports automatic power switching by PMU during sleep/wake-up, automatically switches to backup power supply when entering sleep mode and switches to main power supply when waking up.
|
||||
- Support battery voltage detection, wake up the chip and switch to the main power supply when undervoltage occurs.
|
||||
- Support battery charging, automatically stop charging when threshold voltage is reached, and charging current can be configured.
|
||||
- Supports selection of chip status when charging the battery, options include keep active, entering lightsleep or entering deepsleep.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
This example should be run on a development board with a backup battery. Currently the following development boards are supported:
|
||||
- [ESP32-P4-Function-EV-Board Rev](https://docs.espressif.com/projects/esp-dev-kits/en/latest/esp32p4/esp32-p4-function-ev-board/index.html#esp32-p4-function-ev-board)
|
||||
|
||||
```
|
||||
0(%1)NC ┌──────────┐
|
||||
BAT ─────^^^^─────┐ │ │
|
||||
│ │ │
|
||||
│ │ ESP32-P4 │
|
||||
│ │ │
|
||||
ESP_3V3 ─────^^^^─────┴────┤VBAT │
|
||||
0(%1) └──────────┘
|
||||
```
|
||||
|
||||
By default, the ESP_VBAT power supply on this development board is shorted to ESP_3V3 through a 0 Ω resistor.
|
||||
|
||||
**You need to re-solder the resistor 0 Ω on ESP_3V3 to the empty pad on BAT. (You can find the resistor position on the schematic doc of the development board). Then connect `RTC_Battery +` to the positive terminal of the battery and `RTC_Battery -` to the negative terminal of the battery.**
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
- If you are using a non-rechargeable battery, VBAT brownout wakeup can be enabled via `Component config → Hardware Settings → Power Supplier → RTC Backup Battery`.
|
||||
- If you are using a rechargeable battery, the automatic wake-up charging feature can be enabled via `Component config → Hardware Settings → Power Supplier → RTC Backup Battery -> The battery for RTC battery is a rechargeable battery`.
|
||||
- The charging current limiting resistor can be configured via `Component config → Hardware Settings → Power Supplier → RTC Backup Battery -> vbat charger circuit resistor value (ohms)`.
|
||||
- The chip state while waiting for battery charging the battery can be selected by `Example Configuration → Configure the chip state while waiting for battery charging`.
|
||||
- The period to check whether the battery has been charged done can be selected by `Battery charging done check period (in seconds)`.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
On initial startup, this example will detect that this is the first boot and output the following log:
|
||||
|
||||
```
|
||||
...
|
||||
I (271) main_task: Started on CPU0
|
||||
I (281) main_task: Calling app_main()
|
||||
Not a deep sleep reset
|
||||
Current RTC Time: 110106 (us)
|
||||
Entering deep sleep
|
||||
```
|
||||
|
||||
Then the chip will then enter deep sleep, and switch to VBAT power supply.
|
||||
|
||||
If non-rechargeable battery is used, nothing will happens when VBAT is undervoltage by default, but if `CONFIG_ESP_VBAT_WAKEUP_CHIP_ON_VBAT_BROWNOUT` is enabled, when the battery voltage drops to the configured brownout threshold `ESP_VBAT_BROWNOUT_DET_LVL_SEL`, the chip will wake up and go to sleep again, but will not switch to VBAT power during deepsleep.
|
||||
|
||||
```
|
||||
W VBAT: RTC battery voltage low, please change battery...
|
||||
Battery is low, VBAT power will not be used during deep sleep!
|
||||
Current RTC Time: 18493666 (us)
|
||||
Entering deep sleep
|
||||
```
|
||||
|
||||
If rechargeable battery is used, when the battery voltage drops to the configured charging threshold (`CONFIG_ESP_VBAT_DET_LVL_LOW_SEL`), the chip will wake up and start charge the battery, when the battery voltage rises to the configured stop charging threshold (`CONFIG_ESP_VBAT_DET_LVL_HIGH_SEL`), the chip will stop charging the battery and re-enter deepsleep, and so on. The following log will be output:
|
||||
|
||||
```
|
||||
...
|
||||
W VBAT: RTC battery voltage low, start charging...
|
||||
Wake up from Low power VBAT
|
||||
Battery is low, waiting for charging to complete before going to sleep!
|
||||
W VBAT: RTC battery voltage reaches high limit , stop charging...
|
||||
Current RTC Time: 20753113 (us)
|
||||
Entering deep sleep
|
||||
...
|
||||
```
|
6
examples/lowpower/vbat/main/CMakeLists.txt
Normal file
6
examples/lowpower/vbat/main/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
set(srcs "vbat_example_main.c")
|
||||
set(includes ".")
|
||||
|
||||
idf_component_register(SRCS ${srcs}
|
||||
PRIV_REQUIRES esp_pm
|
||||
INCLUDE_DIRS ${includes})
|
26
examples/lowpower/vbat/main/Kconfig.projbuild
Normal file
26
examples/lowpower/vbat/main/Kconfig.projbuild
Normal file
@ -0,0 +1,26 @@
|
||||
menu "Example Configuration"
|
||||
choice EXAMPLE_STATE_WHILE_WAITING_BATTERY_CHARGE_DONE
|
||||
bool "Configure the chip state while waiting for battery charging"
|
||||
default EXAMPLE_WAITING_BATTERY_CHARGING_IN_ACTIVE
|
||||
depends on ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
help
|
||||
Select chip state when waiting battery charging done
|
||||
|
||||
config EXAMPLE_WAITING_BATTERY_CHARGING_IN_ACTIVE
|
||||
bool "Waiting for battery charging in active state"
|
||||
config EXAMPLE_WAITING_BATTERY_CHARGING_IN_LIGHT_SLEEP
|
||||
bool "Waiting for battery charging in light sleep state"
|
||||
select PM_ENABLE
|
||||
select FREERTOS_USE_TICKLESS_IDLE
|
||||
config EXAMPLE_WAITING_BATTERY_CHARGING_IN_DEEP_SLEEP
|
||||
bool "Waiting for battery charging in deep sleep state"
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_VBAT_CHARGING_DONE_CHECK_PERIOD
|
||||
int "Battery charging done check period (in seconds)"
|
||||
default 60
|
||||
depends on ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
help
|
||||
Period in seconds to check whether the battery has been charged done
|
||||
|
||||
endmenu
|
77
examples/lowpower/vbat/main/vbat_example_main.c
Normal file
77
examples/lowpower/vbat/main/vbat_example_main.c
Normal file
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_vbat.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_rtc_time.h"
|
||||
#include "esp_sleep.h"
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
#if CONFIG_EXAMPLE_WAITING_BATTERY_CHARGING_IN_LIGHT_SLEEP
|
||||
esp_pm_config_t pm_config = {
|
||||
.max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
|
||||
.min_freq_mhz = CONFIG_XTAL_FREQ,
|
||||
.light_sleep_enable = true
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
|
||||
#endif
|
||||
|
||||
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_VBAT_UNDER_VOLT) {
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
printf("Wake up from VBAT low power\n");
|
||||
#else
|
||||
printf("Wake up from VBAT brownout\n");
|
||||
#endif
|
||||
} else if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_TIMER) {
|
||||
printf("Wake up from Timer\n");
|
||||
} else {
|
||||
printf("Not a deep sleep reset\n");
|
||||
}
|
||||
|
||||
esp_err_t sleep_result;
|
||||
do {
|
||||
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
|
||||
if (esp_vbat_get_battery_state() == ESP_VBAT_STATE_CHARGING) {
|
||||
#if CONFIG_EXAMPLE_WAITING_BATTERY_CHARGING_IN_DEEP_SLEEP
|
||||
printf("Battery is charging, wake up the chip every %d seconds to check whether the battery is charged done !\n", CONFIG_EXAMPLE_VBAT_CHARGING_DONE_CHECK_PERIOD);
|
||||
esp_sleep_enable_timer_wakeup(CONFIG_EXAMPLE_VBAT_CHARGING_DONE_CHECK_PERIOD * 1000 * 1000);
|
||||
#else
|
||||
printf("Battery is low, waiting for charging to complete before going to deep sleep!\n");
|
||||
do {
|
||||
// Task will enter block state in `esp_vbat_wait_battery_charge_done`, staying in active state or
|
||||
// entering lightsleep is determined by esp_pm configuration.
|
||||
if (esp_vbat_wait_battery_charge_done(CONFIG_EXAMPLE_VBAT_CHARGING_DONE_CHECK_PERIOD * 1000 / portTICK_PERIOD_MS) == ESP_OK) {
|
||||
printf("Battery charging done!\n");
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
if (esp_vbat_get_battery_state() == ESP_VBAT_STATE_LOWBATTERY) {
|
||||
printf("Battery is low, VBAT power will not be used during deep sleep!\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_EXAMPLE_WAITING_BATTERY_CHARGING_IN_LIGHT_SLEEP
|
||||
// Disable auto-lightsleep configured timer wakeup source here.
|
||||
pm_config.light_sleep_enable = false;
|
||||
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
|
||||
#endif
|
||||
|
||||
// enter deep sleep
|
||||
printf("Current RTC Time: %lld (us)\n", esp_rtc_get_time_us());
|
||||
printf("Entering deep sleep\n");
|
||||
sleep_result = esp_deep_sleep_try_to_start();
|
||||
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||
printf("Failed to enter deepsleep, please check wakeup source setting and state!\n");
|
||||
} while (sleep_result != ESP_OK);
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY=n
|
||||
CONFIG_ESP_VBAT_WAKEUP_CHIP_ON_VBAT_BROWNOUT=y
|
@ -0,0 +1 @@
|
||||
CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY=y
|
2
examples/lowpower/vbat/sdkconfig.defaults
Normal file
2
examples/lowpower/vbat/sdkconfig.defaults
Normal file
@ -0,0 +1,2 @@
|
||||
# Generic config
|
||||
CONFIG_ESP_VBAT_INIT_AUTO=y
|
Reference in New Issue
Block a user