Switch Debounce Code

Last post
65 posts / 0 new

Pages

Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

Can anyone suggest me a sample code for Switch debounce ?

Iam a newbie to C programming in AVR.
Iam using AT90CAN128 and AVRStudio 4.12.460.

Thank You!!!

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

Here is a fat one:

char debouncePA1(void)
{
  char ones=0, zeroes=0, i;
  for(i=0;i<9;i++){
    if(PINA&0x01){ // read pin == 1
      ones++;
    } else { // read pin == 0
      zeroes++;
    }
  _delay_ms(10);
  }
  return ones>zeroes;
}

I really prefer a simple hardware debouncer (RC filter). :?

___(°)^(°)___
A(VR)staroth

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

Here is the best de-bouncing guide I managed to find:
www.ganssle.com/debouncing.pdf
HTH,

Real men don't use backups, they post their stuff on a public ftp server and let the rest of the world make copies.

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

Astaroth wrote:
Here is a fat one:

char debouncePA1(void)
{
  char ones=0, zeroes=0, i;
  for(i=0;i<9;i++){
    if(PINA&0x01){ // read pin == 1
      ones++;
    } else { // read pin == 0
      zeroes++;
    }
  _delay_ms(10);
  }
  return ones>zeroes;
}


This code will overflow. :(

---------------------
John Firestone

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

Peter Fleury has these examples on his homepage

http://jump.to/fleury

http://homepage.hispeed.ch/peterfleury/avr-software.html

http://homepage.hispeed.ch/peterfleury/avrgcc-examples.zip

The debounce is based on code from Peter Danegger if i remember correctly , and is quite neat.

Btw: His Uart & LCD lcd libs are also worth a look.

/Bingo

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

THE ULTIMATE SOFTWARE DEBOUNCER			

Program: debounce.asm
Version: 1.0
Date:    5/5/2005
Author:  Elio Mazzocca
E-mail:  jnz9876@adam.co.uk

micro used: ATmega8 with internal 4 MHz oscillator
assembled with: AVR Studio 4.0
Timer0 interrupt period: 4 mSEC

Description: This is a 1st order recursive digital filter
	with Schmitt trigger output that filters noisy
	digital inputs to ATmega8 microcontroller.
	The formula employed for the recursive filter:

	ynew = 1/4 xnew + 3/4 yold

	Formula for an inverting Schmitt trigger:

	if(ynew>hi) and (flag=0) then flag=1; vout=1;
	if(ynew

Sorry, dont have original link.

J.

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

Hello!
Here is my code for two switches (UP and DOWN).

// ---------- In header file ----------
#define KEY_UP			6
#define KEY_DN			7

// Pins
#define KEY_PINS       PIND

// Macros
#define KEY_DN_PRESS	(~(KEY_PINS) & (1<<KEY_DN))
#define KEY_UP_PRESS	(~(KEY_PINS) & (1<<KEY_UP))

// ---------- In main file ----------
int8_t read_keys(void) {
	if(KEY_UP_PRESS) {
		_delay_ms(10);
		while(KEY_UP_PRESS);		// Wait for key to be released
		_delay_ms(10);
		return 1;
	}
	if(KEY_DN_PRESS) {
		_delay_ms(10);
		while(KEY_DN_PRESS);
		_delay_ms(10);
		return -1;
	}
	else return 0;
}

Simple code, but the AVR will hang in the read_keys function as long as one of the buttons is pressed.
That's no problem in my app, interrupts are doing the housekeeping.

Regards,
Thomas

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

Hi Thomas!
Maybe the code you posted works ok for your app., but it's definitely NOT the way to go for a switch debounce code...
I recommend Jack G. Ganssle de-bouncing guide (it has also C-code examples) and the code suggested by xjedlins (asm code by Elio Mazzocca).

Real men don't use backups, they post their stuff on a public ftp server and let the rest of the world make copies.

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

FYI :idea:

http://www.edn.com/article/CA621638.html?spacedesc=DesignIdeas&taxid=10577

link to asm posted previously and also some other interesting links at the bottom of the article :)

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

Most versions of debouncers are only able to debounce a single key.

My approach need extremely less code and time to debounce 1 ... 8 keys simultaneosly and independent and bulletproofed:

/************************************************************************
;*                                                                      *
;*              Testing Read and debounce up to 8 keys                  *
;*              Bulletproof: 4 equal samples needed                     *
;*                                                                      *
;*              Author: P. Dannegger                                    *
;*                                                                      *
;***********************************************************************/
#include 
#include 
#include 

#define KEY_INPUT       PINC
#define LED_OUTPUT      PORTB
#define LED_DIR         DDRB

char key_state;                         // debounced and inverted key state:
                                        // bit = 1: key pressed
char key_press;                         // key press detect


SIGNAL (SIG_OVERFLOW0)
{
  static char ct0, ct1;
  char i;

  i = key_state ^ ~KEY_INPUT;           // key changed ?
  ct0 = ~( ct0 & i );                   // reset or count ct0
  ct1 = ct0 ^ (ct1 & i);                // reset or count ct1
  i &= ct0 & ct1;                       // count until roll over ?
  key_state ^= i;                       // then toggle debounced state
                                        // now debouncing finished
  key_press |= key_state & i;           // 0->1: key press detect
}


char get_key_press( char key_mask )
{
  cli();
  key_mask &= key_press;                // read key(s)
  key_press ^= key_mask;                // clear key(s)
  sei();
  return key_mask;
}


int main( void )
{
  key_state = 0;
  key_press = 0;

  TCCR0 = 1<<CS02;                      //divide by 256 * 256
  TIMSK = 1<<TOIE0;                     //enable timer interrupt

  LED_DIR = 0xFF;
  LED_OUTPUT = 0xFF;
  sei();
  for(;;)                                       // main loop
    LED_OUTPUT ^= get_key_press( 0xFF );        // toggle LEDs on key press
}

And the same in Assembler:

;*************************************************************************
;*                                                                       *
;*              Testing Read and debounce up to 8 keys                   *
;*              Bulletproof: 4 equal samples needed                      *
;*                                                                       *
;*              Author: P. Dannegger                                     *
;*                                                                       *
;*************************************************************************
.nolist
.include "c:\avr\inc\1200def.inc"
.list
.def    save_sreg       = r0
.def    iwr0            = r1

.def    key_ct0         = r2
.def    key_ct1         = r3
.def    key_state       = r4
.def    key_press       = r5

.def    leds            = r16
.def    wr0             = r17

.equ    key_port        = pind
.equ    led_port        = portb

        rjmp    init
        .org    OVF0addr                ;timer interrupt 6ms
        in      save_sreg, SREG
get8key:
        in      iwr0, key_port
        com     iwr0
        eor     iwr0, key_state
        and     key_ct0, iwr0
        and     key_ct1, iwr0
        com     key_ct0
        eor     key_ct1, key_ct0
        and     iwr0, key_ct0
        and     iwr0, key_ct1
        eor     key_state, iwr0
        and     iwr0, key_state
        or      key_press, iwr0
;
;                       insert other timer functions here
;
        out     SREG, save_sreg
        reti
;-------------------------------------------------------------------------
init:
        ldi     wr0, 0xFF
        out     ddrb, wr0
        ldi     wr0, 1<<CS02^1<<CS00            ;divide by 256 * 256
        out     TCCR0, wr0
        ldi     wr0, 1<<TOIE0           ;enable timer interrupt
        out     TIMSK, wr0

        clr     key_state
        clr     key_press
        ldi     wr0, 0xFF
        mov     key_ct0, wr0
        mov     key_ct1, wr0
        ldi     leds, 0xFF
main:   cli
        eor     leds, key_press         ;toggle LEDs
        clr     key_press               ;clear, if key press action done
        sei
        out     led_port, leds
        rjmp    main
;-------------------------------------------------------------------------

Peter

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

Hi Peter!
Now this is a very *clean* de-bouncing code!
Ursprüngliche Deutsche Qualität?

Real men don't use backups, they post their stuff on a public ftp server and let the rest of the world make copies.

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

Gee that's a lot of code to debounce a single pin.
In my experience the maximum time a switch (tactile style) will bounce is less then 30mS so if the value of the inputs (1-x) is stable for > 30 mS it should be ok. I increment a counter everytime a scan returns the same as the last scan. At 10mS scan interval this would need 3-4 scans (higher number gives "slower" keys) before the actual value (the value passed on to the rest of the program) is updated.

Simple and relative small overhead.

Different types of switches may need a bit of tweaking but 6 scans at 10mS interval works everytime for me.

/claus

/claus

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

Quote:

Gee that's a lot of code to debounce a single pin.

With all the examples posted in the thread you will need to be more specific about which approach you are commenting on.

Most of the above seem quite economical in terms of code space, SRAM/registers, and cycles.

There definitely is a place for more elaborate filtering. I tend to agree that if the "single pin" you are referring to is connected to a common user switch of some type then a straightforward deterministic cookie-cutter tried-and-true debouncing routine does just fine. But that "single pin" could be connected to any input device--I've got lots of apps with multiple banks of fetch & debounce code. The user buttons might be on one bank, simple switches (e.g. limit switches) on another, and sometimes other stuff--I've got some sensors that when they have crept up to the setpoint will bounce around it, off and on, for several seconds. that drove us nuts till we 'scoped the sensor output as well as the sensor input and saw what was going on. Those types of situations require different measures and all will take more space, cycles, & resources.

Lee

You can put lipstick on a pig, but it is still a pig.

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

Lee,
well maybe I was a little to quick on the trigger/Enter key about the code. It doesn't look to bad on a second look. Not that many lines really. My bad.

I defently agree that some sensors output some strange (and sometimes unexpected) garbage which requires som filtering but techie_avr asked about switch debouncing which simplifies this quite a bit.
The idea in xjedlins' code is quite fast to implement and so I often use it if nothing more complicated is required (which haven't really been the case - I'm more digital than analog ;))

/claus

/claus

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

I have made a small debouncing test with gasle:s guide. I'm using example 2 as a starting point.
Mine test program is not responsive enough. It is kinda sluggis and it flashes led sometimes when i press a button.
I'm running Atmega 8 with 1 mhz clock.
Here is mine code

#include 
#include 

#define LED1OFF          PORTC |= (1<<5) 
#define LED1ON           PORTC &= ~(1<<5) 

//Functions
void init_hardware(void);
unsigned char DebounceSwitch(void);
unsigned char RawKeyPressed(void);

//Global variables
unsigned char on_btn;
unsigned char ButtonStatus;
unsigned char LedStatus;

int main (void)
{
   on_btn = 0;
   ButtonStatus = 0;

   init_hardware();
   LED1OFF;
   sei(); //  Enable global interrupts

   for (;;)
   {
    if (ButtonStatus)
	{
	 if (LedStatus)
	  {
	     LED1OFF;
	    LedStatus=0;
      }
      else
	   {
	   LED1ON;
	   LedStatus=1;
	   }
    } 
   }
}

// Timer1 Compare match A interrupt
ISR(TIMER1_COMPA_vect)
{
 ButtonStatus = DebounceSwitch();
}

unsigned char RawKeyPressed(void)
{
 on_btn = ~PINC & (1<<3);
 return on_btn;
}

void init_hardware(void)
{
// PB3 As Output pin
    DDRB |= 1<<DDB3;

 // PC4,5 As Output pin
	DDRC |= 1<<DDC4;
	DDRC |= 1<<DDC5;

 // PC2,3 As Input pin
    DDRC &= ~(1<<2);
	DDRC &= ~(1<<3);

 // PC2,3 Activate internal pullUp resistor
    PORTC |= 1<<DDC2;
    PORTC |= 1<<DDC3;

// Timer setup

 // Configure timer 1 for CTC mode
    TCCR1B |= (1 << WGM12);

// Enable CTC interrupt
    TIMSK |= (1 << OCIE1A); 

// Set CTC compare value to 5ms at 1MHz AVR clock, with a prescaler of 8
    OCR1A   = 625; 

// Start timer at Fcpu/8
   TCCR1B |= ((1 << CS10)); 

}

// Service routine called by a timer interrupt 
unsigned char DebounceSwitch(void) 
 
{ 
 static uint16_t State = 0; // Current debounce status 
 State=(State<<1) | !RawKeyPressed() | 0xe000; 
 if(State==0xf000)return 1; 
 return 0; 
} 
 

Any ideas how to improve this?

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

hi to all. First i'm quite new in AVR programming, but I hardly need a sample software debouncing code for 3 buttons. I need it in assembler source code for ATmega8515. Can anyone suggest some? Thanks

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

CoolHammer - you code has afew issues. Firslty, your code will fail if any optimisation is turned on for the compiler - your shared variables are not declared 'voltaile'

Secondly, you really don't want to call subroutines from your ISR - this incurs a fair amount of overhead.

What does this line do?

State=(State<<1) | !RawKeyPressed() | 0xe000;

Why don't you just do this:


volatile char state;
....
ISR code
state<<=1;
if (PINC & (1<<2))  /we'll assume portc.2 as the input
   {
    State |= 1;
   }

To read the switch state assuming your 5mS tick,


 if (State ==0xff)
    {
    //switch is pressed - has been active for the last 8 ticks (40ms)
    }
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

morpheous, was there not assembler code presented?

Also your question is badly worded - "you hardly need"??? means you really don't need it - not quite what you were trying to say methinks.

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

Kartman wrote:
morpheous, was there not assembler code presented?

Also your question is badly worded - "you hardly need"??? means you really don't need it - not quite what you were trying to say methinks.

hardly means that i need this code a lot. Yes i saw an assembler code but i think it's not for ATmega8515, sry for my noobish... I want to put this debouncing fragment in a program for a BCD Clock.

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

hello i was wondering if you guys wouldnt mind helping me. i am trying to debounce a button using danni's example code, since its been said to be the best for doing multiple buttons at one time. what im trying to do is have the button act as a momentary button. so if you want the output to stay on you have to hold the button down. with the orginal code if you pushed the button it would toggle the input because it used:

if(get_key_press(1 << SW0))
		{
			OUTPUT_PORT ^= (1 << OUT0);		 //Turn OUT0 On
		}

below if the code that i currently have.

#define F_CPU 8000000UL	//Define CPU Clock Speed 8MHz

#include 
#include  
#include 
#include 	// need "--std=c99"

typedef unsigned char	u8;
typedef signed short	s16;

#define INPUT_PIN	 PIND
#define INPUT_PORT	PORTD
#define INPUT_DDR	 DDRD

#define SWO	PD0
#define SW1	PD1
#define SW2	PD2
#define SW3	PD3
#define SW4	PD4
#define SW5	PD5
#define SW6	PD6
#define SW7	PD7
		
#define OUTPUT_DDR	 DDRB
#define OUTPUT_PORT	PORTB

#define OUT0	PB0
#define OUT1	PB1
#define OUT2	PB2
#define OUT3	PB3
#define OUT4	PB4
#define OUT5	PB5
#define OUT6	PB6
#define OUT7	PB7

u8 input_state;		//Debounced and inverted key state:
			  			 //Bit = 1: key pressed
u8 input_press;		//Key Press Detected


ISR(TIMER1_COMPA_vect)				//every 10ms
{
	static u8 ct0 = 0xFF, ct1 = 0xFF;  // 8 * 2bit counters
	u8 i;

	i = ~INPUT_PIN;				  // read keys (low active)
	i ^= input_state;				// key changed ?
	ct0 = ~(ct0 & i);				// reset or count ct0
	ct1 = ct0 ^ (ct1 & i);		 // reset or count ct1
	i &= ct0 & ct1;			     // count until roll over ?
	input_state ^= i;			   // then toggle debounced state
	input_press |= input_state & i;	// 0->1: key press detect
}


u8 get_key_press(u8 input_mask)
{
	ATOMIC_BLOCK(ATOMIC_FORCEON)	// read and clear atomic!
	{
	input_mask &= input_press;			// read key(s)
	input_press ^= input_mask;			// clear key(s)
	}
	return input_mask;
}


int main(void)
{
	TCCR1B |=	(1 << WGM12);	      //Timer1 Mode 2: CTC
	TCCR1B |=	(1 << CS12);		    //Divide by 256
	OCR1A	 =	F_CPU / 256 * 10e-3; //Set CTC compare value to 10ms at 8MHz AVR clock, with a prescaler of 256
	TIMSK1 =	(1 << OCIE1A);		   //Enable T1 interrupt

	INPUT_DDR  = 0x00;				    //Sets INPUT_DDR to be input
	INPUT_PORT = 0xFF;				    //Enables pull up resistors on INPUT_PORT
	
	OUTPUT_DDR	 = 0xFF;			    //Sets OUTPUT_DDR to be output
	
	input_state	 = ~INPUT_PIN;		//no action on keypress during reset
	
	sei();

	while(1)
	{
		if(get_key_press(1 << SW0))
		{
			OUTPUT_PORT |= (1 << OUT0);		 //Turn OUT0 On
		}
		else if(get_key_press(1 << SW1))
		{
			OUTPUT_PORT |= (1 << OUT1);		 //Turn OUT1 On
		}
		else if(get_key_press(1 << SW2))
		{
			OUTPUT_PORT |= (1 << OUT2);		 //Turn OUT2 On
		}
		else if(get_key_press(1 << SW3))
		{
			OUTPUT_PORT |= (1 << OUT3);		 //Turn OUT3 On
		}
		else if(get_key_press(1 << SW4))
		{
			OUTPUT_PORT |= (1 << OUT4);		 //Turn OUT4 On
		}
		else if(get_key_press(1 << SW5))
		{
			OUTPUT_PORT |= (1 << OUT5);		 //Turn OUT5 On
		}
		else if(get_key_press(1 << SW6))
		{
			OUTPUT_PORT |= (1 << OUT6);		 //Turn OUT6 On
		}
		else if(get_key_press(1 << SW7))
		{
			OUTPUT_PORT |= (1 << OUT7);		 //Turn OUT7 On
		}
		else
		{
			OUTPUT_PORT &= ~(1 << OUT0);		//Turn OUT0 Off
			OUTPUT_PORT &= ~(1 << OUT1);		//Turn OUT1 Off
			OUTPUT_PORT &= ~(1 << OUT2);		//Turn OUT2 Off
			OUTPUT_PORT &= ~(1 << OUT3);		//Turn OUT3 Off
			OUTPUT_PORT &= ~(1 << OUT4);		//Turn OUT4 Off
			OUTPUT_PORT &= ~(1 << OUT5);		//Turn OUT5 Off
			OUTPUT_PORT &= ~(1 << OUT6);		//Turn OUT6 Off
			OUTPUT_PORT &= ~(1 << OUT7);		//Turn OUT7 Off
		}
	}
}

if i leave the final else statement in there and push a button, the output never turns on. if i comment out the last else statement, the output will turn on, but i can not turn it back off since it is using an OR instead of XOR

also could someone please explain what is happening in these to sections, as i do not fully understand what is happening inside this interrupt routine and what atomic_block does.

ISR(TIMER1_COMPA_vect)				//every 10ms
{
	static u8 ct0 = 0xFF, ct1 = 0xFF;  // 8 * 2bit counters
	u8 i;

	i = ~INPUT_PIN;				  // read keys (low active)
	i ^= input_state;				// key changed ?
	ct0 = ~(ct0 & i);				// reset or count ct0
	ct1 = ct0 ^ (ct1 & i);		 // reset or count ct1
	i &= ct0 & ct1;			     // count until roll over ?
	input_state ^= i;			   // then toggle debounced state
	input_press |= input_state & i;	// 0->1: key press detect
}


u8 get_key_press(u8 input_mask)
{
	ATOMIC_BLOCK(ATOMIC_FORCEON)	// read and clear atomic!
	{
	input_mask &= input_press;			// read key(s)
	input_press ^= input_mask;			// clear key(s)
	}
	return input_mask;
}

i would also like to use timer1 to flash a light at a 1Hz rate. so say out3 was an led, how would i use timer1 to do both the debouncing at 10ms and then flash an led on out3. im not quite sure how to set that up, or how to use one timer to do multiple things in general.

i am sorry for such a long post, and hope you guys dont mind. as you can tell i am fairly new to c programming and the microcontroller world, so any help is greatly appreciated.

edit: forgot to add that i am using an ATMega644 and that it is running at 8MHz

thanks
dan

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

Quote:

i would also like to use timer1 to flash a light at a 1Hz rate. so say out3 was an led, how would i use timer1 to do both the debouncing at 10ms and then flash an led on out3. im not quite sure how to set that up, or how to use one timer to do multiple things in general.

1. Declare a global volatile variable to hold number of 10ms ticks seen, eg

volatile unsigned char centiseconds;

2. In your ISR, increment that variable on every interrupt

centiseconds++;

3. In your main loop, inspect the variable and when it is time to act, do so:

   if (centiseconds >= 100) {
      centiseconds -= 100;

      // Handle switching the LED on or off here
   }

You might need to get the (start of) this if-clause atomic, but I'm too tired to think that out for you.

There are alternatives. You could have the centiseconds variable static inside the ISR and do not only the increment but also the "trip detection" there. It would in turn set a variable seconds = 1 which you then would inspect, act on and reset, in your main loop.

When doing multiple things based on one timer, think of the ISR as the thing responsible for generating the different tick frequencies for the different things needing them.

Eg, my variations of Dannis debounce code often includes breaking out the debounce code to a separate function. The ISR generates ticks (signaled through a global variable), and the main loop inspects those different "tick signals" and calls the things that need to be called. Sounds complicated at first, but for me it really creates structure and that makes sense in the long run.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington]

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

