Read ADC in ULP (deep sleep) and read pushbutton

atmoz_
Posts: 6
Joined: Tue May 03, 2022 2:59 pm

Read ADC in ULP (deep sleep) and read pushbutton

Postby atmoz_ » Fri May 13, 2022 5:34 am

Hi guys,

For a project, I want to wake the ESP32 from deep-sleep when an analog value reaches a specific threshold on the ADC. I also want to wake the main processor when I push a button.

Everything works except for one thing: when I wake the main processor with the pushbutton, then the next "round" the ADC-readings don't work anymore. That means that I can't wake the main processor with the analog /ADC port anymore.

I have this code in setup():

Code: Select all

if (EnableSleep){
  esp_sleep_wakeup_cause_t WakkerMaakReden = esp_sleep_get_wakeup_cause();
  if (WakkerMaakReden != ESP_SLEEP_WAKEUP_ULP){
    SendUDP("Reden van wakker maken was niet ULP"); 
    delay(1000);  
    init_ulp_program();
  } else {
    ulp_ADC_reading &= UINT16_MAX;
    SendUDP("ulp_ADC_reading: " + String(ulp_ADC_reading));  
    delay(1000);
    init_ulp_program(); 
  }
  
  SendUDP("Slaapstand starten"); 
  delay(1000);
  start_ulp_program();
  ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup());

  esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ALL_LOW);  //wake with pushbutton (BUTTON_PIN_BITMASK is 0x4000)

  esp_deep_sleep_start();

}


static void init_ulp_program()
{
  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);

  digitalWrite(POWER, HIGH); //pcb power
  digitalWrite(26, HIGH);    //tcrt led power

  gpio_hold_en(GPIO_NUM_13); //pcb power
  gpio_hold_en(GPIO_NUM_26); //tcrt power
  gpio_deep_sleep_hold_en(); 

  adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);
  adc1_config_width(ADC_WIDTH_BIT_12);
  adc1_ulp_enable();
  ulp_low_threshold = 4090; 
  ulp_high_threshold = 4096;   
  ulp_set_wakeup_period(0, 40 * 1000);

}

static void start_ulp_program()
{
  esp_err_t err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
  ESP_ERROR_CHECK(err);
}

I also have a file "adc.s" with this code:

Code: Select all

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

/* ADC1 channel 6, GPIO34 */
.set adc_channel, 4

/* Configure the number of ADC samples to average on each measurement.
   For convenience, make it a power of 2. */
.set adc_oversampling_factor_log, 1
.set adc_oversampling_factor, (1 << adc_oversampling_factor_log)

/* Define variables, which go into .bss section (zero-initialized data) */
.bss

/* Low threshold of ADC reading.
   Set by the main program. */
.global low_threshold
low_threshold: .long 0

/* High threshold of ADC reading.
   Set by the main program. */
.global high_threshold
high_threshold: .long 0

/* Counter of measurements done */
.global sample_counter
sample_counter:
.long 0

.global ADC_reading
ADC_reading:
.long 0

/* Code goes into .text section */
.text
.global entry
entry:
/* Disable hold of GPIO13 output */
//WRITE_RTC_REG(RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_HOLD_S, 1, 0)
/* Set the GPIO13 output HIGH */
//WRITE_RTC_REG(RTC_GPIO_OUT_W1TS_REG, RTC_GPIO_OUT_DATA_W1TS_S + 14, 1, 1)

/* increment sample counter */
move r3, sample_counter
ld R2, r3, 0
add R2, R2, 1
st R2, r3, 0

/* do measurements using ADC */
/* r0 will be used as accumulator */
move r0, 0
/* initialize the loop counter */
stage_rst

measure:
/* measure and add value to accumulator */
adc r1, 0, adc_channel + 1
add r0, r0, r1
/* increment loop counter and check exit condition */
stage_inc 1
jumps measure, adc_oversampling_factor, lt

/* divide accumulator by adc_oversampling_factor.
   Since it is chosen as a power of two, use right shift */
