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:
Wu Zheng Hui
2025-05-09 10:35:18 +08:00
32 changed files with 557 additions and 41 deletions

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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

View File

@ -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(&param_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(&param_default, &power_default, sleep_flags, adjustment, slowclk_period, fastclk_period);
return config;
}

View File

@ -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
}

View File

@ -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;
};

View File

@ -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);

View File

@ -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)

View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
/**

View File

@ -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);
}

View File

@ -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;

View 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

View 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.

View 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)

View 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
...
```

View 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})

View 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

View 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);
}

View File

@ -0,0 +1,2 @@
CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY=n
CONFIG_ESP_VBAT_WAKEUP_CHIP_ON_VBAT_BROWNOUT=y

View File

@ -0,0 +1 @@
CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY=y

View File

@ -0,0 +1,2 @@
# Generic config
CONFIG_ESP_VBAT_INIT_AUTO=y