boarchuz / HULP

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

Pulse counter #9

Closed laguill closed 3 years ago

laguill commented 3 years ago

Hi @boarchuz I'm trying to build a pulse counter. I tried using your ADC exemple but it is not working. I have a switch with a pulldown resistor on pin 34 Here is my code `#include "hulp_arduino.h"

define PIN_PLUV GPIO_NUM_34

define PIN_PLUV_WAKE_THRESHOLD (4090)

define ULP_WAKEUP_INTERVAL_MS (20) //Debouncers timer

int TIME_TO_SLEEP = 60; //Time EPS32 will go to sleep in seconds

RTC_DATA_ATTR ulp_var_t counter_pluv;

void ulp_init(){ enum { LBL_PIN_PLUV_HIGH, //This labels are use as if function LBL_HALT, }; const ulp_insn_t program[] = { //ULP I_MOVI(R1,0), //Copy an immediate value into register: R1 I_ANALOG_READ(R1, PIN_PLUV), I_SUBI(R0, R1, PIN_PLUV_WAKE_THRESHOLD), //Subtract register and an immediate value //// Subtract the threshold. If it overflows, the value must be < threshold so process the event. M_BXF(LBL_PIN_PLUV_HIGH), M_BX(LBL_HALT), // M_LABEL(LBL_PIN_PLUV_HIGH), //increment counter if PIN_PLUV == HIGH I_GET(R0,R1, counter_pluv), //Load value from RTC memory into reg_dest register. I_ADDI(R0,R0,1), //R0++ I_PUT(R0,R1, counter_pluv), //Load value from reg_dest register to RTC memory. M_WAKE_WHEN_READY(), // M_LABEL(LBL_HALT), I_HALT(), //Halt the coprocessor but keeps ULP timer active }; ESP_ERROR_CHECK(hulp_configure_analog_pin(PIN_PLUV)); ESP_ERROR_CHECK(hulp_ulp_load(program, sizeof(program), 1000UL * ULP_WAKEUP_INTERVAL_MS, 0)); ESP_ERROR_CHECK(hulp_ulp_run(0));

}

void sleep (){ Serial.println("Going to sleep for "+ String(TIME_TO_SLEEP) + " seconds"); esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000); delay(100); Serial.flush(); esp_sleep_enable_ulp_wakeup(); esp_deep_sleep_start(); } void setup() { // put your setup code here, to run once: Serial.begin(115200); Serial.println("Program is starting"); ulp_init;

Serial.println("Pluv counter = " + String(counter_pluv.val)); sleep();

}

void loop() { }`

Can you help on this ?

Thank you for your help

boarchuz commented 3 years ago

Isn't this a digital signal? Have a look at examples/Interrupt or use simple polling (I_GPIO_READ).

ulp_init; -> ulp_init();

laguill commented 3 years ago

Thank you I'm not sure of how the pin is configure and I have some error in counting with some false trigger if it is to fast. How I can improve it ?

Here my code again

`#include "hulp_arduino.h"

define PIN_PLUV GPIO_NUM_34

define ULP_WAKEUP_INTERVAL_MS (250) //Debouncers timer

int TIME_TO_SLEEP = 60; //Time EPS32 will go to sleep in seconds

RTC_DATA_ATTR ulp_var_t counter_pluv;

void ulp_init(){ enum { LBL_PIN_PLUV_TRIGGERED, //This labels are use as if function LBL_HALT, }; const ulp_insn_t program[] = { //ULP I_MOVI(R1,0), //Copy an immediate value into register: R1 I_GPIO_READ(PIN_PLUV), //If it's set, goto LAB_PIN_PLUV_TRIGGERED M_BGE(LBL_PIN_PLUV_TRIGGERED, 1), //Else HALT I_HALT(), //Halt the coprocessor but keeps ULP timer active // M_LABEL(LBL_PIN_PLUV_TRIGGERED), //increment counter if PIN_PLUV TRIGGERED I_GET(R0,R1, counter_pluv), //Load value from RTC memory into reg_dest register. I_ADDI(R0,R0,1), //R0++ I_PUT(R0,R1, counter_pluv), //Load value from reg_dest register to RTC memory. M_WAKE_WHEN_READY(), I_HALT(),

}; //Pin configuration hulp_peripherals_on(); hulp_configure_pin(PIN_PLUV, RTC_GPIO_MODE_INPUT_ONLY, GPIO_PULLUP_ONLY, 0); hulp_configure_pin_int(PIN_PLUV, GPIO_INTR_ANYEDGE); ESP_ERROR_CHECK(hulp_ulp_load(program, sizeof(program), 1000UL * ULP_WAKEUP_INTERVAL_MS, 0)); ESP_ERROR_CHECK(hulp_ulp_run(0));

}

