Debounce a mechanical switch by periodic sampling.

Dependents:   RedButton WS_7_Seg_mit_LM1635 WS_7_Seg_mit_LM1635 Lichtschalter_M0 ... more

Debouncer.cpp

Committer:
huliyang
Date:
2015-05-18
Revision:
2:7f2f00805d41
Parent:
1:e59480cf6c72

File content as of revision 2:7f2f00805d41:

/* Copyright (c) 2015 Liyang HU, MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#include <mbed.h>
#include <Debouncer.h>

/*! \class Debouncer
 * \brief Debounce a mechanical switch by periodic sampling.
 * \code
#include <mbed.h>
#include <Pulsator.h>

Debouncer butt(PI_BUTTON);
void fall(void) { puts("Pushed!"); }
void rise(void) { puts("Released!"); }

int main(void) {
    butt.attach_fall(&fall).attach_rise(&rise);
    while(true) wait(1.0f);
}
 * \endcode
 * \note This implements \a DebounceSwitch2() as described by
 *  http://www.ganssle.com/debouncing-pt2.htm
 * \note In particular, directly hooking up a mechanical switch as an
 *  interrupt will not suffice: in general, the signal will not always
 *  satisfy any arbitrary processor's interrupt pin's min clock width,
 *  data setup nor hold times & c., and so will not trigger reliably.
 */

void Debouncer::tick(void)
{
    previous = (previous << 1) | in;
    uint32_t rise = (1 << _samples) - 1;
    uint32_t window = rise + rise + 1;
    uint32_t recent = previous & window;
    if(recent == rise)
    {
        if(!now) fun_rise.call();
        now = true;
    }
    else if(recent == (window & ~rise))
    {
        if(now) fun_fall.call();
        now = false;
    }
}

//! Create a \a Debouncer attached to the specified \a pin.
//! \param pin Pin to poll.
//! \param mode Pin mode, e.g. \a PullUp or \a PullDown.
Debouncer::Debouncer(PinName pin, PinMode mode)
    : in(pin, mode), previous(mode == PullUp ? -1 : 0), now(mode == PullUp)
{
    samples(); period();
}

//! Set the number of identical samples needed to regard the input as stable.
//! \note Limited to at most 32 samples.
Debouncer &Debouncer::samples(uint8_t n)
{
    _samples = n;
    return *this;
}

//! Set the time period between samples.
Debouncer &Debouncer::period(float seconds)
{
    detach();
    attach(this, &Debouncer::tick, seconds);
    return *this;
}

//! Get the current stable state of the input.
Debouncer::operator bool(void) { return now; }

//! Attach a function to call when the stable state falls.
Debouncer &Debouncer::attach_fall(void (*fun)(void))
{
    fun_fall.attach(fun);
    return *this;
}

//! Attach a function to call when the stable state rises.
Debouncer &Debouncer::attach_rise(void (*fun)(void))
{
    fun_rise.attach(fun);
    return *this;
}

//! Attach a member function to call when the stable state falls.
template <typename T>
Debouncer &Debouncer::attach_fall(T *obj, void (T::*fun)(void))
{
    fun_fall.attach(obj, fun);
    return *this;
}

//! Attach a member function to call when the stable state rises.
template <typename T>
Debouncer &Debouncer::attach_rise(T *obj, void (T::*fun)(void))
{
    fun_rise.attach(obj, fun);
    return *this;
}