Two State Debounced Pushbutton

There are many functions to be found on the Internet for software button debouncing. The logic is simple. When a button input goes high or low, wait for a brief 30ms and ensure that the button state doesn't change during that time. If it does, restart your 30ms timer and continue to check for button changes.

But what if you want to test for multiple states? Like "Not Pressed", "Short Press" or "Long Press". It gets a little more complicated.

This code performs that function. It returns an integer representing the three states defined above. Additionally, it uses interrupts instead of polling, so that the rest of the program can continue processing.

This function, checkButton, is written on an Arduino Mega 2560, but also works on on Arduino UNO.

First, we define the global variables at the start of the sketch
C:
// Button input related values
static const byte BUTTON_PIN = 2;
static const int STATE_NORMAL = 0; // no button activity
static const int STATE_SHORT = 1; // short button press
static const int STATE_LONG = 2; // long button press
volatile int resultButton = 0; // global value set by checkButton()
Second, we attach the Interrupt Service Routine to the interrupt pin
C:
 // initialize input button pins
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), checkButton, CHANGE);
Third, here is the Interrupt Service Routine
C:
//*****************************************************************
void checkButton() {
/*
* This function implements software debouncing for a two-state button.
* It responds to a short press and a long press and identifies between
* the two states. Your sketch can continue processing while the button
* function is driven by pin changes.
*/
const unsigned long LONG_DELTA = 1000ul; // hold seconds for a long press
const unsigned long DEBOUNCE_DELTA = 30ul; // debounce time
static int lastButtonStatus = HIGH; // HIGH indicates the button is NOT pressed
int buttonStatus; // button atate Pressed/LOW; Open/HIGH
static unsigned long longTime = 0ul, shortTime = 0ul; // future times to determine is button has been poressed a short or long time
boolean Released = true, Transition = false; // various button states
boolean timeoutShort = false, timeoutLong = false; // flags for the state of the presses

buttonStatus = digitalRead(BUTTON_PIN); // read the button state on the pin "BUTTON_PIN"
timeoutShort = (millis() > shortTime); // calculate the current time states for the button presses
timeoutLong = (millis() > longTime);

if (buttonStatus != lastButtonStatus) { // reset the timeouts if the button state changed
longTime = millis() + LONG_DELTA;
shortTime = millis() + DEBOUNCE_DELTA;
}
Transition = (buttonStatus != lastButtonStatus); // has the button changed state
Released = (Transition && (buttonStatus == HIGH)); // for input pullup circuit
lastButtonStatus = buttonStatus; // save the button status

if ( ! Transition) { //without a transition, there's no change in input
// if there has not been a transition, don't change the previous result
resultButton = STATE_NORMAL | resultButton;
return;
}
if (timeoutLong && Released) { // long timeout has occurred and the button was just released
resultButton = STATE_LONG | resultButton; // ensure the button result reflects a long press
} else if (timeoutShort && Released) { // short timeout has occurred (and not long timeout) and button was just released
resultButton = STATE_SHORT | resultButton; // ensure the button result reflects a short press
} else { // else there is no change in status, return the normal state
resultButton = STATE_NORMAL | resultButton; // with no change in status, ensure no change in button status
}
}
//*****************************************************************
The first few lines of the sketch function define its local variables. For example, LONG_DELTA is the elapsed time for a long button press,DEBOUNCE_DELTA is the minimal amount of time to wait for the button to settle down. lastButtonStatus may appear to always be high, since it is defined at the beginning of the sketch. But the static attribute allows its previous value to remain amongst many calls. Most of the variables which remain, are boolean variables that track the button state.

The first logic operation completed checks the current value of the buttonPin. The state of the Long and Short delays are checked to see if the debounce time has expired and the associated state is set (timeoutShort or timeoutLong).

The next group of statements, calculates and saves the states of the buttons. It calculates to see if there was a transition (from Low to High or vice versa) and sets a booloean flag (Transition). This function is written for a button who is pulled up when NOT pressed and goes Low when pressed. A second boolean flag is calculated to determine if the button has been released (Released). Finally, we save the button state in the variable lastButtonStatus, for comparison next call.

Simply, if there was no transition, the function returns that the button is normal, in the variable resultButton. Note that the line calculating the return value has an extra variable.

The last block of code, returns the proper value depending on the state.
If the button has been released, and resultButton is STATE_LONG, return STATE_LONG in the resultButton (+)
If the button has been released, and resultButton is STATE_LONG, return STATE_LONG in the resultButton (+)

(+) As said, setting resultButton is a little different. Since it is interrupt driven, a short press and a long press may both occur before the button status is processed. The result is bit-encoded, where 0 is a normal state, 1 is a short press, 2 is a long press and 3 is both a long press and a short press. Hence, wherever resultButton is set, it is logical ORed with its last value on order to retain any previous results. The one exception is when the results are read, and then the resultButton value is reset (set to 0).

The following is a short program (using the checkButton code above)

C:
// checkButton Demo Test Progrsm

//*****************************************************************
//*****************************************************************
// Function List
void setup ();
void loop();
void checkButton();

//*****************************************************************
//*****************************************************************
// include necessary libraries
#include <Wire.h> // I2C library
#include "RTClib.h" // DS1307 Real Time Clock library
#include <Time.h> // UTC Epoch time converter

#define HALT while (true);

// Button input related values
static const byte BUTTON_PIN = 2;
static const int STATE_NORMAL = 0; // no button activity
static const int STATE_SHORT = 1; // short button press
static const int STATE_LONG = 2; // long button press
volatile int resultButton = 0; // global value set by checkButton()

//*****************************************************************
//*****************************************************************
void setup () {
Serial.begin(57600);
// initialize input button pins
Serial.println(F("Initializing Button pin"));
pinMode(BUTTON_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), checkButton, CHANGE);
Serial.println(F("Button pin initialized"));
}
//*****************************************************************
//*****************************************************************
void loop() {
switch (resultButton) {
case 0: {
Serial.println("Normal");
delay(3000);
}
case 1: {
Serial.println("Lo-o-o-o-o-n-n-g");
}
case 3: {
Serial.println(" Both");
}
delay(3000); // do other stuff here!
}
}
//*****************************************************************
//*****************************************************************
void checkButton() {
// omitted...

Blog entry information

Author
djsfantasi
Views
1,016
Last update

More entries in General

More entries from djsfantasi

Share this entry

Top