rsh r0, r0, adc_oversampling_factor_log
/* averaged value is now in r0; store it into ADC_reading */
move r3, ADC_reading
st r0, r3, 0


/* compare with low_threshold; wake up if value < low_threshold */
move r3, low_threshold
ld r3, r3, 0
sub r3, r0, r3
jump wake_up, ov


/* value within range, end the program */
.global exit
exit:
/* Set the GPIO13 output LOW (clear output) to signal that ULP is now going down */
//WRITE_RTC_REG(RTC_GPIO_OUT_W1TC_REG, RTC_GPIO_OUT_DATA_W1TC_S + 14, 1, 1)
/* Enable hold on GPIO13 output */
//WRITE_RTC_REG(RTC_IO_TOUCH_PAD4_REG, RTC_IO_TOUCH_PAD4_HOLD_S, 1, 1)
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)
jump exit
This is the assembly code that runs in ULP when the main processor is in deep-sleep.

Like I said: everything works independently of each other, but how is it possible that the ADC-reading don't work anymore after I wake the ESP32 with the push button?

I haven't been able to figure it out for days, so any help is highly desirable :)

Thanks in advance,

Atmoz

atmoz_
Posts: 6
Joined: Tue May 03, 2022 2:59 pm

Re: Read ADC in ULP (deep sleep) and read pushbutton

Postby atmoz_ » Wed May 18, 2022 6:28 am

No one that can help me with this? :roll:

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

Re: Read ADC in ULP (deep sleep) and read pushbutton

Postby boarchuz » Wed May 18, 2022 7:36 am

atmoz_ wrote:
Fri May 13, 2022 5:34 am

Code: Select all

  if (WakkerMaakReden != ESP_SLEEP_WAKEUP_ULP){
    init_ulp_program();
  }
  start_ulp_program();
When woken by the push button, you're reinitialising everything related to the ULP while it is still enabled or even active.
In my experience, overwriting its program memory while running can cause the ULP to lock up, also reconfiguring the ADC peripheral while the ULP is executing the adc instruction could conceivably cause it lock up if the measurement state is reset.

Try only initialising the RTC stuff (ULP, ADC, RTCIO) on the reset types where they will have been reset (power on, brownout, RTC WDT) and/or using some other mechanism to determine if necessary (eg. some magic number in RTC memory or https://github.com/boarchuz/HULP/blob/1 ... #L776-L800).

atmoz_
Posts: 6
Joined: Tue May 03, 2022 2:59 pm

Re: Read ADC in ULP (deep sleep) and read pushbutton

Postby atmoz_ » Fri Jan 13, 2023 6:42 am

boarchuz wrote:
Wed May 18, 2022 7:36 am
atmoz_ wrote:
Fri May 13, 2022 5:34 am

Code: Select all

  if (WakkerMaakReden != ESP_SLEEP_WAKEUP_ULP){
    init_ulp_program();
  }
  start_ulp_program();
When woken by the push button, you're reinitialising everything related to the ULP while it is still enabled or even active.
In my experience, overwriting its program memory while running can cause the ULP to lock up, also reconfiguring the ADC peripheral while the ULP is executing the adc instruction could conceivably cause it lock up if the measurement state is reset.

Try only initialising the RTC stuff (ULP, ADC, RTCIO) on the reset types where they will have been reset (power on, brownout, RTC WDT) and/or using some other mechanism to determine if necessary (eg. some magic number in RTC memory or https://github.com/boarchuz/HULP/blob/1 ... #L776-L800).

Thank you for the explanation! 8-)
I don't understand everything exactly 100% of it, but after lots and lots of trying/testing I have this now:

Code: Select all

if (EnableSleep){
  esp_sleep_wakeup_cause_t wakeReason = esp_sleep_get_wakeup_cause();    // 0 = BOOT, 3 = Wake by Push Button, 6 = Wake by ADC
  if (wakeReason != ESP_SLEEP_WAKEUP_ULP){
    Serial.println(String(wakeReason));
  } else {
    ulp_ADC_reading &= UINT16_MAX;
    Serial.println("ulp_ADC_reading: " + String(ulp_ADC_reading)); 
    Serial.println(String(wakeReason));
  }


  if (wakeReason == 0 || wakeReason == 6){
    init_ulp_program(wakeReason);
  }

  start_ulp_program();
  
  ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup());

  esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ALL_LOW);  //wake with pushbutton (BUTTON_PIN_BITMASK is 0x4000)

  Serial.println("Going into sleep...");
  esp_deep_sleep_start();

}