void sleep (){ Serial.println("Going to sleep for "+ String(TIME_TO_SLEEP) + " seconds"); esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000UL); delay(100); Serial.flush(); esp_sleep_enable_ulp_wakeup(); //This is for debugging esp_deep_sleep_start(); } void setup() { // put your setup code here, to run once: Serial.begin(115200); Serial.println("Program is starting"); ulp_init();

Serial.println("Pluv counter = " + String(counter_pluv.val)); counter_pluv.val = 0; sleep();

}

void loop() { }`

boarchuz commented 3 years ago

The ULP is sleeping for 250ms. If you're releasing the switch within 250ms, it's possible to miss the activation (pin will be pulled down again by the time the ULP wakes and reads level).

You can decrease ULP sleep time, or have a look at examples/Interrupt to capture changes while the ULP is sleeping.

laguill commented 3 years ago

Thank you i followed you exemples and now there is no false trigger with a debounce time of 75ms However now I'm triing to share data with SoC following your exemple but I get the error 'union ulp_var_t' has no member named 'get'

Can you help ?

`#include "hulp_arduino.h"

define PIN_PLUV GPIO_NUM_34

define ULP_WAKEUP_INTERVAL_MS (75) //Debouncers timer

int TIME_TO_SLEEP = 10; //Time EPS32 will go to sleep in seconds

RTC_DATA_ATTR ulp_var_t counter_pluv; float pluviometre = 0;

void ulp_init(){ enum { LBL_PIN_PLUV_TRIGGERED, //This labels are use as if function

}; const ulp_insn_t program[] = { //ULP I_MOVI(R1,0), //Copy an immediate value into register: R1 I_GPIO_READ(PIN_PLUV), //If it's set, goto LAB_PIN_PLUV_TRIGGERED M_BGE(LBL_PIN_PLUV_TRIGGERED, 1), //Else HALT I_HALT(), //Halt the coprocessor but keeps ULP timer active // M_LABEL(LBL_PIN_PLUV_TRIGGERED), //increment counter if PIN_PLUV TRIGGERED I_GET(R0,R1, counter_pluv), //Load value from RTC memory into reg_dest register. I_ADDI(R0,R0,1), //R0++ I_PUT(R0,R1, counter_pluv), //Load value from reg_dest register to RTC memory. M_WAKE_WHEN_READY(), I_HALT(),

}; //Pin configuration hulp_peripherals_on(); hulp_configure_pin(PIN_PLUV, RTC_GPIO_MODE_INPUT_ONLY, GPIO_PULLDOWN_ONLY, 0); ESP_ERROR_CHECK(hulp_configure_pin_int(PIN_PLUV, GPIO_INTR_ANYEDGE)); ESP_ERROR_CHECK(hulp_ulp_load(program, sizeof(program), 1000UL * ULP_WAKEUP_INTERVAL_MS, 0)); ESP_ERROR_CHECK(hulp_ulp_run(0));

}

void sleep (){ Serial.println("Going to sleep for "+ String(TIME_TO_SLEEP) + " seconds"); esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000UL); delay(100); Serial.flush(); //esp_sleep_enable_ulp_wakeup(); //This is for debugging esp_deep_sleep_start(); }

void Pluviometre(){ pluviometre = counter_pluv.get(); if(pluviometre != 0){ pluviometre = counter_pluv.val * 0.2794; counter_pluv.val = 0; } counter_pluv.val = 0;

}

void setup() { // put your setup code here, to run once: Serial.begin(115200); Serial.println("Program is starting"); ulp_init(); Serial.println("counter = " + String(counter_pluv.val)); Pluviometre(); Serial.println("Pluv counter = " + String(pluviometre) + " mm"); sleep();

}

