Debouncing pushbutton switch on interrupt pin

I have one of the super-common micro-pc-mount momentary pushbutton microswitches hooked up to one of my interrupt pins to change a runtime parameter. It loops through the possible values and you can see it on the LCD screen. The problem is the switch is bouncy as hell. It generally skips several values every time you push it, and then a couple when you release it. I have the interrupt set to FALLING and have the pull
up resistor set on the microcontroller so pressing the switch grounds the pin.

Any ideas how I can debounce it, either in hardware or software? I was going to put a cap across the switch, but don't know what value to use. What is the nominal value of an Atmega168 pull-up resistor?

I have tried to kludge this out in software but doing something like this

void isr(){

<isr code>

 while(digitalRead(2)==LOW);
  for (long i=0; i<500000; i++); //attempted debouncing

}

It doesn't seem to do much.

As a general rule, you don't want to spend enough time in an interrupt routine to debounce a switch.

Better to spend the 50 cents or so, and do it in hardware.

There are also some techniques here (and a theoretical explanation here).

Ran

In most cases you can put code read switches in a function and call that code from loop. That way you don't use an interrupt and can debounce the switch without worrying about spending too much time in the isr

There are many ways to debounce. you can use any of the debounce code in the tutorials or playground but here is a simple way to do it:

const int inputPin = 2;        // the number of the input pin
const int debounceDelay = 20;  // milliseconds to wait until stable 

// debounce returns the stable switch state 
boolean debounce(int pin)
{
  boolean state;
  boolean previousState; 

  previousState = digitalRead(pin);          // store switch state 
  for(int counter=0; counter < debounceDelay; counter++)
  {
      delay(1);                  // wait for 1 millisecond
      state = digitalRead(pin);  // read the pin 
      if( state != previousState)
      {
         counter = 0; // reset the counter if the state changes
         previousState = state;  // and save the current state
      }
  }     
  return state;   
}

void setup()
{
  pinMode(inputPin, INPUT);
}

void loop()
{ 
  if(debounce(inputPin) == true){
    //  call your function to change values here 
  }

  // your existing loop code here
}

See this tutorial on the Arduino site: http://arduino.cc/en/Tutorial/Debounce.

The software debounce example in the playground is a great tutorial if you want to learn abount software debouncing.

If you need your CPU to other things however it is pretty useless.

Below is a small skecth that will debounce an ISR serviced button (pin 2) with minimal overhead.

/*
 * ISR Debounce
 */

// use 2ms debounce time
#define DEBOUNCE_TICKS (word)microsecondsToClockCycles(2000)

extern volatile unsigned long timer0_overflow_count;
word keytick;  // record time of keypress

void setup() {                
  attachInterrupt(0,KeyPress,FALLING);
}

void KeyPress() {
  keytick=(word)timer0_overflow_count;
}

// returns true if key pressed
boolean KeyCheck() {
  if (keytick!=0) {
    if (((word)timer0_overflow_count-keytick)>DEBOUNCE_TICKS) {
      keytick=0;
      return true;
    }
  }
  return false;
}

void loop()                     
{
  if (KeyCheck()) {
    // do something useful
  }
}
1 Like

A hardware solution would be fine with me. I was just going to bung a capacitor across my switch but needed to know the value of the internal pull-up resistor to calculate an RC time constant of a couple milliseconds or so.

My isr code is already comically bloated. I had some idea that isr's were
supposed to be simple, but it just seemed easier to use the
(available) interrupt pin for the switch and then I wouldn't have to
poll the switch in loop(), which already watches another more
important switch. This switch just changes a parameter, so if it takes
the controller some time to run the isr then it will not affect the
main program as long as it remains some small fraction of a second.

If I remove my software debouncing attempt, I think that it will be quite alright, even though it is probably poor programming practice to run so much code in the isr. But then, I don't know everything there is to know about interrupts so if there are other Bad Things that can happen by having an isr be bloated maybe it would be good to know. Here is the whole bit.

/********************************************
    inputter
*********************************************
this function is the service routine for the second interrupt pin, for the input button.
It increments either the shutter speed or the aperture variable, depending on which mode is set,
when the button is pressed.
*/
void inputter(){
  Serial.print("inputter invoked: ");
    if (mode==1){   //what inputter does depends on which mode you are in, this is aperture mode
      invar++;
      if (invar>=10){
        invar = invar-10; }       //loop values around
        
        input = pow(1.414,invar);  //calculate aperture
        input-=.04;
        input=input*10;          //round, so we don't confuse people with f/5.67 etc
        input = int(input+.5);
        input = float(input);
        input = input/10;
        Serial.print(input);
        lcd.setCursor(0,1);
      lcd.print("   "); //erase previous value
      lcd.setCursor(0,1);
        lcd.print(input);
        lcd.print("   ");
        
      display(storedfreq);    //you see what I did there?
        
          }//endif
    
    if (mode==2){
      invar++;
      if (invar>=13){
          invar=invar-13;
        }
       int speed[14] ={0,1,2,4,8,15,30,60,125,250,500,1000,2000,4000};//conventional shutter speeds don't follow
         input = speed[invar];                     //any logical series unfortunately. so we 
       Serial.print(input);                        //offer the god of convention our sacrificial RAM
         lcd.setCursor(0,1);                        
         lcd.print("   "); 
       lcd.setCursor(0,1);
       lcd.print(input);
         lcd.print("   ");
         display(storedfreq);
       }
    //delay (1); ///trying to cure bouncyness
    

    return;
    //Serial.println("inputter returned");

   }//end inputter

The internal pull-ups are 20k ohms

Your ISR is way to big. I suggest you just set the mode in your ISR and check the mode in loop and call the inputter function if the mode is 1 or 2.

The 'mode' variable is used all over the place in the program; it's not just for the isr; the isr just checks which mode is currently set to decide what to do.

BenF's looks really smart but I don't understand all of it; I see what he did there though. This is what I would think to do:

void setup(){

int newpresses=0;
<setup stuff>

}

void inputter(){  //this is the isr for the input button

newpresses++;

   }//end inputter

void loop(){
if (newpresses){
  <run function that does everything that's all being done in isr now>

newpresses=0;
}//endif

}//end loop

If loop() runs infinitely fast this does nothing for switchbounce but at least gets the code out of the isr. Since my loop() has 100ms of delay in it, this should fix the switchbounce if I'm thinking correctly.

One cheap trick I've used it to simply set a flag in the ISR, check for the flag in the main loop, and service the button press from the main loop. The cheap trick part is the fact that servicing the button press takes more than enough time to deal with debounce. If yours doesn't take that long, add a delay.

-j

I modified my code as I explained in my post #7 and the bounce is totally gone. Well, it's still there I guess, but it's not a problem anymore, which is the whole goal I guess.