hi thanks for they help and info.

however this appears not to toggle the output every 100 centiseconds. added centiseconds to the isr

ISR(TIMER1_COMPA_vect)				//every 10ms
{
	static u8 ct0 = 0xFF, ct1 = 0xFF;	// 8 * 2bit counters
	u8 i;

	i = ~INPUT_PIN;					// read keys (low active)
	i ^= input_state;				// key changed ?
	ct0 = ~(ct0 & i);				// reset or count ct0
	ct1 = ct0 ^ (ct1 & i);			// reset or count ct1
	i &= ct0 & ct1;					// count until roll over ?
	input_state ^= i;				// then toggle debounced state
	input_press |= input_state & i;	// 0->1: key press detect

	centiseconds++;
}

then in the main function

		if(get_key_press(1 << SW3))
		{
			if (centiseconds >= 100) 
			{
				OUTPUT_PORT ^= (1 << OUT3);		 //Turn OUT3 On
				centiseconds = 0;
			}			
		}

i believe however it would work if the button was having to be held down. which is what i want to do, except im not sure on how to change it so the button has to be held down inorder to output. currently it is setup so that if the button is pushed the state will toggle, and then if pushed again it will go back to original state.

this is the way i used to do it, but was told its not a very good way and that i should look into danni's method

int reset_pressed(void)
{
	if(bit_is_clear(INPUT_PIN, RESET))
	{
		_delay_ms(DEBOUNCE_TIME);
		if(bit_is_clear(INPUT_PIN, RESET)) 
			return 1;
	}
	return 0;
}

