diff --git a/include/etl/debounce.h b/include/etl/debounce.h index 20a13ab5..e989b0a4 100644 --- a/include/etl/debounce.h +++ b/include/etl/debounce.h @@ -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); + } }; //*************************************************************************** diff --git a/include/etl/debounce_multi_bit.h b/include/etl/debounce_multi_bit.h index 9f62a340..8655f922 100644 --- a/include/etl/debounce_multi_bit.h +++ b/include/etl/debounce_multi_bit.h @@ -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_lo & bit) > 0); const state_type hi = static_cast((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 counts; // Off / On / Hold / Repeat timers. + + uint_least8_t bit_index; // Index of the next bit to process in incremental mode. }; } // namespace etl diff --git a/include/etl/debounce_value.h b/include/etl/debounce_value.h index 52ffaedc..5ea098bb 100644 --- a/include/etl/debounce_value.h +++ b/include/etl/debounce_value.h @@ -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) diff --git a/test/test_debounce_multi_bit.cpp b/test/test_debounce_multi_bit.cpp index a4577527..62019424 100644 --- a/test/test_debounce_multi_bit.cpp +++ b/test/test_debounce_multi_bit.cpp @@ -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 diff --git a/test/test_debounce_value.cpp b/test/test_debounce_value.cpp index 29e60a8c..64f7e0c9 100644 --- a/test/test_debounce_value.cpp +++ b/test/test_debounce_value.cpp @@ -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()); }