static void init_ulp_program(int wakeReason)
{
  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_low_threshold = 2000; 
  ulp_high_threshold = 4096; 

  digitalWrite(POWER, HIGH); //pcb power
  digitalWrite(26, HIGH);    //tcrt led power

  gpio_hold_en(GPIO_NUM_13); //pcb power
  gpio_hold_en(GPIO_NUM_26); //tcrt power
  gpio_deep_sleep_hold_en(); 

  //if (wakeReason == 0 || wakeReason == 6){
    adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);
    adc1_config_width(ADC_WIDTH_BIT_12);
    adc1_ulp_enable();
  //}

}

static void start_ulp_program()
{
  esp_err_t err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
  ESP_ERROR_CHECK(err);
}


This is (roughly) what you mean right? Whether or not I (re)initialize something depending on the value of wakeReason?
0 = BOOT
3 = Wake by Push Button
6 = Wake by ADC

But no matter what I try (literally spent maybe 20 hours in total trying) in this code, I can't get the ADC to work again after I push the button(wakeReason == 3) :cry:

atmoz_
Posts: 6
Joined: Tue May 03, 2022 2:59 pm

Re: Read ADC in ULP (deep sleep) and read pushbutton

Postby atmoz_ » Sat Jan 14, 2023 6:21 am

Okay, it works now 8-)

Maybe someone in the future has the same problem, hereby the code:

Code: Select all

if (EnableSleep){

  digitalWrite(POWER, HIGH);  //pcb power
  digitalWrite(26, HIGH);     //tcrt led power
  gpio_hold_en(GPIO_NUM_13);  //pcb power
  gpio_hold_en(GPIO_NUM_26);  //tcrt power
  gpio_deep_sleep_hold_en(); 

  esp_sleep_wakeup_cause_t wakeReason = esp_sleep_get_wakeup_cause();    // 0 = BOOT, 3 = Wake by Push Button, 6 = Wake by ADC
  switch(wakeReason){
    case 0 : Serial.println("Reason 0 (boot)"); init_ulp_program(wakeReason); break;
    case 3 : Serial.println("Reason 3 (pushbutton)"); init_ulp_program(wakeReason); break;
    case 6 : Serial.println("Reason 6 (ADC)"); ulp_ADC_reading &= UINT16_MAX; Serial.println("ulp_ADC_reading: " + String(ulp_ADC_reading)); init_ulp_program(wakeReason); break; 
  }  

  start_ulp_program();

  ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup());

  esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ALL_LOW);  //wake with pushbutton (BUTTON_PIN_BITMASK is 0x4000)

  Serial.println("Going into sleep...");
  esp_deep_sleep_start();

}



static void init_ulp_program(int wakeReason)
{

  if (wakeReason == 0){ //load ULP program only once (when booting)
    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_low_threshold = 2000; 
  ulp_high_threshold = 4096; 

  adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_DB_11);
  adc1_config_width(ADC_WIDTH_BIT_12);
  adc1_ulp_enable();

  /* Set ULP wake up period to 20ms */
  ulp_set_wakeup_period(0, 20000);

}

static void start_ulp_program()
{
  esp_err_t err = ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
  ESP_ERROR_CHECK(err);
}
Best regards,

Atmoz

Who is online

Users browsing this forum: No registered users and 111 guests