mirror of
https://github.com/ETLCPP/etl.git
synced 2026-11-09 22:59:47 +08:00
Work in progress
This commit is contained in:
parent
9236f8f313
commit
1d01450757
97
docs/IO/debounce-value.md
Normal file
97
docs/IO/debounce-value.md
Normal file
@ -0,0 +1,97 @@
|
||||
---
|
||||
title: "debounce_value"
|
||||
---
|
||||
|
||||
{{< callout type="info">}}
|
||||
Header: `debounce_value.h`
|
||||
Since: `20.50.0`
|
||||
{{< /callout >}}
|
||||
|
||||
This class is designed to take an input value that may take time to become stable, such as a binary rotary encoder or DIP switches, and indicate when it has settled to a stable state.
|
||||
|
||||
```cpp
|
||||
template <typename TValue, typename TCount>
|
||||
etl::debounce_value;
|
||||
```
|
||||
|
||||
## Types
|
||||
|
||||
`value_type` : The type of the input value.
|
||||
`count_type` : The type for the counter.
|
||||
|
||||
|
||||
## Member functions
|
||||
|
||||
```cpp
|
||||
debounce_value(value_type initial_value, count_type stable_count)
|
||||
```
|
||||
**Description**
|
||||
Constructor.
|
||||
|
||||
**Parameters**
|
||||
`initial_value` : The initial value of the 'last' sample.
|
||||
|
||||
`stable_count` : The count at which an input is deemed to be stable.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool add(value_type value)
|
||||
```
|
||||
**Description**
|
||||
Adds a new value.
|
||||
If the value has changed, the count is reset and the value is marked as unstable.
|
||||
|
||||
**Parameters**
|
||||
`value` : The next input value.
|
||||
|
||||
**Return**
|
||||
Returns `true` if the value has changed.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
value_type get() const
|
||||
```
|
||||
**Description**
|
||||
Gets the current value.
|
||||
May not be stable unless `is_stable()` returns `true`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
operator value_type() const
|
||||
```
|
||||
**Description**
|
||||
Conversion operator.
|
||||
May not be stable unless `is_stable()` returns `true`.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool has_changed() const
|
||||
```
|
||||
**Description**
|
||||
Gets the current change state.
|
||||
|
||||
**Return**
|
||||
`true` if the stable value has changed.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
bool is_stable() const
|
||||
```
|
||||
**Description**
|
||||
Gets the current stable state.
|
||||
|
||||
**Return**
|
||||
`true` if the value is stable.
|
||||
|
||||
---
|
||||
|
||||
```cpp
|
||||
void set_stable_count(count_type stable_count)
|
||||
```
|
||||
**Description**
|
||||
Sets the count at which a non-changing input value is seen as stable.
|
||||
360
include/etl/debounce_multi_bit.h
Normal file
360
include/etl/debounce_multi_bit.h
Normal file
@ -0,0 +1,360 @@
|
||||
///\file
|
||||
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2026 John Wellbelove
|
||||
|
||||
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.
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef DEBOUNCE_MULTI_BIT_INCLUDED
|
||||
#define DEBOUNCE_MULTI_BIT_INCLUDED
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// debounce_multi_bit
|
||||
//
|
||||
// Debounces a contiguous group of bits read from an IO port. Each bit is
|
||||
// debounced independently, using the same semantics as etl::debounce:
|
||||
//
|
||||
// Valid_Count : Number of consecutive samples a bit must differ from its
|
||||
// previous debounced state before the state changes (>= 1).
|
||||
// Hold_Count : Number of samples a bit must remain set (after becoming
|
||||
// valid) before a 'held' event is generated. 0 = disabled.
|
||||
// Repeat_Count : Once held, a 'repeat' event is generated every
|
||||
// Repeat_Count samples. 0 = disabled.
|
||||
//
|
||||
// TValue : unsigned integer type of the port (uint8_t/16_t/32_t/64_t).
|
||||
// Shift : Position of the lowest bit of the field within the port value.
|
||||
// Width : Number of contiguous bits in the field.
|
||||
// TCount : Type of the per-bit counters. Must be able to hold the largest of
|
||||
// Valid_Count, Hold_Count and Repeat_Count.
|
||||
//
|
||||
// All masks returned by this class are field-aligned, i.e. bit 0 of a mask
|
||||
// corresponds to the lowest bit of the field (port bit 'Shift').
|
||||
//
|
||||
// Call update() at a fixed sampling interval (e.g. every 1ms from a timer).
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include "platform.h"
|
||||
#include "binary.h"
|
||||
#include "memory.h"
|
||||
#include "static_assert.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace etl
|
||||
{
|
||||
//*************************************************************************
|
||||
/// Debounces a contiguous group of bits read from an IO port.
|
||||
///\tparam TValue The type of the port value (uint8_t/16_t/32_t/64_t).
|
||||
///\tparam Width The number of bits in the field (1..sizeof(TValue) * 8).
|
||||
///\tparam Valid_Count The number of consecutive samples a bit must differ
|
||||
/// from its previous debounced state before the state
|
||||
/// changes (>= 1).
|
||||
///\tparam Hold_Count The number of samples a bit must remain set (after
|
||||
/// becoming valid) before a 'held' event is generated.
|
||||
/// 0 = disabled.
|
||||
///\tparam Repeat_Count Once held, a 'repeat' event is generated every
|
||||
/// Repeat_Count samples. 0 = disabled.
|
||||
///\tparam TCount The type of the per-bit counters. Must be able to hold
|
||||
/// the largest of Valid_Count, Hold_Count and Repeat_Count.
|
||||
//*************************************************************************
|
||||
template <typename TValue, typename TCount, size_t Width, uint32_t Valid_Count, uint32_t Hold_Count = 0, uint32_t Repeat_Count = 0>
|
||||
class debounce_multi_bit
|
||||
{
|
||||
public:
|
||||
|
||||
ETL_STATIC_ASSERT(std::is_integral<TValue>::value&& std::is_unsigned<TValue>::value, "TValue must be an unsigned integer type");
|
||||
ETL_STATIC_ASSERT(Width >= 1, "Width must be at least 1");
|
||||
ETL_STATIC_ASSERT(Width <= sizeof(TValue) * 8, "Field does not fit in the value type");
|
||||
ETL_STATIC_ASSERT(Valid_Count >= 1, "Valid_Count must be at least 1");
|
||||
ETL_STATIC_ASSERT(Valid_Count <= static_cast<TCount>(~TCount(0)) && Hold_Count <= static_cast<TCount>(~TCount(0))
|
||||
&& Repeat_Count <= static_cast<TCount>(~TCount(0)),
|
||||
"TCount is too small for the configured counts");
|
||||
|
||||
typedef TValue value_type;
|
||||
typedef TCount count_type;
|
||||
typedef uint_least8_t state_type;
|
||||
|
||||
//***************************************************************************
|
||||
/// Constructor. All bits start in the 'clear' state.
|
||||
//***************************************************************************
|
||||
debounce_multi_bit(value_type initial = 0)
|
||||
{
|
||||
reset(initial);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Resets the debouncer state.
|
||||
//***************************************************************************
|
||||
void reset(value_type initial = 0)
|
||||
{
|
||||
sample = initial;
|
||||
state_lo = 0;
|
||||
state_hi = 0;
|
||||
changed = 0;
|
||||
|
||||
// Clear the timers for all bits.
|
||||
etl::fill(ETL_OR_STD::begin(counts), ETL_OR_STD::end(counts), 0);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Adds a new next of the whole port value.
|
||||
/// Returns true if any bit changed state, became held, or repeated.
|
||||
//***************************************************************************
|
||||
void update(const value_type next)
|
||||
{
|
||||
value_type changed_bits = sample ^ next;
|
||||
sample = next;
|
||||
|
||||
changed = 0;
|
||||
|
||||
count_type* pcount = &counts[0];
|
||||
value_type bit = 1U;
|
||||
pcount = &counts[0];
|
||||
|
||||
for (size_t i = 0; i < Width; ++i)
|
||||
{
|
||||
bool bit_is_set = (sample & bit) != 0;
|
||||
bool bit_has_changed = (changed_bits & bit) != 0;
|
||||
|
||||
if (bit_has_changed)
|
||||
{
|
||||
// Reset the timer for this bit.
|
||||
*pcount = 0;
|
||||
}
|
||||
|
||||
switch (get_state(bit))
|
||||
{
|
||||
case Off:
|
||||
{
|
||||
if (bit_is_set)
|
||||
{
|
||||
++(*pcount);
|
||||
|
||||
// If we have reached the 'Valid_Count' move to the On state.
|
||||
if (*pcount >= Valid_Count)
|
||||
{
|
||||
set_state(On, bit);
|
||||
changed |= bit;
|
||||
*pcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case On:
|
||||
{
|
||||
if (Hold_Count > 0)
|
||||
{
|
||||
++(*pcount);
|
||||
|
||||
if (bit_is_set)
|
||||
{
|
||||
// If we have reached the 'Hold_Count', and hold is enabled, we move to the Held state.
|
||||
if (*pcount >= Hold_Count)
|
||||
{
|
||||
set_state(Held, bit);
|
||||
changed |= bit;
|
||||
*pcount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have reached the 'Valid_Count' we move back to the Off state.
|
||||
if (*pcount >= Valid_Count)
|
||||
{
|
||||
set_state(Off, bit);
|
||||
changed |= bit;
|
||||
*pcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Held:
|
||||
{
|
||||
if (Repeat_Count > 0)
|
||||
{
|
||||
++(*pcount);
|
||||
|
||||
if (bit_is_set)
|
||||
{
|
||||
// If we have reached the 'repeat count', and repeat is enabled, we move to the Repeating state.
|
||||
if (*pcount >= Repeat_Count)
|
||||
{
|
||||
set_state(Repeating, bit);
|
||||
changed |= bit;
|
||||
*pcount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have reached the 'Valid_Count' we move back to the Off state.
|
||||
if (*pcount >= Valid_Count)
|
||||
{
|
||||
set_state(Off, bit);
|
||||
changed |= bit;
|
||||
*pcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Repeating:
|
||||
{
|
||||
++(*pcount);
|
||||
|
||||
if (bit_is_set)
|
||||
{
|
||||
// If we have reached the 'Repeat_Count', and repeat is enabled, we set the changed flag again.
|
||||
if (*pcount >= Repeat_Count)
|
||||
{
|
||||
changed |= bit;
|
||||
*pcount = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have reached the 'Valid_Count' we move back to the Off state.
|
||||
if (*pcount >= Valid_Count)
|
||||
{
|
||||
set_state(Off, bit);
|
||||
changed |= bit;
|
||||
*pcount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// changed_bits = changed_bits >> 1U; // Move to the next bit.
|
||||
bit = bit << 1U; // Move to the next bit.
|
||||
++pcount; // Move to the next bit's timer.
|
||||
}
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Returns true if the bit changed state on the last update.
|
||||
///\param n The bit number (0..Width - 1).
|
||||
//***************************************************************************
|
||||
bool has_changed(size_t n) const
|
||||
{
|
||||
value_type bit = bit_mask(n);
|
||||
|
||||
return (changed & bit) != 0;
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Returns true if the bit is currently set.
|
||||
///\param n The bit number (0..Width - 1).
|
||||
//***************************************************************************
|
||||
bool is_set(size_t n) const
|
||||
{
|
||||
value_type bit = bit_mask(n);
|
||||
state_type state = get_state(bit);
|
||||
|
||||
return (state == On) || (state == Held) || (state == Repeating);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Returns true if the bit is currently held.
|
||||
///\param n The bit number (0..Width - 1).
|
||||
//***************************************************************************
|
||||
bool is_held(size_t n) const
|
||||
{
|
||||
value_type bit = bit_mask(n);
|
||||
state_type state = get_state(bit);
|
||||
|
||||
return (state == Held) || (state == Repeating);
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// Returns true if the bit is currently repeating.
|
||||
///\param n The bit number (0..Width - 1).
|
||||
//***************************************************************************
|
||||
bool is_repeating(size_t n) const
|
||||
{
|
||||
value_type bit = bit_mask(n);
|
||||
state_type state = get_state(bit);
|
||||
|
||||
return (state == Repeating);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//***************************************************************************
|
||||
/// State fit in 2 bits, state_lo & state_hi.
|
||||
//***************************************************************************
|
||||
enum state
|
||||
{
|
||||
Off = 0, // Debounced state is Off.
|
||||
On = 1, // Debounced state is On.
|
||||
Held = 2, // Debounced state is Held.
|
||||
Repeating = 3 // Debounced state is Repeating.
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// 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 lo = static_cast<state_type>((state_lo & bit) > 0);
|
||||
state_type hi = static_cast<state_type>((state_hi & bit) > 0);
|
||||
|
||||
return static_cast<state_type>(lo | (hi << 1));
|
||||
}
|
||||
|
||||
//***************************************************************************
|
||||
/// 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)
|
||||
{
|
||||
state_lo = state & 1 ? state_lo | bit : state_lo & ~bit;
|
||||
state_hi = state & 2 ? state_hi | bit : state_hi & ~bit;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Returns a mask with only the nth bit set.
|
||||
//*****************************************************************************
|
||||
static value_type bit_mask(size_t n)
|
||||
{
|
||||
return static_cast<value_type>(value_type(1) << n);
|
||||
}
|
||||
|
||||
value_type sample; // The last known state of the bits.
|
||||
value_type state_lo; // Low bits of the state.
|
||||
value_type state_hi; // High bits of the state.
|
||||
value_type changed; // Bits that have changed at the last update.
|
||||
|
||||
count_type counts[Width]; // Off / On / Hold / Repeat timers.
|
||||
};
|
||||
} // namespace etl
|
||||
|
||||
#endif
|
||||
@ -36,163 +36,101 @@ SOFTWARE.
|
||||
|
||||
namespace etl
|
||||
{
|
||||
template <typename TValue>
|
||||
class debounce_value_notification
|
||||
//***************************************************************************
|
||||
/// This class is designed to take an input value that may take time to become stable,
|
||||
/// such as a binary rotary encoder or DIP switches, and indicate when it has settled to a stable state.
|
||||
/// TValue : The type of the value being debounced.
|
||||
/// TCount : The type of the counter used to determine stability.
|
||||
//***************************************************************************
|
||||
template <typename TValue, typename TCount = uint16_t>
|
||||
class debounce_value
|
||||
{
|
||||
public:
|
||||
|
||||
virtual void on_debounce_value_notification(TValue value) = 0;
|
||||
};
|
||||
|
||||
namespace private_debounce_value
|
||||
{
|
||||
template <typename TValue, typename TCount>
|
||||
class debounce_value_base
|
||||
{
|
||||
public:
|
||||
|
||||
typedef TCount count_type;
|
||||
typedef TValue value_type;
|
||||
|
||||
typedef debounce_value_notification<value_type> notification_type;
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructor.
|
||||
//*************************************************************************
|
||||
debounce_value_base(value_type initial_value)
|
||||
: last_value(initial_value)
|
||||
, count(0)
|
||||
, state(Unstable)
|
||||
, notification(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Adds 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, count_type stable_count)
|
||||
{
|
||||
// Changed from last time?
|
||||
if (value != last_value)
|
||||
{
|
||||
count = 1;
|
||||
state = Unstable;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Increment the counter if we haven't reached the stable count yet.
|
||||
if (count < stable_count)
|
||||
{
|
||||
++count;
|
||||
|
||||
// Check if we've reached the stable count.
|
||||
if (count == stable_count)
|
||||
{
|
||||
state = static_cast<State>(Stable | Changed);
|
||||
|
||||
if (notification)
|
||||
{
|
||||
notification->on_debounce_value_notification(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the counter has already reached the stable count then we are already stable, but no further change has occurred.
|
||||
state = Stable;
|
||||
}
|
||||
}
|
||||
|
||||
last_value = value;
|
||||
|
||||
return has_changed();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Sets the notification interface.
|
||||
///\param notification_ The notification interface.
|
||||
//*************************************************************************
|
||||
void set_notification(notification_type& notification_)
|
||||
{
|
||||
notification = ¬ification_;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Clears the notification interface.
|
||||
//*************************************************************************
|
||||
void clear_notification()
|
||||
{
|
||||
notification = nullptr;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the current value.
|
||||
//*************************************************************************
|
||||
value_type get() const
|
||||
{
|
||||
return last_value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the current change state.
|
||||
///\return 'true' if the value has changed.
|
||||
//*************************************************************************
|
||||
bool has_changed() const
|
||||
{
|
||||
return (state & Changed) != 0;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the current stable state.
|
||||
///\return 'true' if the value is stable.
|
||||
//*************************************************************************
|
||||
bool is_stable() const
|
||||
{
|
||||
return (state & Stable) != 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
enum State
|
||||
{
|
||||
Unstable = 0,
|
||||
Stable = 1,
|
||||
Changed = 2
|
||||
};
|
||||
|
||||
value_type last_value;
|
||||
count_type count;
|
||||
notification_type* notification;
|
||||
State state;
|
||||
};
|
||||
} // namespace private_debounce_value
|
||||
|
||||
template <typename TValue, typename TCount>
|
||||
class debounce_value : public private_debounce_value::debounce_value_base<TValue, TCount>
|
||||
{
|
||||
public:
|
||||
|
||||
using value_type = typename private_debounce_value::debounce_value_base<TValue, TCount>::value_type;
|
||||
using count_type = typename private_debounce_value::debounce_value_base<TValue, TCount>::count_type;
|
||||
typedef TValue value_type;
|
||||
typedef TCount count_type;
|
||||
|
||||
//*************************************************************************
|
||||
/// Constructor.
|
||||
//*************************************************************************
|
||||
explicit debounce_value(value_type initial_value, count_type stable_count)
|
||||
: private_debounce_value::debounce_value_base<TValue, TCount>(initial_value)
|
||||
, stable_count(stable_count)
|
||||
debounce_value(value_type initial_value_, count_type stable_count_)
|
||||
: last_value(initial_value_)
|
||||
, count(0)
|
||||
, stable_count(stable_count_)
|
||||
, state(Unstable)
|
||||
{
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Adds a new value to the debounced.
|
||||
/// If the value has changed, the counter is reset and the debounced is marked as unstable.
|
||||
/// Adds 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)
|
||||
{
|
||||
return private_debounce_value::debounce_value_base<TValue, TCount>::add(value, stable_count);
|
||||
// Changed from last time?
|
||||
if (value != last_value)
|
||||
{
|
||||
count = 1;
|
||||
state = Unstable;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Increment the counter if we haven't reached the stable count yet.
|
||||
if (count < stable_count)
|
||||
{
|
||||
++count;
|
||||
|
||||
// Have we reached the stable count?
|
||||
if (count == stable_count)
|
||||
{
|
||||
state = static_cast<State>(Stable | Changed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the counter has already reached the stable count then we are already stable, but no further change has occurred.
|
||||
state = Stable;
|
||||
}
|
||||
}
|
||||
|
||||
last_value = value;
|
||||
|
||||
return has_changed();
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the current value.
|
||||
//*************************************************************************
|
||||
value_type get() const
|
||||
{
|
||||
return last_value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Conversion operator.
|
||||
//*************************************************************************
|
||||
operator value_type() const
|
||||
{
|
||||
return last_value;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the current change state.
|
||||
///\return 'true' if the value has changed.
|
||||
//*************************************************************************
|
||||
bool has_changed() const
|
||||
{
|
||||
return (state & Changed) != 0;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
/// Gets the current stable state.
|
||||
///\return 'true' if the value is stable.
|
||||
//*************************************************************************
|
||||
bool is_stable() const
|
||||
{
|
||||
return (state & Stable) != 0;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
@ -205,7 +143,17 @@ namespace etl
|
||||
|
||||
private:
|
||||
|
||||
enum State
|
||||
{
|
||||
Unstable = 0,
|
||||
Stable = 1,
|
||||
Changed = 2
|
||||
};
|
||||
|
||||
value_type last_value;
|
||||
count_type count;
|
||||
count_type stable_count;
|
||||
State state;
|
||||
};
|
||||
} // namespace etl
|
||||
|
||||
|
||||
125
test/test_debounce_multi_bit.cpp
Normal file
125
test/test_debounce_multi_bit.cpp
Normal file
@ -0,0 +1,125 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2026 John Wellbelove
|
||||
|
||||
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 "unit_test_framework.h"
|
||||
|
||||
#include "etl/debounce_multi_bit.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
using value_type = uint8_t;
|
||||
using count_type = uint16_t;
|
||||
|
||||
const value_type Initial_Value = 0;
|
||||
|
||||
const count_type Valid_Count = 4;
|
||||
const count_type Hold_Count = 3;
|
||||
const count_type Repeat_Count = 2;
|
||||
|
||||
const value_type Bit0 = 0;
|
||||
const value_type Bit1 = 1;
|
||||
const value_type Bit2 = 2;
|
||||
|
||||
using debounce_type1 = etl::debounce_multi_bit<value_type, count_type, 1, Valid_Count>;
|
||||
|
||||
SUITE(test_debounce_multi_bit)
|
||||
{
|
||||
//*************************************************************************
|
||||
TEST(test_debounce1)
|
||||
{
|
||||
debounce_type1 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.
|
||||
value.update(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));
|
||||
|
||||
value.update(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));
|
||||
|
||||
value.update(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));
|
||||
|
||||
// Stable & changed.
|
||||
value.update(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));
|
||||
|
||||
// Still stable.
|
||||
value.update(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));
|
||||
|
||||
////*******************************
|
||||
//// Transition from unstable to stable.
|
||||
//// Unstable.
|
||||
// CHECK_FALSE(value.add(3));
|
||||
// CHECK_FALSE(value.is_stable());
|
||||
// CHECK_FALSE(value.has_changed());
|
||||
|
||||
// CHECK_FALSE(value.add(3));
|
||||
// CHECK_FALSE(value.is_stable());
|
||||
// CHECK_FALSE(value.has_changed());
|
||||
|
||||
// CHECK_FALSE(value.add(3));
|
||||
// CHECK_FALSE(value.is_stable());
|
||||
// CHECK_FALSE(value.has_changed());
|
||||
|
||||
//// Stable & changed.
|
||||
// CHECK_TRUE(value.add(3));
|
||||
// CHECK_TRUE(value.is_stable());
|
||||
// CHECK_TRUE(value.has_changed());
|
||||
// CHECK_EQUAL(3, value.get());
|
||||
|
||||
//// Stable.
|
||||
// CHECK_FALSE(value.add(3));
|
||||
// CHECK_TRUE(value.is_stable());
|
||||
// CHECK_FALSE(value.has_changed());
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
@ -34,18 +34,6 @@ SOFTWARE.
|
||||
|
||||
namespace
|
||||
{
|
||||
uint8_t notification_value = 0;
|
||||
|
||||
class Notification : public etl::debounce_value<uint8_t, uint8_t>::notification_type
|
||||
{
|
||||
void on_debounce_value_notification(uint8_t value) override
|
||||
{
|
||||
notification_value = value;
|
||||
}
|
||||
};
|
||||
|
||||
Notification notification;
|
||||
|
||||
SUITE(test_debounce)
|
||||
{
|
||||
//*************************************************************************
|
||||
@ -56,10 +44,6 @@ namespace
|
||||
|
||||
etl::debounce_value<uint8_t, uint8_t> value(Initial_Value, Stable_Count);
|
||||
|
||||
value.set_notification(notification);
|
||||
|
||||
notification_value = 0;
|
||||
|
||||
CHECK_FALSE(value.is_stable());
|
||||
CHECK_FALSE(value.has_changed());
|
||||
|
||||
@ -77,18 +61,12 @@ namespace
|
||||
CHECK_FALSE(value.is_stable());
|
||||
CHECK_FALSE(value.has_changed());
|
||||
|
||||
// Not notified yet.
|
||||
CHECK_EQUAL(0, notification_value);
|
||||
|
||||
// Stable & changed.
|
||||
CHECK_TRUE(value.add(2));
|
||||
CHECK_TRUE(value.is_stable());
|
||||
CHECK_TRUE(value.has_changed());
|
||||
CHECK_EQUAL(2, value.get());
|
||||
|
||||
// Notified.
|
||||
CHECK_EQUAL(2, notification_value);
|
||||
|
||||
// Still stable.
|
||||
CHECK_FALSE(value.add(2));
|
||||
CHECK_TRUE(value.is_stable());
|
||||
@ -109,18 +87,12 @@ namespace
|
||||
CHECK_FALSE(value.is_stable());
|
||||
CHECK_FALSE(value.has_changed());
|
||||
|
||||
// Not notified yet.
|
||||
CHECK_EQUAL(2, notification_value);
|
||||
|
||||
// Stable & changed.
|
||||
CHECK_TRUE(value.add(3));
|
||||
CHECK_TRUE(value.is_stable());
|
||||
CHECK_TRUE(value.has_changed());
|
||||
CHECK_EQUAL(3, value.get());
|
||||
|
||||
// Notified.
|
||||
CHECK_EQUAL(3, notification_value);
|
||||
|
||||
// Stable.
|
||||
CHECK_FALSE(value.add(3));
|
||||
CHECK_TRUE(value.is_stable());
|
||||
@ -131,14 +103,10 @@ namespace
|
||||
TEST(test_debounce_value_modify_stable_count)
|
||||
{
|
||||
const uint8_t Initial_Value = 1;
|
||||
const uint8_t Stable_Count = 4;
|
||||
const uint8_t Stable_Count = 4;
|
||||
|
||||
etl::debounce_value<uint8_t, uint8_t> value(Initial_Value, Stable_Count);
|
||||
|
||||
value.set_notification(notification);
|
||||
|
||||
notification_value = 0;
|
||||
|
||||
CHECK_FALSE(value.is_stable());
|
||||
CHECK_FALSE(value.has_changed());
|
||||
|
||||
@ -156,18 +124,12 @@ namespace
|
||||
CHECK_FALSE(value.is_stable());
|
||||
CHECK_FALSE(value.has_changed());
|
||||
|
||||
// Not notified yet.
|
||||
CHECK_EQUAL(0, notification_value);
|
||||
|
||||
// Stable & changed.
|
||||
CHECK_TRUE(value.add(2));
|
||||
CHECK_TRUE(value.is_stable());
|
||||
CHECK_TRUE(value.has_changed());
|
||||
CHECK_EQUAL(2, value.get());
|
||||
|
||||
// Notified.
|
||||
CHECK_EQUAL(2, notification_value);
|
||||
|
||||
// Still stable.
|
||||
CHECK_FALSE(value.add(2));
|
||||
CHECK_TRUE(value.is_stable());
|
||||
@ -184,18 +146,12 @@ namespace
|
||||
CHECK_FALSE(value.is_stable());
|
||||
CHECK_FALSE(value.has_changed());
|
||||
|
||||
// Not notified yet.
|
||||
CHECK_EQUAL(2, notification_value);
|
||||
|
||||
// Stable & changed.
|
||||
CHECK_TRUE(value.add(3));
|
||||
CHECK_TRUE(value.is_stable());
|
||||
CHECK_TRUE(value.has_changed());
|
||||
CHECK_EQUAL(3, value.get());
|
||||
|
||||
// Notified.
|
||||
CHECK_EQUAL(3, notification_value);
|
||||
|
||||
// Stable.
|
||||
CHECK_FALSE(value.add(3));
|
||||
CHECK_TRUE(value.is_stable());
|
||||
@ -203,49 +159,13 @@ namespace
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_debounce_value_modify_notification)
|
||||
TEST(test_debounce_values_from_an_input_stream)
|
||||
{
|
||||
const uint8_t Initial_Value = 1;
|
||||
const uint8_t Stable_Count = 4;
|
||||
|
||||
etl::debounce_value<uint8_t, uint8_t> value(Initial_Value, Stable_Count);
|
||||
|
||||
value.set_notification(notification);
|
||||
|
||||
notification_value = 0;
|
||||
|
||||
//*******************************
|
||||
// Transition from unstable to stable.
|
||||
CHECK_FALSE(value.add(2));
|
||||
CHECK_FALSE(value.add(2));
|
||||
CHECK_FALSE(value.add(2));
|
||||
CHECK_TRUE(value.add(2));
|
||||
|
||||
// Notified.
|
||||
CHECK_EQUAL(2, notification_value);
|
||||
|
||||
value.clear_notification();
|
||||
|
||||
//*******************************
|
||||
// Transition from unstable to stable.
|
||||
// Unstable.
|
||||
CHECK_FALSE(value.add(3));
|
||||
CHECK_FALSE(value.add(3));
|
||||
CHECK_FALSE(value.add(3));
|
||||
CHECK_TRUE(value.add(3));
|
||||
|
||||
// Not notified, so the value remains the same.
|
||||
CHECK_EQUAL(2, notification_value);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_debounce_values_from_an_input)
|
||||
{
|
||||
std::vector<uint8_t> input = { 1, 2, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6 };
|
||||
std::vector<uint8_t> input = {1, 2, 3, 3, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6};
|
||||
std::vector<uint8_t> output;
|
||||
std::vector<uint8_t> expected = { 2, 3, 4, 6 };
|
||||
std::vector<uint8_t> expected = {2, 3, 4, 6};
|
||||
|
||||
const uint8_t Stable_Count = 3;
|
||||
const uint8_t Stable_Count = 3;
|
||||
etl::debounce_value<uint8_t, uint8_t> value(0, Stable_Count);
|
||||
|
||||
for (uint8_t v : input)
|
||||
|
||||
@ -3539,6 +3539,7 @@
|
||||
<ClInclude Include="..\..\include\etl\crc8_opensafety.h" />
|
||||
<ClInclude Include="..\..\include\etl\crc8_rohc.h" />
|
||||
<ClInclude Include="..\..\include\etl\crc8_wcdma.h" />
|
||||
<ClInclude Include="..\..\include\etl\debounce_multi_bit.h" />
|
||||
<ClInclude Include="..\..\include\etl\debounce_value.h" />
|
||||
<ClInclude Include="..\..\include\etl\delegate_observable.h" />
|
||||
<ClInclude Include="..\..\include\etl\expected.h" />
|
||||
@ -10392,6 +10393,7 @@
|
||||
<ClCompile Include="..\test_crc8_opensafety.cpp" />
|
||||
<ClCompile Include="..\test_crc8_rohc.cpp" />
|
||||
<ClCompile Include="..\test_crc8_wcdma.cpp" />
|
||||
<ClCompile Include="..\test_debounce_multi_bit.cpp" />
|
||||
<ClCompile Include="..\test_debounce_value.cpp" />
|
||||
<ClCompile Include="..\test_delegate_observable.cpp" />
|
||||
<ClCompile Include="..\test_etl_assert.cpp" />
|
||||
|
||||
@ -1566,12 +1566,15 @@
|
||||
<ClInclude Include="..\..\include\etl\format.h">
|
||||
<Filter>UnitTest++\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\etl\debounce_value.h">
|
||||
<Filter>UnitTest++\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\etl\notification.h">
|
||||
<Filter>UnitTest++\Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\etl\debounce_value.h">
|
||||
<Filter>ETL\Utilities</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\include\etl\debounce_multi_bit.h">
|
||||
<Filter>ETL\Utilities</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\test_string_char.cpp">
|
||||
@ -3848,6 +3851,9 @@
|
||||
<ClCompile Include="..\test_debounce_value.cpp">
|
||||
<Filter>Tests\Misc</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test_debounce_multi_bit.cpp">
|
||||
<Filter>Tests\Algorithms</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\..\library.properties">
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user