Updates to debounce classes

This commit is contained in:
John Wellbelove 2026-11-05 12:15:55 +00:00
parent 9b6defddfd
commit 0a27322a2b
5 changed files with 309 additions and 140 deletions

View File

@ -198,24 +198,27 @@ namespace etl
bool valid = (count == valid_count);
switch (flags & State)
if (valid)
{
case Off:
{
set_state(sample, valid, valid);
break;
}
switch (flags & State)
{
case Off:
{
set_state(sample, valid, valid);
break;
}
case On:
{
set_state(sample, valid, valid);
break;
}
case On:
{
set_state(sample, valid, valid);
break;
}
default:
{
break;
}
default:
{
break;
}
}
}
}
@ -274,30 +277,33 @@ namespace etl
bool valid = (count == valid_count);
bool hold = (count == hold_count);
switch (flags & State)
if (valid || hold)
{
case Off:
{
set_state(sample, valid, valid);
break;
}
switch (flags & State)
{
case Off:
{
set_state(sample, valid, valid);
break;
}
case On:
{
set_state(sample, hold, valid);
break;
}
case On:
{
set_state(sample, hold, valid);
break;
}
case Held:
{
set_state(sample, hold, valid);
break;
}
case Held:
{
set_state(sample, hold, valid);
break;
}
default:
{
break;
}
default:
{
break;
}
}
}
}
@ -332,6 +338,7 @@ namespace etl
//*************************************************************************
void set_state(bool sample, bool condition_set, bool condition_clear)
{
// clang-format off State Sample Condition Set Condition Clear
static ETL_CONSTANT uint_least8_t state_table[8][2] = {/* Off 0 */ {debounce_base::Off, debounce_base::Off},
/* Off 1 */ {debounce_base::On, debounce_base::Off},
/* On 0 */ {debounce_base::Off, debounce_base::On},
@ -339,8 +346,8 @@ namespace etl
/* Held 0 */ {debounce_base::Off, debounce_base::Held},
/* Held 1 */ {debounce_base::Repeating, debounce_base::Held},
/* Repeating 0 */ {debounce_base::Off, debounce_base::Repeating},
/* Repeating 1 */
{debounce_base::Repeating, debounce_base::Repeating}};
/* Repeating 1 */ {debounce_base::Repeating, debounce_base::Repeating}};
// clang-format on
get_next(sample, condition_set, condition_clear, state_table);
}
@ -360,42 +367,45 @@ namespace etl
bool hold = (count == hold_count);
bool repeat = (count == repeat_count);
switch (flags & State)
if (valid || hold || repeat)
{
case Off:
{
set_state(sample, valid, valid);
break;
}
case On:
{
set_state(sample, hold, valid);
break;
}
case Held:
{
set_state(sample, repeat, valid);
break;
}
case Repeating:
{
set_state(sample, repeat, valid);
if (sample && repeat)
switch (flags & State)
{
case Off:
{
count = 0;
flags |= Change;
set_state(sample, valid, valid);
break;
}
break;
}
default:
{
break;
}
case On:
{
set_state(sample, hold, valid);
break;
}
case Held:
{
set_state(sample, repeat, valid);
break;
}
case Repeating:
{
set_state(sample, repeat, valid);
if (sample && repeat)
{
count = 0;
flags |= Change;
}
break;
}
default:
{
break;
}
}
}
}
@ -428,7 +438,6 @@ namespace etl
//*************************************************************************
/// Adds a new sample.
/// Returns 'true' if the debouncer changes state.
///\param sample The new sample.
///\return 'true' if the debouncer changed state.
//*************************************************************************
@ -436,6 +445,17 @@ namespace etl
{
return process(sample, VALID_COUNT, HOLD_COUNT, REPEAT_COUNT);
}
//*************************************************************************
/// Updates with a new sample.
/// For compatibility with the later etl::debounce_value and etl::debounce_multi_bit classes.
///\param sample The new sample.
///\return 'true' if the debouncer changed state.
//*************************************************************************
bool update(bool sample)
{
return process(sample, VALID_COUNT, HOLD_COUNT, REPEAT_COUNT);
}
};
//***************************************************************************

View File

