Interrupt (RISING) - triggers twice

To the Arduino port I have connected a piezo sensor. My purpose is to count the number of times the sensor is actually pressed (RISING).

Therefore I connected the sensor output to pin D3 and wrote a sketch with INTterrupt handling (see below). The interrupt should be triggered on the RISING signal. This is what it actually does, however the interrupts always are triggered twice.

Afai can see it is not related to bouncing (see attached osc. picture).
Does anybody have an idea why it is triggered twice?

Thanks

volatile bool Int_D3_Flag = false;   
uint16_t intCount = 0;

void setup(void) {
  Serial.begin(9600);
  pinMode(INTD3, INPUT_PULLUP);     // wakeUp pin
} 
// ************************* End of setup *******************************************

void loop() {
  Serial.println("");
  Serial.print("Loop: Starting loop with intFlag = ");
  Serial.println(Int_D3_Flag);

  attachInterrupt(digitalPinToInterrupt(INTD3),wakeUpNow, RISING);
  sleepLight();                                                           // Stay here till INT is detected or sleeping period is over
  detachInterrupt(digitalPinToInterrupt(INTD3));
  
  if (Int_D3_Flag == true)
  {
    Int_D3_Flag = false;
    intCount++;
    Serial.print(F("\t Loop: INT count (PIN D3): "));
    Serial.println(intCount);
    delay(50);
  }
  else {
    Serial.print(F("\t Loop: No interrupt received / just woke up. INT count at: --> "));
    Serial.println(intCount);
    delay(50);
  }
    
  // Clear INT flags 
  EIFR = bit (INTF0);  // clear flag for interrupt 0 (PIN D2)
  EIFR = bit (INTF1);  // clear flag for interrupt 1 (PIN D3)
  
} // end of loop function


void sleepLight(void)
{
  int j = 1;

  Serial.println(F("sleepLight: sleep & wait"));
  delay(100);
  
  while((Int_D3_Flag == false) && (j <= 15))
  {
    LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
    j++;
  }
} // end of sleepLight

void wakeUpNow()
{
    Int_D3_Flag = true;
} // end of wakeUpNow

Which Arduino board are you using ?

How do you know that the interrupt is triggered twice?

I am using the Arduino Uno / 328p.

When the piezo sensor is activated once (1 RISING signal) the Int_D3_Flag is set twice to TRUE. Based on that I assume the interrupt is triggered twice (I cannot find another explanation)

When the piezo sensor is activated once (1 RISING signal) the Int_D3_Flag is set twice to TRUE. Based on that I assume the interrupt is triggered twice (I cannot find another explanation)

I have another explanation: After handling all the interrupt based code you're setting the EIFR flag again, which triggers the interrupt routine. If you have the external interrupt enabled (so the interrupt handler is called) the flag is cleared by the hardware when entering the interrupt handler. So you must not set it again otherwise the interrupt handler is triggered.

pylon:
I have another explanation: After handling all the interrupt based code you're setting the EIFR flag again, which triggers the interrupt routine. If you have the external interrupt enabled (so the interrupt handler is called) the flag is cleared by the hardware when entering the interrupt handler. So you must not set it again otherwise the interrupt handler is triggered.

I have tried without setting the EIFR flags as well and the result is still that the interrupt is triggered twice.

In my opinion setting the EIFR flag does NOT trigger an interrupt but it actually clears the flag. In the Atmel specs the following is written:

"The flag is cleared when the interrupt routine is executed. Alternatively, the flag can be cleared by writing a logical one to it. This flag is always cleared when INT1 is configured as a level interrupt"

Things are not so clear if you use attach/detachInterrupt() :frowning:

I'd only attach the interrupt once, and never detach it. For deactivation use a global flag, that can be tested in the handler.

DrDiettrich:
Things are not so clear if you use attach/detachInterrupt() :frowning:

I'd only attach the interrupt once, and never detach it. For deactivation use a global flag, that can be tested in the handler.

Before I asked my question here on this forum I have tried all combinations. Also to use attachInterrupt() only and not detaching. However the result is the same - get the interrupt twice.

And I am really puzzled and curious how to solve this.

Then I’d check the piezo signal again. You have shown one pulse, which may be followed by a second pulse. Can you give a circuit diagram of the piezo interface?

DrDiettrich:
Then I'd check the piezo signal again. You have shown one pulse, which may be followed by a second pulse. Can you give a circuit diagram of the piezo interface?

Thanks a lot for thinking along. What I did to get a proper signal into the Arduino is the following:

  • Opamp 1: Amplifies the signal (piezo delivers small signal)
  • Opamp 2: Comparator opamp

The result I see on my scoop is a nice "pulse" (40-50 ms wide). With no other pulses afterward. I can send some other images from my oscilloscoop.

How is the piezo forced to produce a signal?

DrDiettrich:
How is the piezo forced to produce a signal?

There are different ways of doing so … can do it by foot, hand etc. Why do you believe it is relevant and influence the interrupt behavior?

In attached screenshot I pressed the piezo for 5 times (total elapsed time of 5 seconds). The Arduino generates 10 interrupts.

This is suspect:

  // Clear INT flags
  EIFR = bit (INTF0);  // clear flag for interrupt 0 (PIN D2)
  EIFR = bit (INTF1);  // clear flag for interrupt 1 (PIN D3)

The first writes a 0 to the D3, and a 1 to the D2 interrupt. The second does the opposite. So that may trigger the second interrupt you see.

You should write only those two specific bits to the register, without affecting any other bits. Try this instead:

  EIFR |= ((1 << INTF0) | (1 << INTF1)) ;  // clear flag for interrupt 0 (PIN D2) and interrupt 1 (PIN D3).

That code writes a 1 to the INTF0 and INTF1 bits, without affecting any other bits in the register. The brackets are redundant, just added them for easier readability.

Nick Gammon also has this solution at https://gammon.com.au/interrupts without explicit manipulation of EIFR

Correction:

The improved version below solves this problem by disabling interrupts before doing the attachInterrupt call. Since you are guaranteed that one instruction will be executed after re-enabling interrupts, we are sure that the sleep_cpu call will be done before the interrupt occurs.

#include <avr/sleep.h>                  

// interrupt service routine in sleep mode
void wake ()
{
  sleep_disable ();        // first thing after waking from sleep:
  detachInterrupt (digitalPinToInterrupt (2));      // stop LOW interrupt on D2
}  // end of wake

void sleepNow ()
{
  set_sleep_mode (SLEEP_MODE_PWR_DOWN); 
  noInterrupts ();          // make sure we don’t get interrupted before we sleep
  sleep_enable ();          // enables the sleep bit in the mcucr register
  attachInterrupt (digitalPinToInterrupt (2), wake, LOW);  // wake up on low level on D2
  interrupts ();          // interrupts allowed now, next instruction WILL be executed
  sleep_cpu ();            // here the device is put to sleep
}  // end of sleepNow

Also, for something which produces a voltage like a piezo sensor, I’d have tended to have an external pull down resistor instead of a pullup, however, if you are now using an op amp to condition the signal, that may not now help.

I guess it's a combination of the attach/detachInterrupt calls and the wrong sleeping mode. According to the datasheet edge detection of external interrupts needs the I/O clock which is not available in power down sleep. Only a level trigger can wake the processor from power down state. This would mean the processor wakes from the powerdown sleep only by the watchdog interrupt which is set to 8s in the code we saw. So either the code posted yet is not the one used for test with the signal posted in answer #11 or the processor doesn't act exactly as the datasheet describes when edge triggered interrupts are configured and a power down sleep is scheduled.

Have you tried the sketch without the sleep code and the detach/attach calls?

BTW: Why are you setting the pin to INPUT_PULLUP? In my understanding of your external circuit the interrupts gets a clear LOW/HIGH signal. Am I wrong?

pylon:
. . .
According to the datasheet edge detection of external interrupts needs the I/O clock which is not available in power down sleep. Only a level trigger can wake the processor from power down state
. . .

The data sheet for the Atmega328p has an error in this respect. These processors can be woken by any interrupt type. Not true however with the ATTiny series.
Also from https://gammon.com.au/interrupt :

Tip:
I received this confirmation from Atmel that the datasheet is wrong about only LOW interrupts waking the processor.

Quote:

Commented by Manoraj Gnanadhas (Atmel)
2015-01-20 06:23:36 GMT
[Recipients: Nick Gammon]

Hello Nick,

Our design team has confirmed that “Note-3 mentioned under Table 10-1” is a datasheet bug. So you can use any type of interrupt (Rising edge/ Falling edge / Low level / Any logical change) to wake up from sleep mode. Sorry for the inconvenience caused.

Best Regards,
Manoraj Gnanadhas

Thus, all interrupt types will wake the processor.

@wvmarle: According to my interpretation of the specs writing a logical zero in the External Interrupt Flag Register does not trigger anything (only the logical one does). Nevertheless I agree that your code is cleaner :slight_smile:

pylon:
I guess it's a combination of the attach/detachInterrupt calls and the wrong sleeping mode. According to the datasheet edge detection of external interrupts needs the I/O clock which is not available in power down sleep. Only a level trigger can wake the processor from power down state. This would mean the processor wakes from the powerdown sleep only by the watchdog interrupt which is set to 8s in the code we saw. So either the code posted yet is not the one used for test with the signal posted in answer #11 or the processor doesn't act exactly as the datasheet describes when edge triggered interrupts are configured and a power down sleep is scheduled.

In all sleep modes external interrupts (INT1 / INT0) can wake up the processor.
Atmel specs:

  • Chapter 13 External Interrupts: “This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode. The External Interrupts can be triggered by a falling or rising edge or a low level”
  • Table 10-1: “For INT1 and INT0, only level interrupt” (in power down modes)

@6v6gt – thanks for the info. I checked the specs and was puzzled as my experience is that the processor always (!) wakes up immediately when I have edge detection interrupts and sleep activated (just the 8SEC cycle).

pylon:
Have you tried the sketch without the sleep code and the detach/attach calls?

BTW: Why are you setting the pin to INPUT_PULLUP? In my understanding of your external circuit the interrupts gets a clear LOW/HIGH signal. Am I wrong?

Yes I have tried the code without SLEEP as well and I get the exact same behavior. The INPUT_PULLUP indeed is not required in my case. Even when stripping the code to a minimum, changing to a new Uno I see interrupts twice. So there must be something which I am doing wrong?

Yes I have tried the code without SLEEP as well and I get the exact same behavior. The INPUT_PULLUP indeed is not required in my case. Even when stripping the code to a minimum, changing to a new Uno I see interrupts twice. So there must be something which I am doing wrong?

Please post that stripped down code.

And just to get a clear statement: the scope pictures show the signal of the pin 3 on the UNO and not a signal from somewhere else in your circuit. Is that correct? If not, post schematics of your setup and explain where your got the signal.

Buffel:
@wvmarle: According to my interpretation of the specs writing a logical zero in the External Interrupt Flag Register does not trigger anything (only the logical one does). Nevertheless I agree that your code is cleaner :slight_smile:

Always tricky those registers.

Key difference between my code and your original though is that the other 6 bits of the register are unaffected. This may or may not make a difference, in general it’s a good idea to not touch unused bits.

All in all it looks like a rather complex issue that you ran into! So you get it without any sleep code? Do post your most barebones code that still shows the problem.

pylon:
Please post that stripped down code.
. . .

Exactly.
Also with library include statements which were missing in the OP. I'm also very curious about this. I'd also start to think about using native AVR commands instead of RocketScream (or similar) helper functions.