i imagine that i have to change something in here, but but im not sure how to change it.

u8 get_key_press(u8 input_mask)
{
	ATOMIC_BLOCK(ATOMIC_FORCEON)	// read and clear atomic!
	{
	input_mask &= input_press;			// read key(s)
	input_press ^= input_mask;			// clear key(s)
	}
	return input_mask;
}

again thanks for your help, it is greatly appreciated.

if anyone knows how to make the button have to be held down in order to output and then turned off when not held down, i would greatly appreciate an info on that.

thanks
dan

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

Quote:

if anyone knows how to make the button have to be held down in order to output and then turned off when not held down, i would greatly appreciate an info on that.

Dannis debounce code has come in several flavours over the last few years. I know that one of'em had a feature for auto-repeat that might be useful. I can't tell you where to find that particular incarnation of the code though, and I desperately need to go to bed now.. Good luck!

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington]

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

Quote:

if(get_key_press(1 << SW0))
{
OUTPUT_PORT |= (1 << OUT0); //Turn OUT0 On
}

Quote:

if anyone knows how to make the button have to be held down in order to output and then turned off when not held down, i would greatly appreciate an info on that.

if (key_state & (1 << SW0))
    {
    OUTPUT_PORT |= (1 << OUT0);       //Turn OUT0 On
    }
else
    {
    OUTPUT_PORT &= ~(1 << OUT0);       //Turn OUT0 Off
    }

Also, why do you have an "else" checking each key for a new press? (a "rising edge"?)

Remembering that key_press has rising edges, and noting that the get_ routine clears the edge, you are then acting on the "event" of the rising edges.

Quote:

debounce a button using danni's example code, since its been said to be the best for doing multiple buttons at one time.

In this case, "best" is definitely in the eye of the beholder. Smallest? Fastest? Most flexible? :twisted:

You can put lipstick on a pig, but it is still a pig.

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

Quote:
Quote:

if(get_key_press(1 << SW0))
{
OUTPUT_PORT |= (1 << OUT0); //Turn OUT0 On
}

Quote:

if anyone knows how to make the button have to be held down in order to output and then turned off when not held down, i would greatly appreciate an info on that.

if (key_state & (1 << SW0))
    {
    OUTPUT_PORT |= (1 << OUT0);       //Turn OUT0 On
    }
else
    {
    OUTPUT_PORT &= ~(1 << OUT0);       //Turn OUT0 Off
    }


hi
thanks for that. however, when i change them all but the last one to that, the program functions correctly. so if i have sw0-6 as

if(input_state & (1 << SW0))
		{
			OUTPUT_PORT |= (1 << OUT0);		 //Turn OUT0 On
		}

