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. Bounce is an inherent property of mechanical buttons and switches that introduce 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 of software solutions, but in this project, we will focus exclusively on a software solution.


Inventory

  • 1 LED
  • 1 Two-port Button
  • 1 330Ω resistor (orange, orange, brown)
  • 1 10kΩ resistors (brown, black, orange)
  • 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 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 functions, possibly causing incorrect circuit operation.

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.

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.


Step 1: Setting up the Circuit

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;
}