Debouncing Circuits with Microcontrollers

Debouncing via Software

Correcting Bounce with Software

In this project, we will write a software sketch to identify and correct the effects of button bounce on the chipKIT™ microcontroller boards.

4.98K
×
Debouncing via Bounce Library

Using Libraries to Debounce Button Circuits

In this project, when the button is pressed the LED shines and the computer receives the number of times the button has been pressed.

5.71K
×
Debouncing via RC Filter

Using Resistors and Capacitors to Debounce

For this project, we will use a resistor and a capacitor to debounce a circuit.

7.18K
×
Using Force Buttons

Create a more complex and sophisticated button circuit that will activate when pressed with the right amount of force.

4.82K
×

Debouncing via Software

Correcting Bounce with Software

Software Debouncing

Introduction

In this project, we will write a software sketch to identify and correct the effects of button bounce on the chipKIT™ microcontroller boards. Bounce is an inherent property of mechanical buttons and switches that introduces electrical noise when they are turned on or off.

This noise associated with transitions is non-ideal and sometimes causes input signals to be read incorrectly. This type of error occurs in all microcontrollers and digital devices, not just in chipKIT boards.

As in the Trainable Delay project, this circuit includes a single button and external LED. However, in this project, the software will show a running count of how many times the button has been pressed. It accomplishes this by counting rising edges. It is visible on your computer screen using MPIDE's serial monitor. We will run the circuit with and without debouncing so that you can observe how button bounce can affect a circuit.

Bounce can be corrected in several different ways and can include either hardware or software solutions, but in this project, we will focus exclusively on a software solution.

Inventory:

Qty Description Typical Image Schematic Symbol Breadboard Image
1 LED
1 Two-port button
1 330 Ω resistor
1 10 kΩ resistor
5 Connecting wires

Bounce

As we previously mentioned, bounce is a mechanical property of switches and buttons that can potentially introduce problems into digital circuits. In a simple button circuit like those introduced in previous projects, we like to think of the rising and falling edges of the signal produced when one presses a button as being perfectly crisp and instantaneous transitions. In reality, when a button is pressed or released (or a switch thrown), there is a small amount of time (in the microsecond range) where the electrical signal can fluctuate anywhere from 0V to the HIGH voltage level (typically 3.3V or 5V). This is caused by the physical material of the switch or button reverberating and finally winding down to a steady state. While the physical material is vibrating, bounce affects the voltage level of the output. In general, this causes the transition edges not to be as clean (ideal) as we would like.

Figure 1 shows the voltage over time of a button releasing (using the same circuit configuration as in project 4). You can see that the button is at a steady voltage state of HIGH. Then, as it is released, a brief period of turbulence occurs for about 400 microseconds just before cutting off. Because the Max32 and Uno32 (and most other microcontrollers) run at such a fast rate, they can potentially capture these fluctuations, possibly causing incorrect circuit operation.

Figure 1. Voltage over time when a button is released.

In Fig. 2, there are two different signals, the yellow showing the voltage over time signal of the button input and the blue showing the voltage over time signal that is driving an external LED output. (This graph uses a simple button circuit and sketch). The circuit/sketch basically reads the input for the button, and then, if HIGH, will drive the corresponding LED HIGH. Like Fig. 1, this shows a close-up of when the button has been released, except now the output from the LED is also depicted. Throughout most of the duration of the button's input signal, the voltage level is sufficiently higher than the input threshold level for the digitalRead function to register it as a logic level HIGH (in the Max32 and Uno32, this is approximately 2.4V). However, as you may notice, there are points where the voltage drops below the threshold level long enough for the chipKIT board to register the button input as a logical level LOW.

Figure 2. Voltage over time of a button release, showing LED output.

The LED output now fluctuates because of bounce. Instead of one fluid ON/OFF signal that correlates to a single press of a button or switch throw, the signal is now choppy on the ends. Without something to mitigate this effect, the microcontroller interprets this as multiple button presses and cannot tell whether a human pressed the button for one second or for 100 microseconds (the latter is physically impossible for humans).