void loop() { }`

boarchuz commented 3 years ago

get() and other member functions don't exist any more, sorry (I'm aware there are still some obsolete references to it).

ulp_var_t is a simple struct, you can access the value using counter_pluv.val

laguill commented 3 years ago

Thank you again it is working now.

I created a repo. I am new on github and I don't want to appropriate myself your work. Please feel free to tell me if something is wrong

laguill commented 3 years ago

Now I am triing to build an anemometer and I am looking at you ADC exemple because I need to make a LSR on the counter to divide it by two. Can you tell me what are this two lines used for I_STAGE_INC(1), M_BSLT(LBL_PIN2_OVERSAMPLE_LOOP, (1 << PIN2_OVERSAMPLE_SHIFT)),

boarchuz commented 3 years ago

I_STAGE_INC increases the 8-bit stage counter by the given value. M_BSLT = branch if stage is less than (just like M_BL but uses stage counter rather than R0).

These ones are standard instructions, you can read the IDF docs for more info.

https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/ulp_instruction_set.html#esp32-ulp-coprocessor-instruction-set

laguill commented 3 years ago

Well thank you I cannot understand the difference between I_LD and I_GET ? can you tell me more about this ?

laguill commented 3 years ago

To build the anemometer I am following this diagram https://github.com/laguill/Weather-station-ulp-esp32/blob/main/Anemometer_Diagram.png but I don't understand how I can use time to trigger a Label how I can compare guts and counter_anemo and store it

include "hulp_arduino.h"

define PIN_ANEMO GPIO_NUM_35

define ULP_WAKEUP_INTERVAL_MS (75) //Debouncer timer

define ACQUISITION (3) //Time acquisition anemometer

int TIME_TO_SLEEP = 60; //Time EPS32 will go to sleep in seconds const float diametre = 0.07; //size of the anemometer

RTC_DATA_ATTR ulp_var_t counter_anemo; RTC_DATA_ATTR ulp_var_t windspd; RTC_DATA_ATTR ulp_var_t guts ; RTC_DATA_ATTR ulp_var_t nb_acq ;

void ulp_init(){ enum { LBL_BEGIN, //This labels are use as if function LBL_TRIGGERED, LBL_ACQUISITION, GUTS, };

const ulp_insn_t program[]{ //Reset R2 to counter_anemo = 0 M_LABEL(LBL_BEGIN), I_MOVI(R2,0), //Pin anemo triggered ? I_GPIO_READ(PIN_ANEMO), //If it's set, go to LBL_TRIGGERED M_BGE(LBL_TRIGGERED, 1),

M_LABEL(LBL_TRIGGERED),
 //goto LBL_ACQUISITION
  M_BX(LBL_ACQUISITION),

M_LABEL(ACQUISITION),
  //increment counter nb_acq if PIN_PANEMO TRIGGERED
  I_GET(R0,R3, nb_acq), //Load value from RTC memory into reg_dest register.
  I_ADDI(R0,R0,1),  //R0++
  I_PUT(R0,R3, nb_acq), //Load value from reg_dest register to RTC memory.  

  //Add counter_anemo to windspd if PIN_ANEMO TRIGGERED    
  I_GET(R2,R2, counter_anemo), //Load value from RTC memory into reg_dest register.
  I_GET(R1,R1, windspd), //Load value from RTC memory into reg_dest register.
  I_ADDR(R0,R1,R2), //R0 = R1 + R2
  I_PUT(R0,R1, windspd), //Store value from R0 into RTC memory in windspd variable

  I_ADDI(R0,R0,1),  //R0++
  I_RSHR(R0,R0,1),  //Logical Shift right of 1 to divide the value by two
  I_PUT(R0,R2, counter_anemo), //Load value from reg_dest register to RTC memory.
  M_BGE(GUTS,guts), //branch to GUTS if R0 is greater or equal than immediate value

M_LABEL(GUTS),
  I_GET(R0,R2, counter_anemo), //Load counter_anemo in R0 register
  I_PUT(R0,R1,guts), //guts = count_anemo
  M_WAKE_WHEN_READY(),
  I_HALT(),

}; //Pin configuration hulp_peripherals_on(); hulp_configure_pin(PIN_ANEMO, RTC_GPIO_MODE_INPUT_ONLY, GPIO_PULLDOWN_ONLY, 0); ESP_ERROR_CHECK(hulp_configure_pin_int(PIN_ANEMO, GPIO_INTR_ANYEDGE)); ESP_ERROR_CHECK(hulp_ulp_load(program, sizeof(program), 1000UL * ULP_WAKEUP_INTERVAL_MS, 0)); ESP_ERROR_CHECK(hulp_ulp_run(0)); }

void sleep(){ Serial.println("Going to sleep for "+ String(TIME_TO_SLEEP) + " seconds"); esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000UL); delay(100); Serial.flush(); //esp_sleep_enable_ulp_wakeup(); //This is for debugging esp_deep_sleep_start();
}

void Anemometre()){ windspd.val = windspd.val / nb_acq.val; windspd.val = windspd.val M_PI diametre / (TIME_TO_SLEEP 3.6); guts.val = guts.val M_PI diametre / (TIME_TO_SLEEP 3.6); nb_acq.val = 0; }

void setup() {

Serial.begin(115200); Serial.println("Program is starting"); ulp_init(); Serial.println("counter = " + String(counter_anemo.val)); Serial.println("counter = " + String(guts.val)); Anemometre(); Serial.println("Wind speed = " + String(windspd.val) + " km/h"); Serial.println("Guts = " + String(guts.val) + " km/h"); sleep();

}

void loop() {

}

Here is my code and I have an error saying "cannot convert 'ulp_var_t' to 'uint32_t' {aka 'unsigned int'} in initialization"

Thank you again

boarchuz commented 3 years ago

I_LD takes the word offset in RTC memory from which to load the value.

I_GET is a hulp macro that is basically a I_LD that takes a ulp_var_t and calculates the offset for you. It's typically easier to use and much easier to maintain.

There's an error here: M_BGE(GUTS,guts) The compiler often won't be able to tell you where exactly your error is; generally you can isolate it by commenting parts of your ulp_insn_t array until the compiler stops complaining.

laguill commented 3 years ago

Hi sorry for the late answer. Thank you for help

In M_BGE I want to compare counter_anemo and guts to save the max value seen How can I do that ?

laguill commented 3 years ago

Is it possible to count the number of pulse during an amount of time And then save the value ?

boarchuz commented 3 years ago

You'll need to subtract them and check the result. There are many ways (most efficient depends on your program), here's one:

// Suppose you want to check if R2 > R1
I_SUBR(R3, R1, R2), // R3 = R1 - R2
M_BXF(LBL_GREATER), // Branch to label "LBL_GREATER" if previous operation overflowed (ie. R2 > R1)
// Else continue if no overflow occurred (ie. R1 >= R2)

There are macros for interacting with the RTC clock for timing, eg. M_IF_MS_ELAPSED. For example, here's a (untested) program that will count interrupts on a pin, and wake the SoC every 5 seconds.

const ulp_insn_t program[] = {
        I_MOVI(R3, 0),

        // Read the interrupt bit
        I_GPIO_INT_RD(BUTTON_PIN),
        // If it's not set, jump to LAB_CHECK_TIME
        M_BL(LAB_CHECK_TIME, 1),

        // Load and increment counter
        I_GET(R1, R3, interrupt_counter),
        I_ADDI(R1, R1, 1),
        I_PUT(R1, R3, interrupt_counter),
        I_GPIO_INT_CLR(BUTTON_PIN),

        M_LABEL(LAB_CHECK_TIME),
        M_UPDATE_TICKS(), // Update RTC time
        M_IF_MS_ELAPSED(LBL_ELAPSED_CHECK, 5000 /* every 5 seconds */, LBL_HALT),
            // Interval has elapsed
            // Set previous counter and reset current counter
            I_GET(R1, R3, interrupt_counter),
            I_PUT(R1, R3, previous_counter),
            I_PUT(R3, R3, interrupt_counter),
            I_WAKE(),
        M_LABEL(LBL_HALT),
            I_HALT(),
    };

If you know the wakeup interval, and your program has a consistent run time, then it might be easier to use a simple counter. Note that some macros like M_IF_MS_ELAPSED will mess with registers; have a look at the implementation in hulp_macros.h if you want to check or change.

laguill commented 3 years ago

Thank you Is it better to use I_GPIO_INT_RD(BUTTON_PIN) and I_GPIO_INT_CLR(BUTTON_PIN) than I_GPIO_READ(PIN_ANEMO) ?

laguill commented 3 years ago

I tried this code but something wrong the counter stay with 0 How is working when I go to label on condition ? after the code restart from where it left ?

I am a bit confuse right now

'#include` "hulp_arduino.h"

