(resolved) handling GPIO interrupts

User avatar
mzimmers
Posts: 643
Joined: Wed Mar 07, 2018 11:54 pm
Location: USA

(resolved) Re: handling GPIO interrupts

Postby mzimmers » Wed Apr 04, 2018 4:33 pm

Maybe I'm over-complicating this, and I don't need the call to gpio_get_level(). I just thought it made sense to verify the state of the pin after getting an interrupt...is this not common practice?

As far as intent...when the button is pressed, I need to send a message (via WiFi) to another device. I'll probably use another event group to communicate between the button handling task and the WiFi handling task. That's really about all there is to it right now.
Last edited by mzimmers on Thu Dec 06, 2018 9:44 pm, edited 1 time in total.

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

Re: handling GPIO interrupts

Postby kolban » Wed Apr 04, 2018 4:56 pm

Howdy,
If we ask for an interrupt to be driven when the signal goes from low to high ... then we should assume that if the interrupt is invoked then the cause of the interrupt was truly the signal went from low to high. I don't see any obvious reason to question that assumption.

Imagine now that we have an interrupt being fired when the signal goes low to high. That causes an interrupt and you write the indication that the interrupt happened to a queue and then end the interrupt handler. At some time later (the latency) you then detect the new message in the queue. If you read the value of the pin at this point ... it may not be high. The reason for this is that time has passed when you were alerted that the pin went high to the time you later read the value of the pin.

If what you want to do is detect that a button has been pressed, then you have two primary choices. Interrupts and polling. There is nothing wrong with polling for button presses. Even a slow polling loop of once every 100msecs is still a button check 10 times a second. With polling, you don't have to worry (as much) about debounce as you yourself are in control of the next poll.

To the best of my knowledge, don't assume that if you are blocked on a queue with nothing on it and have configured an interrupt to post to the queue when a pin changes state that it is somehow more efficient. Under the covers, the CPU is still clocking away with the same power consumption rate as polling.
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

User avatar
mzimmers
Posts: 643
Joined: Wed Mar 07, 2018 11:54 pm
Location: USA

Re: handling GPIO interrupts

Postby mzimmers » Wed Apr 04, 2018 9:01 pm

That's all good information. In reading your answer, I wondered: is it kosher to use a combination of interrupts and polling on the same GPIO pin? In my case, I need to be sure the button is held depressed for at least 250ms. The first way of doing this that came to mind was a loop of say, 25 samples, 10ms apart. If any of them return with the wrong value, I break the loop and ignore that pressing.

Another way of doing it would be to change the interrupt to fire on the opposite edge, and wait 250ms for that to occur. If it does; ignore that pressing. Don't know whether this would be considered better or worse...

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

Re: handling GPIO interrupts

Postby kolban » Wed Apr 04, 2018 10:54 pm

If it were me, and assuming what we are doing here is debounce processing. My 1st pass at an algorithm would be as follows:

Code: Select all

while(1) {
   if (state == down) {
      if (gpio is high) {
         do button pressed work
         state = up
      }
   } else (if state == up) {
     if (gpio is low) {
        state = down
     }
   }
   sleep(250ms)
}
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

User avatar
mzimmers
Posts: 643
Joined: Wed Mar 07, 2018 11:54 pm
Location: USA

Re: handling GPIO interrupts

Postby mzimmers » Mon Apr 16, 2018 9:12 pm

Hi Neil - I got pulled off on some TDY and just got back to this today. I borrowed from the logic in your example, and came up with this:

Code: Select all

    for (;;)
    {
        validPress = true;  // innocent until proven guilty

        if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY))
        {
            // probably going to want to disable the interrupt on this pin
            // until we've sent the message and received a reply.
            // will use gpio_isr_handler_remove() to disable the interrupt.

            for (int i = 0; i < READ_INTERVAL; i += SLEEP_INTERVAL)
            {
                gpio_level = gpio_get_level(io_num);
                if (gpio_level != 0)
                {
                    validPress = false;
                    break;
                }
                vTaskDelay(SLEEP_INTERVAL/portTICK_PERIOD_MS);
            }
            if (validPress)
            {
                printf("GPIO[%d] valid button press received. Count: %d\n", io_num, count++);
                // do other stuff based on valid button.
            }
        }
    }
Seems to work, though I'm interested in any feedback.

User avatar
kolban
Posts: 1683
Joined: Mon Nov 16, 2015 4:43 pm
Location: Texas, USA

Re: handling GPIO interrupts

Postby kolban » Mon Apr 16, 2018 10:18 pm

I've got this horrible feeling we are both over thinking it :-)

Lets imagine that an event hits the queue when the button is down ... we should also imagine that there is a debounce time ... lets call it for the sake of a number 500ms during which we might get multiple bounces.

I'm thinking ...

Code: Select all

while(1) {
   wait for event from queue;
   sleep 500ms;
   purge all events from queue;
   if (gpio still shows button is down) {
      // We have detected a valid down press and discarded any debounces ...
   }
}
Free book on ESP32 available here: https://leanpub.com/kolban-ESP32