and sw7 as

else if(get_key_press(1 << SW7))
		{
			OUTPUT_PORT |= (1 << OUT7);		 //Turn OUT7 On
		}

the outputs will turn on when the buttons are pushed(0-6), but if i then change sw7 to if(input_state & (1 << SW7)), then the outputs never seem to turn on.

any idea on why that would happen?
do i even need the get_key_press function after that?

Quote:

Also, why do you have an "else" checking each key for a new press? (a "rising edge"?)

Remembering that key_press has rising edges, and noting that the get_ routine clears the edge, you are then acting on the "event" of the rising edges.


not sure i understand what you mean. do you mean the else if?? i did it that way so i could have all the no key pressed conditions together in the final else at the bottom. is there a better way to do it?

Quote:
Quote:

debounce a button using danni's example code, since its been said to be the best for doing multiple buttons at one time.

In this case, "best" is definitely in the eye of the beholder. Smallest? Fastest? Most flexible? :twisted:

when reading the thread it seemed like the one people liked the most. is there a better one to use? or one that might be easier to understand what is going on?

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

SFLG35 wrote:
hello i was wondering if you guys wouldnt mind helping me. i am trying to debounce a button using danni's example code, since its been said to be the best for doing multiple buttons at one time. what im trying to do is have the button act as a momentary button. so if you want the output to stay on you have to hold the button down. with the orginal code if you pushed the button it would toggle the input because it used:
...
if i leave the final else statement in there and push a button, the output never turns on.

Thats correct, because you use the get_key_press() function in a wrong manner. :shock:

get_key_press() catch the keypress event, but not the pressed state. :!:

This was very useful on most applications.
E.g. to start something on the first keypress and stop it on the next keypress.
Or increase a variable on every keypress and so on.

If you want the debounced key state, then simple read the variable "input_state" directly.

Peter

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

SFLG35 wrote:
when reading the thread it seemed like the one people liked the most. is there a better one to use? or one that might be easier to understand what is going on?

Maybe, you should explain your task, which you want to solve.

get_key_press() works exactly like your PC-keyboard.
E.g. if you press "A", then you want to display only a single "A" and not the whole screen filled with "AAAAAAA".

Peter

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

if i just read input_state does it still debounce the button with the timer?

any idea and why if i change them all to if(input_state & (1<<SWx)), none of the outputs turn on? i have to leave atleast one button as if(get_key_pressed(1<<SWx)). if i do that then buttons SWx to SW(x-1) will function correctly, but button SWx does not work.

thanks for your help

dan

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

danni wrote:
SFLG35 wrote:
when reading the thread it seemed like the one people liked the most. is there a better one to use? or one that might be easier to understand what is going on?

Maybe, you should explain your task, which you want to solve.

get_key_press() works exactly like your PC-keyboard.
E.g. if you press "A", then you want to display only a single "A" and not the whole screen filled with "AAAAAAA".

Peter

i would want it to produce AAAAAAAAAAAAAAAAAA and then stop when i let go of A, like a normal keyboard does.

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

That's the code I gave you, which you would carry out on a periodic basis. (I suppose you could just wait for the UDR to be free and do it at near max baud rate.) As danni said, you don't use get_key_press() for that, which gives you the rising (make) edge. danni has posted auto-repeat variants of his routine(s) several times; search them out.

Lee

You can put lipstick on a pig, but it is still a pig.

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

SFLG35 wrote:
any idea and why if i change them all to if(input_state & (1<<SWx)), none of the outputs turn on?

If you want to use "input_state" from the main inside the same object file, then it must be declared as "volatile".

Peter

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

hi, i'm trying Peter's debouncing software which i still haven't been able to figure out completely and is not working reliably at the push of a button (i know it's my fault and Peter's code is right - i created a thread for it at
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=723689#723689 ) and i think i will also try Dan's code (which is another flavor of Peter's code...)

In order to check the values every 10ms, Dan is using:

 OCR1A  =   F_CPU / 256 * 10e-3;		//Set CTC compare value to 10ms at 8MHz AVR clock, with a prescaler of 256

i have been looking on the ATmega64 manual how to set up OCR1A / CTC to compare value to 10ms but i can't find it - i'm using F_CPU 7372800UL . Could you guide me in the right direction?

thanks for you help,
- Eric

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

Quote:

Could you guide me in the right direction?

Search for "avrcalc" or "kavrcalc". Either tool will help you calculate the values. Here's what avrcalc says. The OCR1AH/L value shown is 0x0120 but you always set the register to -1 from the given value (because 0 is included in the count) so the OCR1A you want is 0x011F

PS guess what value you get if you use the formula above:

OCR1A  =   F_CPU / 256 * 10e-3; 

;-) ;-) ;-)

Attachment(s): 

 

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

nice calculator : ) using the formula i get 288, not sure how exact it should be, because to get 0x011F / 287 DEC I'd have to use

OCR1A  =   F_CPU / 256 * 0.009965;

instead of the 10e-3...

I am trying Dan's code and it's not lighting an LED connected to PB0 at the press of a button connected to PD0 :| i'm using an atmega64. Any ideas why?

#define F_CPU 7372800UL  

#include  
#include  
#include  
#include    // need "--std=c99" 

typedef unsigned char   u8; 
typedef signed short   s16; 

#define INPUT_PIN    PIND 
#define INPUT_PORT   PORTD 
#define INPUT_DDR    DDRD 

#define SW0	  PD0 
#define SW1   PD1 
       
#define OUTPUT_DDR    DDRB 
#define OUTPUT_PORT   PORTB 

#define OUT0   PB0 
#define OUT1   PB1 

volatile u8 input_state;		//Debounced and inverted key state: 
								//Bit = 1: key pressed 
volatile u8 input_press;		//Key Press Detected 

ISR(TIMER1_COMPA_vect)			//every 10ms 
{ 
   static u8 ct0 = 0xFF, ct1 = 0xFF;	// 8 * 2bit counters 
   u8 i; 

   i = ~INPUT_PIN;				// read keys (low active) 
   i ^= input_state;			// key changed ? 
   ct0 = ~(ct0 & i);			// reset or count ct0 
   ct1 = ct0 ^ (ct1 & i);		// reset or count ct1 
   i &= ct0 & ct1;				// count until roll over ? 
   input_state ^= i;			// then toggle debounced state 
   input_press |= input_state & i;	// 0->1: key press detect -- RISING EDGE?
}

int main(void) 
{ 
	TCCR1B |=   (1 << WGM12);	//Timer1 Mode 2: CTC 
	TCCR1B |=   (1 << CS12);	//Divide by 256 
	OCR1A = F_CPU / 256 * 10e-3;//Set CTC compare value to 10ms at 8MHz AVR clock, with a prescaler of 256 
	TIMSK = (1 << OCIE1A);		//Enable T1 interrupt 

	INPUT_DDR = 0x00;			//Sets INPUT_DDR to be input 
	//INPUT_PORT = 0xFF;		//Enables pull up resistors on INPUT_PORT 
	//USING EXTERNAL 4.7K RESITOR --> 5V
	OUTPUT_DDR = 0xFF;			//Sets OUTPUT_DDR to be output 
	input_state = ~INPUT_PIN;	//no action on keypress during reset 
	
	sei(); 

	while(1) 
	{ 
		if(input_state & (1 << SW0)) 
		{ 
			OUTPUT_PORT |= (1 << OUT0);	//Turn OUT0 On 
		} 
		else if(input_state & (1 << SW1)) 
		{ 
			OUTPUT_PORT |= (1 << OUT1);	//Turn OUT1 On 
		}	
		else 
		{ 
			OUTPUT_PORT &= ~(1 << OUT0);//Turn OUT0 Off 
			OUTPUT_PORT &= ~(1 << OUT1);//Turn OUT1 Off 
		} 
	} 
} 
// not being used
/*u8 get_key_press(u8 input_mask) 
{ 
   ATOMIC_BLOCK(ATOMIC_FORCEON)		// read and clear atomic! 
   { 
   input_mask &= input_press;		// read key(s) 
   input_press ^= input_mask;		// clear key(s) 
   } 
   return input_mask; 
}*/ 

edit: i'm not creating a new thread since this code should be almost exactly to Dan's code posted a few messages above and with the modifications posted after it

thank you,
- Eric

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

Quote:

using the formula i get 288, not sure how exact it should be, because to get 0x011F / 287 DEC I'd have to use

Code:

OCR1A = F_CPU / 256 * 0.009965;

instead of the 10e-3...


Huh? Gratuitous use of floating point is frowned upon. (And the villagers will revolt, chanting "Preprocessor! Preprocessor!" My retort is "why tempt fate".)

Anyway, did you miss:

Quote:
The OCR1AH/L value shown is 0x0120 but you always set the register to -1 from the given value (because 0 is included in the count) so the OCR1A you want is 0x011F

So you need a -1 in your formula somewhere, the same way as in the common baud-rate "formulas". If you want to count three-click groups of something and you start at 0, the sequence is 0-1-2-0-1-2-0-...; for 3 clicks the max value that triggers a reset is 2.

I lost in the thread somewhere why it would matter being exactly 10ms for debouncing. It is indeed, though, very helpful to have an accurate "tick" of e.g. 10ms so seconds and hours and other coarser clocks keep proper time.

Lee

You can put lipstick on a pig, but it is still a pig.

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

hehe ok, so OCR1A is now calculated by

OCR1A = (F_CPU / 256 * 10e-3) - 1;

but it's still not working... should it matter if i need to catch a rising or falling edge, or is the code only caring about a change of state in PD0 / PD1?

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

I have created a new thread as i have found the original 'efficient key debounce' project posted by Peter / danni. If anyone could take a look at it, i'll greatly appreciate it
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=724358#724358
thanks,
- Eric

[thanks for following my advice and adding this - cliff]

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

Quote:
SFLG35 wrote:
hello i was wondering if you guys wouldnt mind helping me. i am trying to debounce a button using danni's example code, since its been said to be the best for doing multiple buttons at one time. what im trying to do is have the button act as a momentary button. so if you want the output to stay on you have to hold the button down. with the orginal code if you pushed the button it would toggle the input because it used:
...
if i leave the final else statement in there and push a button, the output never turns on.

Thats correct, because you use the get_key_press() function in a wrong manner. Shocked

get_key_press() catch the keypress event, but not the pressed state. Exclamation

This was very useful on most applications.
E.g. to start something on the first keypress and stop it on the next keypress.
Or increase a variable on every keypress and so on.

If you want the debounced key state, then simple read the variable "input_state" directly.

Peter

There is not variable "input_state", so in trying to do the same thing the OP was trying to do, I am failing! EDIT:(But my switches are wired active low, and work fine...as toggles...)

Any thoughts??

-fab

Later Edit: I've found if I clear "key_state" at the end of the if sequence, I can repeat the sequence if the button remains pressed! Thank You Peter Fleury! (He shows this in his version of danni's code.)

key_state = 0;
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm using Peter's code from this thread as it seem elegant for reading the whole port. I am using the code just as is (using STK600 w/ Mega2560). The problem I have and can't figure out is that the top four bits don't toggle, the LED's are always on and the switches do nothing. The first four switches (PC0-PC3) work find and the LED's toggle. Does anyone see anything in this code that would expplain this behavior?

/************************************************************************ 
;*                                                                      * 
;*              Testing Read and debounce up to 8 keys                  * 
;*              Bulletproof: 4 equal samples needed                     * 
;*                                                                      * 
;*              Author: P. Dannegger                                    * 
;*                                                                      * 
;***********************************************************************/ 
#include  
#include  
#include  

#define KEY_INPUT       PINC 
#define LED_OUTPUT      PORTB 
#define LED_DIR         DDRB 