define PIN_ANEMO GPIO_NUM_35

define ULP_WAKEUP_INTERVAL_MS (75) //Debouncer timer

define TIME_ANEMO (3) //Time acquisition anemometer

int TIME_TO_SLEEP = 10; //Time EPS32 will go to sleep in seconds const float diametre = 0.07; //size of the anemometer

RTC_DATA_ATTR ulp_var_t counter_anemo; RTC_DATA_ATTR ulp_var_t windspd; RTC_DATA_ATTR ulp_var_t guts ; RTC_DATA_ATTR ulp_var_t nb_acq ;

void ulp_init(){ enum { LBL_BEGIN, //This labels are use as if function LBL_TRIGGERED, LBL_ACQUISITION, LBL_GUTS, LBL_HALT, LBL_TIMER_ANEMO, };

const ulp_insn_t program[]{ //Reset R2 to counter_anemo = 0 M_LABEL(LBL_BEGIN), I_MOVI(R2,0), I_PUT(R2,R2, counter_anemo),

M_LABEL(LBL_TRIGGERED),  
//Pin anemo triggered ?
  I_GPIO_READ(PIN_ANEMO),
//If it's set, go to LBL_ACQUISITION
  M_BGE(LBL_ACQUISITION, 1),

M_LABEL(LBL_ACQUISITION),
  //increment counter nb_acq if PIN_ANEMO ACQUISITION
  I_GET(R0,R3, nb_acq), //Load value from RTC memory into reg_dest register.
  I_ADDI(R0,R0,1),  //R0++
  I_PUT(R0,R3, nb_acq), //Load value from reg_dest register to RTC memory.  

  //Add counter_anemo to windspd 
  I_GET(R2,R2, counter_anemo), //Load value from RTC memory into reg_dest register.
  I_ADDI(R0,R0,1),  //R0++
  I_RSHR(R0,R0,1),  //Logical Shift right of 1 to divide the value by two
  I_PUT(R0,R2, counter_anemo), //Load value from reg_dest register to RTC memory.

  //To check if counter_anemo > guts
  I_GET(R2,R2, guts),
  I_GET(R1,R1, counter_anemo),     
  I_SUBR(R0, R1, R2), // R3 = R1 - R2
  M_BXF(LBL_GUTS), //branch to label "GUTS" if previous operation overflowed (ie. R2 > R1)
  // Else continue if no overflow occurred (ie. R1 >= R2)

  //Check timer anemo
  M_UPDATE_TICKS(), // Update RTC time
  M_IF_MS_ELAPSED(LBL_TIMER_ANEMO, TIME_ANEMO * 1000  /* every 3 seconds */, LBL_TRIGGERED), //M_IF_MS_ELAPSED(id_label, interval_ms, else_goto_label)
  // Interval has elapsed
  I_GET(R2,R2, counter_anemo), //Load value from RTC memory into reg_dest register.      
  I_GET(R1,R1, windspd), //Load value from RTC memory into reg_dest register.
  I_ADDR(R0,R1,R2), //R0 = R1 + R2
  I_PUT(R0,R1, windspd), //Store value from R0 into RTC memory in windspd variable   

  //reset counter,  
    M_BX(LBL_BEGIN),

M_LABEL(LBL_GUTS),
  I_GET(R0,R1, counter_anemo), //Load counter_anemo in R0 register
  I_PUT(R0,R1,guts), //guts = counter_anemo

M_LABEL(LBL_HALT),
  I_HALT(),

}; //Pin configuration hulp_peripherals_on(); hulp_configure_pin(PIN_ANEMO, RTC_GPIO_MODE_INPUT_ONLY, GPIO_PULLDOWN_ONLY, 0); ESP_ERROR_CHECK(hulp_configure_pin_int(PIN_ANEMO, GPIO_INTR_ANYEDGE)); ESP_ERROR_CHECK(hulp_ulp_load(program, sizeof(program), 1000UL * ULP_WAKEUP_INTERVAL_MS, 0)); ESP_ERROR_CHECK(hulp_ulp_run(0)); }

void sleep(){ Serial.println("Going to sleep for "+ String(TIME_TO_SLEEP) + " seconds"); esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * 1000000UL); delay(100); Serial.flush(); //esp_sleep_enable_ulp_wakeup(); //This is for debugging esp_deep_sleep_start();
}

void Anemometre(){ windspd.val = windspd.val / nb_acq.val; windspd.val = windspd.val M_PI diametre / (TIME_TO_SLEEP 3.6); guts.val = guts.val M_PI diametre / (TIME_TO_SLEEP 3.6);

nb_acq.val = 0;

}

void setup() {

Serial.begin(115200); Serial.println("Program is starting"); ulp_init(); Serial.println("counter = " + String(counter_anemo.val)); Serial.println("counter = " + String(guts.val)); Anemometre(); Serial.println("Wind speed = " + String(windspd.val) + " km/h"); Serial.println("Guts = " + String(guts.val) + " km/h");
windspd.val = 0; guts.val = 0; sleep();

}

void loop() {

} '

boarchuz commented 3 years ago

I_GET and I_PUT expect the second register to have value 0.