Introduction: Arduino + Push Switch + Debouncing + Interrupts

Picture of Arduino + Push Switch + Debouncing + Interrupts
Since I've started using the Arduino I've loved it. Although I found one thing very complicating with the Arduino that I couldn't figure out why it was happening until today, literally today. What is it? It's connected a push button to an Arduino interrupt pin to make it change certain variables. 

The application that I am working on requires the use of 2 push buttons, each will link into an Arduino (as an interrupt) and control the current position of the motor. I have 6 positions that I want the motor in. 

I originally set up my circuit like so: 


hi


The problem with this setup was when the button was pressed the interrupt was being called multiple times and even toggling other buttons. Why was this happening? It is caused from a switch bouncing feedback. To fix this, you need to modify your circuit around this manner below: 


hi2


What this did was when the push button was on (=1) the pin was HIGH and only HIGH. It never bounces around like it used to. 

-----

The project that I was working on had the following schematic (created with PSpice Student 9.1). 


hi3


The pin configuration is as follows: 
  • PIN 2 - Push button 1 (increase motor position)
    • interrupt 0
  • PIN 3 - Push button 2 (decrease motor position)
    • interrupt 1
  • PIN 22 - LED 1 (position 1)
  • PIN 24 - LED 2 (position 2)
  • PIN 26 - LED 3 (position 3)
  • PIN 28 - LED 4 (position 4)
  • PIN 30 - LED 5 (position 5)
  • PIN 32 - LED 6 (position 6)

What this application is going to do is start off in position 1 then as you push button 1, the LED will change based on the new position. I call these positions gears. Here is the Arudino code... it's really straight forward. 






const int led6 = 32;
const int led5 = 30;
const int led4 = 28;
const int led3 = 26;
const int led2 = 24;
const int led1 = 22;
volatile unsigned int current_gear = 1;

volatile long long timeout = 3000; // 3 seconds
volatile long long last_change_time = 0;

void loop()
{
  switch(current_gear)
  {
    case 1:
      digitalWrite(led1,HIGH);
      digitalWrite(led2,LOW);
      digitalWrite(led3,LOW);
      digitalWrite(led4,LOW);
      digitalWrite(led5,LOW);
      digitalWrite(led6,LOW);
      break;
    case 2: 
      digitalWrite(led1,LOW);
      digitalWrite(led2,HIGH);
      digitalWrite(led3,LOW);
      digitalWrite(led4,LOW);
      digitalWrite(led5,LOW);
      digitalWrite(led6,LOW);
      break;
    case 3: 
      digitalWrite(led1,LOW);
      digitalWrite(led2,LOW);
      digitalWrite(led3,HIGH);
      digitalWrite(led4,LOW);
      digitalWrite(led5,LOW);
      digitalWrite(led6,LOW);
      break; 
    case 4: 
      digitalWrite(led1,LOW);
      digitalWrite(led2,LOW);
      digitalWrite(led3,LOW);
      digitalWrite(led4,HIGH);
      digitalWrite(led5,LOW);
      digitalWrite(led6,LOW);
      break;
    case 5: 
      digitalWrite(led1,LOW);
      digitalWrite(led2,LOW);
      digitalWrite(led3,LOW);
      digitalWrite(led4,LOW);
      digitalWrite(led5,HIGH);
      digitalWrite(led6,LOW);
      break;
    case 6: 
      digitalWrite(led1,LOW);
      digitalWrite(led2,LOW);
      digitalWrite(led3,LOW);
      digitalWrite(led4,LOW);
      digitalWrite(led5,LOW);
      digitalWrite(led6,HIGH);
      break;
  }
}
void setup()
{
  Serial.begin(9600);
  pinMode(led1,OUTPUT);
  pinMode(led2,OUTPUT);
  pinMode(led3,OUTPUT);
  pinMode(led4,OUTPUT);
  pinMode(led5,OUTPUT);
  pinMode(led6,OUTPUT);
  attachInterrupt(0,up,RISING); // digital pin 2 // up
  attachInterrupt(1,down,RISING); // digital pin 3 // down
}

