Button debouncing on AVR microcontrollers

Often when working with microcontrollers, it is necessary to interface with buttons of many kinds. One thing that needs to be considered is no switch or button switches from one state to another perfectly cleanly. Because of this, the microcontroller could see multiple 'button presses' when only one occurred. To handle this, we must do something called debouncing.

On the image shown below, you can see the output of an active-high button in yellow, and the clean output signal of the micro in blue. Looking to the start of the yellow trace, it initially goes high but for approximately 2ms, its state is unstable. The blue trace shows the output of the micro, going high when it decides that button has stabilized.

Scope view showing button bounce in yellow and clean signal in blue.

My preferred  debounce strategy is based on an algorithm I have found through Kenneth Kuhn, debounce.c found here. His files outlines a strategy in which the state of the button is read on a regular interval. On each read, depending on the state, an 'integrator' value is either added to or subtracted from by one. If that value reaches a fixed threshold, then output goes high. It only goes low once the value returns to 0.

Below is my implementation of this algorithm in a simple function. By calling poll_btn(0) on a timer (I usually put in in a timer interrupt that triggers every millisecond or so), at any point in you code you can check if poll_btn(1) returns a 1.

#define BTNPIN 3


uint8_t poll_btn(uint8_t number) {
	static uint8_t integrator;
	static uint8_t output;

	switch(number) {
		case 0 :
			// Poll and integrate fire and set buttons
			if ((BTNPORT & (1<<BTNPIN))) {
				if (integrator > 0) {
			} else if (integrator < DEBOUNCECYCLES) {

		case 1 :
			// If integrator is at threshhold, return 1
			// If integrator is at 0, return 0
			if (integrator == 0) {
				output = 0;
			else if (integrator >= DEBOUNCECYCLES) {
				integrator = DEBOUNCECYCLES;
				output = 1;

The function could be easily expanded to more inputs by adding their polling and integrating code to case 0 and making a new case to check them.

Email me with any suggestions or questions!