This is where debouncing becomes useful. Software debouncing is accomplished by taking multiple samples of the input signal and determining whether to assert an output signal (the debounced version of the signal) HIGH or LOW based on whether consecutive samples are received. For instance if two samples are both HIGH, and the time between them is much longer than the average duration of noise introduced by a bounce, then it is safe to say that the signal is in a HIGH state. (Similarly, the output signal is considered to be LOW if the samples that are taken are both LOW.) Generally, only two samples are ever needed to accurately debounce a signal, provided that the period between samples is long enough.

Figure 3 illustrates this algorithmic method of debouncing by showing a theoretical input signal and corresponding output signal (the input signal is the one in gray; the output signal is the one in blue). In this example, the debounced output signal remains LOW until time T4. At this point the output signal is driven HIGH because T4 and the previous point T3 were both HIGH.

Figure 3. Voltage over time of input signal and output signal of a button press.

Step 1: Setting up the Circuit

Figure 4. Circuit with a button and an LED.

The circuit set up for this project is identical to that of Blink LED with Trainable Delay, and the steps for setup will be simply reiterated. For a refresher on button and LED setup and theory, refer to Button-Controlled LEDs.

  1. Connect the 5V pin from the chipKIT board to a bus strip; we will now refer to this bus strip as the 5V bus strip.
  2. Connect the ground pin on the chipKIT board to a bus strip adjacent to the 5V bus strip. This strip will be designated as the ground bus strip.
  3. Place the LED into the breadboard, noting the anode and cathode.
  4. Use a wire to connect the anode of the LED to pin 12 of the chipKIT board.
  5. Connect the cathode of the LED to a 330Ω resistor.
  6. Now, connect the other end of the resistor to the ground bus strip.
  7. Place a push button into the breadboard so that it spans the gap between columns in the breadboard. Remember that buttons used for this project are four-terminal devices, where the terminals are grouped into sets of two, and each terminal in a set is electrically connected. For simplicity, figure 4 designates these groups as “A” terminals and “B” terminals.
  8. Using a wire, connect one of the “A” terminals of the button to the 5V bus strip.
  9. Connect one of the “B” terminals of the button to a 10 kΩ resistor, and then connect the end of the resistor not connected to the button to the ground bus strip.
  10. Using a wire, connect from one of the “B” terminals of the button to pin 7 on the chipKIT board.

Step 2: Non-debounced Software

This step's purpose is to show the effects of button bounce and allows you to visualize when bounces occur in the circuit. If you feel you already have a firm understanding of bounce, you can skip this step and proceed to step 3. In other words, the following code is only designed for the user to observe bounce (it will not debounce the circuit).

                  const int btnPin = 7;      // number of the pushbutton pin
                  const int ledPin = 8;      // number of the LED pin
                  
                  int currentBtn;            // current state of the LED
                  int previousBtn;           // state of the LED from the previous loop
                  unsigned int count;        // running sum of rising edges
                  
                  void setup() {
                  
                  pinMode(btnPin, INPUT);
                  pinMode(ledPin, OUTPUT);
                  
                  currentBtn = LOW;
                  previousBtn = LOW;
                  count = 0;
                  
                  Serial.begin(9600);
            
                  }
                  
                  void loop() {
                  
                  currentBtn = digitalRead(btnPin);
                  
                  if ((previousBtn == LOW) && (currentBtn ==HIGH)) {  // check for transition from LOW to HIGH
                  count++;
                  Serial.println(count);
                  }
                  
                  previousBtn = currentBtn; 
                  digitalWrite(ledPin, currentBtn);
                  
                  
                  }
                

The code is fairly straightforward and all functions we use are from previous projects. The sketch reads the current state of the input button while keeping track of the state of the button from the previous loop of the main program. If the previous button state is LOW and the current state is HIGH, then this indicates a rising edge transition (i.e., the signal starting to assert HIGH). The sketch then increments a counter variable and outputs that value to the serial port. You can view this count through the serial monitor window.

Ideally, the sketch would increment the counter variable every time you press the button. However, after a few button presses, you will start to notice that sometimes the counter variable will increment multiple times.

