boarchuz / HULP

ESP32 ULP Coprocessor Helper
MIT License
180 stars 18 forks source link

ADC example, comparing if value is wihin a range, rather than just under a threshold #27

Closed 0x0fe closed 1 year ago

0x0fe commented 1 year ago

So, in the ADC example, which by the way is very convenient and covers many cases, i try to compare the PiN3 against a range, rather than if it is below a certain threashold.

Because, when used with ADKEYS typically several keys are on the same GPIO and i want to wake up the system only if a certain key is pressed.

What is the best way to do this? I see where you compare wether it is below the threashold, but i am not sure how to compare if it is below the threshold and over another threshold.

            I_ANALOG_READ(R1, PIN_ADC_PIN3),
            I_PUT(R1, R3, ulp_vars.pin3.debug),
            // Skip if the trigger isn't 'armed'. SoC enables this before going to sleep (see below).
            I_GET(R0, R3, ulp_vars.pin3.armed),
            M_BL(LBL_HALT, 1),
            // Subtract the threshold. If it overflows, the value must be < threshold so process the event.
            I_SUBI(R0, R1, PIN3_WAKE_THRESHOLD),
            M_BXF(LBL_PIN3_WAKEUP_TRIGGERED),
            M_BX(LBL_HALT),
            M_LABEL(LBL_PIN3_WAKEUP_TRIGGERED),

I think updating the example with that function would be useful to others working with ADKEYs.

Thanks.

boarchuz commented 1 year ago

Hi @0x0fe, There are many ways to skin this cat. It's probably not worth an extra example but here are a few ways:

    #define ADC_THRESHOLD_MIN 123 // Wake if >=
    #define ADC_THRESHOLD_MAX 456 // Wake if <=

    // 1
    I_ANALOG_READ(R0, PIN_ADC), // R0 = ADC
    M_BL(LBL_NOT_TRIGGERED, ADC_THRESHOLD_MIN), // goto NOT_TRIGGERED if R0 < MIN
    M_BGE(LBL_NOT_TRIGGERED, ADC_THRESHOLD_MAX + 1), // goto NOT_TRIGGERED if R0 > MAX
    // Triggered

    // 2
    I_ANALOG_READ(R0, PIN_ADC), // R0 = ADC
    I_SUBI(R0, R0, ADC_THRESHOLD_MIN), // Cause values below threshold to overflow
    M_BGE(LBL_NOT_TRIGGERED, (ADC_THRESHOLD_MAX - ADC_THRESHOLD_MIN) + 1), // Check if above threshold (compensating for previous subtraction)
    // Triggered

    // 3
    I_ANALOG_READ(R1, PIN_ADC), // R1 = ADC
    I_SUBI(R1, R1, ADC_THRESHOLD_MIN), // Set overflow flag if below threshold
    M_BXF(LBL_NOT_TRIGGERED),
    I_ADDI(R1, R1, 65535-(ADC_THRESHOLD_MAX-ADC_THRESHOLD_MIN)), // Set overflow flag if above threshold (compensating for previous subtraction)
    M_BXF(LBL_NOT_TRIGGERED),
    // Triggered

If you care about optimising for size/speed, R0 allows the most efficient (and easiest) instructions, and you can consider reversing the logic if that suits your program flow better.

0x0fe commented 1 year ago

@0x0fe Wow, nice, thank you very much, my pathetic attempt was failing miserably.

(compensating for previous subtraction)

Maybe the bit i missed.

            // Subtract the threshold. If it overflows, the value must be < threshold so process the event.
            I_SUBI(R0, R1, PIN3_WAKE_THRESHOLD_HIGH),
            //branch to halt if no overflow (adc>high threshold)
            M_BX(LBL_HALT),
            I_SUBI(R0, R1, PIN3_WAKE_THRESHOLD_LOW),
            //branch to label if no overflow (adc>low threashold)
            M_BX(LBL_PIN3_WAKEUP_TRIGGERED),
            //branch to halt if overflow (adc<low threashold)
            M_BXF(LBL_HALT),
            //M_BXF(LBL_PIN3_WAKEUP_TRIGGERED),
            //M_BX(LBL_HALT),
0x0fe commented 1 year ago

and so i can confirm, it worked perfectly

void ulp_init(void){

  enum {
    LBL_PIN2_OVERSAMPLE_LOOP,
    LBL_PIN3_WAKEUP_TRIGGERED,
    LBL_HALT,
  };

  const ulp_insn_t program[] = {

    // Set R3 to 0 for use with I_GET and I_PUT
    I_MOVI(R3, 0),

    I_ANALOG_READ(R1, ADKEY),                         // R1 = ADC
    I_PUT(R1, R3, ulp_vars.pin3.debug),           
    I_GET(R0, R3, ulp_vars.pin3.armed),               //Skip if the trigger isn't 'armed'
    M_BL(LBL_HALT, 1),            
    I_SUBI(R1, R1, ADC_THRESHOLD_MIN),                // Set overflow flag if below threshold
    M_BXF(LBL_HALT),
    I_ADDI(R1, R1, 65535-(ADC_THRESHOLD_MAX-ADC_THRESHOLD_MIN)), // Set overflow flag if above threshold (compensating for previous subtraction)
    M_BXF(LBL_HALT),

    I_PUT(R1, R3, ulp_vars.pin3.wakeup.reason),       // Store the wakeup value for SOC access             
    I_GET(R0, R3, ulp_vars.pin3.wakeup.counter),      // Increment the counter.
    I_ADDI(R0, R0, 1),
    I_PUT(R0, R3, ulp_vars.pin3.wakeup.counter),               
    I_PUT(R3, R3, ulp_vars.pin3.armed),               // Clear 'armed'
    M_WAKE_WHEN_READY(),

    M_LABEL(LBL_HALT),
    I_HALT(),
  };

  ESP_ERROR_CHECK(hulp_configure_analog_pin(ADKEY, ADC_ATTEN_DB_0, ADC_WIDTH_BIT_12));

  ESP_ERROR_CHECK(hulp_ulp_load(program, sizeof(program), 1000UL*ULP_WAKEUP_INTERVAL_MS, 0));
  ESP_ERROR_CHECK(hulp_ulp_run(0));
}
17:53:48.926 -> system init
17:53:54.500 -> adkey:592
17:53:57.070 -> adkey:758
17:53:57.680 -> adkey:436
17:53:59.317 -> adkey:1513
17:54:00.951 -> adkey:960 [this key triggers the deep sleep]
17:54:01.185 -> ULP start
17:54:01.185 -> going to deep sleep
17:54:07.883 -> [no key triggered the wakeup, except the one within the defined range]
17:54:08.211 -> waking from deep sleep
17:54:08.211 -> ULP stop
17:54:08.211 -> system init