Programming an ISR to debounce a switch

I did a Google search using the same parameters of the subject line, and the very first hit returned was an older post from this forum - debouncing an interrupt trigger - Syntax & Programs - Arduino Forum

Admittedly, it was from 2010 but I'm wondering if the fix for the OPs problem is still extant.

I was always told, NEVER use the millis() function, ( or indeed the delay() function ) from within an ISR when calling an external pin ISR ( pins D2 and D3 on the Uno and Nano ).

Like the OP in the linked post, I'm also having problems debouncing a switch connected to pin D2 which, on CHANGE, calls my ISR.

The original OP was instructed to rewrite his ISR as follows:

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;
}

As you can see, there is a millis() function within the ISR, but I always thought millis() interrupts were disabled during an external pin ISR such as the above.

Can someone confirm that something has changed and we can now use the millis() function inside an ISR, or is the above fix no longer valid ?

To save me paging through the 63 million hits on Google about the subject, does anyone have a working fix for debouncing a button on an external pin ISR ( D2 or D3 on Uno ) that I can use, please ?

Regards

It's easy, DO NOT USE INTERRUPTS WITH SWITCHES

Mark

Using the millis() function inside an ISR is perfectly fine. You're basically just reading a value.

There is a timer interrupt behind millis() which runs just under 1000 times per second. The reason for this is Timer0 only counts up to 256. Then it calls an interrupt which adds 256 to a "bigger" counter which is able to store values up to 4294967295 (about 49 days.)

If you turn off interrupts for a long period, then that interrupt doesn't get executed and the millis() 'forgets' a unit of time. "Long" in this context is anything over 2 milliseconds.

If you call millis() while interrupts are disabled, it can check if there's a pending interrupt and add the 256 units back again. So you don't lose any time.

I was always told, NEVER use the millis() function, ( or indeed the delay() function ) from within an ISR when calling an external pin ISR ( pins D2 and D3 on the Uno and Nano ).

You CAN call millis() from inside an ISR. You can call it as many times as you like. It will ALWAYS return the same value, since the clock it is watching doesn't tick while an ISR is running.

That's the same reason you can't use delay() in an ISR.

As you can see, there is a millis() function within the ISR, but I always thought millis() interrupts were disabled during an external pin ISR such as the above.

The interrupt that advances the clock that millis() watches doesn't happen. But, millis() can still see the clock...

Can someone confirm that something has changed and we can now use the millis() function inside an ISR, or is the above fix no longer valid ?

Nothing has changed. You can still use millis() in that way.

Look at what that code is doing. It says compare now to the last time this ISR was called. The clock happily ticked away between the last call to the ISR and this one.

It then says "if the time between this call and the last call was small enough, consider this a valid call".

200 milliseconds of bouncing means you need a better switch. 20 milliseconds is a lot of bouncing.

Thank you all very much for the replies, however I have a couple of points about them:

  1. to Holmes4 - I have to use an ISR for this particular switch, otherwise I'm overly polling for it in the main program. Using the ISR means my main program can do it's thing without continually having to check for the state of the switch.

  2. to PaulS - yes, you are right, 200 milliseconds of bounce is a lot, but my daughter is heavy handed on the momentary switch button, no matter how hard I try to get her to release it sooner, ( she's only 5, give it a few years ), plus it's an old switch I'm using until my newer and more modern ones arrive in the mail, which probably accounts for much of the bounce. A 200 mS bounce check works at this time, without any visible problems with the main program. I'll reduce it as necessary when the new switch is installed.

  3. I came upon an article by Nick Gammon, whose reputation you are probably aware, on his site under the URL Gammon Forum : Electronics : Microprocessors : Interrupts . He gives information in that article on why you should not, or can not, use the millis() or delay() functions in an ISR. HOWEVER, as I now find out, you CAN use the micros() function, which leads me on to my next point:

  4. The following code now works perfectly for me, notwithstanding the issue with using an old switch at the moment. Again, you notice I use a 200 mS check for now, but that will be amended when I install a new switch. Only the relevant parts of the code are displayed below:

const byte buttonPin = 2;
long debounceTime = 200;
volatile unsigned long last_micros;

void setup()
{
  digitalWrite ( buttonPin, HIGH );     // pull up resistor

  attachInterrupt ( digitalPinToInterrupt ( buttonPin ), buttonPressed, CHANGE );  // initialise the ISR

  last_micros = micros();                 // initialise last_micros
}
void loop()
{
  // .... do stuff ....
}

void buttonPressed()
{
  if ( long ( micros() - last_micros ) >= debounceTime * 1000 )
  {
    // handle the ISR routine here then update last_micros

    last_micros = micros();
  }
}

Many thanks to delphino-999, whose article at http://www.instructables.com/id/Arduino-Software-debouncing-in-interrupt-function/ was a help.

With regard to Nick Gammon's article, I'll continue to give millis() and delay() functions a wide berth inside my ISRs in favour, where possible, of the above code.

With regard to Nick Gammon's article, I'll continue to give millis() and delay() functions a wide berth inside my ISRs in favour, where possible, of the above code.

There is no reason not to use millis() in an ISR, as long as you know that millis() continues to report the same value until the ISR ends.

That is one of the reasons that ISRs need to be very, very fast.

Let's get back to basics. Why would you need an ISR to debounce a switch? The simplest way to debounce a switch is to wait a short time (say 50 millisecs) between readings of the switch. And DON'T use delay()

...R

When you are polling a switch there is no need to check it every time through loop(). Put the switch check (and debounce) in a function that you call every 50-100 milliseconds (see blink without delay or several things at a time). You will not miss switch presses and you don't use (waste) an interrupt. And if you use state change detection, you may not need debounce.

Unlike people, the Arduino never gets bored. It does not care if you do a digitalRead() 20,000 times per second for a year waiting for one button press.

Calling millis() to decide if you should do a digitalRead is a giant waste of time. millis() is actually a slow function due to the long division operation that it must do. Just make sure your loop() executes fast enough to get an acceptable period between reads and read the pin.

MorganS:
Calling millis() to decide if you should do a digitalRead is a giant waste of time.

I beg to disagree when the purpose of the interval is to avoid switch bounces.

...R