Skip to content
Snippets Groups Projects
Commit c952b21b authored by Malaurie Bernard's avatar Malaurie Bernard
Browse files

Merge branch 'debouncer' into 'master'

+debouncer.h

See merge request !19
parents d35df548 7d9662b4
No related branches found
No related tags found
1 merge request!19+debouncer.h
#pragma once
#include <stdint.h>
#ifndef DEBOUNCER_DEFAULT_DEBOUNCE_PUSH_MS
#define DEBOUNCER_DEFAULT_DEBOUNCE_PUSH_MS 50 // debounce delay for push button
#endif
#ifndef DEBOUNCER_DEFAULT_DEBOUNCE_ROTARY_MS
#define DEBOUNCER_DEFAULT_DEBOUNCE_ROTARY_MS 1 // debounce delay for rotary button
#endif
#ifndef DEBOUNCER_DEFAULT_SHORT_MS
#define DEBOUNCER_DEFAULT_SHORT_MS 200 // max interval between multiple short clicks
#endif
#ifndef DEBOUNCER_DEFAULT_LONG_MS
#define DEBOUNCER_DEFAULT_LONG_MS 400 // min interval for long click
#endif
#if defined(ESP8266) && !DEBOUNCER_WITH_MILLIS
#pragma message "Debouncer: using esp_get_cycle_count"
#define DEBOUNCER_DEFAULT_CYCLE_FUNC esp_get_cycle_count
#define DEBOUNCER_DEFAULT_CYCLE_PER_MS (F_CPU / 1000)
#else
#if !defined(__AVR__)
#pragma message "Debouncer: using millis"
#endif
#define DEBOUNCER_DEFAULT_CYCLE_FUNC millis
#define DEBOUNCER_DEFAULT_CYCLE_PER_MS (1)
#endif
#if 0
#define DEBUGBUTTON(x...) do { Serial.print(cycle_f()); Serial.print(":"); x; } while (0)
#else
#define DEBUGBUTTON(x...) do { } while (0)
#endif
#ifndef IRAM_ATTR
#define IRAM_ATTR // nothing
#endif
///////////////////////////////////////////////////////////////
/*
ButtonFast::
!enable_multi && !enable_long:
short click: response on click
no long click
ButtonLong::
!enable_multi && enable_long:
short click: response on release + debounce time
long click: response on long click delay
Debouncer<long_cycle=0>::
enable_multi && !enable_long:
short clicks number: response on final release + debounce time
no long click
Debouncer<true,≠0>:: (default)
enable_multi && enable_long:
short clicks number: reponse on final release + debounce time
long click: response on long click delay
*/
#if __GNUC__ <= 7
#define __cycle_template_decl
#define __cycle_template_args
#define __cycle_template_impl
#define cycle_f DEBOUNCER_DEFAULT_CYCLE_FUNC
#define cyclePerMs DEBOUNCER_DEFAULT_CYCLE_PER_MS
#else
#define __cycle_template_decl \
auto cycle_f = DEBOUNCER_DEFAULT_CYCLE_FUNC, \
auto cyclePerMs = DEBOUNCER_DEFAULT_CYCLE_PER_MS, \
#define __cycle_template_impl \
auto cycle_f, \
auto cyclePerMs, \
#define __cycle_template_args \
cycle_f, \
cyclePerMs, \
#endif
template <const bool enable_multi = true,
__cycle_template_decl
const decltype(cycle_f()) long_cycle = DEBOUNCER_DEFAULT_LONG_MS * cyclePerMs, /* 0 to disable long press */
const decltype(cycle_f()) debounce_cycle = DEBOUNCER_DEFAULT_DEBOUNCE_PUSH_MS * cyclePerMs,
const decltype(cycle_f()) short_cycle = DEBOUNCER_DEFAULT_SHORT_MS * cyclePerMs>
class Debouncer
{
protected:
using T = decltype(cycle_f());
enum state_e
{
off,
on,
on_long,
on_debounce,
off_debounce,
};
state_e state;
uint_fast8_t short_count;
bool long_state;
T state_cycle;
public:
Debouncer (): state(off), short_count(0), long_state(false), state_cycle(0)
{
DEBUGBUTTON(Serial.println(enable_multi));
DEBUGBUTTON(Serial.println(long_cycle));
DEBUGBUTTON(Serial.println(debounce_cycle));
DEBUGBUTTON(Serial.println(short_cycle));
}
// - must be called in the main loop or from interrupt
// - return true if a change has occured
bool update (bool pressed);
// simulate a long press
void simLongClick ()
{
long_state = true;
}
// simulate n short presses
void simShortClick (uint_fast8_t n)
{
short_count += n;
}
// returns long state, does not clear shorts
bool longState ()
{
bool state = long_state;
long_state = false;
return state;
}
// returns short count, does not clear long
uint_fast8_t shortCount ()
{
auto count = short_count;
short_count = 0;
return count;
}
// returns long state and clears/ignore shorts
bool longClick ()
{
return longState();
}
// returns short count and clears/ignore long
uint_fast8_t shortClick ()
{
return shortCount();
}
bool isOff () const
{
return state == off;
}
bool isLong ()
{
long_state = false;
return state == on_long;
}
};
///////////////////////////////////////////////////////////////
template <__cycle_template_decl
const decltype(cycle_f()) debounce_cycle = DEBOUNCER_DEFAULT_DEBOUNCE_PUSH_MS * cyclePerMs,
const decltype(cycle_f()) short_cycle = DEBOUNCER_DEFAULT_SHORT_MS * cyclePerMs>
class ButtonFast: public Debouncer</*no multi*/false, __cycle_template_args /*no long*/0, debounce_cycle, short_cycle>
{
public:
// multiclick disabled, longclick disabled
ButtonFast (): Debouncer<false, __cycle_template_args 0, debounce_cycle, short_cycle>() { }
bool pressed ()
{
using d = Debouncer<false, __cycle_template_args 0, debounce_cycle, short_cycle>;
return d::state == d::on_debounce || d::state == d::on;
}
bool longState () = delete;
uint_fast8_t shortCount () = delete;
bool longClick () = delete;
uint_fast8_t shortClick () = delete;
};
///////////////////////////////////////////////////////////////
template <__cycle_template_decl
const decltype(cycle_f()) long_cycle = DEBOUNCER_DEFAULT_LONG_MS * cyclePerMs>
class ButtonLong: public Debouncer</* no multi */false, __cycle_template_args long_cycle>
{
public:
ButtonLong (): Debouncer<false, __cycle_template_args long_cycle>() { }
bool pressed () { return Debouncer<false, __cycle_template_args long_cycle>::isLong(); }
bool longState () = delete;
uint_fast8_t shortCount () = delete;
bool longClick () = delete;
uint_fast8_t shortClick () = delete;
};
///////////////////////////////////////////////////////////////
template <__cycle_template_decl
const decltype(cycle_f()) debounce_cycle = DEBOUNCER_DEFAULT_DEBOUNCE_ROTARY_MS * cyclePerMs>
class Rotary
{
protected:
ButtonFast<__cycle_template_args debounce_cycle> b1, b2;
int_fast8_t rot;
public:
Rotary (): b1(), b2(), rot(0) { }
bool update (bool in1, bool in2); // steady state with pullup: true, true
bool right () { bool ret; if ((ret = (rot > 0))) --rot; return ret; }
bool left () { bool ret; if ((ret = (rot < 0))) ++rot; return ret; }
};
///////////////////////////////////////////////////////////////
template <const bool enable_multi,
__cycle_template_impl
const decltype(cycle_f()) long_cycle,
const decltype(cycle_f()) debounce_cycle,
const decltype(cycle_f()) short_cycle>
IRAM_ATTR // can be called from ISR
bool Debouncer<enable_multi, __cycle_template_args long_cycle, debounce_cycle, short_cycle>::update (bool pressed)
{
// get current time
auto cycle = cycle_f();
switch (state)
{
case off:
if (pressed)
{
// button is off, and now pressed
DEBUGBUTTON(Serial.println("off->on_debounce"));
state_cycle = cycle;
if (!enable_multi && /*long press disabled*/(long_cycle == 0))
{
bool ret = state != on_debounce;
state = on_debounce;
return ret; // true on first change
}
state = on_debounce;
}
break; // off, no change, return unreaded state
case on_long:
if (pressed)
return false; // no change
DEBUGBUTTON(Serial.println("on_long->off_debounce"));
state = off_debounce;
state_cycle = cycle;
return !enable_multi;
case on_debounce:
if ((cycle - state_cycle) < debounce_cycle)
{
// don't care before debounce delay
DEBUGBUTTON(Serial.println("debounce-on"));
return false;
}
DEBUGBUTTON(Serial.println("on_debounce->on"));
state = on;
// fall through
case on:
if (/*long press enabled*/(long_cycle > 0) && (cycle - state_cycle) > long_cycle)
{
// long press in progress
DEBUGBUTTON(Serial.println("on->on_long"));
state = on_long;
long_state = true;
return true;
}
if (!pressed)
{
// off after debounce delay
DEBUGBUTTON(Serial.println("on->off_debounce"));
state = off_debounce;
state_cycle = cycle;
if (!enable_multi)
// single press is released
return true;
// it was a short click
short_count++;
return !enable_multi && /*long press enabled*/(long_cycle > 0);
}
return false; // no change
case off_debounce:
if ((cycle - state_cycle) < debounce_cycle)
{
// don't care before debounce delay
DEBUGBUTTON(Serial.println("debounce-off"));
return false;
}
if (pressed)
{
// multiclick in progress
DEBUGBUTTON(Serial.println("off_debounce->on_debounce"));
state = on_debounce;
state_cycle = cycle;
return false;
}
if (enable_multi && (cycle - state_cycle) < short_cycle)
{
// wait for multiclick
DEBUGBUTTON(Serial.println("(multiclick?)"));
return false;
}
// released for good
DEBUGBUTTON(Serial.println("off_debounce->off"));
state = off;
break;
}
return short_count || long_state;
}
///////////////////////////////////////////////////////////////
template <__cycle_template_impl
const decltype(cycle_f()) debounce_cycle>
bool Rotary<__cycle_template_args debounce_cycle>::update (bool pressed1, bool pressed2)
{
bool b1u = b1.update(pressed1);
bool b2u = b2.update(pressed2);
if (b1u && b2.isOff() && b1.pressed())
{
rot++;
return true;
}
if (b2u && b1.isOff() && b2.pressed())
{
rot--;
return true;
}
return false;
}
///////////////////////////////////////////////////////////////
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment