Another debounce routine

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi -

I'm sure that this 'horse' has been beaten to a pulp but I came up with this simple state machine version of a push-button debounce routine that has served me well for years and wanted to offer it to the group.  It is likely that someone else has already submitted a similar version of this algorithm and if so, my apologies.  In any event, if anyone finds this useful, feel free to add it to your bag of tricks.  I have never needed to use more than a few buttons but you can expand this up to the size limit of the variables. 

 

I am leaving the setup of the timer interrupt routine to the user, as I believe we all have done that enough in our lives.

 

Take care -

Jim

 

 

 //***********************************************
 //
 //  debounce state machine
 //  * execute in a 5 or 10 mSec timer interrupt
 //  * pb_deb is global variable
 //  * For Reference:
 //	8 push buttons on Port A
 //	(config as input w/internal pull-ups)
 //
 //***********************************************
void debounce(void)
{
static 	uint8_t pb_last;
	uint8_t pb_now;

//	read the current button state
//     (PORTA used as reference in this example)
	pb_now = ~PORTA_IN;

//  process the debounce filtering algorithm
	pb_deb =( pb_last & pb_now)|
		( pb_last & pb_deb)|
		(~pb_last & pb_now & pb_deb);

//  save the button state for the next sample period
	pb_last = pb_now;
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

rfdes wrote:
I am leaving the setup of the timer interrupt routine to the user, as I believe we all have done that enough in our lives.

That is similar to one I use, but for the benefit of others, it would not hurt to post a simple demo program that shows how to use it, perhaps showing how to toggle an LED with each push of the button.

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You should initialise pb_last and pb_deb.

 

David

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

They're static, they're automatically initialized. ... that said, stylistically I agree, explicit initializers are nice.

Last Edited: Wed. Apr 10, 2019 - 02:54 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the_real_seebs wrote:
They're static,
ONE of them is static ;-)

 

Can't say I'm too thrilled about the indentation - as it is it's too easy to read that as if the static applies to both vars (maybe that was even the intention ??)

 

Actually, looking at pb_now seems to just be a temp local so probably was not intended to be "static"

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 


 

Hi Jim -

I'm bit late with this reply but I took your advice and threw together a quicky program that will toggle leds with push buttons.  This state machine is a bit different than the one I originally posted.  This version is slightly more efficient than the earlier.

 

I've also included another state machine (Mealy) that will create a pulsed/latched output that simplifies the LED toggle routine. For those who are not familiar with switch bounce, I've included a routine that will allow the user to see first hand what switch bounce is all about.

 

If someone has anything simpler, please contribute.

Take care -

Jim

 

 

/*
 * main.c
 *
 * Created: 4/17/2019
 * Author: Jim Flanagan
 *
 */ 

//Port D used for the example using Atmega328p @ 16 MHz
//Timer 0 used as 5 mSec overflow interrupt
// 4 LEDs setup on Port D bits 0-3
// 4 PBs setup; on Port D bits 4-7

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>

#define COUNT_5MS	178	//5 mSec int with 16 MHz clock
#define KEY1 	(1 << 7)  //0b1000 0000
#define KEY2 	(1 << 6)  //0b0100 0000
#define KEY3 	(1 << 5)
#define KEY4 	(1 << 4)

#define LED1 	(1 << 3)
#define LED2 	(1 << 2)
#define LED3 	(1 << 1)
#define LED4 	(1 << 0)

//Global
uint8_t pb_deb  = 0;

//*****************************************
//	toggle_led - NOT DEBOUNCED
//*****************************************
void toggle_led(uint8_t key, uint8_t led)
{
static uint8_t flags = 0xFF;

	//process PB/LED
	if ((~PIND & 0xF0) & key) {
		if (flags &=  key){
			flags &= ~key;	//clear the toggle flag
			PORTD ^=  led;  //XOR toggles LED
		}
	}
	else	flags |= key;

}

//*****************************************
//	toggle_led - DEBOUNCED
//*****************************************
void toggle_led_deb(uint8_t key, uint8_t led)
{
static uint8_t flags = 0xFF;

	//process PB/LED
	if (pb_deb & key) {
		if (flags & key){
			flags &= ~key;	//clear the toggle flag
			PORTD ^=  led;  //XOR toggles LED
		}
	}
	else	flags |= key;

}

//*****************************************
//	toggle_led - DEBOUNCED & LATCHED
//*****************************************
void toggle_led_deblat(uint8_t key, uint8_t led)
{
	//process PB/LED
	if (pb_deb & key) {
			pb_deb &= ~key;	//clear latched bit
			PORTD ^=  led;	//XOR toggles LED
		}
}

//*****************************************
//	main
//*****************************************
int main(void){

	cli();

	//setup port
	PORTD = 0xFF;
	DDRD  = 0x0F;

	//setup timer for overflow interrupt
    TCNT0 = COUNT_5MS;
    TIMSK0 = 1<<TOIE0;
    TCCR0B = (1<<CS02)|(0<<CS01)|(1<<CS00);

	sei();

	while (1){	

		//uncomment for Non-Debounced Test
		//toggle_led(KEY1,LED1);
		//toggle_led(KEY2,LED2);
		//toggle_led(KEY3,LED3);
		//toggle_led(KEY4,LED4);

		//toggle_led_deb(KEY1,LED1);
		//toggle_led_deb(KEY2,LED2);
		//toggle_led_deb(KEY3,LED3);
		//toggle_led_deb(KEY4,LED4);

		toggle_led_deblat(KEY1,LED1);
		toggle_led_deblat(KEY2,LED2);
		toggle_led_deblat(KEY3,LED3);
		toggle_led_deblat(KEY4,LED4);
	}	

	return 0;
}

//This timer interrupt will use the Debounced but not - latched version of the state machine
//ISR(TIMER0_OVF_vect)
//{
	//static uint8_t Q0,Q1 = 0;
	//uint8_t D0,D1;
	//uint8_t pb_now;
//
	////reload counter
    //TCNT0 = COUNT_5MS;
	//
	////	read the current button state
	//pb_now = ~PIND & 0xF0;
	//
	//// **** process Mealy State Machine ****
        //// (9 clock cycles)
	////debounced output
	//pb_deb  = Q1 | (pb_now & Q0);
	//
	////D Flip Flop Input
	//D1		= pb_now & Q0;
	//D0		= pb_now | Q1;
//
	////D Flip Flop next state
	//Q0 = D0;
	//Q1 = D1;
				//
//}

//This timer interrupt will use the latched version of the state machine
ISR(TIMER0_OVF_vect)
{
	static uint8_t Q0,Q1 = 0;
	uint8_t D0,D1;
	uint8_t pb_now;

	//reload counter
	TCNT0 = COUNT_5MS;

	//	read the current button state
	pb_now = ~PIND & 0xF0;

	// **** process Mealy State Machine ****
	//debounced with latched output
	pb_deb  |=  pb_now & ~Q1  &  Q0;

	//D Flip Flop Input
	D1 = (Q1 & ~Q0) | (Q0 & pb_now);
	D0 = (~Q1 & ~Q0 & pb_now) | (Q1 & ~Q0 & ~pb_now);

	//D Flip Flop next state
	Q0 = D0;
	Q1 = D1;

}