feat(esp_hw_support): support switch to VBAT power supply in deepsleep on esp32p4

This commit is contained in:
wuzhenghui
2025-02-24 14:37:24 +08:00
parent 190eca39b2
commit 9b5944b795
21 changed files with 317 additions and 41 deletions

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;