char key_state;                         // debounced and inverted key state: 
                                        // bit = 1: key pressed 
char key_press;                         // key press detect 


SIGNAL (SIG_OVERFLOW0) 
{ 
  static char ct0, ct1; 
  char i; 

  i = key_state ^ ~KEY_INPUT;           // key changed ? 
  ct0 = ~( ct0 & i );                   // reset or count ct0 
  ct1 = ct0 ^ (ct1 & i);                // reset or count ct1 
  i &= ct0 & ct1;                       // count until roll over ? 
  key_state ^= i;                       // then toggle debounced state 
                                        // now debouncing finished 
  key_press |= key_state & i;           // 0->1: key press detect 
} 


char get_key_press( char key_mask ) 
{ 
  cli(); 
  key_mask &= key_press;                // read key(s) 
  key_press ^= key_mask;                // clear key(s) 
  sei(); 
  return key_mask; 
} 


int main( void ) 
{ 
  key_state = 0; 
  key_press = 0; 

  TCCR0 = 1<<CS02;                      //divide by 256 * 256 
  TIMSK = 1<<TOIE0;                     //enable timer interrupt 

  LED_DIR = 0xFF; 
  LED_OUTPUT = 0xFF; 
  sei(); 
  for(;;)                                       // main loop 
    LED_OUTPUT ^= get_key_press( 0xFF );        // toggle LEDs on key press 
} 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Jtag is enabled by default? Thus tying up the top 4 bits of portc.

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

Quote:
tag is enabled by default? Thus tying up the top 4 bits of portc.
yeah, I switched ports and it works so something was enabled on one of the other ports (was using Ports J & K).
This code seems very good, not overly intensive and to the point.

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

You can disable jtag either by fuse setting or code. Refer to the datasheet.

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

Hi guys, I also need help debouncing a keypad but Im working in assembly. I have tried some stuff that does not seem have worked. The code that I am trying is this for reading col1 for instance and then depending on the bits 4, 5, 6, and 7 if they are set I jump to the corresponding key press function.

ldi r16, 0x0D
sts PORTD_OUT, r16
rcall delay2
lds r17, PORTD_IN

where delay2 is a subroutine that empties a reg loaded with 0xff in a loop.

I have tried increasing this delay by repeating the delay2 subroutine a couple of times but does not seem to work

I have also tried doing the delays at the functions of the keys and nothing.

I am using the ATxmega128A1U in Atmel Studio 6.0

I have also attached my asm file

Thanks

Attachment(s): 

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

Counting down a single register is surely orders of magnitude too short? You want to be in the realms of tens of milliseconds.

Why not do what all the common solutions do and run a timer interrupt at 1/5/10ms and sample states on each tick?

BTW brave man electing to program a complex 128K chip in asm!

 

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

Quote:
Why not do what all the common solutions do and run a timer interrupt at 1/5/10ms and sample states on each tick?

And I'll add in that Danni's debouncing algorithm should be straight-forward to port to assembler.

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington]

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

Can someone post their switch debounce code test program, with these menu options: 1) read switch raw 2) read switch edge (closing contacts this pass, open last pass) 3) read switch with other debounce algo engaged 4) adj looptime up/dn. With item 1) we should see divergence between number of presses and number of reported presses as looptime shortens into the bounce period. With 3) the number of presses and reported presses should be the same even as the looptime gets much faster than a human can actiate a button (10s of ms?). My hypothesis is 2) will work fine as long as looptime is 10ms or more. I always use 2). I dont even know what the algo is for 1). If its just 'switch closed', it will just run up the count till you release, which isnt really correct.

Imagecraft compiler user

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

The problem with bounce isn't about detecting a real change in state, it's about ignoring phoney changes in state after a real change has occurred.

If that is true, would something like following work?

Program an input to produce an interrupt when the switch state changes, and record that as the new state. Disable further interrupts from that input.
After a timeout that that is substantially greater than the worst-case bounce interval, re-enable interrupts from the switch input, but in the opposite sense.

I don't think there is a lot of point in repeated sampling because it doesn't really tell us anything useful. Once we know a switch has changed state, it's really just a case ignoring it for long enough to let it settle into a steady state.

(If the algorithm is also expected to filter out noise, this method will not work.)

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

Restart00 wrote:
After a timeout that that is substantially greater than the worst-case bounce interval, re-enable interrupts from the switch input, but in the opposite sense.

But often you have not only a single key.
So a code, which handle 8 keys in parallel, was better suited.

Peter

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

danni wrote:

But often you have not only a single key.
So a code, which handle 8 keys in parallel, was better suited.

Peter

Agreed - yes, you frequently have a lot of switches, but you can usually produce a unique interrupt for each switch, and it's not so hard to keep track of several timeouts simultaneously.

Anyway, there are lots of different ways to handle the problem. I was just wondering if anyone had tried anything like this. It should help to minimize latency which can be useful in certain situations.

I may be forced to code it and find out for myself :)

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

Restart00 wrote:
Agreed - yes, you frequently have a lot of switches, but you can usually produce a unique interrupt for each switch, and it's not so hard to keep track of several timeouts simultaneously.

That's multiplying the effort without any benefit.

A typical human need >300ms to press a key.
So some further ms delay was not noticeable.

Peter

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

pernils wrote:
1)
The LED_OUTPUT will be toggled on/off on/off while the button is pressed.
(From what I can understand it's like that).

Yes/No ?

No. It will toggle the LED every time you push down a button.

Pages