(resolved) handling GPIO interrupts
(resolved) Re: handling GPIO interrupts
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.
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.
Re: handling GPIO interrupts
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.
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
Re: handling GPIO interrupts
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...
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...
Re: handling GPIO interrupts
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
Re: handling GPIO interrupts
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:
Seems to work, though I'm interested in any feedback.
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.
}
}
}
Re: handling GPIO interrupts
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 ...
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
Re: handling GPIO interrupts
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] 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...
My setup does this:
Any insights or help would be appreciated.
[/code] 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.
}
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);
- Attachments
-
- 20181123_074806.jpg (40.44 KiB) Viewed 16832 times
Re: handling GPIO interrupts - I can now see why its happening...
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
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
Re: handling GPIO interrupts
@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.
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