Merge branch 'feat/support_lp_debug_probe' into 'master'

feat(esp_hw_support): support esp32p4 VDD_BAT sleep mode main power control by LP_Probe

Closes IDF-15413

See merge request espressif/esp-idf!46509
This commit is contained in:
Jiang Jiang Jian
2026-05-08 17:23:15 +08:00
20 changed files with 399 additions and 9025 deletions
@@ -634,12 +634,36 @@ FORCE_INLINE_ATTR uint32_t pmu_ll_lp_get_digital_power_down_wait_cycle(pmu_dev_t
FORCE_INLINE_ATTR void pmu_ll_lp_set_analog_wait_target_cycle(pmu_dev_t *hw, uint32_t slow_clk_cycle) FORCE_INLINE_ATTR void pmu_ll_lp_set_analog_wait_target_cycle(pmu_dev_t *hw, uint32_t slow_clk_cycle)
{ {
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
HAL_ASSERT(slow_clk_cycle <= 1020);
uint32_t target = 0, expand = 0;
if (slow_clk_cycle <= 255) {
target = slow_clk_cycle;
expand = 0;
} else if (slow_clk_cycle <= 510) {
expand = 1;
target = (slow_clk_cycle + 1) >> 1;
} else if (slow_clk_cycle <= 1020) {
expand = 2;
target = (slow_clk_cycle + 3) >> 2;
}
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->wakeup.cntl5, lp_ana_wait_target, target);
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->wakeup.cntl5, lp_ana_wait_target_expand, expand);
#else
HAL_ASSERT(slow_clk_cycle <= 255);
HAL_FORCE_MODIFY_U32_REG_FIELD(hw->wakeup.cntl5, lp_ana_wait_target, slow_clk_cycle); HAL_FORCE_MODIFY_U32_REG_FIELD(hw->wakeup.cntl5, lp_ana_wait_target, slow_clk_cycle);
#endif
} }
FORCE_INLINE_ATTR uint32_t pmu_ll_lp_get_analog_wait_target_cycle(pmu_dev_t *hw) FORCE_INLINE_ATTR uint32_t pmu_ll_lp_get_analog_wait_target_cycle(pmu_dev_t *hw)
{ {
#if HAL_CONFIG(CHIP_SUPPORT_MIN_REV) >= 300
uint32_t target = HAL_FORCE_READ_U32_REG_FIELD(hw->wakeup.cntl5, lp_ana_wait_target);
uint32_t expand = HAL_FORCE_READ_U32_REG_FIELD(hw->wakeup.cntl5, lp_ana_wait_target_expand);
return target << expand;
#else
return HAL_FORCE_READ_U32_REG_FIELD(hw->wakeup.cntl5, lp_ana_wait_target); return HAL_FORCE_READ_U32_REG_FIELD(hw->wakeup.cntl5, lp_ana_wait_target);
#endif
} }
FORCE_INLINE_ATTR void pmu_ll_set_xtal_stable_wait_cycle(pmu_dev_t *hw, uint32_t cycle) FORCE_INLINE_ATTR void pmu_ll_set_xtal_stable_wait_cycle(pmu_dev_t *hw, uint32_t cycle)
@@ -28,6 +28,8 @@ typedef enum {
#define VBAT_LL_CHARGER_MASK (BIT(27)|BIT(28)) #define VBAT_LL_CHARGER_MASK (BIT(27)|BIT(28))
#define VBAT_LL_DETECT_MASK (BIT(30)) #define VBAT_LL_DETECT_MASK (BIT(30))
#define VBAT_LL_LP_SLEEP_PROBE_ID (9)
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -16,6 +16,10 @@
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "soc/debug_probe_periph.h" #include "soc/debug_probe_periph.h"
#include "soc/io_mux_reg.h" #include "soc/io_mux_reg.h"
#if SOC_DEBUG_PROBE_NUM_UNIT >= 2 && SOC_LP_GPIO_MATRIX_SUPPORTED
#include "driver/rtc_io.h"
#include "driver/lp_io.h"
#endif
#include "hal/debug_probe_ll.h" #include "hal/debug_probe_ll.h"
#include "esp_private/debug_probe.h" #include "esp_private/debug_probe.h"
#include "esp_private/gpio.h" #include "esp_private/gpio.h"
@@ -28,9 +32,10 @@ typedef struct debug_probe_unit_t debug_probe_unit_t;
typedef struct debug_probe_channel_t debug_probe_channel_t; typedef struct debug_probe_channel_t debug_probe_channel_t;
struct debug_probe_unit_t { struct debug_probe_unit_t {
int unit_id; // unit id debug_probe_unit_id_t unit_id; // unit id
debug_probe_channel_t *channels[DEBUG_PROBE_LL_CHANNELS_PER_UNIT]; // channels installed in this unit debug_probe_channel_t *channels[DEBUG_PROBE_LL_CHANNELS_PER_UNIT]; // channels installed in this unit
uint64_t pin_bit_mask; // bit-mask of the GPIOs used by this unit uint64_t pin_bit_mask; // bit-mask of the GPIOs used by this unit
uint32_t probe_out_inv_mask; // bit i set = invert i-th probe signal
}; };
struct debug_probe_channel_t { struct debug_probe_channel_t {
@@ -45,9 +50,31 @@ typedef struct debug_probe_platform_t {
static debug_probe_platform_t s_platform; // singleton platform static debug_probe_platform_t s_platform; // singleton platform
static esp_err_t connect_probe_out_to_pin(debug_probe_unit_id_t unit_id, int pin, unsigned sig_idx, bool out_inv)
{
if (unit_id == DEBUG_PROBE_UNIT_HP) {
esp_err_t ret = gpio_func_sel((gpio_num_t)pin, PIN_FUNC_GPIO);
if (ret != ESP_OK) {
return ret;
}
esp_rom_gpio_connect_out_signal(pin, debug_probe_periph_signals.units[0].out_sig[sig_idx], out_inv, false);
return ESP_OK;
}
#if SOC_DEBUG_PROBE_NUM_UNIT >= 2
if (!rtc_gpio_is_valid_gpio((gpio_num_t)pin)) {
return ESP_ERR_INVALID_ARG;
}
esp_err_t ret = rtc_gpio_init((gpio_num_t)pin);
return (ret == ESP_OK) ? lp_gpio_connect_out_signal((gpio_num_t)pin,
debug_probe_periph_signals.units[1].out_sig[sig_idx], out_inv, false) : ret;
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
}
static esp_err_t debug_probe_unit_destroy(debug_probe_unit_t *unit) static esp_err_t debug_probe_unit_destroy(debug_probe_unit_t *unit)
{ {
int unit_id = unit->unit_id; debug_probe_unit_id_t unit_id = unit->unit_id;
// remove the unit from the platform // remove the unit from the platform
_lock_acquire(&s_platform.mutex); _lock_acquire(&s_platform.mutex);
@@ -57,6 +84,13 @@ static esp_err_t debug_probe_unit_destroy(debug_probe_unit_t *unit)
// disable the probe output // disable the probe output
debug_probe_ll_enable_unit(unit_id, false); debug_probe_ll_enable_unit(unit_id, false);
esp_gpio_revoke(unit->pin_bit_mask); esp_gpio_revoke(unit->pin_bit_mask);
#if SOC_DEBUG_PROBE_NUM_UNIT >= 2
if (unit_id == DEBUG_PROBE_UNIT_LP) {
for (uint64_t m = unit->pin_bit_mask; m; m &= m - 1) {
rtc_gpio_deinit((gpio_num_t)(__builtin_ffsll(m) - 1));
}
}
#endif
// free the memory // free the memory
free(unit); free(unit);
return ESP_OK; return ESP_OK;
@@ -65,21 +99,20 @@ static esp_err_t debug_probe_unit_destroy(debug_probe_unit_t *unit)
esp_err_t debug_probe_new_unit(const debug_probe_unit_config_t *config, debug_probe_unit_handle_t *out_handle) esp_err_t debug_probe_new_unit(const debug_probe_unit_config_t *config, debug_probe_unit_handle_t *out_handle)
{ {
debug_probe_unit_t *unit = NULL; debug_probe_unit_t *unit = NULL;
int unit_id = -1; debug_probe_unit_id_t unit_id = config->unit_id;
ESP_RETURN_ON_FALSE(config && out_handle, ESP_ERR_INVALID_ARG, TAG, "invalid args"); ESP_RETURN_ON_FALSE(config && out_handle && unit_id < SOC_DEBUG_PROBE_NUM_UNIT, ESP_ERR_INVALID_ARG, TAG, "invalid args");
// search for a free unit slot /* Check the requested unit slot is free (unit_id from config->unit_id) */
_lock_acquire(&s_platform.mutex); _lock_acquire(&s_platform.mutex);
for (int i = 0; i < SOC_DEBUG_PROBE_NUM_UNIT; i++) { if (s_platform.units[unit_id] != NULL) {
if (s_platform.units[i] == NULL) { _lock_release(&s_platform.mutex);
unit_id = i; ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_FOUND, TAG, "unit slot in use");
unit = calloc(1, sizeof(debug_probe_unit_t));
s_platform.units[i] = unit;
break;
} }
unit = calloc(1, sizeof(debug_probe_unit_t));
if (unit) {
s_platform.units[unit_id] = unit;
} }
_lock_release(&s_platform.mutex); _lock_release(&s_platform.mutex);
ESP_RETURN_ON_FALSE(unit_id >= 0, ESP_ERR_NOT_FOUND, TAG, "no free unit slot");
ESP_RETURN_ON_FALSE(unit, ESP_ERR_NO_MEM, TAG, "no mem for unit"); ESP_RETURN_ON_FALSE(unit, ESP_ERR_NO_MEM, TAG, "no mem for unit");
unit->unit_id = unit_id; unit->unit_id = unit_id;
@@ -97,21 +130,32 @@ esp_err_t debug_probe_new_unit(const debug_probe_unit_config_t *config, debug_pr
} }
// connect the probe output signals to the GPIOs // connect the probe output signals to the GPIOs
esp_err_t ret = ESP_OK;
for (int i = 0; i < SOC_DEBUG_PROBE_MAX_OUTPUT_WIDTH; i++) { for (int i = 0; i < SOC_DEBUG_PROBE_MAX_OUTPUT_WIDTH; i++) {
if (config->probe_out_gpio_nums[i] >= 0) { int pin = config->probe_out_gpio_nums[i];
gpio_func_sel(config->probe_out_gpio_nums[i], PIN_FUNC_GPIO); if (pin >= 0) {
esp_rom_gpio_connect_out_signal(config->probe_out_gpio_nums[i], bool out_inv = !!(config->probe_out_inv_mask & BIT(i));
debug_probe_periph_signals.units[unit_id].out_sig[i], ret = connect_probe_out_to_pin(unit_id, pin, (unsigned)i, out_inv);
false, false); if (ret != ESP_OK) {
ESP_LOGE(TAG, "Probe out pin %d failed: %s", pin, esp_err_to_name(ret));
break;
} }
} }
}
if (ret != ESP_OK) {
unit->pin_bit_mask = pin_bit_mask; unit->pin_bit_mask = pin_bit_mask;
(void)debug_probe_unit_destroy(unit);
return ret;
}
unit->pin_bit_mask = pin_bit_mask;
unit->probe_out_inv_mask = config->probe_out_inv_mask;
// enable the probe unit // enable the probe unit
debug_probe_ll_enable_unit(unit_id, true); debug_probe_ll_enable_unit(unit_id, true);
*out_handle = unit; *out_handle = unit;
return ESP_OK; return ret;
} }
esp_err_t debug_probe_del_unit(debug_probe_unit_handle_t unit) esp_err_t debug_probe_del_unit(debug_probe_unit_handle_t unit)
@@ -134,7 +178,7 @@ esp_err_t debug_probe_del_unit(debug_probe_unit_handle_t unit)
static esp_err_t debug_probe_channel_destroy(debug_probe_channel_t *chan) static esp_err_t debug_probe_channel_destroy(debug_probe_channel_t *chan)
{ {
debug_probe_unit_t *unit = chan->unit; debug_probe_unit_t *unit = chan->unit;
int unit_id = unit->unit_id; debug_probe_unit_id_t unit_id = unit->unit_id;
int chan_id = chan->chan_id; int chan_id = chan->chan_id;
// remove the channel from the unit // remove the channel from the unit
@@ -154,7 +198,7 @@ esp_err_t debug_probe_new_channel(debug_probe_unit_handle_t unit, const debug_pr
debug_probe_channel_t *chan = NULL; debug_probe_channel_t *chan = NULL;
int chan_id = -1; int chan_id = -1;
ESP_RETURN_ON_FALSE(unit && config && out_handle, ESP_ERR_INVALID_ARG, TAG, "invalid args"); ESP_RETURN_ON_FALSE(unit && config && out_handle, ESP_ERR_INVALID_ARG, TAG, "invalid args");
int unit_id = unit->unit_id; debug_probe_unit_id_t unit_id = unit->unit_id;
// search for a free channel slot // search for a free channel slot
_lock_acquire(&s_platform.mutex); _lock_acquire(&s_platform.mutex);
@@ -173,7 +217,10 @@ esp_err_t debug_probe_new_channel(debug_probe_unit_handle_t unit, const debug_pr
chan->unit = unit; chan->unit = unit;
// one channel can only monitor one target module // one channel can only monitor one target module
debug_probe_ll_channel_set_target_module(unit_id, chan_id, config->target_module); uint8_t target_module = (unit_id == DEBUG_PROBE_UNIT_HP)
? (uint8_t)config->target_module.hp_target
: (uint8_t)config->target_module.lp_target;
debug_probe_ll_channel_set_target_module(unit_id, chan_id, target_module);
debug_probe_ll_enable_channel(unit_id, chan_id, true); debug_probe_ll_enable_channel(unit_id, chan_id, true);
*out_handle = chan; *out_handle = chan;
@@ -189,9 +236,10 @@ esp_err_t debug_probe_del_channel(debug_probe_channel_handle_t chan)
esp_err_t debug_probe_chan_add_signal_by_byte(debug_probe_channel_handle_t chan, uint8_t byte_idx, uint8_t sig_group) esp_err_t debug_probe_chan_add_signal_by_byte(debug_probe_channel_handle_t chan, uint8_t byte_idx, uint8_t sig_group)
{ {
ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid args"); ESP_RETURN_ON_FALSE(chan, ESP_ERR_INVALID_ARG, TAG, "invalid args");
ESP_RETURN_ON_FALSE(byte_idx < 4, ESP_ERR_INVALID_ARG, TAG, "byte_idx out of range");
ESP_RETURN_ON_FALSE(sig_group < 16, ESP_ERR_INVALID_ARG, TAG, "sig_group out of range");
debug_probe_unit_t *unit = chan->unit; debug_probe_unit_t *unit = chan->unit;
ESP_RETURN_ON_FALSE(byte_idx < ((unit->unit_id == DEBUG_PROBE_UNIT_LP) ? 2 : 4),
ESP_ERR_INVALID_ARG, TAG, "byte_idx out of range");
ESP_RETURN_ON_FALSE(sig_group < 16, ESP_ERR_INVALID_ARG, TAG, "sig_group out of range");
debug_probe_ll_channel_add_signal_group(unit->unit_id, chan->chan_id, byte_idx, sig_group); debug_probe_ll_channel_add_signal_group(unit->unit_id, chan->chan_id, byte_idx, sig_group);
return ESP_OK; return ESP_OK;
} }
@@ -199,18 +247,25 @@ esp_err_t debug_probe_chan_add_signal_by_byte(debug_probe_channel_handle_t chan,
esp_err_t debug_probe_unit_merge16(debug_probe_unit_handle_t unit, debug_probe_channel_handle_t chan0, debug_probe_split_u16_t split_of_chan0, esp_err_t debug_probe_unit_merge16(debug_probe_unit_handle_t unit, debug_probe_channel_handle_t chan0, debug_probe_split_u16_t split_of_chan0,
debug_probe_channel_handle_t chan1, debug_probe_split_u16_t split_of_chan1) debug_probe_channel_handle_t chan1, debug_probe_split_u16_t split_of_chan1)
{ {
ESP_RETURN_ON_FALSE(unit && chan0 && chan1, ESP_ERR_INVALID_ARG, TAG, "invalid args"); ESP_RETURN_ON_FALSE(unit && chan0, ESP_ERR_INVALID_ARG, TAG, "invalid args");
ESP_RETURN_ON_FALSE(chan0->unit == unit && chan1->unit == unit, ESP_ERR_INVALID_ARG, TAG, "chan not belong to unit"); ESP_RETURN_ON_FALSE(chan0->unit == unit, ESP_ERR_INVALID_ARG, TAG, "chan0 not belong to unit");
int unit_id = unit->unit_id; if (chan1 != NULL) {
ESP_RETURN_ON_FALSE(chan1->unit == unit, ESP_ERR_INVALID_ARG, TAG, "chan1 not belong to unit");
}
debug_probe_unit_id_t unit_id = unit->unit_id;
debug_probe_ll_set_lower16_output(unit_id, chan0->chan_id, split_of_chan0); debug_probe_ll_set_lower16_output(unit_id, chan0->chan_id, split_of_chan0);
if (chan1 != NULL) {
debug_probe_ll_set_upper16_output(unit_id, chan1->chan_id, split_of_chan1); debug_probe_ll_set_upper16_output(unit_id, chan1->chan_id, split_of_chan1);
}
return ESP_OK; return ESP_OK;
} }
esp_err_t debug_probe_unit_read(debug_probe_unit_handle_t unit, uint32_t *value) esp_err_t debug_probe_unit_read(debug_probe_unit_handle_t unit, uint32_t *value)
{ {
ESP_RETURN_ON_FALSE(unit && value, ESP_ERR_INVALID_ARG, TAG, "invalid args"); ESP_RETURN_ON_FALSE(unit && value, ESP_ERR_INVALID_ARG, TAG, "invalid args");
int unit_id = unit->unit_id; debug_probe_unit_id_t unit_id = unit->unit_id;
*value = debug_probe_ll_read_output(unit_id); uint32_t raw = debug_probe_ll_read_output(unit_id);
*value = raw ^ unit->probe_out_inv_mask;
return ESP_OK; return ESP_OK;
} }
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -30,7 +30,9 @@ typedef struct debug_probe_channel_t *debug_probe_channel_handle_t;
* @brief Configuration for a debug probe unit * @brief Configuration for a debug probe unit
*/ */
typedef struct { typedef struct {
debug_probe_unit_id_t unit_id; ///< DEBUG_PROBE_UNIT_HP or DEBUG_PROBE_UNIT_LP
gpio_num_t probe_out_gpio_nums[DEBUG_PROBE_MAX_OUTPUT_WIDTH]; ///< GPIO numbers for probe output gpio_num_t probe_out_gpio_nums[DEBUG_PROBE_MAX_OUTPUT_WIDTH]; ///< GPIO numbers for probe output
uint32_t probe_out_inv_mask; ///< Bit mask: bit i set = invert output for i-th probe signal (default 0 = no inversion)
} debug_probe_unit_config_t; } debug_probe_unit_config_t;
/** /**
@@ -60,9 +62,13 @@ esp_err_t debug_probe_del_unit(debug_probe_unit_handle_t unit);
/** /**
* @brief Configuration for a debug probe channel * @brief Configuration for a debug probe channel
* @note Use hp_target for HP unit, lp_target for LP unit
*/ */
typedef struct { typedef struct {
debug_probe_target_t target_module; ///< Target module of the debug probe channel union {
debug_probe_target_t hp_target; ///< Target when unit is HP
debug_probe_target_lp_t lp_target; ///< Target when unit is LP
} target_module;
} debug_probe_channel_config_t; } debug_probe_channel_config_t;
/** /**
@@ -103,7 +109,7 @@ esp_err_t debug_probe_del_channel(debug_probe_channel_handle_t chan);
* @note You can save up to 32 signals in a channel, but in the end, only the part of them (e.g. upper or lower 16 signals) can be output to the GPIO pads. * @note You can save up to 32 signals in a channel, but in the end, only the part of them (e.g. upper or lower 16 signals) can be output to the GPIO pads.
* *
* @param[in] chan Handle of the debug probe channel * @param[in] chan Handle of the debug probe channel
* @param[in] byte_idx Byte index of the signals, ranges from 0 to 3 * @param[in] byte_idx Byte index (HP: 0-3, LP: 0-1)
* @param[in] sig_group Signal group of the signal, ranges from 0 to 15 * @param[in] sig_group Signal group of the signal, ranges from 0 to 15
* @return * @return
* - ESP_OK on success * - ESP_OK on success
@@ -128,8 +134,8 @@ esp_err_t debug_probe_chan_add_signal_by_byte(debug_probe_channel_handle_t chan,
* @param[in] unit Handle of the debug probe unit * @param[in] unit Handle of the debug probe unit
* @param[in] chan0 Handle of the debug probe channel 0, whose output will be merged to the lower 16 signals of the unit output * @param[in] chan0 Handle of the debug probe channel 0, whose output will be merged to the lower 16 signals of the unit output
* @param[in] split_of_chan0 Part of the channel 0 output to be merged * @param[in] split_of_chan0 Part of the channel 0 output to be merged
* @param[in] chan1 Handle of the debug probe channel 1, whose output will be merged to the upper 16 signals of the unit output * @param[in] chan1 Handle of the debug probe channel 1 for upper 16 bits; can be NULL to ignore upper16
* @param[in] split_of_chan1 Part of the channel 1 output to be merged * @param[in] split_of_chan1 Part of the channel 1 output (ignored when chan1 is NULL)
* @return * @return
* - ESP_OK on success * - ESP_OK on success
* - ESP_ERR_INVALID_ARG if the parameters are invalid * - ESP_ERR_INVALID_ARG if the parameters are invalid
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -21,10 +21,14 @@ extern "C" {
#if SOC_DEBUG_PROBE_SUPPORTED #if SOC_DEBUG_PROBE_SUPPORTED
#define DEBUG_PROBE_MAX_OUTPUT_WIDTH SOC_DEBUG_PROBE_MAX_OUTPUT_WIDTH #define DEBUG_PROBE_MAX_OUTPUT_WIDTH SOC_DEBUG_PROBE_MAX_OUTPUT_WIDTH
#define DEBUG_PROBE_GPIO_NUMS_NONE { [0 ... (SOC_DEBUG_PROBE_MAX_OUTPUT_WIDTH - 1)] = -1 }
typedef soc_debug_probe_target_t debug_probe_target_t; typedef soc_debug_probe_target_t debug_probe_target_t;
typedef soc_debug_probe_target_lp_t debug_probe_target_lp_t;
#else #else
#define DEBUG_PROBE_MAX_OUTPUT_WIDTH 16 #define DEBUG_PROBE_MAX_OUTPUT_WIDTH 16
#define DEBUG_PROBE_GPIO_NUMS_NONE { [0 ... 15] = -1 }
typedef int debug_probe_target_t; typedef int debug_probe_target_t;
typedef int debug_probe_target_lp_t;
#endif #endif
#ifdef __cplusplus #ifdef __cplusplus
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -237,7 +237,7 @@ typedef struct {
typedef struct { typedef struct {
uint16_t digital_power_supply_wait_cycle; uint16_t digital_power_supply_wait_cycle;
uint8_t min_slp_slow_clk_cycle; uint8_t min_slp_slow_clk_cycle;
uint8_t analog_wait_target_cycle; uint16_t analog_wait_target_cycle;
uint8_t digital_power_down_wait_cycle; uint8_t digital_power_down_wait_cycle;
uint8_t digital_power_up_wait_cycle; uint8_t digital_power_up_wait_cycle;
} pmu_lp_param_t; } pmu_lp_param_t;
@@ -526,11 +526,21 @@ typedef struct pmu_sleep_machine_constant {
// DCDC startup, which saves more power compared to waiting in the Active state. // DCDC startup, which saves more power compared to waiting in the Active state.
#define PMU_HP_ANA_WAIT_TIME_PU_TOP_US (PMU_HP_ANA_WAIT_TIME_PD_TOP_US + PMU_REGDMA_S2A_WORK_TIME_US) // 945 #define PMU_HP_ANA_WAIT_TIME_PU_TOP_US (PMU_HP_ANA_WAIT_TIME_PD_TOP_US + PMU_REGDMA_S2A_WORK_TIME_US) // 945
#ifndef MAX
#define MAX(a, b) (((a) > (b)) ? (a) : (b))
#endif
#if CONFIG_ESP_VBAT_CONTROL_MAIN_POWER_IN_DSLP
#define PMU_LP_ANA_WAIT_TIME_US MAX(154, CONFIG_ESP_VBAT_MAIN_POWER_WAKEUP_ADVANCE_US)
#else
#define PMU_LP_ANA_WAIT_TIME_US 154
#endif
#define PMU_SLEEP_MC_DEFAULT() { \ #define PMU_SLEEP_MC_DEFAULT() { \
.lp = { \ .lp = { \
.min_slp_time_us = 450, \ .min_slp_time_us = 450, \
.wakeup_wait_cycle = 4, \ .wakeup_wait_cycle = 4, \
.analog_wait_time_us = 154, \ .analog_wait_time_us = PMU_LP_ANA_WAIT_TIME_US, \
.xtal_wait_stable_time_us = 250, \ .xtal_wait_stable_time_us = 250, \
.clk_switch_cycle = 1, \ .clk_switch_cycle = 1, \
.clk_power_on_wait_cycle = 1, \ .clk_power_on_wait_cycle = 1, \
@@ -200,6 +200,48 @@ menu "Power Supplier"
help help
The resistor value of charger circuit. The resistor value of charger circuit.
config ESP_VBAT_CONTROL_MAIN_POWER_IN_DSLP
bool "Control main power (ESP_3V3) enable via GPIO when VBAT powers deep sleep"
default n
depends on ESP_VBAT_INIT_AUTO && SOC_MAIN_POWER_CONTROL_SUPPORTED
help
When enabled, after the chip enters deep sleep and switches to VDD_BAT (VBAT)
power, ESP_VBAT_MAIN_POWER_CTRL_PIN is used to disable the main power (ESP_3V3)
enable. The main power is re-enabled in advance before wakeup.
config ESP_VBAT_MAIN_POWER_CTRL_PIN
int "GPIO for main power (ESP_3V3) enable"
default 2
range 0 3 if !RTC_CLK_SRC_EXT_CRYS
range 2 3 if RTC_CLK_SRC_EXT_CRYS
depends on ESP_VBAT_CONTROL_MAIN_POWER_IN_DSLP
help
RTC GPIO used to enable/disable the main power rail (ESP_3V3) when VBAT is active.
config ESP_VBAT_MAIN_POWER_CTRL_PIN_INV
bool "Invert main power control pin output (Output high to enable power by default)"
default n
depends on ESP_VBAT_CONTROL_MAIN_POWER_IN_DSLP
help
When enabled, the ESP_VBAT_MAIN_POWER_CTRL_PIN logic for the main power control
pin is inverted. (n: output high to enable power; y = output low to enable power).
config ESP_VBAT_MAIN_POWER_WAKEUP_ADVANCE_US
int "Main power enable advance time before wakeup (us)"
default 1000
range 0 1500 if !RTC_CLK_SRC_EXT_CRYS && ESP32P4_SELECTS_REV_LESS_V3
range 0 7500 if RTC_CLK_SRC_EXT_CRYS && ESP32P4_SELECTS_REV_LESS_V3
range 0 6000 if !RTC_CLK_SRC_EXT_CRYS && !ESP32P4_SELECTS_REV_LESS_V3
range 0 30000 if RTC_CLK_SRC_EXT_CRYS && !ESP32P4_SELECTS_REV_LESS_V3
depends on ESP_VBAT_CONTROL_MAIN_POWER_IN_DSLP
help
Advance time in microseconds to assert main power (ESP_3V3) enable before the
chip starts the wakeup sequence and switches to main voltage supply. The delay
allows the external main power rail to reach target voltage before chip exits
deep sleep. Set this value to be greater than or equal to your main power
supply's startup/settling time (from enable assertion to stable output voltage).
If too small, the main rail may not be ready when the chip wakes, which can
cause brownout or unstable boot.
endmenu endmenu
endmenu endmenu
+30 -2
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -15,6 +15,7 @@
#include "hal/vbat_hal.h" #include "hal/vbat_hal.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "esp_private/debug_probe.h"
#include "esp_private/esp_sleep_internal.h" #include "esp_private/esp_sleep_internal.h"
#include "esp_private/startup_internal.h" #include "esp_private/startup_internal.h"
#include "esp_private/rtc_ctrl.h" #include "esp_private/rtc_ctrl.h"
@@ -46,6 +47,10 @@
#define VBAT_CHARGER_HYSTERESIS_THRESHOLD_HIGH (VBAT_CHARGER_HYSTERESIS_THRESHOLD_LOW + (VBAT_CHARGER_FILTER_TIME_US * SOC_CLK_RC_FAST_FREQ_APPROX) / MHZ) #define VBAT_CHARGER_HYSTERESIS_THRESHOLD_HIGH (VBAT_CHARGER_HYSTERESIS_THRESHOLD_LOW + (VBAT_CHARGER_FILTER_TIME_US * SOC_CLK_RC_FAST_FREQ_APPROX) / MHZ)
#endif #endif
#if CONFIG_ESP_VBAT_CONTROL_MAIN_POWER_IN_DSLP
#define ESP_3V3_CTRL_PIN CONFIG_ESP_VBAT_MAIN_POWER_CTRL_PIN
#endif
#if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY #if CONFIG_ESP_VBAT_USE_RECHARGEABLE_BATTERY
#if (VBAT_CHARGER_RESISTOR_VALUE < 1000 || VBAT_CHARGER_RESISTOR_VALUE > 4500 || VBAT_CHARGER_RESISTOR_VALUE % 500 != 0) #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." #error "vbat charger resistor (ESP_VBAT_CHARGER_CIRCUIT_RESISTOR_VAL) must be between 1000 and 4500 ohms and must be a multiple of 500."
@@ -54,7 +59,7 @@
#if (VBAT_BROWNOUT_DET_LVL >= VBAT_CHARGE_DET_LVL_LOW) #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" #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
#endif #endif // CONFIG_ESP_VBAT_INIT_AUTO
ESP_LOG_ATTR_TAG(TAG, "VBAT"); ESP_LOG_ATTR_TAG(TAG, "VBAT");
#endif #endif
@@ -171,6 +176,29 @@ esp_err_t esp_vbat_init(void)
esp_sleep_sub_mode_force_disable(ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE); 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_sub_mode_config(ESP_SLEEP_VBAT_POWER_DEEPSLEEP_MODE, true);
esp_sleep_enable_vbat_under_volt_wakeup(); esp_sleep_enable_vbat_under_volt_wakeup();
#if CONFIG_ESP_VBAT_CONTROL_MAIN_POWER_IN_DSLP
if (esp_reset_reason() != ESP_RST_DEEPSLEEP) {
debug_probe_unit_handle_t lp_probe_hdl;
debug_probe_unit_config_t unit_config = {
.unit_id = DEBUG_PROBE_UNIT_LP,
.probe_out_gpio_nums = DEBUG_PROBE_GPIO_NUMS_NONE,
};
debug_probe_channel_config_t chan_config = {0};
debug_probe_channel_handle_t lp_probe_channel_hdl;
chan_config.target_module.lp_target = DEBUG_PROBE_TARGET_LP_PMU;
unit_config.probe_out_gpio_nums[VBAT_LL_LP_SLEEP_PROBE_ID] = ESP_3V3_CTRL_PIN;
#if !CONFIG_ESP_VBAT_MAIN_POWER_CTRL_PIN_INV
unit_config.probe_out_inv_mask |= BIT(VBAT_LL_LP_SLEEP_PROBE_ID);
#endif
ESP_ERROR_CHECK(debug_probe_new_unit(&unit_config, &lp_probe_hdl));
ESP_ERROR_CHECK(debug_probe_new_channel(lp_probe_hdl, &chan_config, &lp_probe_channel_hdl));
ESP_ERROR_CHECK(debug_probe_chan_add_signal_by_byte(lp_probe_channel_hdl, 1, DEBUG_PROBE_TARGET_LP_PMU));
ESP_ERROR_CHECK(debug_probe_unit_merge16(lp_probe_hdl, lp_probe_channel_hdl, DEBUG_PROBE_SPLIT_UPPER16, NULL, 0));
}
// The LP Probe & LP IOMUX pathways depend on the LP_PERIPH power domain.
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif
return ESP_OK; return ESP_OK;
} }
#endif #endif
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -15,21 +15,29 @@
TEST_CASE("debug probe install & uninstall", "[debug_probe]") TEST_CASE("debug probe install & uninstall", "[debug_probe]")
{ {
debug_probe_unit_config_t unit_config = {
.probe_out_gpio_nums = {0},
};
debug_probe_unit_handle_t probe_units[SOC_DEBUG_PROBE_NUM_UNIT] = {0}; debug_probe_unit_handle_t probe_units[SOC_DEBUG_PROBE_NUM_UNIT] = {0};
printf("install debug probe exhaustively\r\n"); printf("install debug probe exhaustively\r\n");
for (int i = 0; i < SOC_DEBUG_PROBE_NUM_UNIT; i++) { for (int i = 0; i < SOC_DEBUG_PROBE_NUM_UNIT; i++) {
debug_probe_unit_config_t unit_config = {
.unit_id = (debug_probe_unit_id_t)i,
.probe_out_gpio_nums = {0},
};
TEST_ESP_OK(debug_probe_new_unit(&unit_config, &probe_units[i])); TEST_ESP_OK(debug_probe_new_unit(&unit_config, &probe_units[i]));
} }
debug_probe_unit_config_t unit_config = {
.unit_id = DEBUG_PROBE_UNIT_HP,
.probe_out_gpio_nums = {0},
};
TEST_ESP_ERR(ESP_ERR_NOT_FOUND, debug_probe_new_unit(&unit_config, &probe_units[0])); TEST_ESP_ERR(ESP_ERR_NOT_FOUND, debug_probe_new_unit(&unit_config, &probe_units[0]));
debug_probe_channel_config_t chan_config = {
.target_module = 1,
};
debug_probe_channel_handle_t probe_chans[SOC_DEBUG_PROBE_NUM_UNIT][DEBUG_PROBE_LL_CHANNELS_PER_UNIT] = {0}; debug_probe_channel_handle_t probe_chans[SOC_DEBUG_PROBE_NUM_UNIT][DEBUG_PROBE_LL_CHANNELS_PER_UNIT] = {0};
for (int i = 0; i < SOC_DEBUG_PROBE_NUM_UNIT; i++) { for (int i = 0; i < SOC_DEBUG_PROBE_NUM_UNIT; i++) {
debug_probe_channel_config_t chan_config = {0};
if (i == 0) {
chan_config.target_module.hp_target = DEBUG_PROBE_TARGET_AXI_GDMA;
} else {
chan_config.target_module.lp_target = DEBUG_PROBE_TARGET_LP_PMU;
}
for (int j = 0; j < DEBUG_PROBE_LL_CHANNELS_PER_UNIT; j++) { for (int j = 0; j < DEBUG_PROBE_LL_CHANNELS_PER_UNIT; j++) {
TEST_ESP_OK(debug_probe_new_channel(probe_units[i], &chan_config, &probe_chans[i][j])); TEST_ESP_OK(debug_probe_new_channel(probe_units[i], &chan_config, &probe_chans[i][j]));
} }
@@ -47,10 +55,8 @@ TEST_CASE("debug probe install & uninstall", "[debug_probe]")
TEST_CASE("debug probe read", "[debug_probe]") TEST_CASE("debug probe read", "[debug_probe]")
{ {
debug_probe_unit_config_t unit_config = { debug_probe_unit_config_t unit_config = {
.probe_out_gpio_nums = { .unit_id = DEBUG_PROBE_UNIT_HP,
-1, -1, -1, -1, -1, -1, -1, -1, .probe_out_gpio_nums = DEBUG_PROBE_GPIO_NUMS_NONE,
-1, -1, -1, -1, -1, -1, -1, -1,
}
}; };
debug_probe_unit_handle_t probe_unit = NULL; debug_probe_unit_handle_t probe_unit = NULL;
TEST_ESP_OK(debug_probe_new_unit(&unit_config, &probe_unit)); TEST_ESP_OK(debug_probe_new_unit(&unit_config, &probe_unit));
@@ -58,9 +64,8 @@ TEST_CASE("debug probe read", "[debug_probe]")
// allocate two channels from the unit, each channel monitor different target module // allocate two channels from the unit, each channel monitor different target module
debug_probe_channel_handle_t probe_chans[2] = {0}; debug_probe_channel_handle_t probe_chans[2] = {0};
for (int i = 0; i < 2; i++) { for (int i = 0; i < 2; i++) {
debug_probe_channel_config_t chan_config = { debug_probe_channel_config_t chan_config = {0};
.target_module = i + 1, chan_config.target_module.hp_target = (debug_probe_target_t)(i + 1);
};
TEST_ESP_OK(debug_probe_new_channel(probe_unit, &chan_config, &probe_chans[i])); TEST_ESP_OK(debug_probe_new_channel(probe_unit, &chan_config, &probe_chans[i]));
// group15[7:0] contains the debug target module ID // group15[7:0] contains the debug target module ID
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -10,8 +10,8 @@
#include <stdbool.h> #include <stdbool.h>
#include "hal/misc.h" #include "hal/misc.h"
#include "hal/assert.h" #include "hal/assert.h"
#include "soc/debug_probe_targets.h"
#include "soc/hp_system_struct.h" #include "soc/hp_system_struct.h"
#include "soc/lp_system_struct.h"
#include "hal/debug_probe_types.h" #include "hal/debug_probe_types.h"
/** /**
@@ -29,39 +29,58 @@ extern "C" {
/** /**
* @brief Enable the debug probe module * @brief Enable the debug probe module
* *
* @param unit_id unit identifier, see debug_probe_unit_id_t
* @param en true: enable, false: disable * @param en true: enable, false: disable
*/ */
static inline void debug_probe_ll_enable_unit(int unit_id, bool en) static inline void debug_probe_ll_enable_unit(debug_probe_unit_id_t unit_id, bool en)
{ {
if (unit_id == DEBUG_PROBE_UNIT_HP) {
HP_SYSTEM.probea_ctrl.reg_probe_global_en = en; HP_SYSTEM.probea_ctrl.reg_probe_global_en = en;
} else {
LP_SYS.lp_probea_ctrl.probe_global_en = en;
}
} }
/** /**
* @brief Enable a specific channel in the debug probe unit * @brief Enable a specific channel in the debug probe unit
* *
* @param unit_id unit identifier, see debug_probe_unit_id_t
* @param channel channel number (only support 0 and 1) * @param channel channel number (only support 0 and 1)
* @param en true: enable, false: disable * @param en true: enable, false: disable
*/ */
static inline void debug_probe_ll_enable_channel(int unit_id, int channel, bool en) static inline void debug_probe_ll_enable_channel(debug_probe_unit_id_t unit_id, int channel, bool en)
{ {
// channel 0 is always enabled // channel 0 is always enabled
if (channel == 1) { if (channel == 1) {
if (unit_id == DEBUG_PROBE_UNIT_HP) {
HP_SYSTEM.probeb_ctrl.reg_probe_b_en = en; HP_SYSTEM.probeb_ctrl.reg_probe_b_en = en;
} else {
LP_SYS.lp_probeb_ctrl.probe_b_en = en;
}
} }
} }
/** /**
* @brief Set the target module for a probe channel * @brief Set the target module for a probe channel
* *
* @param unit_id unit identifier, see debug_probe_unit_id_t
* @param channel channel number (only support 0 and 1) * @param channel channel number (only support 0 and 1)
* @param target_module target module, see soc_debug_probe_target_t * @param target_module HP: soc_debug_probe_target_t, LP: soc_debug_probe_target_lp_t
*/ */
static inline void debug_probe_ll_channel_set_target_module(int unit_id, uint8_t channel, soc_debug_probe_target_t target_module) static inline void debug_probe_ll_channel_set_target_module(debug_probe_unit_id_t unit_id, uint8_t channel, uint8_t target_module)
{ {
if (channel == 0) { if (channel == 0) {
if (unit_id == DEBUG_PROBE_UNIT_HP) {
HP_SYSTEM.probea_ctrl.reg_probe_a_top_sel = target_module; HP_SYSTEM.probea_ctrl.reg_probe_a_top_sel = target_module;
} else { } else {
LP_SYS.lp_probea_ctrl.probe_a_top_sel = target_module;
}
} else {
if (unit_id == DEBUG_PROBE_UNIT_HP) {
HP_SYSTEM.probeb_ctrl.reg_probe_b_top_sel = target_module; HP_SYSTEM.probeb_ctrl.reg_probe_b_top_sel = target_module;
} else {
LP_SYS.lp_probeb_ctrl.probe_b_top_sel = target_module;
}
} }
} }
@@ -74,8 +93,9 @@ static inline void debug_probe_ll_channel_set_target_module(int unit_id, uint8_t
* @param sig_byte_idx signal byte index (0-3) * @param sig_byte_idx signal byte index (0-3)
* @param sig_group signal group (0-15) * @param sig_group signal group (0-15)
*/ */
static inline void debug_probe_ll_channel_add_signal_group(int unit_id, uint8_t channel, uint8_t sig_byte_idx, uint8_t sig_group) static inline void debug_probe_ll_channel_add_signal_group(debug_probe_unit_id_t unit_id, uint8_t channel, uint8_t sig_byte_idx, uint8_t sig_group)
{ {
if (unit_id == DEBUG_PROBE_UNIT_HP) {
if (channel == 0) { if (channel == 0) {
HP_SYSTEM.probea_ctrl.reg_probe_a_mod_sel &= ~(0xf << (sig_byte_idx * 4)); HP_SYSTEM.probea_ctrl.reg_probe_a_mod_sel &= ~(0xf << (sig_byte_idx * 4));
HP_SYSTEM.probea_ctrl.reg_probe_a_mod_sel |= (sig_group << (sig_byte_idx * 4)); HP_SYSTEM.probea_ctrl.reg_probe_a_mod_sel |= (sig_group << (sig_byte_idx * 4));
@@ -83,6 +103,15 @@ static inline void debug_probe_ll_channel_add_signal_group(int unit_id, uint8_t
HP_SYSTEM.probeb_ctrl.reg_probe_b_mod_sel &= ~(0xf << (sig_byte_idx * 4)); HP_SYSTEM.probeb_ctrl.reg_probe_b_mod_sel &= ~(0xf << (sig_byte_idx * 4));
HP_SYSTEM.probeb_ctrl.reg_probe_b_mod_sel |= (sig_group << (sig_byte_idx * 4)); HP_SYSTEM.probeb_ctrl.reg_probe_b_mod_sel |= (sig_group << (sig_byte_idx * 4));
} }
} else {
if (channel == 0) {
LP_SYS.lp_probea_ctrl.probe_a_mod_sel &= ~(0xf << (sig_byte_idx * 4));
LP_SYS.lp_probea_ctrl.probe_a_mod_sel |= (sig_group << (sig_byte_idx * 4));
} else {
LP_SYS.lp_probeb_ctrl.probe_b_mod_sel &= ~(0xf << (sig_byte_idx * 4));
LP_SYS.lp_probeb_ctrl.probe_b_mod_sel |= (sig_group << (sig_byte_idx * 4));
}
}
} }
/** /**
@@ -90,18 +119,27 @@ static inline void debug_probe_ll_channel_add_signal_group(int unit_id, uint8_t
* *
* @param part where does the probe_top_out[15:0] come from * @param part where does the probe_top_out[15:0] come from
*/ */
static inline void debug_probe_ll_set_lower16_output(int unit_id, int channel, debug_probe_split_u16_t part) static inline void debug_probe_ll_set_lower16_output(debug_probe_unit_id_t unit_id, int channel, debug_probe_split_u16_t part)
{ {
HP_SYSTEM.probea_ctrl.reg_probe_l_sel = channel * 2 + DEBUG_PROBE_LL_PART_TO_REG_VAL(part); uint8_t val = (uint8_t)(channel * 2 + DEBUG_PROBE_LL_PART_TO_REG_VAL(part));
if (unit_id == DEBUG_PROBE_UNIT_HP) {
HP_SYSTEM.probea_ctrl.reg_probe_l_sel = val;
} else {
LP_SYS.lp_probea_ctrl.probe_l_sel = val;
}
} }
/** /**
* @brief Set the upper 16 bits of the probe output * @brief Set the upper 16 bits of the probe output
* *
* @param part where does the probe_top_out[31:16] come from * @param part where does the probe_top_out[31:16] come from
* @note No-op for LP unit: only 16 bits are routed to LP GPIO, so upper 16 selection has no effect.
*/ */
static inline void debug_probe_ll_set_upper16_output(int unit_id, int channel, debug_probe_split_u16_t part) static inline void debug_probe_ll_set_upper16_output(debug_probe_unit_id_t unit_id, int channel, debug_probe_split_u16_t part)
{ {
if (unit_id != DEBUG_PROBE_UNIT_HP) {
return; /* LP has only 16 bits to GPIO; probe_top_out[31:16] is not routed */
}
HP_SYSTEM.probea_ctrl.reg_probe_h_sel = channel * 2 + DEBUG_PROBE_LL_PART_TO_REG_VAL(part); HP_SYSTEM.probea_ctrl.reg_probe_h_sel = channel * 2 + DEBUG_PROBE_LL_PART_TO_REG_VAL(part);
} }
@@ -112,9 +150,13 @@ static inline void debug_probe_ll_set_upper16_output(int unit_id, int channel,
* *
* @return the value that currently being probed * @return the value that currently being probed
*/ */
static inline uint32_t debug_probe_ll_read_output(int unit_id) static inline uint32_t debug_probe_ll_read_output(debug_probe_unit_id_t unit_id)
{ {
if (unit_id == DEBUG_PROBE_UNIT_HP) {
return HP_SYSTEM.probe_out.reg_probe_top_out; return HP_SYSTEM.probe_out.reg_probe_top_out;
} else {
return LP_SYS.lp_probe_out.probe_top_out;
}
} }
#ifdef __cplusplus #ifdef __cplusplus
+10 -1
View File
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -10,6 +10,15 @@
extern "C" { extern "C" {
#endif #endif
/**
* @brief Debug probe unit identifier (HP vs LP)
*/
typedef enum {
DEBUG_PROBE_UNIT_HP = 0,
DEBUG_PROBE_UNIT_LP = 1,
} debug_probe_unit_id_t;
/** /**
* @brief The 32bit debug probe output can be split into two 16bit parts * @brief The 32bit debug probe output can be split into two 16bit parts
*/ */
+22 -1
View File
@@ -1,10 +1,11 @@
/* /*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 OR MIT * SPDX-License-Identifier: Apache-2.0 OR MIT
*/ */
#include "soc/gpio_sig_map.h" #include "soc/gpio_sig_map.h"
#include "soc/lp_gpio_sig_map.h"
#include "soc/debug_probe_periph.h" #include "soc/debug_probe_periph.h"
const debug_probe_signal_conn_t debug_probe_periph_signals = { const debug_probe_signal_conn_t debug_probe_periph_signals = {
@@ -28,6 +29,26 @@ const debug_probe_signal_conn_t debug_probe_periph_signals = {
[14] = HP_PROBE_TOP_OUT14_IDX, [14] = HP_PROBE_TOP_OUT14_IDX,
[15] = HP_PROBE_TOP_OUT15_IDX, [15] = HP_PROBE_TOP_OUT15_IDX,
} }
},
[1] = {
.out_sig = {
[0] = LP_PROBE_TOP_OUT0_IDX,
[1] = LP_PROBE_TOP_OUT1_IDX,
[2] = LP_PROBE_TOP_OUT2_IDX,
[3] = LP_PROBE_TOP_OUT3_IDX,
[4] = LP_PROBE_TOP_OUT4_IDX,
[5] = LP_PROBE_TOP_OUT5_IDX,
[6] = LP_PROBE_TOP_OUT6_IDX,
[7] = LP_PROBE_TOP_OUT7_IDX,
[8] = LP_PROBE_TOP_OUT8_IDX,
[9] = LP_PROBE_TOP_OUT9_IDX,
[10] = LP_PROBE_TOP_OUT10_IDX,
[11] = LP_PROBE_TOP_OUT11_IDX,
[12] = LP_PROBE_TOP_OUT12_IDX,
[13] = LP_PROBE_TOP_OUT13_IDX,
[14] = LP_PROBE_TOP_OUT14_IDX,
[15] = LP_PROBE_TOP_OUT15_IDX,
}
} }
} }
}; };
@@ -745,7 +745,7 @@ config SOC_CLOCKOUT_SUPPORT_CHANNEL_DIVIDER
config SOC_DEBUG_PROBE_NUM_UNIT config SOC_DEBUG_PROBE_NUM_UNIT
int int
default 1 default 2
config SOC_DEBUG_PROBE_MAX_OUTPUT_WIDTH config SOC_DEBUG_PROBE_MAX_OUTPUT_WIDTH
int int
@@ -1811,6 +1811,10 @@ config SOC_PM_RETENTION_MODULE_NUM
int int
default 64 default 64
config SOC_MAIN_POWER_CONTROL_SUPPORTED
bool
default y
config SOC_CLK_RC_FAST_SUPPORT_CALIBRATION config SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
bool bool
default y default y
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 OR MIT * SPDX-License-Identifier: Apache-2.0 OR MIT
*/ */
@@ -49,6 +49,23 @@ typedef enum {
DEBUG_PROBE_TARGET_PVT = 30, // PVT DEBUG_PROBE_TARGET_PVT = 30, // PVT
} soc_debug_probe_target_t; } soc_debug_probe_target_t;
/**
* @brief LP probe target module (probe_a_top_sel 0..10)
*/
typedef enum {
DEBUG_PROBE_TARGET_LP_PMU = 0, // LP PMU
DEBUG_PROBE_TARGET_LP_TCM_RAM = 1, // LP TCM RAM
DEBUG_PROBE_TARGET_LP_TCM_ROM = 2, // LP TCM ROM
DEBUG_PROBE_TARGET_LP_CORE = 3, // LP Core
DEBUG_PROBE_TARGET_LP_ADC = 4, // LP ADC
DEBUG_PROBE_TARGET_LP_SPI = 5, // LP SPI
DEBUG_PROBE_TARGET_LP_TOUCH = 6, // LP Touch
DEBUG_PROBE_TARGET_LP_ANA_PERI = 7, // LP Analog Peripherals
DEBUG_PROBE_TARGET_LP_AON_CLKRST = 8, // LP AON Clock/Reset
DEBUG_PROBE_TARGET_LP_SYS_MISC = 9, // LP System Misc
DEBUG_PROBE_TARGET_LP_SYS_PROBE_IN = 10, // LP System Probe In
} soc_debug_probe_target_lp_t;
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
@@ -285,7 +285,7 @@
#define SOC_GPIO_CLOCKOUT_CHANNEL_NUM (2) #define SOC_GPIO_CLOCKOUT_CHANNEL_NUM (2)
#define SOC_CLOCKOUT_SUPPORT_CHANNEL_DIVIDER (1) #define SOC_CLOCKOUT_SUPPORT_CHANNEL_DIVIDER (1)
#define SOC_DEBUG_PROBE_NUM_UNIT (1U) // Number of debug probe units #define SOC_DEBUG_PROBE_NUM_UNIT (2U) // Unit 0: HP probe, Unit 1: LP probe
#define SOC_DEBUG_PROBE_MAX_OUTPUT_WIDTH (16) // Maximum width of the debug probe output in each unit #define SOC_DEBUG_PROBE_MAX_OUTPUT_WIDTH (16) // Maximum width of the debug probe output in each unit
/*-------------------------- RTCIO CAPS --------------------------------------*/ /*-------------------------- RTCIO CAPS --------------------------------------*/
@@ -686,6 +686,8 @@
#define SOC_PM_RETENTION_MODULE_NUM (64) #define SOC_PM_RETENTION_MODULE_NUM (64)
#define SOC_MAIN_POWER_CONTROL_SUPPORTED (1) /*!<Supports outputting an enable signal to control the power-on and power-off of the main power supply when powered by VBAT.*/
/*-------------------------- CLOCK SUBSYSTEM CAPS ----------------------------------------*/ /*-------------------------- CLOCK SUBSYSTEM CAPS ----------------------------------------*/
#define SOC_CLK_RC_FAST_SUPPORT_CALIBRATION (1) #define SOC_CLK_RC_FAST_SUPPORT_CALIBRATION (1)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2
View File
@@ -12,6 +12,8 @@ examples/lowpower/power_management:
examples/lowpower/vbat: examples/lowpower/vbat:
enable: enable:
- if: SOC_VBAT_SUPPORTED == 1 - if: SOC_VBAT_SUPPORTED == 1
disable:
- if: IDF_TARGET in ["esp32h2"] and CONFIG_NAME == "mainpower_ctrl"
disable_test: disable_test:
- if: IDF_TARGET in ["esp32h2"] - if: IDF_TARGET in ["esp32h2"]
temporary: true temporary: true
+39
View File
@@ -14,6 +14,7 @@ This example demonstrates the ESP backup battery power supply solution, which ha
- Support battery voltage detection, wake up the chip and switch to the main power supply when undervoltage occurs. - 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. - 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. - Supports selection of chip status when charging the battery, options include keep active, entering lightsleep or entering deepsleep.
- Optional main power (ESP_3V3) control via GPIO when in deep sleep on VBAT: turn off main power after switching to VBAT, and turn it on again in advance before wakeup (configurable advance time).
## How to use example ## How to use example
@@ -48,6 +49,44 @@ idf.py menuconfig
- 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 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)`. - The period to check whether the battery has been charged done can be selected by `Battery charging done check period (in seconds)`.
#### Main power control when running on VBAT (ESP32-P4)
When the chip is in deep sleep and powered by VBAT, the main power rail (ESP_3V3) may be turned off to reduce power consumption (for example, when it also supplies other peripherals on the board). ESP32-P4 supports controlling the main power enable via an RTC GPIO so that the rail is disabled after switching to VBAT and re-enabled in advance before wakeup, allowing the supply to stabilize before the chip exits deep sleep and switches back to main supply.
Application scenario — circuit connections:
```
┌──────────────┐ ┌───────────────────────────────────┐
│ Button │ VBAT │ ESP32-P4 │
│ Battery ├──────>│ VBAT │
└──────────────┘ │ │
│ ESP_3V3 (RTC IO)│
└───────────────────────────────┬───┘
^ │ Enable (power ctrl)
Vout (3.3V) │ v
┌─────┴───────────────────────────────┐
│ Main Power Enable Pin │
└─────────────────────────────────────┘
│ 3.3V
v
┌───────────────────┐
│ Other Circuits │
└───────────────────┘
```
- **Button Battery** → ESP32-P4 **VBAT**: backup supply during deep sleep, keeps RTC alive.
- **Power Module** Vout → ESP32-P4 **ESP_3V3** and **Other Circuits**: main 3.3 V rail.
- ESP32-P4 **GPIO (RTC IO 0~3)** → Power Module **Enable**: the chip disables main power after entering deep sleep on VBAT, and re-enables it in advance before wakeup.
Configuration (under `Component config → Hardware Settings → Power Supplier → RTC Backup Battery`):
- Enable the feature: **Control main power (ESP_3V3) enable via GPIO when VBAT powers deep sleep**.
- Select the control pin: **GPIO for main power (ESP_3V3) enable** — RTC GPIO 0~3 (or 2~3 only when the RTC clock source is the external 32 kHz crystal, because GPIO0/1 are then reserved).
- Set output polarity: **Invert main power control pin output** — enable this if the main power is enabled by a low level (active-low).
- Set the wakeup advance time: **Main power enable advance time before wakeup (us)** — the time in microseconds by which the main power enable is asserted before the chip starts the wakeup sequence. This value must be at least the main power supply’s startup/settling time (from enable assertion to stable output); otherwise the chip may brown out or boot unstably after wake.
This feature requires a hardware design in which the main power enable is driven by the chosen RTC GPIO (for example, via an external FET or the enable pin of a regulator).
### Build and Flash ### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output: Build the project and flash it to the board, then run monitor tool to view serial output:
@@ -0,0 +1,2 @@
CONFIG_ESP_VBAT_CONTROL_MAIN_POWER_IN_DSLP=y
CONFIG_ESP_VBAT_MAIN_POWER_CTRL_PIN=3