Quantcast
Channel: Blogs at All About Circuits
Viewing all articles
Browse latest Browse all 742

Two State Debounced Pushbutton

$
0
0
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
Code (C):
  1. // Button input related values
  2. static const byte BUTTON_PIN = 2;
  3. static const int STATE_NORMAL = 0; // no button activity
  4. static const int STATE_SHORT = 1; // short button press
  5. static const int STATE_LONG = 2; // long button press
  6. volatile int resultButton = 0; // global value set by checkButton()
Second, we attach the Interrupt Service Routine to the interrupt pin
Code (C):
  1.  // initialize input button pins
  2. pinMode(BUTTON_PIN, INPUT_PULLUP);
  3. attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), checkButton, CHANGE);
Third, here is the Interrupt Service Routine
Code (C):
  1. //*****************************************************************
  2. void checkButton() {
  3. /*
  4. * This function implements software debouncing for a two-state button.
  5. * It responds to a short press and a long press and identifies between
  6. * the two states. Your sketch can continue processing while the button
  7. * function is driven by pin changes.
  8. */
  9. const unsigned long LONG_DELTA = 1000ul; // hold seconds for a long press
  10. const unsigned long DEBOUNCE_DELTA = 30ul; // debounce time
  11. static int lastButtonStatus = HIGH; // HIGH indicates the button is NOT pressed
  12. int buttonStatus; // button atate Pressed/LOW; Open/HIGH
  13. static unsigned long longTime = 0ul, shortTime = 0ul; // future times to determine is button has been poressed a short or long time
  14. boolean Released = true, Transition = false; // various button states
  15. boolean timeoutShort = false, timeoutLong = false; // flags for the state of the presses
  16.  
  17. buttonStatus = digitalRead(BUTTON_PIN); // read the button state on the pin "BUTTON_PIN"
  18. timeoutShort = (millis() > shortTime); // calculate the current time states for the button presses
  19. timeoutLong = (millis() > longTime);
  20.  
  21. if (buttonStatus != lastButtonStatus) { // reset the timeouts if the button state changed
  22. longTime = millis() + LONG_DELTA;
  23. shortTime = millis() + DEBOUNCE_DELTA;
  24. }
  25. Transition = (buttonStatus != lastButtonStatus); // has the button changed state
  26. Released = (Transition && (buttonStatus == HIGH)); // for input pullup circuit
  27. lastButtonStatus = buttonStatus; // save the button status
  28.  
  29. if ( ! Transition) { //without a transition, there's no change in input
  30. // if there has not been a transition, don't change the previous result
  31. resultButton = STATE_NORMAL | resultButton;
  32. return;
  33. }
  34. if (timeoutLong && Released) { // long timeout has occurred and the button was just released
  35. resultButton = STATE_LONG | resultButton; // ensure the button result reflects a long press
  36. } else if (timeoutShort && Released) { // short timeout has occurred (and not long timeout) and button was just released
  37. resultButton = STATE_SHORT | resultButton; // ensure the button result reflects a short press
  38. } else { // else there is no change in status, return the normal state
  39. resultButton = STATE_NORMAL | resultButton; // with no change in status, ensure no change in button status
  40. }
  41. }
  42. //*****************************************************************
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)

Code (C):
  1.  
  2. // checkButton Demo Test Progrsm
  3.  
  4. //*****************************************************************
  5. //*****************************************************************
  6. // Function List
  7. void setup ();
  8. void loop();
  9. void checkButton();
  10.  
  11. //*****************************************************************
  12. //*****************************************************************
  13. // include necessary libraries
  14. #include <Wire.h> // I2C library
  15. #include "RTClib.h" // DS1307 Real Time Clock library
  16. #include <Time.h> // UTC Epoch time converter
  17.  
  18. #define HALT while (true);
  19.  
  20. // Button input related values
  21. static const byte BUTTON_PIN = 2;
  22. static const int STATE_NORMAL = 0; // no button activity
  23. static const int STATE_SHORT = 1; // short button press
  24. static const int STATE_LONG = 2; // long button press
  25. volatile int resultButton = 0; // global value set by checkButton()
  26.  
  27. //*****************************************************************
  28. //*****************************************************************
  29. void setup () {
  30. Serial.begin(57600);
  31. // initialize input button pins
  32. Serial.println(F("Initializing Button pin"));
  33. pinMode(BUTTON_PIN, INPUT_PULLUP);
  34. attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), checkButton, CHANGE);
  35. Serial.println(F("Button pin initialized"));
  36. }
  37. //*****************************************************************
  38. //*****************************************************************
  39. void loop() {
  40. switch (resultButton) {
  41. case 0: {
  42. Serial.println("Normal");
  43. delay(3000);
  44. }
  45. case 1: {
  46. Serial.println("Lo-o-o-o-o-n-n-g");
  47. }
  48. case 3: {
  49. Serial.println(" Both");
  50. }
  51. delay(3000); // do other stuff here!
  52. }
  53. }
  54. //*****************************************************************
  55. //*****************************************************************
  56. void checkButton() {
  57. // omitted...
  58.  

Viewing all articles
Browse latest Browse all 742

Trending Articles