cspwcspw
Posts: 18
Joined: Mon Apr 16, 2018 5:08 pm

Re: handling GPIO interrupts

Postby cspwcspw » Fri Nov 23, 2018 6:18 am

Hi. Can someone in this forum of expertise explain this ESP32 interrupt behaviour, please? I can work around the problem I have, but I obviously have something misunderstood.

[/code]
20181123_070851.jpg
20181123_070851.jpg (52.86 KiB) Viewed 16832 times
I'm counting fairly slow pulses (a flashing LED which I sense with a light-dependent resistor). My input signal for one pulse is shown at the top trace.

I send some debugging information (a byte counter in range 0-255) to the DAC output on the ESP32. That's shown here on the lower trace.

I trigger an interrupt on the falling edge of my input. I get and clear the interrupt and set my ESP32 DAC level to zero.
Now as my input signal climbs slowly back through the range 1.6v to 1.9v, I get a burst of unwanted interrupts - I count them and reflect the count to the DAC. So the second trace shows the DAC output, which climbs as I get unwanted interrupts. (with modulus 256 wrapping back to zero if I get too many unwanted interrupts - the one attachment below shows about 1500 unwanted interrupts while my source pulse climbs back to its rest voltage).

I've looked at this signal at higher time resolution in case my external source is using some kind of high-frequency PWM, but I cannot find any evidence.

So what exactly does it mean to trigger a GPIO pin interrupt on a "falling edge"? How far does the voltage have to fall, and are there some kind of Schmitt trigger lower and upper bounds? Why am I apparently seeing falling-edge interrupts as my source signal is climbing?

I did swap out the ESP32 just in case - it is not a faulty device :-) And I checked the argument passed into the ISR handler - all the interrupts are arriving from my source, not elsewhere.

Here is the heart of what I did in the code...

Code: Select all

const byte dacPin = 25;
byte dacCount = 0;

void IRAM_ATTR pulseOccurred(void* arg)
{
  int st = GPIO.status;
  GPIO.status_w1tc = st;      // clear the interrupt
  dacCount++;                    // and count it
  dacWrite(dacPin, dacCount); // Make counter externally visible on the scope. 
  
  if (ledState) {             // ledState is my own time-based window to ignore the noise
    pulsesRejected++;
    return;  // filter these out
  }
  
  dacCount = 0;               // When I get a valid pulse, reset the dac to its minimum level.
  dacWrite(dacPin, dacCount);
  pulsesCounted++;
  ledState = true;
  ledOn();
  ledTimeOff = millis() + 50;  // leave my BUILTIN LED on for a fixed time.  My main loop will
                               // turn off the led when the time is ripe, 
                               // and it will reset the ledState flag.
}
My setup does this:

Code: Select all

  gpio_num_t iPin = (gpio_num_t) interruptPin;
  gpio_set_intr_type(iPin, GPIO_INTR_NEGEDGE);  
  gpio_intr_enable(iPin);   // Enable the pin for interrupts

  // Bind the pin interrupt to our handler via the interrupt matrix.  
  // This is potentially many-to-one.
  if (gpio_isr_register(&pulseOccurred, (void*)"pulseOccurred",
                        ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_IRAM, 
                        &pulseOccurredHandle) != ESP_OK)
  {
    sayf("Failed to register interrupt handler!\n");
  }
  else {
    sayf("Interrupt handler registered\n");
  }

  esp_intr_enable(pulseOccurredHandle);
 
Any insights or help would be appreciated.
Attachments
20181123_074806.jpg
20181123_074806.jpg (40.44 KiB) Viewed 16832 times

cspwcspw
Posts: 18
Joined: Mon Apr 16, 2018 5:08 pm

Re: handling GPIO interrupts - I can now see why its happening...

Postby cspwcspw » Fri Nov 23, 2018 8:19 am

The interrupt triggers when the logic level of the GPIO pad changes from 1 to 0 (in the above case).

So what would happen if we simply readDigital() on that line, as fast as possible?
Well it goes 01010101010 as the source comes through the climbing part of my curve. So the problem is that the source digital pad seem to have a "grey area" where the 0 and 1 levels flip about noisily. The interrupts I'm getting are a secondary effect.

Peter

User avatar
loboris
Posts: 514
Joined: Wed Dec 21, 2016 7:40 pm

Re: handling GPIO interrupts

Postby loboris » Fri Nov 23, 2018 8:45 am

@cspwcspw

As far as I know, ESP32 has no Schmitt trigger inputs, so what you get is the expected behaviour.
Maximum voltage for low input is 0.25VDD and the minimum voltage for the high input os 0.75xVDD. When the voltage on the input is beetween those values, you can expect undefined behaviour.
You should either condition your signal externaly or implement some kind of filtering (debounce) in software.

Who is online

Users browsing this forum: No registered users and 108 guests