[TUT][SOFT][HARD] Button debouncing in software

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

This tutorial is about debouncing button's in software. The final described method isn't really invented by me as it ends up to be about the same as Peter Dannegger's (avr freak user danni) excellent debounce code. I start with a simple method usable for one button and try to describe the steps needed to generalize it to a solution that only uses logic operations, which have the benefit that you can debounce up to a whole port of buttons at about the same cost as the one button method. Recommended reading before you jump into this is Ganssle's text about debouncing and Bit manipulation (AKA "Programming 101") in this forum. Edit: Some small corrections and added the zip-file that was lost in the migration to the new forum.

Attachment(s): 

Last Edited: Thu. Feb 5, 2015 - 08:43 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

thnx 4 that good illustration

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

You're welcome! I'm glad that someone found it useful.

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

I just had a quick look but that PDF looks really nice, Snigelen!

For a walkthrough of 'danni's debounce code, see this: https://www.avrfreaks.net/index.p...

Aside: What did you use to format and create the PDF?

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

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

JohanEkdahl wrote:
I just had a quick look but that PDF looks really nice, Snigelen!
Thanks!
Quote:
For a walkthrough of 'danni's debounce code, see this: https://www.avrfreaks.net/index.p...
I have missed that thread, I'll check it out.
Quote:
Aside: What did you use to format and create the PDF?
It's written in LaTeX, with the listings package for source code.

Btw, I've updated the pdf in the first post (again), minor modifications and some spelcheckign.

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