@ -57,6 +57,7 @@ SOFTWARE.
//-----------------------------------------------------------------------------
#include "platform.h"
#include "array.h"
#include "binary.h"
#include "memory.h"
#include "static_assert.h"
@ -114,72 +115,81 @@ namespace etl
//***************************************************************************
void reset(value_type initial = 0)
{
sample = initial;
state_lo = 0;
state_hi = 0;
changed = 0;
sample = initial;
state_lo = 0;
state_hi = 0;
changed = 0;
bit_index = 0;
// Clear the timers for all bits.
etl::fill(ETL_OR_STD::begin(counts), ETL_OR_STD::end(counts), 0);
counts.fill(0);
}
//***************************************************************************
/// Adds a new next of the whole port value.
/// Returns true if any bit changed state, became held, or repeated.
/// Updates a bit.
/// The value of 'next' must not change between calls to update_incremental() until all bits have been processed.
/// Do not call update() after update_incremental() on the same instance unless update_incremental() has completed a full cycle (returned true).
/// Returns true if all of the bits have been processed.
//***************************************************************************
bool update_incremental(const value_type next)
{
// Determine which bits have changed since the last sample.
const value_type changed_bits = sample ^ next;
// First bit?
if (bit_index == 0)
{
changed = 0;
}
count_type* pcount = &counts[bit_index];
const value_type bit = bit_mask(bit_index);
const bool bit_has_changed = (changed_bits & bit) != 0;
update_bit(bit_has_changed, bit, pcount, next);
++bit_index;
// Have we processed all of the bits?
if (bit_index == Width)
{
bit_index = 0;
sample = next;
return true;
}
else
{
return false;
}
}
//***************************************************************************
/// Updates the whole port value.
/// Returns true if any bit changed state.
//***************************************************************************
bool update(const value_type next)
{
// Determine which bits have changed since the last sample.
const value_type changed_bits = sample ^ next;
sample = next;
changed = 0;
// The initial loop conditions.
count_type* pcount = &counts[0]; // Pointer to the first bit's timer.
value_type bit = 1U; // Mask for the first bit in the field.
count_type* pcount = counts.data(); // Pointer to the first bit's timer.
value_type bit = 1U; // Mask for the first bit in the field.
for (size_t i = 0; i < Width; ++i)
{
const bool bit_is_set = (sample & bit) != 0;
const bool bit_has_changed = (changed_bits & bit) != 0;
if (bit_has_changed)
{
*pcount = 0;
}
switch (get_state(bit))
{
case Off:
{
handle_off(pcount, bit, bit_is_set);
break;
}
case On:
{
handle_on(pcount, bit, bit_is_set);
break;
}
case Held:
{
handle_held(pcount, bit, bit_is_set);
break;
}
case Repeating:
{
handle_repeating(pcount, bit, bit_is_set);
break;
}
}
update_bit(bit_has_changed, bit, pcount, next);
bit = bit << 1U; // Move to the next bit.
++pcount; // Move to the next bit's timer.
}
sample = next;
return (changed != 0);
}
@ -201,7 +211,7 @@ namespace etl
bool is_set(size_t n) const
{
const value_type bit = bit_mask(n);
const state_type state = get_state(bit);
const state_type state = get_bit_state(bit);
return (state == On) || (state == Held) || (state == Repeating);
}
@ -213,7 +223,7 @@ namespace etl
bool is_held(size_t n) const
{
const value_type bit = bit_mask(n);
const state_type state = get_state(bit);
const state_type state = get_bit_state(bit);
return (state == Held) || (state == Repeating);
}
@ -225,11 +235,35 @@ namespace etl
bool is_repeating(size_t n) const
{
const value_type bit = bit_mask(n);
const state_type state = get_state(bit);
const state_type state = get_bit_state(bit);
return (state == Repeating);
}
//***************************************************************************
/// Returns the width of the bit field.
//***************************************************************************
size_t get_width() const
{
return Width;
}
//***************************************************************************
/// Returns the current state.
//***************************************************************************
value_type get_state() const
{
value_type bit = 1U;
value_type result = 0;
for (size_t i = 0; i < Width; ++i)
{
result |= (get_bit_state(bit) >= On) ? bit : 0;
}
return result;
}
private:
//***************************************************************************
@ -243,11 +277,52 @@ namespace etl
Repeating = 3 // Debounced state is Repeating. Implies Held and On.
};
//***************************************************************************
/// Updates a bit.
/// Returns true if the bit changed state.
//***************************************************************************
void update_bit(const bool bit_has_changed, const value_type bit, count_type* pcount, const value_type next)
{
const bool bit_is_set = (next & bit) != 0;
if (bit_has_changed)
{
*pcount = 0;
}
switch (get_bit_state(bit))
{
case Off:
{
handle_off(pcount, bit, bit_is_set);
break;
}
case On:
{
handle_on(pcount, bit, bit_is_set);
break;
}
case Held:
{
handle_held(pcount, bit, bit_is_set);
break;
}
case Repeating:
{
handle_repeating(pcount, bit, bit_is_set);
break;
}
}
}
//***************************************************************************
/// Gets the current debounce state for a specific bit.
///\return The current state of the bit.
//***************************************************************************
state_type get_state(const value_type bit) const
state_type get_bit_state(const value_type bit) const
{
const state_type lo = static_cast<state_type>((state_lo & bit) > 0);
const state_type hi = static_cast<state_type>((state_hi & bit) > 0);
@ -259,7 +334,7 @@ namespace etl
/// Gets the current debounce state for a specific bit.
///\return The current state of the bit.
//***************************************************************************
void set_state(state_type state, const value_type bit)
void set_bit_state(state_type state, const value_type bit)
{
state_lo = state & 1 ? state_lo | bit : state_lo & ~bit;
state_hi = state & 2 ? state_hi | bit : state_hi & ~bit;
@ -275,7 +350,7 @@ namespace etl
if (*pcount >= threshold)
{
set_state(new_state, bit);
set_bit_state(new_state, bit);
changed |= bit;
*pcount = 0;
}
@ -356,7 +431,9 @@ namespace etl
value_type state_hi; // High bits of the state. Combined with state_lo to form a 2-bit state for each bit in the field.
value_type changed; // Bits that have changed at the last update.
count_type counts[Width]; // Off / On / Hold / Repeat timers.
etl::array<count_type, Width> counts; // Off / On / Hold / Repeat timers.
uint_least8_t bit_index; // Index of the next bit to process in incremental mode.
};
} // namespace etl

View File

@ -62,11 +62,11 @@ namespace etl
}
//*************************************************************************
/// Adds a new value.
/// Updates with a new value.
/// If the value has changed, the counter is reset and the value is marked as unstable.
/// Returns 'true' if the value has changed.
//*************************************************************************
bool add(value_type value)
bool update(value_type value)
{
// Changed from last time?
if (value != last_value)

View File

@ -62,6 +62,7 @@ namespace
CHECK_FALSE(value.is_set(Bit0));
CHECK_FALSE(value.is_held(Bit0));
CHECK_FALSE(value.is_repeating(Bit0));
CHECK_EQUAL(0, value.get_state());
//*******************************
// Transition from Off to On.
@ -74,6 +75,8 @@ namespace
CHECK_FALSE(value.is_repeating(Bit0));
}
CHECK_EQUAL(0, value.get_state());
// Changed.
CHECK_TRUE(value.update(1));
CHECK_TRUE(value.has_changed(Bit0));
@ -81,6 +84,8 @@ namespace
CHECK_FALSE(value.is_held(Bit0));
CHECK_FALSE(value.is_repeating(Bit0));
CHECK_EQUAL(1, value.get_state());
//*******************************
// Ensure no more transitions.
for (size_t i = 0; i < Valid_Count; ++i)
@ -92,5 +97,72 @@ namespace
CHECK_FALSE(value.is_repeating(Bit0));
}
}
//*************************************************************************
TEST(test_debounce_no_hold_or_repeat_incremental)
{
debounce_valid value(Initial_Value);
CHECK_FALSE(value.has_changed(Bit0));
CHECK_FALSE(value.is_set(Bit0));
CHECK_FALSE(value.is_held(Bit0));
CHECK_FALSE(value.is_repeating(Bit0));
//*******************************
// Transition from Off to On.
for (size_t i = 0; i < Valid_Count - 1; ++i)
{
for (size_t j = 0; j < value.get_width() - 1; ++j)
{
CHECK_FALSE(value.update_incremental(1));
CHECK_FALSE(value.has_changed(Bit0));
CHECK_FALSE(value.is_set(Bit0));
CHECK_FALSE(value.is_held(Bit0));
CHECK_FALSE(value.is_repeating(Bit0));
}
CHECK_TRUE(value.update_incremental(1));
CHECK_FALSE(value.has_changed(Bit0));
CHECK_FALSE(value.is_set(Bit0));
CHECK_FALSE(value.is_held(Bit0));
CHECK_FALSE(value.is_repeating(Bit0));
}
CHECK_EQUAL(0, value.get_state());
// Final incremental update.
// Changed.
for (size_t j = 0; j < value.get_width() - 1; ++j)
{
CHECK_FALSE(value.update_incremental(1));
CHECK_FALSE(value.has_changed(Bit0));
CHECK_FALSE(value.is_set(Bit0));
CHECK_FALSE(value.is_held(Bit0));
CHECK_FALSE(value.is_repeating(Bit0));
}
CHECK_TRUE(value.update_incremental(1));
CHECK_TRUE(value.has_changed(Bit0));
CHECK_TRUE(value.is_set(Bit0));
CHECK_FALSE(value.is_held(Bit0));
CHECK_FALSE(value.is_repeating(Bit0));
CHECK_EQUAL(1, value.get_state());
//*******************************
// Ensure no more transitions.
for (size_t i = 0; i < Valid_Count; ++i)
{
for (size_t j = 0; j < value.get_width(); ++j)
{
value.update_incremental(1);
}
CHECK_FALSE(value.has_changed(Bit0));
CHECK_TRUE(value.is_set(Bit0));
CHECK_FALSE(value.is_held(Bit0));
CHECK_FALSE(value.is_repeating(Bit0));
}
}
}
} // namespace

