Become a subscriber (Free)

Join 29,000 other subscribers to receive subscriber sale discounts and other free resources.
:
:
Don't worry -- youre-mail address is totally secure. I promise to use it only to send you MicroZine.

Switch DeBouncing

Switch Debounce or "how to stop switch bounce" which is caused by the contacts in any mechanical switch bouncing off each other when you press the switch (and of course there's a spring in there which adds more bounce). Different switches cause different bounce characteristics, and even how hard you press the switch changes them as well.

The diagram below shows an input push button switch connected to a microcontroller input. Normally the input button closes to ground and a pullup is used at the input - microcontrollers usually provide an internal pullup so you won't always see the resistor shown below (some pins don't have internal pullups).

When pushed the microcontroller input is low, and when released the input value is high.Example switch bounce signal at microcontroller input


When you write code for the microcontroller to read an input switch you will be expecting it to go low only once (the ideal switch!), so you may have thought that a simple read of the port would be enough - its not! In fact the switch bounce can last for several milliseconds and the bounce periods can be 10's of nanoseconds or even microseconds so the microcontroller will think that the button is pushed multiple times.

If you are trying to increase a variable by 1 you don't want it to increase by a random amount!

Switch Bounce Demonstration Code

The program below lets you see switch bounce transitions when an input button is pressed and released i.e. the output shows you what the microcontroller is actually seeing as an input signal.

The variable b is set to read the input every time round the loop (since the input is pulled up by the internal pullup) the button pulls it low so pressing a key sends zero to the microcontroller input.

If this value is not the same as the last read of b (then b is printed out). So this prints out every change of input that is read by the microcontroller. The text "Key Stable" is only output once the value of b has not changed for greater than 500ms i.e. if you push and hold the key, the routine detects that the input is high and not changed after 500 ms, and outputs that text.

The results of the program are shown below the code.

Code for the Arduino Uno with a button connected across pin 8 and ground.

#define BUTTON_PIN 8

void setup(void) {

   pinMode(BUTTON_PIN, INPUT_PULLUP);   

   Serial.begin(250000);
}

void loop(void) {
static byte lastb = 0, bstate = 0;
static byte printNextStable=0;
static uint32_t lastUnstable = millis();

   byte b = !digitalRead(BUTTON_PIN);  // Pulled up so zero = hit.

   if (lastb != b) {
      Serial.println(b);
      printNextStable = 1;
      lastUnstable = millis();
   }
   lastb = b;

   if (printNextStable && millis()-lastUnstable>500) {
      Serial.println("Key Stable"); printNextStable=0;
   }
}

Key Stable
1
0
1
0
1
0
1
Key Stable
0
1
0
1
0
1
0
1
0
1
0

These results are for pressing the button ONCE and holding, then releasing the button ONCE! As you can see there is lot of switch bounce. If you try this for yourself you will see a lot of variation - sometimes no bounce and sometimes a lot.

Since there are a different number of bounces each time it means that the button is taking time to settle and this bounce time can vary a lot. Some switches bounce more than others so looking at the output using a DSO will allow you to see how long the bounce lasts (or you  could add a millis() display code to the above code).

Delay Switch Debouncers

The simplest way of implementing switch debounce is to detect the first key press, wait a while and see if is still the same. If it is then the key is valid, and if not you ignore it. This is an extremely common method for switch debouncing.

It sounds like the ideal debouncer but there is a problem with it!

Problem with Delay Time switch debounce

It's that word 'delay', and the simplest delay method is to use the delay function but that means that the processor is held up doing nothing since the delay function is a do-nothing function.

The more subtle problem with this decode method is that you need to vary the delay time based on the switching characteristics that the switch exhibits, so you may end up with a delay time of 100ms just because of the type of switch you used!

This is time that could be spent doing something else, and in some cases you can not afford to allow delays in a program.

Code for a delay switch debouncer

#define BUTTON_PIN 8

void setup(void) {

   pinMode(BUTTON_PIN, INPUT_PULLUP);
   pinMode(LED_PIN,OUTPUT);

   Serial.begin(250000);
}

void processor_operation(void) {}

void loop(void) {
byte lastb = 0, b;


   // Check for keypress
   if ( b = !digitalRead(BUTTON_PIN) ) {          // Pulled up so zero = hit.

      delay(50);

      if (b == !digitalRead(BUTTON_PIN))
         Serial.println("Key Stable");

      while(!digitalRead(BUTTON_PIN)); // wait for low
   }

   // Processor can operate here but
   // only after the above delays finish if triggered by a button press.
   processor_operation();

}