suppose we want to check debounce condition on PORTC of atmega 8 where a switch is connected on PC0 then following code can be used successfully .

 
if(PINC&(1<<0)
{
_delay_ms(5);
if(PINC&(1<<0)
{
whatever you want to do ,please do here;
like count++;
}

:)

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

Are you kidding? If I fix the bugs in that snippet and run it in a loop then push a button and release it as fast as I can, count++ will be executed at least 16 times.

If you read the tutorial you'll hopefully see that there is more than one purpose of this. One is the bounce filtering and the other one is to keep track of the state of the buttons. You can hold a button for a minute if you want, it still only counts as one button press. And the program can take action on the press event and then continue to do other things while the button is still pressed. No delays needed.

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

No my friend ,have u check it for count++..it vl work successfully

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

You think it works but it only samples the input twice. You want to sample as many times as possible to lessen the statistical risk.

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

Snigelen,

 

First off - thanks for the great tutorial!

 

Second - I hope this isn't against forum etiquette to bring this back from the dead, but I think its worth noting the initial "bad example" which the rest of the tutorial is built off of has a slight typo. In your while loop, to toggle the LED on and off, you're xor-ing PORTB with the LED attached to it against the BUTTON_MASK,

 

if(~BUTTON_PIN & BUTTON_MASK){
    // Toggle the LED
    PORTB ^= BUTTON_MASK;
}

 

but I think you should XOR PORTB with the LED_MASK like so to toggle the LED on and off. The code in the tutorial works as is, but this becomes apparent the case where the LED and button are on different pin numbers

 

        if (~BUTTON_PIN & BUTTON_MASK){
            // Toggle the LED
            LED_PORT ^= LED_MASK;   //LED_PORT = PORTB
        }

 

 

Hope this helps somebody who may stumble upon this like I have!

Last Edited: Thu. Jan 8, 2015 - 08:48 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello,

 

Thanks for the correction. I've updated the pdf file in the first post (and added the example files in debounce.zip that was lost in the migration to the new forum).

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

Sorry for digging up this post from a way back, but I did a search and it was the first thing that appeared.  It also happened to perfectly answer my question.  I feel like I must be missing something but I am trying to implement a section of your code as listed in the pdf.  I am a NOOB so I am careful to work in baby steps so that I can learn.  In the debounce function the variable count is set to 0.  What I am confused about is why this does not defeat the possibility of the counter ever reaching 4.  Would count not be reset to 0 every time the function is executed causing the count to never get above 1?  Thanks in advance for the response.

 

EDIT:  Sorry.  Answered my own question.  It's the static function that makes this work.  My bad.

 

static inline void debounce(void)
{
// Counter for number of equal states
static uint8_t count = 0;
// Keeps track of current (debounced) state
static uint8_t button_state = 0;
// Check if button is high or low for the moment
uint8_t current_state = (~BUTTON_PIN & BUTTON_MASK) != 0;
if (current_state != button_state) {
// Button state is about to be changed, increase counter
count++;
if (count >= 4) {
// The button have not bounced for four checks, change state
button_state = current_state;
// If the button was pressed (not released), tell main so
if (current_state != 0) {
button_down = 1;
}
count = 0;
}
} else {
// Reset counter
count = 0;
}
}

 

Andrew

Last Edited: Fri. Feb 2, 2018 - 03:09 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Static on the function is different to static on the variable. Static on the variable makes it persistant.

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

This is the sort of things where it's much easier to learn about C on a PC than an AVR. If you don't understand the operation of "static" (on a variable) then run a quick test:

D:\c>type static.c
#include <stdio.h>

int fn(void) {
        static int count = 3;
        int retval;
        retval = count;
        count++;
        if (count > 5) {
                count = 0;
        }
        return retval;
}

int main(void) {
        int n;
        for (n=0; n < 12; n++) {
                printf("fn() returns %d\n", fn());
        }
}

D:\c>cl static.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

static.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:static.exe
static.obj

D:\c>static
fn() returns 3
fn() returns 4
fn() returns 5
fn() returns 0
fn() returns 1
fn() returns 2
fn() returns 3
fn() returns 4
fn() returns 5
fn() returns 0
fn() returns 1
fn() returns 2

So what actually happens here is that the use of "static" on the 'count' variable in fn() stops it being created on the stack each time the function is entered (as happens with automatics). Instead it is created in a fixed part of memory away from the fn() itself. The "= 3" ensures that when the program first loads it starts with the value 3 in it.

 

Each time fn() is called it just "inherits" the current state of 'count'. So on the first call it holds 3. Then, because of the ++ the next time it is 4 and so on and because of the "> 5" stuff it gets set back to 0 if it exceeds 5.

 

If I had written the code like this:

#include <stdio.h>

int count = 3;

int fn(void) {
        int retval;
        retval = count;
        count++;
        if (count > 5) {
                count = 0;
        }
        return retval;
}

int main(void) {
        int n;
        for (n=0; n < 12; n++) {
                printf("fn() returns %d\n", fn());
        }
}

then I'm sure you would totally expect the observed behaviour. But the difference here is that now "count" is global so can be seen and accessed outside the fn() code. Whereas when "static" inside that function its "name scope" was localized. There is a third option:

#include <stdio.h>

static int count = 3;

int fn(void) {
        int retval;
etc.

where it is outside the function but has "static" applied. So it can be seen/changed by any function in this file but the static means that no other "compilation unit" can get access to it. It is no longer a "global" symbol.

 

As I say, if you are just learning "C concepts" you can throw together an example like this and get immediate printf() output to see what's going on in a minute or two. To achieve the same in AVR would be far more complex. For a start where does printf() output actually go?

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

Let's just stress some theory that is very practical, namely two "aspects" of variables:

 

Scope is about where in the program a variable is visible.

 

Extent  is about the timespan in which a variable exists.

 

For "the usual" function-local variable, e.g

void foo(void) {
    int bar;
    
    .
    .
    baz();
    .
    .
}

the variable has local scope (i.e. is accessible or "visible" only from within the function). Its extent is for the duration for the execution of the function - i.e. when foo() exits the variable ceases to exist. If foo() is called again an new variable will be created. The extent of the variable bar is for the duration of the function invocation.

 

Notice that I've added a call from foo() to baz(), and understand that while baz() is executing the variable bar still exists - but is not accessible from inside baz() since it's scope is limited to foo().

 

Now for the static variant:

void foo(void) {
    static int bar;
    
    .
    .
    baz();
    .
    .
}

The scope of bar is identical to the first example - no difference there. The variable is only visible/accessible inside the function.

 

But the extent of the variable is for the duration of the program execution. One and the same variable will exists throughout all function invocations.

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"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] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]