In other words, you will press the button once, but you will see more than one counter value printed to the serial monitor. This happens because the program sketch has detected multiple rising edges in very close succession (this occurs because of a button bounce).

Step 3: Debounced Software

The following code corrects button bounce and is very similar to the code example that is provided within MPIDE examples (File->Example->Digital-> Debounce). This code is extended to provide a button press counter capability (by counting rising edges) to verify the code is operating correctly.

The debouncing algorithm used for this sketch at first may appear slightly different than our theoretical explanation, but once examined you can see the differences are only marginal.

When analyzing the debouncing algorithm used for our sketch, we only look at the point when the input signal changes (as opposed to taking a periodic time sample like our theory explanation suggested).

For this algorithm, we will call the point when the signal changes “point A,” and a predefined period of time after point A will be “point (A - 1).” Point A is set the first time the input signal changes, but If the input signal happens to change again before a set period of time has elapsed, then point A will become this new point (i.e., the process restarts). Now if the input signal doesn't change and the set time has elapsed, then it means that point A and point (A -1) have the same value. (If they were different, the algorithm would have already reset itself.) Thus, if A and (A-1) are both HIGH , the output variable is set HIGH accordingly . (Likewise if both points were LOW , the output signal would be driven LOW).

                  const int btnPin = 7;                                     // Number of the pushbutton pin
                  const int ledPin =  8;                                    // Number of the LED pin
                  
                  int currentLedState;                                      // Current and previous states of output LED pin
                  int previousLedState;               
                  int currentBtnState;                                      // Current and previous states of input Button pin
                  int previousBtnState;               
                  
                  
                  unsigned int count;                                       // Rising edge count of LED state
                  unsigned int lastDebounceTime;      
                  unsigned int debounceDelay;                               // Delay time
                  
                  void setup() {
                  
                  pinMode(btnPin, INPUT);
                  pinMode(ledPin, OUTPUT);
                  
                  currentLedState = LOW;  
                  previousLedState = LOW;
                  currentBtnState = LOW;            
                  previousBtnState = LOW;
                  
                  count = 0;
                  lastDebounceTime = 0;  
                  debounceDelay = 50;   
                  
                  Serial.begin(9600);
                  
                  }
                  
                  void loop() {
            
                  currentBtnState = digitalRead(btnPin);
                  
                  if (currentBtnState != previousBtnState) {            
                  
                  lastDebounceTime = millis();
                  // every time the button state changes, get the time of that change
                  } 
                  
                  if ((millis() - lastDebounceTime) > debounceDelay) {
                  
                  /*
                  *if the difference between the last time the button changed is greater
                          *than the delay period, it is safe to say
                          *the button is in the final steady state, so set the LED state to
                          *button state.
                  */
                  currentLedState = currentBtnState;
                  
                  }
                  
                  
                  
                  // ********************* start functional code **************************************
                  
                  
                  
                  
                  
                  // verification code, and a button press counter
                  
                  if ((previousLedState == LOW) && (currentLedState == HIGH)) {
                  //count rising edges
                  count++; 
                  Serial.println(count);
                  }
                  
                  
                  
                  
                  // ********************* end functional code **************************************
                  
                  
                  
                  // set current states to previous states
                  digitalWrite(ledPin, currentLedState);
                  previousBtnState = currentBtnState;
                  previousLedState = currentLedState;
                  }
	        

Summary:

Debouncing code is always expected to be used in conjunction with other kinds of code. Using the project code, any segment of code can be placed within the “start function” code and “end function” code comment lines and can benefit from a debounced version of the input signal. While this project uses a simple edge counting block of code, we can substitute any other block of code. It is important to note that this is only one way to correct button bounce. We will discuss some of the other ways this can be done in subsequent projects.

Core Concepts:
  • Theory of button/switch bounce
  • Effects of bounce on a digital system
  • Software method for correcting bounce

  • Other product and company names mentioned herein are trademarks or trade names of their respective companies. © 2014 Digilent Inc. All rights reserved.
  • Circuit and breadboard images were created using Fritzing.