Skip to content

Strange behaviour in HW interrupt firering on wrong edge #955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
henricazottes opened this issue Dec 29, 2017 · 14 comments
Closed

Strange behaviour in HW interrupt firering on wrong edge #955

henricazottes opened this issue Dec 29, 2017 · 14 comments
Labels
Status: Stale Issue is stale stage (outdated/stuck)

Comments

@henricazottes
Copy link

Hardware:

Board: Wemos Lolin32-lite
Core Installation/update date: 29/jdec/2017
IDE name: Platform.io, Arduino framework
Flash Frequency: ?40Mhz?
Upload Speed: ?115200?

Description:

My interrupt fires multiple time on the wrong edge. It is configured to fire on a FALLING edge (which works fine) but then it also fires multiple times during the RISING edge.

Sketch:

//Change the code below by your sketch
#include <Arduino.h>
int CFG_PIN = 16;

void falling(){
  Serial.println("Falling: " + String(digitalRead(CFG_PIN)));
}

void setup() {
  pinMode(CFG_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(CFG_PIN), falling, FALLING);
  Serial.begin(9600);
}

void loop() {
}

Output:

Falling: 0  <--- Triggered when pressing my button
// waiting some time with the button pressed, and then release the button
Falling: 0
Falling: 0
Falling: 1
Falling: 1
Falling: 1
Falling: 1

Why would I get all this agitation when releasing my button ? and moreover, why would I get the interruption when the state of the pin is HIGH ??

Note that I have a 100nF cap for the debounce, and my edges are clean on the oscilloscope. Is it bad doctor ?

@stickbreaker
Copy link
Contributor

stickbreaker commented Dec 29, 2017

@henricazottes
you cannot reliably call Serial.print() inside an ISR, the Serial.print() can take too long.
Also, you need to apply IRAM_ATTR to any ISR

volatile bool lastState;
volatile int count;
void IRAM_ATTR falling(){
  count++;
  lastState = digitalRead(CFG_PIN);
}

void loop(){
portMUX_TYPE mux=portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&mux); // so that value of count,lastState are atomic
int save=count;
bool currentState=lastState;
portEXIT_CRITICAL(&mux);
if(save!=0){
  Serial.printf("Int Triggered %d times, current State=%u\n",save,currentState);
  portENTER_CRITICAL(&mux); // can't change it unless, atomic
  count = count - save;
  portEXIT_CRITICAL(&mux);
  }
}

Chuck.

Edited to create atomic access to volatile values 29DEC17

@henricazottes
Copy link
Author

henricazottes commented Jan 10, 2018

Thanks for the tip. Just tried it today but still have interruption triggered on the wrong edge with your code

Int Triggered 1 times, current State=0 <--- pressing
Int Triggered 4 times, current State=1 <--- releasing
Int Triggered 1 times, current State=0 <--- pressing
Int Triggered 2 times, current State=1 <--- releasing

I can test the state of the pin to determine if it's a falling or rising edge but why isn't it handled by the intterupt configuration ?

attachInterrupt(digitalPinToInterrupt(CFG_PIN), falling, FALLING <--- );

[edit] ping @stickbreaker

@stickbreaker
Copy link
Contributor

stickbreaker commented Jan 10, 2018

@henricazottes The second line of the output you posted shows that the interrupt trigger 4 times between two executions of loop() It is not triggering on multiple edges of the signal. The signal oscillated from high to low 4 times in quick succession.

Your signal is not a single high to low translation. It is multiple pulses. The ESP32 is fast enough that it can activate that interrupt millions of times a second. When the interrupt triggered you know that the signal went from High to Low. Why do you need to know the current state (level) of the signal?

If you are using the interrupt to monitor a switch you are going to have to debounce the signal. There are multiple ways to debounce it. Create a multi stage interrupt that has to have multiple conditions before it decides that it has detected a valid key press:

volatile bool lastState;
volatile uint32_t debounceTimeout=0;
volatile int count;
void IRAM_ATTR change(){
  count++;
  lastState = digitalRead(CFG_PIN);
  debounceTimeout=xTaskGetTickCount(); //version of millis() that works from interrupt
 }

