Home Services Products Blog/Downloads Contact About Us

logoStephens Micro Systems Pty Ltd

(Excellence in electronic design solutions)

Code Examples – Debounce a mechanical button (or switch) – made easy...

Debounce can sometimes be a difficult task to get your head around. I am going to make this task a bit easier for you – I have provided 3 code examples below.

All 3 examples use a simple serial sampling technique. This is a very robust, compact and fast approach.

It has 2 parts – A Timer Interrupt, and a Foreground Task (usually in main()). These 2 parts are connected via a global volatile boolean as shown in the examples.

The Timer is usually in the range of 1 to 10mS, but you can tailor it to your specific requirements. IMPORTANT, the sample variable (static uint button_samples) needs to sized to suit the number of samples – 100mS debounce with a 1mS timer will require at least 100 bits… It is usually best to use a Timer interval so the number of samples fits into your CPU’s native integer size – 8, 16 or 32 bit.


Example 1 is just a simple debounce process with a button_down flag.

        

/*Example 1 - design spec:- Button down = pin high. Minimum debounce (for both press and release) = 60mS. Using a 5mS timer interrupt. Fixed sampling timers, like the one below, have an iteration tolerance of +0, -1. So, if your debounce time needs to be no less than 60mS, you will need to add 1 to the iteration count. A 5mS timer makes it 13 (60-65mS debounce), a 10mS timer makes it 7 (30-40mS debounce)... */ volatile bool button_down = false; #define DEBOUNCEDNMASK 0b0001111111111111 /// = 13 bit mask = 2^((60 \ 5) + 1) - 1 #define DEBOUNCEUPMASK DEBOUNCEDNMASK void timer_5mS_interrupt(void) { static uint button_samples = 0; button_samples <<= 1; if ((my_button_port->input_data_register & my_button_bit) != 0) { // for active low button - change !=0 to ==0 button_samples++; } if ((button_samples & DEBOUNCEUPMASK) == 0) { button_down = false; } else if ((button_samples & DEBOUNCEDNMASK) == DEBOUNCEDNMASK) { button_down = true; } } ------ meanwhile, back at main.c ------- extern volatile bool button_down; main() { ... if (button_down) { ... handle button down } ... }


Example 2 uses a different Timer and Debounce than Example 1. It has a button_pressed flag which must be acknowledged in the Foreground task.

        

/*Example 2 - design spec:- This is similar to Example 1, with a transition state as 'button_press' Button down = pin high. Minimum debounce (for both press and release) = 30mS. Using a 5mS timer interrupt. Fixed sampling timers, like the one below, have an iteration tolerance of +0, -1. So, if your debounce time needs to be no less than 30mS, you will need to add 1 to the iteration count. A 5mS timer makes it 7 (30-35mS debounce). */ volatile bool button_pressed = false; #define DEBOUNCEDNMASK 0b01111111 /// = 7 bit mask = 2^((30 \ 5) + 1) - 1 #define DEBOUNCEUPMASK DEBOUNCEDNMASK void timer_5mS_interrupt(void) { static uint button_samples = 0; static bool button_down = false; button_samples <<= 1; if ((my_button_port->input_data_register & my_button_bit) != 0) { // for active low button - change !=0 to ==0 button_samples++; } if ((button_samples & DEBOUNCEUPMASK) == 0) { button_down = false; } else if ((button_samples & DEBOUNCEDNMASK) == DEBOUNCEDNMASK) { if (!button_down) { button_down = true; button_pressed = true; } } } ------ meanwhile, back at main.c ------- extern volatile bool button_pressed; main() { ... if (button_pressed) { button_pressed = false; /// acknowledge button_pressed ... handle button press } ... }


Example 3 extends Example 2 with key-repeat function.

        

/*Example 3 design spec:- Using Example 2 above Following the debounce, if the button is held down for > 1 second, it repeats 5 times per second. */ volatile bool button_pressed = false; #define DEBOUNCEDNMASK 0b01111111 /// = 7 bit mask = 2^((30 \ 5) + 1) - 1 #define DEBOUNCEUPMASK DEBOUNCEDNMASK #define INITBUTTONRPT 200 /// initial button press threshold (1 second / 5mS = 200) #define BUTTONRPT INITBUTTONRPT-40 /// repeat interval (1 second / 5 repeats = 200mS, 200mS / 5mS = 40) void timer_5mS_interrupt(void) { static uint button_samples = 0; static bool button_down = false; static int button_repeat_cnt = 0; button_samples <<= 1; if ((my_button_port->input_data_register & my_button_bit) != 0) { // for active low button - change !=0 to ==0 button_samples++; } if ((button_samples & DEBOUNCEUPMASK) == 0) { button_down = false; } else if ((button_samples & DEBOUNCEDNMASK) == DEBOUNCEDNMASK) { if (!button_down) { button_down = true; button_pressed = true; button_repeat_cnt = 0; } else { if (++button_repeat_cnt >= INITBUTTONRPT) { button_pressed = true; button_repeat_cnt = BUTTONRPT; } } } } ------ meanwhile, back at main.c ------- extern volatile bool button_pressed; main() { ... if (button_pressed) { button_pressed = false; /// acknowledge button_pressed ... handle button press } ... }


Please feel free to contact me if you have any questions.