This library provides an easy way for reading and debouncing one or many buttons connected to a microcontroller. It can also read a button combination, button long press and button double press.
What is button debouncing
When a button is pressed or released it takes a certain amount of time before the two contacts reach a stable state. During that time the contacts are known to be bouncing sending multiple on off signals to the microcontroller before they settle to either on or off.
The bouncing time period depends on the speed the button is pressed, the force, the button quality and the age of the button. With time the contacts will oxidize especially with low quality buttons.
For one or two buttons the debouncing can be made in hardware by connecting a capacitor in parallel with the button that will keep the voltage across the button stable during press or release. But when having many buttons it is more practical to make the debouncing in software where it is also easier to modify the debouncing time compared to a capacitor.
Button debouncing in software using the Binary Button Debounce (BBM) method
Every time the debouncing function runs, the states of all buttons are saved in a binary representation in one or multiple bytes. In a byte can fit up to 8 buttons and each bit represents a button - 0 the button is not pressed, 1 the button is pressed. I call this a snapshot. Each snapshot is saved in an array and at the end every bit must be 1 for a particular button to be considered pressed.
![]() |
Connecting push buttons to a microcontroller |
Using the library
It is not necessary to modify the settings inside the library header file but there are a few settings that can be modified if the default values are not desired.
BUTTONS_SIZE_LIMIT - can be BTNS_LIMIT_8 that can hold up to 8 buttons or BTNS_LIMIT_16 for up to 16 buttons.
MIN_PRESS_TIME - debouncing time in milliseconds. 10 or 20 m. Default value is 10ms.
TIMER_ISR_RESOLUTION - by default is 1ms because the millis interrupt triggers every 1ms.
Setup function
debouncerSetup(BUTTONS_LOCATION, NR_OF_BUTTONS_AND_PORTS)
This function will set the pins for the buttons as inputs. Since the buttons can be on multiple ports and their number can vary this is the simplest solution I found for the user to pass the location of the buttons to the library.
BUTTONS_LOCATION: an array that indicates on what ports and pin numbers the buttons are located.
NR_OF_BUTTONS_AND_PORTS: the size of the array.
Example:
#define NR_OF_BUTTONS_AND_PORTS 9
uint8_t BUTTONS_LOCATION[NR_OF_BUTTONS_AND_PORTS] = {'B', 2, 0, 5, 4, 'C', 5, 'D', 7};
Here we have some buttons on port B on pins 2, 0, 5 and 4, one button on port C pin 5 and one on port D pin 7. The array size is 3 ports + 6 buttons = 9.
To be able to identify what each button does and also if a combination of buttons was pressed an enum fits perfect for this purpose.
enum Buttons{ BTN_UP = 1, // PB2 BTN_DOWN = 2, // PB0 BTN_LEFT = 4, // PB5 BTN_RIGHT = 8, // PB4 BTN_MENU_OK = 16, // PC5 BTN_START_STOP = 32 // PD7 }
The name of the buttons can be anything and more buttons can be added or removed from the list. The important thing is the order that they are inside the enum. Notice that the order of the enum is similar to that of the array. Also important is the number of each button in the enum - they must start at 1 and continue as follows 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 and so on.
Reasons for this can be seen later in an example but the cool thing is that any combination of buttons can be checked like this BTN_UP | BTN_DOWN. This will be true in an if statement or a switch case if both buttons are pressed.
Reading the buttons
There are two functions for reading the buttons. One returns the currently pressed buttons and the other the buttons that were pressed and released.
For example in many projects like a clock or a flashlight there is no room for many buttons and 1 or 2 buttons must accomplish many functions. Entering setup mode can be made as a secondary function by a long press of 2 seconds while a short press can accomplish a main action like switching the lights. Or some times a single button press must do something only once regardless if the user holds the button down and other times it is desired to repeat that button's function - when changing a numerical value for example.
debouncerPushedButtons()
Pushed buttons: returns an 8 or 16 bit value where each bit represents the state of the button at that bit position. Thanks to the Buttons enum there is no need for binary shifting to check which button was pressed. See the below examples on how easy is to check the button state.
Even if the user holds the button pressed continuously the function will
return the on states only once. For example holding the key "a" will only
return "a" whereas the following function will repeat the action.
debouncerPressedButtons()
Pressed buttons: same as the function above except that here the function returns the on states of the buttons as long as they are pressed. For example holding the key "a" will return "aaaaaaaaaaaaa" if the user doesn't implement a delay in code to account for this.
debouncerDoubleClicked()
Double clicked buttons: returns the button that was pressed twice very quickly. This is simillar to a mouse doubleclick.
debouncerButtonCombination()
Button combination: returns the button combination that was pressed during a certain time interval. Default: 200ms.
Debouncing functions
buttonDebouncerTimer()
Must be placed in a timer interrupt (ideally 1ms resolution). After a certain time it will set the NEXT_DEBOUNCE_READY flag that indicates that the debouncing function can run.
buttonDebouncer(monitorPressTime)
This is the debouncing function that is executed by the main loop when NEXT_DEBOUNCE_READY is 1. When debouncing is ready it will set the DEBOUNCE_FINISH flag then the main loop can read which buttons are pressed.
monitorPressTime: represents how many milliseconds a button must be pressed to be considered a long press. 0 means this option is not used. After this timeout the flag BUTTON_LONG_PRESS will be set and the main loop can check which button was long pressed.
void btnSetLongPressTime(uint16_t monitorPressTime);
The time in milliseconds after which a button is considered long pressed, can be set using the above function.
Example
#include "buttonDebouncer.h" #include "millis.h" #define NR_OF_BUTTONS_AND_PORTS 9 uint8_t BUTTONS_LOCATION[NR_OF_BUTTONS_AND_PORTS] = {'B', 2, 0, 5, 4, 'C', 5, 'D', 7}; enum Buttons{ BTN_UP = 1, // PB2 BTN_DOWN = 2, // PB0 BTN_LEFT = 4, // PB5 BTN_RIGHT = 8, // PB4 BTN_MENU_OK = 16, // PC5 BTN_START_STOP = 32 // PD7 }Buttons; int main(void){ buttons_t pressed_buttons = 0; buttons_t pushed_buttons = 0; buttons_t button_combination = 0; // Setup millis millis_init(); // Setup button debouncer debouncerSetup(BUTTONS_LOCATION, NR_OF_BUTTONS_AND_PORTS); // Trigger button long press after 2000 milliseconds (2 seconds) btnSetLongPressTime(2000); // Enable global interrupts sei(); while(1){ // Check if debouncing is complete if(DEBOUNCE_FINISH){ DEBOUNCE_FINISH = 0; pressed_buttons = debouncerPressedButtons(); pushed_buttons = debouncerPushedButtons(); button_combination = debouncerButtonCombination(); // If a button was long pressed if(BUTTON_LONG_PRESS){ // Check which button pushed_buttons = pressed_buttons; } // Button combination switch(button_combination){ case BTN_PLUS | BTN_ALARM: // Both buttons are pressed // Code... break; } // Check if a button was double clicked if(BUTTON_DOUBLE_CLICK && debouncerDoubleClicked() == BTN_SETUP){ // BTN_SETUP was double clicked // Code... } // Check what button or combination of buttons were pressed switch(pushed_buttons){ case BTN_UP: if(BUTTON_LONG_PRESS){ // This button was long pressed // Code... }else{ // This button was pushed but not long pressed // Code... } break; case BTN_DOWN: // Code... break; case BTN_LEFT: // Code... break; case BTN_RIGHT: // Code... break; case BTN_MENU_OK: // Code... break; case BTN_START_STOP: // Code... break; } } } } // Millis timer ISR(ISR_VECT){ ++milliseconds; // Default: debouncing every 10ms, 1ms timer resolution buttonDebouncerTimer(); }
If you don't have a timer interrupt you can use the millis library from https://www.programming-electronics-diy.xyz/2021/01/millis-and-micros-library-for-avr.html
Download
v2.2
No comments:
Post a Comment