Interrupt code error: Message sending twice

For the life of me, I can't figure out why my Uno is doing this. I wrote a simple program that detects an interrupt, and then displays "Got One!" on a serial LCD once the interrupt was detected. But when I run my program and flip the switch to ground the interrupt pin, the message gets sent twice. I've come to the conclusion that I've probably just done something stupid in my program and I'm just not noticing it.

I initially was using the internal pull-up resistor (I think the guide says its 30k) on pin 2 (interrupt pin 0), but after having this problem and reading the tutorial on this site http://www.thebox.myzen.co.uk/Tutorial/Inputs.html and reading that it could still just be noise I disabled the internal pull-up and used my own "stronger" 2k pull-up. But I still got the same problem.

It's a simple circuit with this schematic for the 2k pull-up ( I didn't have a 3k lying around), and then the Tx pin, 3.3v pin, and a GND going to the LCD.

Here's the code. Can anyone help me and tell me why it is sending the message twice?

volatile int detect = 0;
void setup() {
      Serial.begin(9600);
  
      Serial.write(254);            // alerts LCD of incoming command
      Serial.write(83);             // command to reset and clear
      delay(100);           // gives lcd time to reset and clear
      
      attachInterrupt(0, MyInterrupt, FALLING);
      digitalWrite(2, LOW);  // disable pull-up resistor (connected external 2k pull-up)
}

void loop() {
  if (detect == 1) {
      noInterrupts();
      Serial.print("Got One!");
      delay(2000);                 
      detect = 0;                 // reset  "detect"
      interrupts();
  }
}
  
void MyInterrupt() {
  detect ++;
}
  • Thank you

might be bouncing of the switch ? http://en.wikipedia.org/wiki/Switch#Contact_bounce and http://www.arduino.cc/playground/Learning/SoftwareDebounce

Now I'm definitely not an expert on bouncing/debouncing, but I am somewhat familiar with the idea. So I could be way off with my thinking, but if it was due to bouncing (interrupt running more than once because it thinks there's more than one falling edge for one flip of the switch), wouldn't my message not get displayed at all? i.e. If it bounced, my "detect" variable would go from 0 to let's say 2 or 3, and then my if-statement of (detect == 1) would be false: not allowing any message to be sent.

If I am wrong, please let me know.

  • Thanks

Q1: how much time is there between the two messages? Two seconds or ??

I am pretty much sure that this is bounce.

  1. You push the switch → the edge is detected. ISR is triggered. The condition for the IF statement becomes true. You block further interrupts.

This happens within some microseconds.

  1. Now the switch still bounces → interrupt would be triggered but interrupts are blocked → interrupt gets queued. Further interrupts do not get into the queue because it can hold only one event.

  2. While (2) is happening you are printing and waiting 2 seconds

  3. You release interrupts again

  4. The queued interrupt fires.

  5. You output the message a second time, this time the switch does not bounce anymore and thus there is no third message.

Udo

Thank you for the quick responses.

Rob, I’m sorry I didn’t explain it in my initial post, but it sends them what appears to be simultaneously or one right after the other.

Udo, since there isn’t the 2 second delay between the two messages, can it still be because of the bounce? Any ideas?

  • Thanks

First, decide what you're trying to accomplish. Is this a proof-of-concept (e.g. can I write a Sketch that handles interrupts)? Or, do you really intend to have an interrupt handler triggered by a switch?

I need an interrupt handler that will cause my Uno to send serial data. It won’t be a simple switch like I’m using now, it will be a signal from a magentic sensor (that works just like a switch) that sends pulses. In my finished project and without getting into too much detail, the Uno will detect a specified number of pulses, calculate the time between the first pulse and the last pulse, and then send that to the LCD.

I figured it would be a lot simpler for me to start slow, and just use a simple on/off switch to make sure that I completely understood the behavior of the interrupts. I wanted to make sure I could walk before I tried to run. So I’m assuming that I have to be able to make it work with a switch before I use it in my project. But it looks like I’m struggling with the walking part :(.

So to answer the question. No, it’s not just a proof-of-concept. I really need this to actually work. So if anyone has any ideas why my Uno isn’t doing what I want, I’d really appreciate it. I’m stuck…

  • Thanks

:) :) :) It works! It turns out there were 2 problems, but what it basically came down to is that Udo was right. I didn't think he was initially correct because there wasn't a 2 second delay between the messages. But of course (I'm a n00b to the arduinos so I didn't realize how the "delay" works) there wouldn't be a delay because I have that delay function being called 2 lines after the "noInterrupts" function and the "delay" is an interrupt. Therefor: no delay - ISR gets called twice due to bouncing.

But I still had problems after discovering this. The ISR would run again as soon as I re-enabled the interrupts. The answer once again came down to what Udo said: even though interrupts are disabled, the event will still get into the queue (meaning the INT0 interrupt flag will still get set) and as soon interrupts get turned back on, the program jumps to the interrupt vector.

After some searching on this forum, I found I can clear the INT0 flag by writing a 1 to it, and I can do that just with the code "EIFR = 0x01;". I put that one line of code into my loop function at the end and now it works like a charm.

  • Thanks for all the help

Please share woking code :)

@PAKring: the crucial observation to learn this stuff is that you do not need any Arduino specific expertise. What you want to learn is how the controller works. Almost everything you need to know is in the datasheets by Atmel. You want to read those. They are much more helpful then one might expect ;)

Another thing is your explanation of the delay issue. The delay is NOT an interrupt. Hence there must be a different explanation.

My solution would have been to not block interrupts at all. There is an additional issue as well. Any fast bounce after you leave the loop might increment the counter to 2 before you enter it a second time. Thus you should better check for >1 instead of ==1.

And there is something lurking in the dark. The if condition is only atomic if int is a short int. If it is a 2 byte int it would still work but this is somewhat by chance. This is a time bomb waiting for an unsuitable moment to explode. I would protect this part of the loop by blocking interrupts there. The rest is not really critical.

In addition I would not count inside the ISR calls but set the result to a fixed value. Reason: if for whatever strange reason the switch would bounce to produce a wrap around you would not enter the loop as well.

Udo

Thanks for the help (especially Udo). Here's the working code that I have that might help somebody out in the future. I haven't made the changes that Udo said I should make in his last post. I just haven't gotten around to doing them yet. Also, I made a few changes to the function of this program. It detects one switch (from off to on), sends a message that it "Got One" and records the time, detects a second off-to-on interrupt, displays "Got Two" and then displays the time between the two switches.

I know it's a somewhat crude way of doing it, and it's not pretty, but it works..

// ***** This program detects two flips of the switch and then displays the time between the two "ons" *****

volatile int detect = 0;
int StartTime, EndTime, Time, GotOne;


void setup() {
      GotOne = 0;
      
      Serial.begin(9600);
  
      Serial.write(254);                         // alerts LCD of incoming command
      Serial.write(83);                          // command to reset and clear
      delay(100);                                // gives lcd time to reset and clear
      
      attachInterrupt(0, MyInterrupt, FALLING);
      digitalWrite(2, LOW);                      // disable pull-up resistor (connected external 2k pull-up)
}

void loop() {
  if (detect !=0) {
      noInterrupts();
      if (detect == 1 && GotOne == 0) {            // First time switch comes on
          StartTime = millis();
          Serial.print("Got One!");
          detect = 0;
          GotOne = 1;
      }
      if (detect == 1 && GotOne == 1) {            // Second time switch comes on
          EndTime = millis();      
          Serial.print("Got Two!");
          Time = EndTime - StartTime;               // Time between switches
          Serial.print(Time, DEC);                
          detect = 0;                               // reset  "detect"
          GotOne = 0;
      }
      
      EIFR = 0x01;                                  // clears INTO interrupt flag
      interrupts();
  }
}

void MyInterrupt() {
  detect++;
  }