Go Down

Topic: Interrupt (RISING) - triggers twice (Read 556 times) previous topic - next topic

Buffel

May 17, 2018, 08:02 am Last Edit: May 17, 2018, 08:08 am by Buffel
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

Code: [Select]

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



UKHeliBob

Which Arduino board are you using ?
Please do not send me PMs asking for help.  Post in the forum then everyone will benefit from seeing the questions and answers.

DrDiettrich

How do you know that the interrupt is triggered twice?

Buffel

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)

pylon

Quote
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.

Buffel

#5
May 17, 2018, 11:07 am Last Edit: May 17, 2018, 11:08 am by Buffel
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"


DrDiettrich

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

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

Buffel

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

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.

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?

Buffel

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.

DrDiettrich

How is the piezo forced to produce a signal?

Buffel

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.

wvmarle

This is suspect:

Code: [Select]

  // 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:
Code: [Select]

  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.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

6v6gt

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


Quote
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.

Code: [Select]


#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.

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.

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?

Go Up