Debounced buttons with auto-repeat in AVR C++
This article presents a C++ class that implements an auto-repeat button. An auto-repeat button behaves like a key on a keyboard. That is, it fires first when you press it and then repeatedly when you hold it down.
The class allows you to configure the length of time that the button waits before it starts to auto-repeat and also the length of time in between repetitions.
This article builds upon my previous article. If you haven’t already done so then I recommend that you read that article because I will refer to it and I will also re-use the test circuit.
The C++ class
Here is the C++ header file that you will need.
#ifndef __02D4E887_5440_4a5d_836F_769935FB36B1 #define __02D4E887_5440_4a5d_836F_769935FB36B1 #include "DebouncedButton.h" // // extension of the debounced button to provide a button // that auto-repeats in the same way as a keyboard button // class AutoRepeatButton { private: // our internal state enum InternalState { Idle, WaitingForInitial, WaitingForRepeat } _internalState; // the time before repeating starts uint32_t _initialDelayMillis; // the time between each repeat uint32_t _repeatDelayMillis; // debounced button handler DebouncedButton _debouncedButton; // the last time something happened uint32_t _lastEventTime; public: // setup the class void setup( uint8_t digitalPin_, uint8_t pressedState_, uint32_t initialDelayMillis_, uint32_t repeatDelayMillis_); // Get the current state of the button DebouncedButton::ButtonState getState(); }; #endif
AutoRepeatButton.h
To use the class you first need to initialise it by declaring an instance of it and calling the setup() method, something like this:
AutoRepeatButton theButton; theButton.setup(2,HIGH,500,100);
This example sets up the button on Arduino digital pin #2 with an active (pressed) state of HIGH. These parameters are used to initialise the DebouncedButton encapsulated member class.
initialDelayMillis_ is the time, in milliseconds, that the button will wait after being pressed before it starts to auto-repeat. repeatDelayMillis_ is the time in between repeats while the button is held down.
Next you simply poll the class whenever you want to know the current state of the button, for example:
if(theButton.getState()==DebouncedButton::Pressed) { // do something }
The class is asynchronous, that is you can call getState() in your main program loop and it will return the correct current state without blocking or delaying. However, you should ensure that you do call getState() more frequently than either of your millisecond delay parameters or the debounced button delay constant in DebouncedButton.h. If you fail to do that then you may get unexpected results.
Source Code
Here’s the full source code to AutoRepeatButton.cpp so you can just copy and paste into your own project:
#include <wiring.h> #include "AutoRepeatButton.h" /* * Setup the class */ void AutoRepeatButton::setup( uint8_t digitalPin_, uint8_t pressedState_, uint32_t initialDelayMillis_, uint32_t repeatDelayMillis_) { // setup the encapsulated class and remember params _debouncedButton.setup(digitalPin_,pressedState_); _initialDelayMillis=initialDelayMillis_; _repeatDelayMillis=repeatDelayMillis_; } /* * Get the current state of the button */ DebouncedButton::ButtonState AutoRepeatButton::getState() { uint32_t now; // if button is not pressed then our state machine is reset if(_debouncedButton.getState()==DebouncedButton::NotPressed) { _internalState=Idle; return DebouncedButton::NotPressed; } now=millis(); switch(_internalState) { // first press, record that we are now waiting for the // initial repeat and save the time we started. case Idle: _internalState=WaitingForInitial; _lastEventTime=now; return DebouncedButton::Pressed; // lead up to the initial repeat. When the time is reached // advance the state into the multi-repeat stage case WaitingForInitial: if(now-_lastEventTime>=_initialDelayMillis) { _internalState=WaitingForRepeat; _lastEventTime=now; return DebouncedButton::Pressed; } else return DebouncedButton::NotPressed; // we're in the repeat loop. Return 'Pressed' each time // the time interval is passed case WaitingForRepeat: if(now-_lastEventTime>=_repeatDelayMillis) { _lastEventTime=now; return DebouncedButton::Pressed; } else return DebouncedButton::NotPressed; } // should never get here return DebouncedButton::NotPressed; }
AutoRepeatButton.cpp
Test Project
The test circuit is identical to that which I presented in my DebouncedButton article. Please refer back to that if you need to.
The test source code is shown below. Pressing and holding the button will result in the LED attached to pin 13 flashing in accordance with the delay settings of 500ms and 100ms.
#include <wiring.h> #include <avr/wdt.h> #include "AutoRepeatButton.h" // Compatibility stub for undefined pure virtual extern "C" void __cxa_pure_virtual() { for(;;); } // the button class AutoRepeatButton theButton; /* * Main entry point */ int main(void) { init(); setup(); for(;;) loop(); } /* * Setup before loop */ void setup() { // setup the LED pinMode(13,OUTPUT); digitalWrite(13,LOW); // setup the button on pin 2 (active HIGH) // 500ms/100ms initial/repeat delays theButton.setup(2,HIGH,500,100); } /* * Main loop */ void loop() { // check if the button is pressed if(theButton.getState()==DebouncedButton::Pressed) { // flash it once digitalWrite(13,HIGH); delay(5); digitalWrite(13,LOW); } }
main.cpp
The layout of this code is designed for Eclipse users. If you are using the Arduino IDE then you should just be able to copy and paste the setup() and loop() functions into the IDE Window.
Watch the test video
Look out for the on-board LED lighting with the initial button press followed by the rapid repeats as the button is held down.
Download the test project
The above test project is available for download. Click here to go to the downloads page to get it.
The test project has a dependency on the Arduino libraries for Eclipse that you can also get from my downloads page. You may have to adjust the Eclipse compiler C++ include and link settings to reflect the location of the Arduino libraries on your PC.