Use ADC in main program and ULP
Posted: Wed Jun 07, 2023 9:35 pm
I have run into an odd problem and I cannot seem to identify a solution reading the documentation. I need to use the ADC in my main program (using adc1_get_raw) and I also need to use it in ULP. The documentation of adc1_ulp_enable seems to suggest this is possible as long as I call adc1_ulp_enable before activating my ULP program. Below you will find a minimal-ish example, if the line Serial.println(adc1_get_raw(ADC1_CHANNEL_6)); is commented out, the ULP runs and wakes the device up as expected if the ADC reading exceeds a set threshold. If the line is present (the main program uses the ADC) there is a bit of a bizarre behavior. The first time I put the device to sleep the ULP runs as normal (ADC works), it will wake on a timer interrupt and show 11 ULP runs... after that first run though, the ULP seems to hang (I suspect on the ADC instruction). I have tried various configurations and sequences of instructions but it seems that accessing the ADC in the main program causes problems on the ULP side, even though I call adc1_ulp_enable before each sleep.
Appreciate any help!
Appreciate any help!
Code: Select all
#include <Arduino.h>
#include "esp32/ulp.h"
#include "ulp_main.h"
#include "ulptool.h"
#include "driver/adc.h"
// Unlike the esp-idf always use these binary blob names
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");
RTC_DATA_ATTR bool first_init = false;
static void init_ulp();
static void run_ulp();
void setup()
{
Serial.begin(115200);
// get wakeup cause
auto wake_cause = esp_sleep_get_wakeup_cause();
switch (wake_cause)
{
case ESP_SLEEP_WAKEUP_ULP:
Serial.println("ULP WAKEUP!");
Serial.println(ulp_count & 0xFFFF);
break;
case ESP_SLEEP_WAKEUP_TIMER:
Serial.println("TIMER WAKEUP");
break;
default:
Serial.println("OTHER WAKEUP");
init_ulp();
}
Serial.print("COUNT: ");
Serial.println(ulp_count & 0xFFFF);
Serial.println(ulp_adc_value & 0xFFFF);
Serial.println(ulp_trigger_threshold & 0xFFFF);
adc1_config_channel_atten(ADC1_CHANNEL_6, ADC_ATTEN_DB_11);
adc1_config_width(ADC_WIDTH_BIT_12);
Serial.println(adc1_get_raw(ADC1_CHANNEL_6));
Serial.println();
Serial.flush();
delay(2000);
run_ulp();
}
static void run_ulp(){
adc1_ulp_enable();
ulp_run((&ulp_entry - RTC_SLOW_MEM) / sizeof(uint32_t));
esp_sleep_enable_ulp_wakeup();
esp_sleep_enable_timer_wakeup(5000000);
esp_deep_sleep_start();
}
static void init_ulp(){
ulp_set_wakeup_period(0, 500000);
long hold;
if(!first_init){
hold = 0;
first_init = true;
} else {
hold = ulp_count;
}
esp_err_t err = ulptool_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));
ulp_count = hold;
ulp_trigger_threshold = (uint32_t) 3000;
}
void loop()
{
}
Code: Select all
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/soc_ulp.h"
.set adc_oversampling_factor_log, 1
.set adc_oversampling_factor, (1 << adc_oversampling_factor_log)
.data
.global count
count:
.long 0
.global adc_value
adc_value:
.long 0
.global trigger_threshold
trigger_threshold:
.long 0
.text
.global entry
entry:
move r3, count
ld r0, r3, 0
add r0, r0, 1
st r0, r3, 0
move r0, 0 // clear r0
stage_rst // clear stage counter
measure:
adc r1, 0, 7 // Read from ADC1_6 into R1
add r0, r0, r1 // Add r1 to r0
stage_inc 1 // increment counter
jumps measure, adc_oversampling_factor, LT // return to measure if the right number of samples haven't been taken
calculate:
/* average the value */
rsh r0, r0, adc_oversampling_factor_log
stage_rst
move r3, adc_value
st r0, r3, 0 // write the averaged value to adc_value
move r3, trigger_threshold
ld r1, r3, 0 // load the threshold
sub r1, r1, r0
jump wakeup, ov
halt // exit if it is not the right value
wakeup:
READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP) // Check if it is ready for a wakeup
and r0, r0, 1
jump wakeup, eq
wake
REG_WR 0x006, 24, 24, 0 // this stops the ULP from running again
halt