View File

@ -49,52 +49,52 @@ namespace
//*******************************
// Transition from unstable to stable.
CHECK_FALSE(value.add(2));
CHECK_FALSE(value.update(2));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
CHECK_FALSE(value.add(2));
CHECK_FALSE(value.update(2));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
CHECK_FALSE(value.add(2));
CHECK_FALSE(value.update(2));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
// Stable & changed.
CHECK_TRUE(value.add(2));
CHECK_TRUE(value.update(2));
CHECK_TRUE(value.is_stable());
CHECK_TRUE(value.has_changed());
CHECK_EQUAL(2, value.get());
// Still stable.
CHECK_FALSE(value.add(2));
CHECK_FALSE(value.update(2));
CHECK_TRUE(value.is_stable());
CHECK_FALSE(value.has_changed());
//*******************************
// Transition from unstable to stable.
// Unstable.
CHECK_FALSE(value.add(3));
CHECK_FALSE(value.update(3));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
CHECK_FALSE(value.add(3));
CHECK_FALSE(value.update(3));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
CHECK_FALSE(value.add(3));
CHECK_FALSE(value.update(3));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
// Stable & changed.
CHECK_TRUE(value.add(3));
CHECK_TRUE(value.update(3));
CHECK_TRUE(value.is_stable());
CHECK_TRUE(value.has_changed());
CHECK_EQUAL(3, value.get());
// Stable.
CHECK_FALSE(value.add(3));
CHECK_FALSE(value.update(3));
CHECK_TRUE(value.is_stable());
CHECK_FALSE(value.has_changed());
}
@ -112,26 +112,26 @@ namespace
//*******************************
// Transition from unstable to stable.
CHECK_FALSE(value.add(2));
CHECK_FALSE(value.update(2));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
CHECK_FALSE(value.add(2));
CHECK_FALSE(value.update(2));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
CHECK_FALSE(value.add(2));
CHECK_FALSE(value.update(2));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
// Stable & changed.
CHECK_TRUE(value.add(2));
CHECK_TRUE(value.update(2));
CHECK_TRUE(value.is_stable());
CHECK_TRUE(value.has_changed());
CHECK_EQUAL(2, value.get());
// Still stable.
CHECK_FALSE(value.add(2));
CHECK_FALSE(value.update(2));
CHECK_TRUE(value.is_stable());
CHECK_FALSE(value.has_changed());
@ -142,18 +142,18 @@ namespace
//*******************************
// Transition from unstable to stable.
// Unstable.
CHECK_FALSE(value.add(3));
CHECK_FALSE(value.update(3));
CHECK_FALSE(value.is_stable());
CHECK_FALSE(value.has_changed());
// Stable & changed.
CHECK_TRUE(value.add(3));
CHECK_TRUE(value.update(3));
CHECK_TRUE(value.is_stable());
CHECK_TRUE(value.has_changed());
CHECK_EQUAL(3, value.get());
// Stable.
CHECK_FALSE(value.add(3));
CHECK_FALSE(value.update(3));
CHECK_TRUE(value.is_stable());
CHECK_FALSE(value.has_changed());
}
@ -170,7 +170,7 @@ namespace
for (uint8_t v : input)
{
if (value.add(v))
if (value.update(v))
{
output.push_back(value.get());
}