Skip to main content

Debouncing a switch

Mechanical switches do not make or break a connection cleanly due to microscopic conditions on the contact surface. This is referred to as “Switch Bounce” and can cause problems in digital circuits. In this tutorial we will build a circuit that demonstrates this problem, then modify it slightly to resolve it.

We will be reusing the circuit from the ATmega8 breadboard circuit tutorial.

The schematic is shown below.

Circuit Diagram

Next we write and upload some code to the microcontroller. This program waits for you to press a button, then records how many times you press
that button until it senses a quiet period. At this point it flashes the LED for as many times as button presses were recorded.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#include <avr/io.h>
#include <util/delay.h>
 
 
//Define functions
//======================
void ioinit(void);
void led_on(void);
void led_off(void);
//======================
 
int main (void)
{
   ioinit(); //Setup IO pins and defaults
 
   while (1)
   {
      int num_presses;                  //Number of times button has been iterations
      int num_nopress_iterations;       //Number of loop iterations since last button press
      char button_state;                //1=pressed, 0=not pressed
 
         //Wait for button to be pressed
         while (!bit_is_clear(PINC, 5)) {}
         num_presses=1;
         button_state=1;
 
         while (1)
         {
            if (bit_is_clear(PINC, 5)) //button is pressed
            {
               if (button_state==0) //was previously not pressed)
               {
                  num_presses++;
               }
               button_state=1;
            }
            else //button is not pressed
            {
               if (button_state==1) //was previously pressed
               {
                  num_nopress_iterations=0;
               }
               num_nopress_iterations++;
               button_state=0;
               if (num_nopress_iterations>20000) //If we haven't seen a button press for a while, terminate the loop
               {   
                  break;
               }
            }
         }
 
         for (int i=0;i<num_presses;i++)
         {
            led_on();
            _delay_ms(300);
            led_off();
            _delay_ms(300);
         }
 
         if (bit_is_clear(PINC, 5))
         {
         }
   }
}
 
 
void ioinit (void)
{
   DDRC  = 0b11011111; //1 = output, 0 = input
   PORTC = 0b00100000; //Enable pin 5 internal pullup
}
 
void led_on(void)
{
   PORTC |= _BV(PC4);
}
 
void led_off(void)
{
   PORTC &= ~_BV(PC4);
}

When you run the program and press the button, you will often notice the LED blinking too many times. This is due to the button bounce.

The first figure below shows how we normally think a button press looks like. The Next one is closer to the truth (even this one is a bit too clean and elegant)

Button Press without bounce
Button Press without bounce

Fixing the problem is very easy. All you need to do is add a capacitor. I used a 100nF capacitor, but experiment with what works best for you.

Circuit with debounce capacitor
Circuit with debounce capacitor

Note: The problem isn’t too pronounced on a breadboard as it has a lot of natural capacitance. On a PCB the problem is much more severe.

Related News

External Interrupts on an ATmega168

This tutorial will teach you how to use external and pin change interrupts on an...

Driving a 7 segment display with a 4511 BCD to 7 Segment Driver

In this tutorial I will describe how to drive a 7 segment module using a...

Atmega8 breadboard circuit – Part 1 of 3 – Power supply

This is the 1st of a 3 part series where I will describe how to...

3 Comments

  1. Chandana

    I believe you can add little delay like
    if (bit_is_clear(PINC, 5)) //button is pressed
    {
    _delay_ms(50);//Add this line
    if (button_state==0) //was previously not pressed)
    {
    num_presses++;
    }
    button_state=1;
    }

    to prevent bounce issue…..
    Thanks for the tutorial.

    1. پروژه الکترونیک

      Using delays for debouncing is the simplest way but is not the most efficient!
      Try this method and you’ll always use it!

      #include

      #define SW1 0 // PINB.0
      #define DEBOUNCE_CNT 5 // Debounce Counter
      unsigned char SW1Pressed (void) { // Checks if SW1 is pressed and debounced.
      static unsigned char SW1KeyCount = 0, SW1KeyPressed = 0;
      if (SW1KeyPressed) {
      if (BITISSET(PINA,SW1)) {
      if (SW1KeyCount < DEBOUNCE_CNT)
      SW1KeyCount++;
      else {
      SW1KeyPressed = 0;
      SW1KeyCount = 0;
      }
      }
      } else {
      if (BITISCLEAR(PINA,SW1)) {
      if (SW1KeyCount < DEBOUNCE_CNT)
      SW1KeyCount++;
      else {
      SW1KeyPressed = 1;
      SW1KeyCount = 0;
      return (1);
      }
      }
      }
      return (0);
      }
      int main(void) {
      PORTB = (1 << SW1); // Internal Pull-Up
      for(;;) {
      if (SW1Pressed()) { // SW1 pressed and debounced
      printf("SW1 Pressed.");
      }
      }
      }

      پروژه الکترونیک ARM STM32 LPC AVR PIC اتوماسیون مانیتورینگ کنترل صنعتی

  2. andrew

    nice tutorial-nice code…

    to get the code to compile, i had to change this line:
    PORTC &= ~_BV(PC4);

    to this:
    PORTC = ~_BV(PC4);

    is &amp necessary, or just a fat-finger?

    also, if a switch can be nicely debounced w just a cap, why does everyone rant about schmitt triggers?

Leave a reply

Your email address will not be published. Required fields are marked *

Shopping Cart