void up()
{
  Serial.println("-------------GOING UP-------------");
  Serial.print("Current Time - ");
  Serial.println(millis());
  Serial.print("Last Change Time - ");
  Serial.println((long) last_change_time);
  int difference = millis()-last_change_time;
  Serial.print("Difference - ");
  Serial.println((long) difference);
  Serial.print("Within Threshold? - ");
  if(difference > timeout || last_change_time == 0)
  {
    Serial.println("YES");
  }
  else
  {
    Serial.println("NO");
  }
  Serial.print("Previous Gear = ");
  Serial.println(current_gear);
  if((current_gear > 0 && ((millis()-last_change_time)>timeout)) || last_change_time == 0)
  {
    if(current_gear <= 5 && current_gear != 6) 
    {
current_gear++;
last_change_time = millis();
}
}
Serial.print("New Gear = ");
Serial.println(current_gear);
delay(150);
}
void down()
{
Serial.println("-------------GOING DOWN-------------");
Serial.print("Current Time - ");
Serial.println(millis());
Serial.print("Last Change Time - ");
Serial.println((long) last_change_time);
int difference = millis()-last_change_time;
Serial.print("Difference - ");
Serial.println((long) difference);
Serial.print("Within Threshold? - ");
if(difference > timeout)
{
Serial.println("YES");
}
else
{
Serial.println("NO");
}
Serial.print("Previous Gear = ");
Serial.println(current_gear);
if(current_gear > 0 && ((millis()-last_change_time)>timeout))
{
int tempGear = current_gear - 1;
if(tempGear != 0)
{
current_gear--;
last_change_time = millis();
}
}
Serial.print("New Gear = ");
Serial.println(current_gear);
delay(150);




There you have it, a switch debouncer that will work in any case. This tutorial was customized specifically for the Arduino! Enjoy!!

 

Comments

Orbitalgun (author)2011-10-27

This is a pretty good instructable, but shouldn't your Hex inverter actually be a 7414 Schmitt trigger? Also what diode are you using?

rlogiacco (author)Orbitalgun2014-07-22

Actually there's no need for the inverter at all, just switch the button logic in software: LOW on digitalRead means 'button pressed', HIGH means 'button released'.

AlphaOmega1 (author)rlogiacco2017-08-05

It's not quite that simple. The problem is the "bounce" when you close a switch, the contacts actually bounce, making and breaking the contact many times in a few mS, which is an age for an MPU! we are effectively slowing down time and so "see" the bounce!

Mercury whetted contacts, sliding contacts etc. can help (this is a big can of worms!).

But as I posted elsewhere (unless MPU time is at a real premium), then software debouncing is a simple and effective measure.

In its simplest form you can do the following.

(assume a momentary press button, but there are different types of switches and the software may be looking for different press lengths etc.)

if contact{

pause x mS (this is the equiv of an RC network)

it's stopped bouncing by now!

check it's still down (could have been an accidental contact - too short for our needs) so exit.

Loop until the switch is open again, if time-out then exit

(user is sensible and released the button! yippeeeeee)

Indicate a valid button press and exit!

}

But we can be much more subtle when we start to us interrupts and control those interrupts!

There are many permutations and techniques for what on the face of it is a simple button press!

DIY-Guy (author)Orbitalgun2011-11-22

Is there a way to debounce with a small cheap capacitor?

rlogiacco (author)DIY-Guy2014-07-22

One capacitor and two resistors is all you need to do hardware debouncing. A capacitor only will produces current spikes that might damage your Arduino if you power it from your board through the voltage regulator.

DIY-Guy (author)rlogiacco2014-08-18

Thank you for the diagram RioGiacco.

AlphaOmega1 (author)2017-08-05

Very nicely presented.

What is U1? You don't give an ID (that I can see), a Schmitt might be even better (say CD4093B) . You could of course create a monostable with a better output characteristic using two gates of a CD4000B/7400 rather than one gate.

Did your first circuit work? with a N/O switch, and 10K, it's going to keep the pin pretty high! I would have put the resistor in the 5V line.

One might use this technique with cmos or TTL logic but...

The thing about using microcontrollers is that you can remove a lot of hardware with code!

So, if you connect the switch directly to the pin (2) and the other side to ground using a weak pull up, you only need a switch!

This gives the pull up....

pinMode(interruptPin, INPUT_PULLUP);

Then turn off the interrupt once triggered.

e.g.

button press (negative transition)

having used attachInterrupt(digitalPinToInterrupt(interruptPin), checkSwitch, LOW); to set up the interrupt

checkSwitch being your interrupt service routine

enter the interrupt routine

turn off the interrupt with

detachInterrupt(digitalPinToInterrupt(interruptPin));

set buttonPressedFlag (volatile of course)

exit the interrupt

In the main loop restart the interrupt if buttonPressedFlag with

attachInterrupt(digitalPinToInterrupt(interruptPin), checkSwitch, LOW); again

and remember to clear buttonPressedFlag (you may want to check for button up first)

.

May I suggest that you keep your ISRs as short as possible, and process everything elsewhere. You can of course explicitly change the direction of the interrupt detection to identify when the switch is released.

This technique works beautifully with rotary encoders. You will see many people using all manner of conditioning circuits, but you only require two ISRs to give jitter-free output.

I hope that's useful


Zephter (author)2017-07-17

What about an IC555 as a switch debouncer?

Zephter (author)Zephter2017-07-18

Invert the output to work with the input and change the 1uF to a 4.7uF cap.

Work fine if you dont need it to switch more than twice a second.

Phil_S (author)2016-01-14

Shame the author gone to ground.

7404 or 7414? Big difference. How about a parts list?

74XX14 Schmitt always helps

gussmith (author)2015-12-11

I am using a similar hardware debouncer like on http://hackaday.com/2015/12/09/embed-with-elliot-debounce-your-noisy-buttons-part-i/ But when using this in an interrupt, the interrupt is still called multiple times. Between 1 and 5 times when pressing the switch.

gussmith (author)gussmith2015-12-11

Btw, in my interrupt I only toggle a flag. "flag = !flag" nothing else. And it is still called multiple times using this hardware debouncer.

RomanM5 (author)2015-06-08

Maybe this is a good solution for end products, but I don't think for hobbyist and new comers like me this is suitable - because it is too complicated.

Will it be easier to use internal pull ups on button pin and connect it to GND when pushed, then just software debounce?

wfw (author)2014-07-12

Looks good but I would've just done debouncing in code. Simply store the timestamp of the most recent button press and ignore wanted and unwanted button presses for a fixed period of time which is greater than the bouncing time.

About This Instructable

60,588views

25favorites

More by mtrimm:Setup Daisy Chained RAID with LaCie's Little Big Disk ThunderboltArduino + Push Switch + Debouncing + Interrupts
Add instructable to: