Button debouncing.

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

I have a problem with my code

I'm trying to solve the bouncing button problem and I did it, but I am stuck in debouncing() function[look down].

Every time I press the button PINB0 is changed(that is ok),  it also changes PINB2 values but ONLY ONCE and then comes back to function stays there, even if I'm not pressing a button. 

So I can't do anything outside the function debouncing().

I'm using atmega32A with 16MHz OSC.

Connect a button to GND. 

#define F_CPU 16000000ul
#include <avr/io.h>
#include <util/delay.h>


bool button_push()
{
	int pin_value;
	pin_value = PIND & (1<<PIND2);
	
	if ((pin_value != 0))
	{	
		
		return true;
	}else{
		return false;
	}
}

bool debouncing()
{
	int count;
	if (button_push())
	{
		count = 0;
		while (count < 100)
		{
			if (button_push())
			{
				count = 0;
			}
			count++;
		}
		
		return true;
	}
	return false;
}

int main(void)
{
	DDRD &= ~(1<<PIND2);
	PORTD |= (1<<PIND2);
	
	DDRB |= (1<<PINB0) | (1<<PINB2); 
	PORTB |= (1<<PINB0) | (1<<PINB2);
	while (1)
	{
		if (debouncing())
		{
			PORTB ^= (1<<PINB0);
		}
		
		PORTB |= (1<<PINB2);
		_delay_ms(100);
		PORTB &= ~(1<<PINB2);		
		
		
	}
}

 

 

This topic has a solution.
Last Edited: Thu. Feb 11, 2021 - 10:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just read the port twice with a gap of 20-30ms.
If the result is the same both times, the button has been operated.

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

Do a search of the forum.  This topic has been beaten to death many times.

 

jim

I would rather attempt something great and fail, than attempt nothing and succeed - Fortune Cookie

 

"The critical shortage here is not stuff, but time." - Johan Ekdahl

 

"Step N is required before you can do step N+1!" - ka7ehk

 

"If you want a career with a known path - become an undertaker. Dead people don't sue!" - Kartman

"Why is there a "Highway to Hell" and only a "Stairway to Heaven"? A prediction of the expected traffic load?"  - Lee "theusch"

 

Speak sweetly. It makes your words easier to digest when at a later date you have to eat them ;-)  - Source Unknown

Please Read: Code-of-Conduct

Atmel Studio6.2/AS7, DipTrace, Quartus, MPLAB, RSLogix user

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

kabasan wrote:

Just read the port twice with a gap of 20-30ms.
If the result is the same both times, the button has been operated.

 

It doesn't work either.

But now it is not changing any of the output pins.

Both pins stay high.

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

Isn't the logic inverted for the button? I.e. if it is pressed, then the value of PIND should be 0. When not pressed, it should be 1.

/Jakob Selbing

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

jgmdesign wrote:
Do a search of the forum.  This topic has been beaten to death many times.

Indeed!

 

Only this morning: https://www.avrfreaks.net/commen...

 

Fifteen tutorials: https://www.avrfreaks.net/search...

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Thu. Feb 11, 2021 - 02:18 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For button debouncing, I use code similar to this:

//push on push off toggle
#define SWPUSHED() (PORTB & 0x01)==0  //reads the button input on PB0 (button connects from port pin to gnd, with pullup enabled) change as needed for your app
uint8_t swpushed;            //a place to save the current state of button 
volatile uint8_t swpushedfallingedge; //one shot of button being pushed (debounced button state)
uint8_t swpushedlast;        //saves current state for next pass (takes min two passes to debounce)
volatile uint8_t swtoggleon; //the software toggle push on(1) / push off(0) 
.
.
.
//somewhere in the main loop or use in timer ISR() to sample button state (20ms tick timer)
{
	swpushed = SWPUSHED();                            //read button state right now
	swpushedfallingedge = swpushed && !swpushedlast;  //edge detect (ie. is it different from the last time checked)
	swpushedlast = swpushed;                          //remember state for next pass
	if(swpushedfallingedge) swtoggleon = !swtoggleon; //flip state on falling edge (toggle)
}
.
.
.
//use toggle state for something (example use)
if(swtoggleon) setLEDon(); //toggle is on
else setLEDoff();          //toggle is off
//delay 20ms. sample button rate should be longer than bounce duration, by several ms

Hope it helps

 

Jim

 

 

(Possum Lodge oath) Quando omni flunkus, moritati.

