Simple (and imo) awesome debouncing 'library'

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

I've been trying to get some firmware off the ground lately, and for me anyways starting is always the hardest part.. Since what I'm working on will involve software-controlled power switching, I decided to write the button debouncing logic first.

This debouncing project is probably a little different from the ones most of you have written. It's definitely a little more bloated than one written with a particular project in mind.. But that's the price you pay for the convenience of a one-size-fits-all approach..

This debouncing 'library' uses a single timer to debounce multiple bits. It relies on the developer to signal it anytime one of its managed pins has changed states. Other than that it's pretty much automatic.

Here's how you use this library

1. initialize the debouncing timer

//Initial timer setup (does not start timer)
initializeDebouncerTimer();

2. Store initial pin states for detecting individual pin changes

lastState = PINB;

3. register the pins you want to debounce

registerDebouncer(&PINB,PB0,0,1,&ButtonClicker);
registerDebouncer(&PINB,PB1,1,1,&ButtonClicker);
registerDebouncer(&PINB,PB2,2,1,&ButtonClicker);
registerDebouncer(&PINB,PB3,3,1,&ButtonClicker);
registerDebouncer(&PINB,PB4,4,0,&ButtonClicker);
registerDebouncer(&PINB,PB5,5,0,&ButtonClicker);
registerDebouncer(&PINB,PB6,6,0,&ButtonClicker);
registerDebouncer(&PINB,PB7,7,0,&ButtonClicker);

The format for this function is
(
, bit, index (or id), asynchronous,callback)

If asynchronous is '1' the callback will be called
from within the timer ISR, otherwise it will be
called from within the main loop.

4. Use some method to detect changes to pins,
e.g. polling in main loop/INT0/INT1 pin change
or pin-change interrupts... signal the library
when this happens, e.g.

signalChangedState(< index (i.e. id) > , );

The 2nd parameter is what defines the individual
debounce delay, using 0 will default to a #define
constant

5. if using some or ALL synchronous callbacks, add a step in your main loop to process them, e.g.

while (1)
{
   do_stuff();

   callSignaledHandlers();
  _delay_ms(5);
}

Note: if using all asynchronous callbacks, step 5 isn't necessary

6. Sit back and wait for callbacks to notify of debounced state-changes, i.e.

void ButtonClicker(uint8_t index,uint8_t state)
{
	if (state == 0)
	{

		PORTD ^= _BV(index);
	}
}

So that's it! It's pretty much a cookie-cutter, one-size-fits-all kind of approach. The benefit is ease of use, ease of integration into projects and the ability to debounce across multiple ports easily.

The negatives: larger overhead, and it wasn't written from the ground up to be released. I'm just sharing it to inspire other ideas..if anyone wants to improve upon this send me a message. I'd like to see what you come up with

P.S.

There are some caveats though.. the mai n timer fires at a constant rate (though it halts when all buttons go idle), so you must balance granularity and CPU time.

Also of note, when a button's state changes, the timer ticks-down the debounce counter for that switch
each time the timer ISR fires. Because of the granularity of the timer firing (fixed period), the 'ticks' do not correspond to a fixed debouncing period but instead a maximum debouncing periood.

I.E. If the main timer ISR executes at 20ms and you are debouncing PIND0 with 2 debounce ticks, that doesn't mean you're debouncing over 40ms. It means you're debouncing over a minimum of 20ms and a maximum of 40ms, due to the granularity of the timer firing.

For that reason, do not define the tick counter for any pin as '1', it may not debounce it properly.

Check out my project!
http://www.avrfreaks.net/index.php?module=Freaks%20Academy&func=viewItem&item_type=project&item_id=2424

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

The big downside I see is that the event routines are effectively isr code therefore you would need to be real careful to avoid atomic access and volatile issues.

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

I don't like asynchronous event callback thingies. You usually end up just setting a flag that polled somewhere; and you cannot do any real work inside them because, as Kartman points out, they run in the ISR context.

So just a function to inspect the state of a button is much easier then an async callback.

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

I agree. Nice piece of work, but could cause some major grief when used without regard to the issues pointed out.