Note how the microcontroller can do nothing while waiting for the delay time 'delay(50)'.

Timer alternative to delay() debounce

One way around this problem is to use a non blocking delay - this is a delay method that uses the millis() timer to record when events happen. Since the delay times are stored your program can exit that function and return to it later - 'non blocking'. To make it work you will need a state machine similar to the one below.

The code works by storing a future time to re-check the key input. The loop function below is continually re-entered so a state machine is needed to keep track of the required program actions. These actions depend on the stored time - in this case 30ms after the 1st key press.

Code for Timed Switch Debouncer

In state 0 you are waiting for the 1st keypress. In state 1 you are waiting for a delay time after the first keypress and then re-checking the key. If the key has remain pressed then the output "Key pressed" is generated. The last state (2) is entered to stop multiple "Key pressed" outputs and simply waits for the key to be released, after which the state is set to 0 - back to the start.


void setup(void) {

   pinMode(BUTTON_PIN, INPUT_PULLUP);
   pinMode(LED_PIN,OUTPUT);

   Serial.begin(250000);
}

#define STATE_WAIT_KEY  0
#define STATE_CHECK_KEY 1
#define STATE_WAIT_KEY_RELEASE 2

void loop(void) {
static byte state = STATE_WAIT_KEY;
static uint32_t startKeyPress,stopKeyPress;

   byte b = !digitalRead(BUTTON_PIN);          // Pulled up so zero = hit.

   if (state == STATE_WAIT_KEY && b==1) {
      startKeyPress = millis();
      state = STATE_CHECK_KEY;
   }

   // After n milliseconds, is the key still pressed?
   if (state == STATE_CHECK_KEY && ( millis()-startKeyPress) > 30) {
       if (b==1) {
          Serial.print("Key pressed ");   Serial.println(millis());
          state = STATE_WAIT_KEY_RELEASE;
       } else state = STATE_WAIT_KEY;
   }

   if (state == STATE_WAIT_KEY_RELEASE && b==0) state = STATE_WAIT_KEY;
}
Note: In the above code b is the inverted input value, so (1=pressed & 0=released).

Originally I set the debounce delay to 5 (5ms) but the key bounce caused multiple output so I set it to 10ms. Then that was not good enough (multiple keypress detected) so I set it to 30ms. Other switches behave better and you can get away with 1ms!

This is the problem with delay type methods - you have to characterise the switch in use and adjust the code to suit it. The other problem is that the routine is getting complicated.

The reason is that the code can be placed in a function and that function will not block the processor from doing other work.

Gansel Switch debounce

I came across this debouncing method from university notes (source Jack Gansel : Guide to Debouncing) and it is really a very clever piece of code and the essential part of it is this:

uint16_t btndbc;

btndbc=(btndbc<<1) | digitalRead(BUTTON_PIN) | 0xe000;

In one go, a button is reliably debounced and there's no delay function in sight!

However when you first see it you won't know why it works, so it needs a bit of deconstruction to understand as there is a lot of code in one line.

First of all the line above is designed to be called regularly so you either put it into the loop() function or call it regularly via a timer interrupt.

At each call of that line the storage element is shifted left by one bit and a new value of the input pin is 'ored' into the lowest bit. So those two commands are creating a shift register with the lowest bit fed in as input.

After that the result is 'ored' with 0xe000.

Note that the digital input pin is low for a key-press.

The next part of the code reads as follows:

if (btndbc==0xf000) 
      button_press_action();

So this is saying that if the store 'btndbc' reads 0xf000 then the button has been debounced and is valid. This can only be true if the following hex number has been accumulated in btndbc:
0x1000 i.e. the input has had a 'one' followed by 12 'zeros' accumulated around the loop. When ored with 0xe000 you get the value 0xf000. (0xe000 is masking off the top 3 bits as not used)

If at any time around the loop during the accumulation phase you get a one then the debouncer will not trigger since the value is not 0xf000. e.g. 0x1004 would fail i.e. the input bounced while accumulating.

When bouncing has stopped there will be a first 'one' followed by 12 'zeros' i.e. debounced.

If you use this code in the loop() function then you have to have some time spent doing other work so that you don't call the debouncer too fast - either processing or add some delay! This delay will be the time between accumulation of input data in the debouncer (time between calls to the debouncer routine).

Code for Gansel Switch Debouncer

Here is the code for real, in action. Note how simple it looks compared to the delay timer non-blocking method, and it works!

#define BUTTON_PIN 8

void setup(void) {
   pinMode(BUTTON_PIN, INPUT_PULLUP);
   pinMode(LED_PIN,OUTPUT);

   Serial.begin(250000);
}