"I thought growing old would take longer"

 

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

Peter Danneger ('danni') and Lee Theusch ('theusch') have previously posted their well respected solutions for debounce here. Both poll pin states on a timer. One records history horizontally and one vertically. When the same state exists at multiple poll periods the signal can be read as being stable in that state. Look for a post by Johan Ekdahl where he analyzed each technique and explained how the algorithms work.

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

clawson wrote:
Look for a post by Johan Ekdahl

If only the forum had a 'search by user' feature ...

 

frown

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

Look for a post by Johan Ekdahl where he analyzed each technique and explained how the algorithms work.

 

Can you live a link?

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

I have astonished myself but I actually found it..

 

https://www.avrfreaks.net/forum/...

Last Edited: Thu. Feb 11, 2021 - 02:44 PM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I made a simple sample.
Debounce all PINDs by polling every 30ms.
Only the bits that match 30ms before and now will update pind_dbnc.
You just use pind_dbnc as an input port.

 

#define F_CPU 16000000ul
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdbool.h>

volatile uint16_t tick_ms, tmr_dbnc;
volatile uint8_t pind_dbnc = 0xFF;
bool button_down = false;

// Timer0 1ms interrupt
ISR (TIMER0_COMP_vect){
    tick_ms++;
}

uint16_t my_millis(void){
    cli();
    uint16_t ret = tick_ms;
    sei();
    return ret;
}

void debouncing(void){
    static uint8_t prev = 0xFF;
    uint8_t work1, work2;

    work1 = work2 = prev;
    prev = PIND;
    work1 &= prev;
    work2 |= prev;
    pind_dbnc |= work1;
    pind_dbnc &= work2;
}

int main(void){
    // Timer0 init(1ms tick)
    TIMSK = _BV(OCIE0);
    OCR0 = 249;
    TCCR0 = _BV(WGM01) | _BV(CS01) | _BV(CS00);
    tmr_dbnc = my_millis();
    sei();

    DDRD &= ~(1<<DDRD2);
    PORTD |= (1<<PORTD2);
    DDRB |= (1<<DDRB0);
    PORTB |= (1<<PORTB0);

    while (1){
        if (my_millis() - tmr_dbnc >= 30){
            tmr_dbnc = my_millis();
            debouncing();
        }

        if ((pind_dbnc & _BV(PIND2)) && button_down){
            // button up
            button_down = false;
        } 

        if (!(pind_dbnc & _BV(PIND2)) && !button_down){
            // button down
            button_down = true;
            PORTB ^= (1<<PORTB0);
        }
    }
}

 

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

The vertical counter scheme is one of the best!  It has come in very handy more than once, and is optimal compact logic when you need a number of buttons taken care of.

 

If you just need one button, you can simply have a sampling timer (or some method of spacing the samples)...If the switch reads closed, count a tick up towards some max (say 25, assuming 1 millisecond samples), If reading open, count down one tick towards 0.  If you reach the high limit (25) set button_state to 1, if you reach the lower limit (0), set button_state to 0, otherwise leave button_state alone.  Works great & so simple it's easy to get working the first time. 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

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

It works great!!

I think that problem was the _deley_ms_ function.

After I changed it works fine.

It makes so many problems, needs to be forbidden.

 

Thanks a lot!!

Well done

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

kabasan wrote:

 

uint16_t my_millis(void){
    cli();
    uint16_t ret = tick_ms;
    sei();
    return ret;
}

 

Strictly technically, kabasan, this is considered 'poor style' because it's possible that this subroutine could be called when interrupts are disabled - for outside reasons - and then it enables interrupts again, which may be undesirable.

 

Testing the I bit first adds a few cycles, but makes it safer.

 

Strictly technically.  smiley  S.

 

ETA: Fixed the quotation.

Last Edited: Fri. Feb 12, 2021 - 03:29 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I abandoned versatility and simplified it to make it easier for the OP to understand.
It doesn't matter if someone else has trouble because of it.
In fact, I used atomic halfway through, but I removed it.

 

xmega should use atomic even in interrupts, but many don't point out. It's the same.

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

Fair 'nuff.  That or you could use register pairs that have atomic 16-bit 'add' instructions - dunno if the compiler will do that.  Anyhow, point being it's a technicality.  The OP seems happy with it, so there!  S.

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

Yes, the OP issue is debounce.
I don't want to make a huge thread with more than 100 comments on topics that are not directly related.