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:
@@ -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;
|
||||
|
Reference in New Issue
Block a user