/////////////////////////////////////////////////////////////////
void button_press_action(void) {
  Serial.println("BUTTON PRESS");
}

/////////////////////////////////////////////////////////////////
// Debounce a button.
void button_press_and_action(void) {
static uint16_t btndbc = 0;

   btndbc=(btndbc<<1) | digitalRead(BUTTON_PIN) | 0xe000;

   if (btndbc==0xf000) { // High for 1 bits low for 12 (each bit is 1 loop thru this code).
      button_press_action();
   }
}

void loop(void) {
   button_press_and_action();
   delay(1);
}

Note that the above debouncer does not itself use delays but actually needs a delay (or timer) to function. The debouncer is very useful for quickly and reliably debouncing an input and uses a very small code footprint.

Demo of Output States from Gansel Decoder

The following code shows the output states of btndbc showing how the shift register (btndbc) is filled as the loop is iterated.

#define BUTTON_PIN 8

void setup(void) {
   pinMode(BUTTON_PIN, INPUT_PULLUP);
   pinMode(LED_PIN,OUTPUT);

   Serial.begin(250000);
}

/////////////////////////////////////////////////////////////////
void button_press_action(void) {
  Serial.println("BUTTON PRESS");
}

/////////////////////////////////////////////////////////////////
// Debounce a button.
uint8_t button_press_and_action(void) {
static uint16_t btndbc = 0, lastb = 0;

   btndbc=(btndbc<<1) | digitalRead(BUTTON_PIN) | 0xe000;

   if (btndbc!=lastb) Serial.println(btndbc,HEX);
   lastb = btndbc;

   if (btndbc==0xf000) button_press_action();
}

void loop(void) {
   button_press_and_action();
   delay(1);
}

Here is  the output it produces showing btndbc filling with zero from FFFF to F000. The next value is E000 when the button press is detected and the text "BUTTON PRESS" is generated. Then btndbc fills with ones from E000 to FFFF when the push button is released again.

FFFF
FFFE
FFFC
FFF8
FFF0
FFE0
FFC0
FF80
FF00
FE00
FC00
F800
F000
BUTTON PRESS
E000
E001
E003
E007
E00F
E01F
E03F
E07F
E0FF
E1FF
E3FF
E7FF
EFFF
FFFF


New! Comments

Have your say about what you just read! Leave me a comment in the box below.




Privacy Policy | Contact | About Me

Site Map | Terms of Use





Visit our Facebook Page:

   Click Here

Recent Articles

  1. Easy Switch Debounce

    Switch debounce: Three different ways to debounce input push switches with one amazing method that you can't miss.

    Read more

  2. How to use a DS18B20 on the Arduino for easy temperature measurement

    How to Easily Use the DS18B20 Maxim One-Wire thermometer with Arduino example code showing external or parasitic power modes.

    Read more

  3. How to use the BMP280 Barometric Pressure chip with the Arduino

    How to use the BMP280 for weather pressure measurement or altitude change detection.

    Read more

  4. The TCS230 Color Sensing Chip: How it works and how to use it.

    How to use the TCS230 (/TCS3200) Color detector chip and easily add it to any of your projects.

    Read more

  5. All about the MC78M05BDTRKG Linear Voltage Regulator

    How to use MC78M05BDTRKG for maximum current without using too large a heatsink pad and how to select the optimum input voltage.

    Read more

  6. Fixed point: A fast and easy way to save tons of microcntroller flash memory

    How to use fixed point maths to save microcontroller memory by avoiding use of the floating point library.

    Read more

Readers Comments

"I wanted to thank
you so so so much
for all the information
you have provided in
your site it's

SUPERB and FANTASTIC."

- Ranish Pottath

"This site really is
the best and my favorite.
I find here many useful
projects and tips."

- Milan

bursach<at>gmail.com<

"Awesome site,
very, very easy and nice
to navigate!"


- Matt
matt_tr<at>
wolf359.cjb.net


Learn Microcontrollers

"Interested in
Microcontrollers?"

Sign up for The
Free 7 day guide:

FREE GUIDE : CLICK HERE


"I am a newbie to PIC
and I wanted to say
 how great your
site has been for me."


- Dave

de_scott<at>bellsouth.net

"Your site is a great
and perfect work.
congratulations."


- Suresh

integratredinfosys<at>
yahoo.com

"I couldn't find the correct
words to define
yourweb site.

Very useful, uncovered,
honest and clear.

Thanks so much for
your time and works.
Regards."


- Anon

Back to Top