ESP32-S3 ULP power consumption

hcasper
Posts: 10
Joined: Tue Jan 07, 2025 5:20 pm

Re: ESP32-S3 ULP power consumption

Postby hcasper » Sun Feb 16, 2025 9:01 am

So I have updated to the current ESP-IDF 5.4 and started using Visual Studio Code. I have run the ULP ADC examples for both FSM and RISCV, also modifying each one with the previously discussed statements to disable the ADC from ULP. The result: In all cases the consumption is still around 50 µA (# 272023). It just seems to be like that.
That means that this chip is not suitable for ULP applications that need the ADC, which really limits its use. That is very disappointing.
The power consumption of 8 µA in the datatheet for "RTC memory and RTC peripherals are powered up" is at least misleading. And the large fluctuation of current consumtion with the ADC on between different batches (see my first post) does not cast a good light on process control, introducing a lot of risk into product development using this chip.

I want a statement by Espressif on this situation.

boarchuz
Posts: 634
Joined: Tue Aug 21, 2018 5:28 am

Re: ESP32-S3 ULP power consumption

Postby boarchuz » Sun Feb 16, 2025 9:48 am

Can you share your code and hardware schematic?

I want to rule out ULP duty cycle (in a previous post you had "WAIT 100000..." which will increase the ULP runtime and hence average power draw) and any external factors (eg. external pullup on the same pin where a RTC pulldown is being enabled).

hcasper
Posts: 10
Joined: Tue Jan 07, 2025 5:20 pm

Re: ESP32-S3 ULP power consumption

Postby hcasper » Sun Feb 16, 2025 11:02 am

Sure, this is the code for RISCV.

Main Processor:

Code: Select all

#include <stdio.h>
#include <inttypes.h>
#include "esp_sleep.h"
#include "ulp_riscv.h"
#include "ulp_adc.h"
#include "ulp_main.h"
#include "ulp/example_config.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[]   asm("_binary_ulp_main_bin_end");

static void init_ulp_program(void);

void app_main(void)
{
    vTaskDelay(pdMS_TO_TICKS(1000));

    esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();

    /* not a wakeup from ULP, load the firmware */
    if ((cause != ESP_SLEEP_WAKEUP_ULP) && (cause != ESP_SLEEP_WAKEUP_TIMER)) {
        printf("Not a ULP-RISC-V wakeup (cause = %d), initializing it! \n", cause);
        init_ulp_program();
    }

    /* RTC peripheral power domain needs to be kept on to keep SAR ADC related configs during sleep */
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);

    ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup());

    esp_deep_sleep_start();
}

static void init_ulp_program(void)
{
    ulp_adc_cfg_t cfg = {
        .adc_n    = EXAMPLE_ADC_UNIT,
        .channel  = EXAMPLE_ADC_CHANNEL,
        .width    = EXAMPLE_ADC_WIDTH,
        .atten    = EXAMPLE_ADC_ATTEN,
        .ulp_mode = ADC_ULP_MODE_RISCV,
    };

    // Commenting out this line will drop the current consumption to ~9 µA
    ESP_ERROR_CHECK(ulp_adc_init(&cfg));

    esp_err_t err = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start));
    ESP_ERROR_CHECK(err);

    /* The first argument is the period index, which is not used by the ULP-RISC-V timer
     * The second argument is the period in microseconds, which gives a wakeup time period of: 2 s
     */
    ulp_set_wakeup_period(0, 2000000);

    /* Start the program */
    err = ulp_riscv_run();
    ESP_ERROR_CHECK(err);
}
ULP:

Code: Select all

#include <stdint.h>
#include "ulp_riscv_utils.h"
#include "ulp_riscv_adc_ulp_core.h"

int main (void)
{

    SENS.sar_peri_clk_gate_conf.saradc_clk_en = 0;
    SENS.sar_power_xpd_sar.force_xpd_sar = 0x2;

    return 0;
}
This is the code for FSM.

Main Processor:

Code: Select all

#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "esp_sleep.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "ulp.h"
#include "ulp_main.h"
#include "esp_adc/adc_oneshot.h"
#include "ulp/example_config.h"
#include "ulp_adc.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[]   asm("_binary_ulp_main_bin_end");

/* This function is called once after power-on reset, to load ULP program into
 * RTC memory and configure the ADC.
 */
static void init_ulp_program(void);

/* This function is called every time before going into deep sleep.
 * It starts the ULP program and resets measurement counter.
 */
static void start_ulp_program(void);

void app_main(void)
{
    /* If user is using USB-serial-jtag then idf monitor needs some time to
    *  re-connect to the USB port. We wait 1 sec here to allow for it to make the reconnection
    *  before we print anything. Otherwise the chip will go back to sleep again before the user
    *  has time to monitor any output.
    */
    vTaskDelay(pdMS_TO_TICKS(1000));

    esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
    if (cause != ESP_SLEEP_WAKEUP_ULP) {
        printf("Not ULP wakeup\n");
        init_ulp_program();
    }
    printf("Entering deep sleep\n\n");
    start_ulp_program();
    ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup() );

#if !CONFIG_IDF_TARGET_ESP32
    /* RTC peripheral power domain needs to be kept on to keep SAR ADC related configs during sleep */
    esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);
#endif

    esp_deep_sleep_start();
}

static void init_ulp_program(void)
{
    esp_err_t err = ulp_load_binary(0, ulp_main_bin_start,
            (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
    ESP_ERROR_CHECK(err);

    ulp_adc_cfg_t cfg = {
        .adc_n    = EXAMPLE_ADC_UNIT,
        .channel  = EXAMPLE_ADC_CHANNEL,
        .width    = EXAMPLE_ADC_WIDTH,
        .atten    = EXAMPLE_ADC_ATTEN,
        .ulp_mode = ADC_ULP_MODE_FSM,
    };

    // Commenting out this line will drop the current consumption to ~9 µA
    ESP_ERROR_CHECK(ulp_adc_init(&cfg));

    /* Set ULP wake up period to 2 s */
    ulp_set_wakeup_period(0, 2000000);

#if CONFIG_IDF_TARGET_ESP32
    /* Disconnect GPIO12 and GPIO15 to remove current drain through
     * pullup/pulldown resistors on modules which have these (e.g. ESP32-WROVER)
     * GPIO12 may be pulled high to select flash voltage.
     */
    rtc_gpio_isolate(GPIO_NUM_12);
    rtc_gpio_isolate(GPIO_NUM_15);
#endif // CONFIG_IDF_TARGET_ESP32

    esp_deep_sleep_disable_rom_logging(); // suppress boot messages
}

static void start_ulp_program(void)
{
    /* Start the program */
    esp_err_t err = ulp_run(&ulp_entry - RTC_SLOW_MEM);
    ESP_ERROR_CHECK(err);
}

ULP:

Code: Select all

#include "soc/rtc_cntl_reg.h"
#include "soc/soc_ulp.h"
#include "soc/sens_reg.h"
#include "soc/rtc_io_reg.h"


	/* Code goes into .text section */
	.text
	.global entry
	
entry:

    // Disable the SAR ADC
    WRITE_RTC_REG(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_SARADC_CLK_EN_S, 1, 0)
    WRITE_RTC_REG(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_IOMUX_CLK_EN_S, 1, 0)
    WRITE_RTC_REG(SENS_SAR_POWER_XPD_SAR_REG, SENS_SARCLK_EN_S, 1, 0)
    WRITE_RTC_REG(SENS_SAR_POWER_XPD_SAR_REG, SENS_FORCE_XPD_SAR_S, 2, 2)
    WAIT 100000

	jump wake_up

exit:
	halt

	.global wake_up
wake_up:
	/* Check if the system can be woken up */
	READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
	and r0, r0, 1
	jump exit, eq

	/* Wake up the SoC, end program */
	wake
	WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_ULP_CP_SLP_TIMER_EN, 0)
	halt
This is the schematic of the board I'm using: https://www.wemos.cc/en/latest/_static/ ... v1.0.0.pdf
I have removed the RGB-LED and I'm powering the 3.3 V directly, bypassing the regulator.

Since the current drops to about 9 µA when just the single line is commented where the ADC is enabled, I am fairly certain that the problem is not on the board. The differences between the chips are coming definitely from the chips themselves. I know that because I swapped chips between two boards (one at ~20 µA, one at ~50 µA) and the consumption stayed with the chip.

hcasper
Posts: 10
Joined: Tue Jan 07, 2025 5:20 pm

Re: ESP32-S3 ULP power consumption

Postby hcasper » Sun Mar 02, 2025 7:06 pm

Any news on this?

boarchuz
Posts: 634
Joined: Tue Aug 21, 2018 5:28 am

Re: ESP32-S3 ULP power consumption

Postby boarchuz » Thu Mar 06, 2025 4:19 pm

It stands to reason that if you undo all changes made as a result of the ADC initialisation then the sleep current will be as if it had never been initialised at all.

The registers you're altering look about right at first glance, but I'm not sure this code is helping with troubleshooting. There are all kinds of race conditions going on in there, and the two are doing different things.

If you still aren't seeing ~10uA then post your updated code and ESP-IDF version, and I'll have a look.

hcasper
Posts: 10
Joined: Tue Jan 07, 2025 5:20 pm

Re: ESP32-S3 ULP power consumption

Postby hcasper » Sun Mar 09, 2025 11:13 am

Thank you, I appreciate any feedback. I'm not sure I quite understand. Could you be more specific about what you mean, what race conditions you see and what I should change in the example code? Should we maybe move forward with just one of the ULPs? FSM always had lower currents than RISCV, so I think that's the way to go.

Who is online

Users browsing this forum: No registered users and 84 guests