void loop(){
portMUX_TYPE mux=portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL(&mux); // so that value of count,lastState are atomic
int save=count;
uint32_t saveDebouceTimeout = debouceTimeout;
bool saveLastState = lastState;
portEXIT_CRITICAL(&mux);
bool currentState = digitalRead(CFG_PIN);
if((save!=0) //interrupt has triggered
 &&(currentState==saveLastState) // pin is still in the same state as when intr triggered
 &&(millis()-saveTimeout>10)){ // and it has been low for at lease 10ms, then valid keypress
  if(currentState == LOW)  Serial.printf("Key is press and debounced, current tick=%d\n",millis());
  else Serial.printf("Key is released and debounced, current tick=%d\n",millis());
  Serial.printf("Int Triggered %d times, current State=%u, time since last trigger %dms\n",save,currentState,saveTimeout);
  portENTER_CRITICAL(&mux); // can't change it unless, atomic
  count = 0; // acknowledge keypressed
  portEXIT_CRITICAL(&mux);
  }
}

change the attachInterrupt call to attachInterrupt(digitalPinToInterrupt(CFG_PIN, change, CHANGE);

Chuck.

@usmanshahid001
Copy link

usmanshahid001 commented Jan 25, 2018

i don't think this is problem of debouncing i have used proximity sensor which is giving exactly 1 low to high pulse but interrupt is triggering on both high and low transition so i am having two pulse per 1 low to high pulse from proximity sensor

@alborzs
Copy link

alborzs commented Jul 2, 2018

I can confirm that I have the exact same issue. Also if you start searching there are many threads all over about people experiencing the same issue. I have my signals connected to a oscilloscope and I can confirm there are no de-bouncing issues. I have a clean signal going from 0 to 3.3V, still the esp32 registers a ton of ISRs.

@pctj101
Copy link

pctj101 commented Sep 15, 2018

FYI Even using the ESP-IDF directly (which I assume the arduino leverages to some extent) I found that the ESP32 Rev0 triggers on the "wrong" edge, while the ESP32 Rev1 triggers on the "right" edge.

This is with all wires/program otherwise exactly the same.

Perhaps since the Rev1 works correctly, the Rev0 needs some kind of detection before setting the interrupt (Reverse Falling/Rising for Rev0 Chips)

@cyberman54
Copy link
Contributor

I see same weird effect having a RTC DS3131 SQW/INT output connected to GPIO0, with 10k pullup to VDD. Triggering on falling edge generates 2-3 interrupts on each edge. Checked the signal with oscilloscope proves, the slope is clean, but maybe too slow: 50 microseconds.

@stickbreaker do you know if 50 microseconds slope is maybe too slow?

@stickbreaker
Copy link
Contributor

@cyberman54 50us fall or low pulse? Either should work. If i remember correct the pin intr function has to read the current state of the Gpio port, mask it, then cycle through each assigned intr callback.

@cyberman54
Copy link
Contributor

@stickbreaker triggering on falling edge. I now ended up removing a capacitor from the used gpio pin, this reduced the slope time from 50us to 5us. Now it works, interrupt fires only one time. So, it seems 50us is too much for triggering on GPIO inputs of esp32.

@leoNavarro95
Copy link

I am working on a parallel robot with an esp32, so there is no way to read a button than with hardware interrupt, because the algorithm of inverse kinematics is running all the time.
I have the same problem to read a push-button that is on a joystick. So, any other solution, when I press the button of the joystick, the ISR runs twice and that is not desire. SORRY FOR MY ENGLISH

@lbernstone
Copy link
Contributor

https://www.google.com/search?q=debounce+button+interrupt

@leoNavarro95
Copy link

https://www.google.com/search?q=debounce+button+interrupt

OK, thats help me, thank you very much.
I don't try to find that in google.
This code works perfect for me:
void my_interrupt_handler()
{
static unsigned long last_interrupt_time = 0;
unsigned long interrupt_time = millis();
// If interrupts come faster than 200ms, assume it's a bounce and ignore
if (interrupt_time - last_interrupt_time > 200)
{
... do your thing
}
last_interrupt_time = interrupt_time;
}

Regards, Leo

@stale
Copy link

stale bot commented Jul 31, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the Status: Stale Issue is stale stage (outdated/stuck) label Jul 31, 2019
@stale
Copy link

stale bot commented Aug 14, 2019

This stale issue has been automatically closed. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Stale Issue is stale stage (outdated/stuck)
Projects
None yet
Development

No branches or pull requests

8 participants