Initial code. Committed for reference

This commit is contained in:
John Wellbelove 2026-10-20 10:36:59 +01:00
parent 60ba92928e
commit fe1bc0b029
5 changed files with 1612 additions and 0 deletions

View File

@ -0,0 +1,694 @@
/******************************************************************************
The MIT License(MIT)
Embedded Template Library.
https://github.com/ETLCPP/etl
https://www.etlcpp.com
Copyright(c) 2017 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 ETL_CALLBACK_TIMER_INCLUDED
#define ETL_CALLBACK_TIMER_INCLUDED
#include "platform.h"
#include "algorithm.h"
#include "atomic.h"
#include "delegate.h"
#include "error_handler.h"
#include "function.h"
#include "nullptr.h"
#include "placement_new.h"
#include "static_assert.h"
#include "timer_data.h"
#include <stdint.h>
namespace etl
{
//***************************************************************************
/// Interface for callback timer
//***************************************************************************
class iintrusive_callback_timer
{
public:
typedef etl::delegate<void(void)> callback_type;
typedef etl::delegate<void(etl::timer_data::id::type, uint32_t delta)> event_callback_type;
//*******************************************
/// Register a timer.
//*******************************************
etl::timer::id::type register_timer(void (*p_callback_)(), uint32_t period_, bool repeating_)
{
return id;
}
//*******************************************
/// Register a timer.
//*******************************************
etl::timer::id::type register_timer(etl::ifunction<void>& callback_, uint32_t period_, bool repeating_)
{
etl::timer::id::type id = etl::timer::id::NO_TIMER;
bool is_space = (registered_timers < MAX_TIMERS);
if (is_space)
{
// Search for the free space.
for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
{
timer_data& timer = timer_array[i];
if (timer.id == etl::timer::id::NO_TIMER)
{
// Create in-place.
new (&timer) timer_data(i, callback_, period_, repeating_);
++registered_timers;
id = i;
break;
}
}
}
return id;
}
//*******************************************
/// Register a timer.
//*******************************************
#if ETL_USING_CPP11
etl::timer::id::type register_timer(callback_type& callback_, uint32_t period_, bool repeating_)
{
etl::timer::id::type id = etl::timer::id::NO_TIMER;
bool is_space = (registered_timers < MAX_TIMERS);
if (is_space)
{
// Search for the free space.
for (uint_least8_t i = 0U; i < MAX_TIMERS; ++i)
{
timer_data& timer = timer_array[i];
if (timer.id == etl::timer::id::NO_TIMER)
{
// Create in-place.
new (&timer) timer_data(i, callback_, period_, repeating_);
++registered_timers;
id = i;
break;
}
}
}
return id;
}
#endif
//*******************************************
/// Unregister a timer.
//*******************************************
bool unregister_timer(etl::timer::id::type id_)
{
bool result = false;
if (id_ != etl::timer::id::NO_TIMER)
{
timer_data& timer = timer_array[id_];
if (timer.id != etl::timer::id::NO_TIMER)
{
if (timer.is_active())
{
ETL_DISABLE_TIMER_UPDATES;
active_list.remove(timer.id, false);
remove_callback.call_if(timer.id);
ETL_ENABLE_TIMER_UPDATES;
}
// Reset in-place.
new (&timer) timer_data();
--registered_timers;
result = true;
}
}
return result;
}
//*******************************************
/// Enable/disable the timer.
//*******************************************
void enable(bool state_)
{
enabled = state_;
}
//*******************************************
/// Get the enable/disable state.
//*******************************************
bool is_running() const
{
return enabled;
}
//*******************************************
/// Clears the timer of data.
//*******************************************
void clear()
{
ETL_DISABLE_TIMER_UPDATES;
active_list.clear();
ETL_ENABLE_TIMER_UPDATES;
for (int i = 0; i < MAX_TIMERS; ++i)
{
::new (&timer_array[i]) timer_data();
}
registered_timers = 0;
}
//*******************************************
// Called by the timer service to indicate the
// amount of time that has elapsed since the last successful call to 'tick'.
// Returns true if the tick was processed,
// false if not.
//*******************************************
bool tick(uint32_t count)
{
if (enabled)
{
if (ETL_TIMER_UPDATES_ENABLED)
{
// We have something to do?
bool has_active = !active_list.empty();
if (has_active)
{
while (has_active && (count >= active_list.front().delta))
{
timer_data& timer = active_list.front();
count -= timer.delta;
active_list.remove(timer.id, true);
remove_callback.call_if(timer.id);
if (timer.repeating)
{
// Reinsert the timer.
timer.delta = timer.period;
active_list.insert(timer.id);
insert_callback.call_if(timer.id);
}
if (timer.p_callback != ETL_NULLPTR)
{
if (timer.cbk_type == timer_data::C_CALLBACK)
{
// Call the C callback.
reinterpret_cast<void (*)()>(timer.p_callback)();
}
else if (timer.cbk_type == timer_data::IFUNCTION)
{
// Call the function wrapper callback.
(*reinterpret_cast<etl::ifunction<void>*>(timer.p_callback))();
}
else if (timer.cbk_type == timer_data::DELEGATE)
{
// Call the delegate callback.
(*reinterpret_cast<callback_type*>(timer.p_callback))();
}
}
has_active = !active_list.empty();
}
if (has_active)
{
// Subtract any remainder from the next due timeout.
active_list.front().delta -= count;
}
}
return true;
}
}
return false;
}
//*******************************************
/// Starts a timer.
//*******************************************
bool start(etl::timer::id::type id_, bool immediate_ = false)
{
bool result = false;
// Valid timer id?
if (id_ != etl::timer::id::NO_TIMER)
{
timer_data& timer = timer_array[id_];
// Registered timer?
if (timer.id != etl::timer::id::NO_TIMER)
{
// Has a valid period.
if (timer.period != etl::timer::state::Inactive)
{
ETL_DISABLE_TIMER_UPDATES;
if (timer.is_active())
{
active_list.remove(timer.id, false);
remove_callback.call_if(timer.id);
}
timer.delta = immediate_ ? 0 : timer.period;
active_list.insert(timer.id);
insert_callback.call_if(timer.id);
ETL_ENABLE_TIMER_UPDATES;
result = true;
}
}
}
return result;
}
//*******************************************
/// Stops a timer.
//*******************************************
bool stop(etl::timer::id::type id_)
{
bool result = false;
// Valid timer id?
if (id_ != etl::timer::id::NO_TIMER)
{
timer_data& timer = timer_array[id_];
// Registered timer?
if (timer.id != etl::timer::id::NO_TIMER)
{
if (timer.is_active())
{
ETL_DISABLE_TIMER_UPDATES;
active_list.remove(timer.id, false);
remove_callback.call_if(timer.id);
ETL_ENABLE_TIMER_UPDATES;
}
result = true;
}
}
return result;
}
//*******************************************
/// Sets a timer's period.
//*******************************************
bool set_period(etl::timer::id::type id_, uint32_t period_)
{
if (stop(id_))
{
timer_array[id_].period = period_;
return true;
}
return false;
}
//*******************************************
/// Sets a timer's mode.
//*******************************************
bool set_mode(etl::timer::id::type id_, bool repeating_)
{
if (stop(id_))
{
timer_array[id_].repeating = repeating_;
return true;
}
return false;
}
//*******************************************
/// Returns true if an attempt was ever made to insert a timer into the
/// active list when it was already there. Such an insert is rejected, as
/// it would create a circular reference in the list.
//*******************************************
bool duplicate_insert_detected() const
{
return active_list.duplicate_insert_detected();
}
//*******************************************
/// Check if there is an active timer.
//*******************************************
bool has_active_timer() const
{
return !active_list.empty();
}
//*******************************************
/// Get the time to the next timer event.
/// Returns etl::timer::interval::No_Active_Interval if there is no active
/// timer.
//*******************************************
uint32_t time_to_next() const
{
uint32_t delta = static_cast<uint32_t>(etl::timer::interval::No_Active_Interval);
if (has_active_timer())
{
delta = active_list.front().delta;
}
return delta;
}
//*******************************************
/// Checks if a timer is currently active.
/// Returns <b>true</b> if the timer is active, otherwise <b>false</b>.
//*******************************************
bool is_active(etl::timer::id::type id_) const
{
//// Valid timer id?
// if (is_valid_timer_id(id_))
//{
// if (has_active_timer())
// {
// const timer_data& timer = timer_array[id_];
// // Registered timer?
// if (timer.id != etl::timer::id::NO_TIMER)
// {
// return timer.is_active();
// }
// }
//}
return false;
}
////*******************************************
///// Set a callback when a timer is inserted on list
////*******************************************
// void set_insert_callback(event_callback_type insert_)
//{
// insert_callback = insert_;
// }
////*******************************************
///// Set a callback when a timer is removed from list
////*******************************************
// void set_remove_callback(event_callback_type remove_)
//{
// remove_callback = remove_;
// }
//*******************************************
void clear_insert_callback()
{
insert_callback.clear();
}
//*******************************************
void clear_remove_callback()
{
remove_callback.clear();
}
protected:
//*******************************************
/// Constructor.
//*******************************************
icallback_timer(timer_data* const timer_array_, const uint_least8_t Max_Timers_)
: timer_array(timer_array_)
, active_list(timer_array_)
, enabled(false)
,
#if defined(ETL_CALLBACK_TIMER_USE_ATOMIC_LOCK)
process_semaphore(0)
,
#endif
registered_timers(0)
, MAX_TIMERS(Max_Timers_)
{
}
private:
//*******************************************
/// Check that the timer id is valid.
//*******************************************
bool is_valid_timer_id(etl::timer::id::type id_) const
{
return (id_ < MAX_TIMERS);
}
//*************************************************************************
class timer_list
{
public:
//*******************************
timer_list(timer_data* ptimers_)
: head(etl::timer::id::NO_TIMER)
, tail(etl::timer::id::NO_TIMER)
, ptimers(ptimers_)
, duplicate_insert(false)
{
}
//*******************************
bool empty() const
{
return head == etl::timer::id::NO_TIMER;
}
//*******************************
/// Returns true if an attempt was ever made to insert a timer that was
/// already in the list.
//*******************************
bool duplicate_insert_detected() const
{
return duplicate_insert;
}
//*******************************
// Inserts the timer at the correct delta position
//*******************************
void insert(etl::timer::id::type id_)
{
timer_data& timer = ptimers[id_];
// A timer is in this list exactly when it is the head or has a previous
// link, as remove() clears both links. Inserting one that is already
// here would link it to itself and create a circular reference, so
// record the attempt and abandon the insert to keep the list intact.
if ((head == id_) || (timer.previous != etl::timer::id::NO_TIMER))
{
duplicate_insert = true;
return;
}
if (head == etl::timer::id::NO_TIMER)
{
// No entries yet.
head = id_;
tail = id_;
timer.previous = etl::timer::id::NO_TIMER;
timer.next = etl::timer::id::NO_TIMER;
}
else
{
// We already have entries.
etl::timer::id::type test_id = begin();
while (test_id != etl::timer::id::NO_TIMER)
{
timer_data& test = ptimers[test_id];
// Find the correct place to insert.
if (timer.delta <= test.delta)
{
if (test.id == head)
{
head = timer.id;
}
// Insert before test.
timer.previous = test.previous;
test.previous = timer.id;
timer.next = test.id;
// Adjust the next delta to compensate.
test.delta -= timer.delta;
if (timer.previous != etl::timer::id::NO_TIMER)
{
ptimers[timer.previous].next = timer.id;
}
break;
}
else
{
timer.delta -= test.delta;
}
test_id = next(test_id);
}
// Reached the end?
if (test_id == etl::timer::id::NO_TIMER)
{
// Tag on to the tail.
ptimers[tail].next = timer.id;
timer.previous = tail;
timer.next = etl::timer::id::NO_TIMER;
tail = timer.id;
}
}
}
//*******************************
void remove(etl::timer::id::type id_, bool has_expired)
{
timer_data& timer = ptimers[id_];
if (head == id_)
{
head = timer.next;
}
else
{
ptimers[timer.previous].next = timer.next;
}
if (tail == id_)
{
tail = timer.previous;
}
else
{
ptimers[timer.next].previous = timer.previous;
}
if (!has_expired)
{
// Adjust the next delta.
if (timer.next != etl::timer::id::NO_TIMER)
{
ptimers[timer.next].delta += timer.delta;
}
}
timer.previous = etl::timer::id::NO_TIMER;
timer.next = etl::timer::id::NO_TIMER;
timer.delta = etl::timer::state::Inactive;
}
//*******************************
timer_data& front()
{
return ptimers[head];
}
//*******************************
const timer_data& front() const
{
return ptimers[head];
}
//*******************************
etl::timer::id::type begin()
{
return head;
}
//*******************************
etl::timer::id::type next(etl::timer::id::type last)
{
return ptimers[last].next;
}
//*******************************
void clear()
{
etl::timer::id::type id = begin();
while (id != etl::timer::id::NO_TIMER)
{
timer_data& timer = ptimers[id];
id = next(id);
timer.next = etl::timer::id::NO_TIMER;
}
head = etl::timer::id::NO_TIMER;
tail = etl::timer::id::NO_TIMER;
}
private:
etl::timer::id::type head;
etl::timer::id::type tail;
timer_data* const ptimers;
// Set if an attempt was ever made to insert a timer that was already
// in the list.
bool duplicate_insert;
};
// The array of timer data structures.
timer_data* const timer_array;
// The list of active timers.
timer_list active_list;
volatile bool enabled;
uint_least8_t registered_timers;
event_callback_type timeout_callback;
etl::timer_data terminator;
};
//***************************************************************************
/// The callback timer
//***************************************************************************
class intrusive_callback_timer : public etl::iintrusive_callback_timer
{
public:
private:
};
} // namespace etl
#endif

588
include/etl/timer_config.h Normal file
View File

@ -0,0 +1,588 @@
/******************************************************************************
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 ETL_TIMER_DATA_INCLUDED
#define ETL_TIMER_DATA_INCLUDED
#include "platform.h"
#include "nullptr.h"
#include "timer.h"
#include <stdint.h>
//*****************************************************************************
// Definitions common to intrusive timers.
//*****************************************************************************
namespace etl
{
//***************************************************************************
/// Common definitions for the intrusive timer framework.
//***************************************************************************
class timer_config
{
public:
friend class timer_register_list;
friend class timer_active_list;
friend class timer_triggered_list;
friend class intrusive_callback_timer;
//*******************************************
timer_config() ETL_NOEXCEPT
: period(etl::timer::state::Inactive)
, delta(etl::timer::state::Inactive)
, mode(etl::timer::mode::Repeating)
, registered_next(ETL_NULLPTR)
, active_next(ETL_NULLPTR)
, triggered_next(ETL_NULLPTR)
{
}
//*******************************************
timer_config(uint32_t period_, etl::timer::mode::type repeating_) ETL_NOEXCEPT
: period(period_)
, delta(period_)
, mode(repeating_)
, registered_next(ETL_NULLPTR)
, active_next(ETL_NULLPTR)
, triggered_next(ETL_NULLPTR)
{
}
//*******************************************
uint32_t get_period() const ETL_NOEXCEPT
{
return period;
}
//*******************************************
uint32_t get_delta() const ETL_NOEXCEPT
{
return delta;
}
//*******************************************
uint32_t get_mode() const ETL_NOEXCEPT
{
return mode;
}
//*******************************************
void clear() ETL_NOEXCEPT
{
period = etl::timer::state::Inactive;
delta = etl::timer::state::Inactive;
mode = etl::timer::mode::Repeating;
registered_next = ETL_NULLPTR;
active_next = ETL_NULLPTR;
triggered_next = ETL_NULLPTR;
}
private:
uint32_t period; ///< The period of the timer.
uint32_t delta; ///< The time remaining until the timer expires.
timer_config* registered_next; ///< The next timer in the registered timer list. nullptr if the timer is not registered.
timer_config* active_next; ///< The next timer in the active timer list. nullptr if the timer is not active.
timer_config* triggered_next; ///< The next timer in the triggered timer list. nullptr if the timer is not triggered.
etl::timer::mode::type mode; ///< Whether the timer is repeating or single-shot.
};
//***************************************************************************
/// A timer data structure that includes a callback delegate.
//***************************************************************************
template <typename TCallback>
struct callback_timer_config : public timer_config
{
callback_timer_config(etl::timer::id::type id_, TCallback callback_) ETL_NOEXCEPT
: timer_config()
, callback(callback_)
{
}
TCallback callback; ///< The callback delegate for the timer.
};
//*************************************************************************
/// The list of timers that are registered.
//*************************************************************************
class timer_register_list
{
public:
//*******************************
timer_register_list() ETL_NOEXCEPT
: head(ETL_NULLPTR)
, tail(ETL_NULLPTR)
{
}
//*******************************
bool empty() const
{
return head == ETL_NULLPTR;
}
//*******************************
// Adds the timer in to the list.
//*******************************
void insert(etl::timer_config* new_timer) ETL_NOEXCEPT
{
if (empty())
{
head = new_timer;
}
else if (head == new_timer)
{
}
else
{
// Remove any existing timer.
etl::timer_config* previous = head;
// Step through the list until we find the timer to remove, or reach the end of the list.
while ((previous->registered_next != ETL_NULLPTR) && (previous->registered_next != new_timer))
{
previous = previous->registered_next;
}
// If we found the timer, remove it from the list.
if (previous->registered_next == new_timer)
{
}
tail->registered_next = new_timer;
}
tail = new_timer;
}
//*******************************
// Remove the timer from the list.
// Returns the next timer in the list after the removed timer, or nullptr if the removed timer was the last in the list.
//*******************************
etl::timer_config* remove(etl::timer_config* timer) ETL_NOEXCEPT
{
if (timer == head)
{
head = timer->registered_next;
if (head == ETL_NULLPTR)
{
tail = ETL_NULLPTR;
}
timer->registered_next = ETL_NULLPTR;
return head;
}
else
{
etl::timer_config* previous = head;
// Step through the list until we find the timer to remove, or reach the end of the list.
while ((previous->registered_next != ETL_NULLPTR) && (previous->registered_next != timer))
{
previous = previous->registered_next;
}
// If we found the timer, remove it from the list.
if (previous->registered_next == timer)
{
previous->registered_next = timer->registered_next;
if (timer == tail)
{
tail = previous;
}
timer->registered_next = ETL_NULLPTR;
return previous->registered_next;
}
else
{
// Timer not found in the list.
return ETL_NULLPTR;
}
}
}
//*******************************
/// Returns the first timer in the list.
/// If the list is empty, returns nullptr.
//*******************************
etl::timer_config* first() const ETL_NOEXCEPT
{
return head;
}
//*******************************
/// Returns the next timer in the list after the given timer.
/// May be nullptr if the given timer is the last in the list.
/// If the given timer is nullptr, returns nullptr.
//*******************************
etl::timer_config* next(etl::timer_config* timer) const ETL_NOEXCEPT
{
if (timer != ETL_NULLPTR)
{
return timer->registered_next;
}
else
{
return ETL_NULLPTR;
}
}
//*******************************
/// Clear the list of entries.
//*******************************
void clear()
{
etl::timer_config* timer = head;
while (timer != ETL_NULLPTR)
{
timer = pop_front();
}
head = ETL_NULLPTR;
tail = ETL_NULLPTR;
}
private:
//*******************************
// Removes the timer at the front of the list.
// Returns the new head of the list, which may be nullptr if the list is now empty.
//*******************************
etl::timer_config* pop_front() ETL_NOEXCEPT
{
etl::timer_config* timer = head;
head = timer->registered_next;
timer->registered_next = ETL_NULLPTR;
return head;
}
etl::timer_config* head;
etl::timer_config* tail;
};
//*************************************************************************
/// The list of timers that are active.
//*************************************************************************
class timer_active_list
{
public:
//*******************************
timer_active_list() ETL_NOEXCEPT
: head(ETL_NULLPTR)
{
}
//*******************************
bool empty() const ETL_NOEXCEPT
{
return head == ETL_NULLPTR;
}
//*******************************
// Inserts the timer at the correct delta position
//*******************************
void insert(etl::timer_config* new_timer) ETL_NOEXCEPT
{
if (empty())
{
// No entries yet.
head = new_timer;
new_timer->active_next = ETL_NULLPTR;
}
else if (new_timer->delta < head->delta)
{
// Goes before the head.
head->delta -= new_timer->delta;
new_timer->active_next = head;
head = new_timer;
}
else
{
// We already have entries, and new_timer doesn't go before head.
timer_config* previous_timer = head;
timer_config* timer = head->active_next;
// Adjust the new_timer delta to take account of the head timer's delta.
new_timer->delta -= previous_timer->delta;
bool inserted = false;
while (!inserted && (timer != ETL_NULLPTR))
{
// Find the correct place to insert.
// The list is sorted by delta, so we need to find the first timer with a delta greater than the new timer's delta.
if (new_timer->delta < timer->delta)
{
// Insert after the previous timer.
previous_timer->active_next = new_timer;
new_timer->active_next = timer;
// Adjust the next delta for the new timer's delta.
timer->delta -= new_timer->delta;
inserted = true;
}
else
{
// Adjust the new_timer delta to take account of the last timer's delta.
new_timer->delta -= timer->delta;
}
// Move to the next timer in the list.
previous_timer = timer;
timer = timer->active_next;
}
if (!inserted)
{
// Reached the end so attach to the tail.
previous_timer->active_next = new_timer;
new_timer->active_next = ETL_NULLPTR;
}
}
}
//*******************************
// Removes the selected timer from the list.
// No action is taken if the timer is not found.
// returns the next timer in the list after the removed timer, or nullptr if the removed timer was the last in the list.
//*******************************
etl::timer_config* remove(etl::timer_config* timer) ETL_NOEXCEPT
{
// Find the timer in the list.
etl::timer_config* current = head;
etl::timer_config* last = ETL_NULLPTR;
while (current != timer && current != ETL_NULLPTR)
{
last = current;
current = current->active_next;
}
// Not found?
if (current == ETL_NULLPTR)
{
return ETL_NULLPTR;
}
etl::timer_config* next = current->active_next;
// Adjust the next delta.
if (next != ETL_NULLPTR)
{
next->delta += current->delta;
}
// Remove it from the list.
if (last == ETL_NULLPTR)
{
head = next;
}
else
{
last->active_next = next;
}
// Clear the next pointer of the removed timer.
current->active_next = ETL_NULLPTR;
return next;
}
//*******************************
/// Returns the first timer in the list.
/// If the list is empty, returns nullptr.
//*******************************
etl::timer_config* first() const ETL_NOEXCEPT
{
return head;
}
//*******************************
/// Returns the next timer in the list after the given timer.
/// If the given timer is nullptr, returns nullptr.
//*******************************
etl::timer_config* next(etl::timer_config* timer) const ETL_NOEXCEPT
{
if (timer != ETL_NULLPTR)
{
return timer->active_next;
}
else
{
return ETL_NULLPTR;
}
}
//*******************************
/// Clear the list of entries.
//*******************************
void clear()
{
etl::timer_config* timer = head;
while (timer != ETL_NULLPTR)
{
timer = pop_front();
}
head = ETL_NULLPTR;
}
private:
//*******************************
// Removes the timer at the front of the list.
// Returns the new head of the list, which may be nullptr if the list is now empty.
//*******************************
etl::timer_config* pop_front() ETL_NOEXCEPT
{
etl::timer_config* timer = head;
head = timer->active_next;
timer->active_next = ETL_NULLPTR;
return head;
}
etl::timer_config* head;
};
//*************************************************************************
/// The list of timers that are registered.
//*************************************************************************
class timer_triggered_list
{
public:
//*******************************
timer_triggered_list() ETL_NOEXCEPT
: head(ETL_NULLPTR)
, tail(ETL_NULLPTR)
{
}
//*******************************
bool empty() const
{
return head == ETL_NULLPTR;
}
//*******************************
// Adds the timer in to the list.
//*******************************
void insert(etl::timer_config* new_timer) ETL_NOEXCEPT
{
new_timer->triggered_next = ETL_NULLPTR;
if (empty())
{
head = new_timer;
}
else
{
tail->triggered_next = new_timer;
}
tail = new_timer;
}
//*******************************
/// Returns the first timer in the list.
/// If the list is empty, returns nullptr.
//*******************************
etl::timer_config* first() const ETL_NOEXCEPT
{
return head;
}
//*******************************
/// Returns the next timer in the list after the given timer.
/// May be nullptr if the given timer is the last in the list.
/// If the given timer is nullptr, returns nullptr.
//*******************************
etl::timer_config* next(etl::timer_config* timer) const ETL_NOEXCEPT
{
if (timer != ETL_NULLPTR)
{
return timer->triggered_next;
}
else
{
return ETL_NULLPTR;
}
}
//*******************************
/// Clear the list of entries.
//*******************************
void clear()
{
etl::timer_config* timer = head;
while (timer != ETL_NULLPTR)
{
timer = pop_front();
}
head = ETL_NULLPTR;
tail = ETL_NULLPTR;
}
private:
//*******************************
// Removes the timer at the front of the list.
// Returns the new head of the list, which may be nullptr if the list is now empty.
//*******************************
etl::timer_config* pop_front() ETL_NOEXCEPT
{
etl::timer_config* timer = head;
head = timer->triggered_next;
timer->triggered_next = ETL_NULLPTR;
return head;
}
etl::timer_config* head;
etl::timer_config* tail;
};
} // namespace etl
#endif

319
test/test_timer_data.cpp Normal file
View File

@ -0,0 +1,319 @@
/******************************************************************************
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/timer_config.h"
namespace
{
SUITE(test_timer_config)
{
//*************************************************************************
TEST(test_register_list)
{
etl::timer_config timer1;
etl::timer_config timer2;
etl::timer_config timer3;
etl::timer_config timer4;
etl::timer_config timer5;
etl::timer_register_list register_list;
CHECK_TRUE(register_list.empty());
CHECK_TRUE(register_list.first() == ETL_NULLPTR);
// Add the timers to the list.
register_list.insert(&timer1);
register_list.insert(&timer2);
register_list.insert(&timer3);
register_list.insert(&timer4);
register_list.insert(&timer5);
CHECK_FALSE(register_list.empty());
CHECK_TRUE(register_list.first() == &timer1);
etl::timer_config* timer = register_list.first();
// The timers should be in the order they were added.
CHECK_EQUAL(&timer1, timer);
timer = register_list.next(timer);
CHECK_EQUAL(&timer2, timer);
timer = register_list.next(timer);
CHECK_EQUAL(&timer3, timer);
timer = register_list.next(timer);
CHECK_EQUAL(&timer4, timer);
timer = register_list.next(timer);
CHECK_EQUAL(&timer5, timer);
timer = register_list.next(timer);
CHECK_EQUAL(ETL_NULLPTR, timer);
// Remove timer3 from the list.
register_list.remove(&timer3);
// Search the list to ensure the other timers are still present.
bool timer1_found = false;
bool timer2_found = false;
bool timer3_found = false;
bool timer4_found = false;
bool timer5_found = false;
timer = register_list.first();
while (timer != ETL_NULLPTR)
{
if (timer == &timer1)
{
timer1_found = true;
}
else if (timer == &timer2)
{
timer2_found = true;
}
else if (timer == &timer3)
{
timer3_found = true;
}
else if (timer == &timer4)
{
timer4_found = true;
}
else if (timer == &timer5)
{
timer5_found = true;
}
timer = register_list.next(timer);
}
CHECK_TRUE(timer1_found);
CHECK_TRUE(timer2_found);
CHECK_FALSE(timer3_found);
CHECK_TRUE(timer4_found);
CHECK_TRUE(timer5_found);
// Clear the list.
register_list.clear();
CHECK_TRUE(register_list.empty());
CHECK_TRUE(register_list.first() == ETL_NULLPTR);
}
//*************************************************************************
TEST(test_active_list)
{
etl::timer_config timer1(100, etl::timer::mode::Repeating);
etl::timer_config timer2(65, etl::timer::mode::Repeating);
etl::timer_config timer3(85, etl::timer::mode::Repeating);
etl::timer_config timer4(130, etl::timer::mode::Repeating);
etl::timer_config timer5(85, etl::timer::mode::Repeating);
etl::timer_config timer6(85, etl::timer::mode::Repeating);
etl::timer_active_list active_list;
CHECK_TRUE(active_list.empty());
CHECK_TRUE(active_list.first() == ETL_NULLPTR);
active_list.insert(&timer1);
active_list.insert(&timer2);
active_list.insert(&timer3);
active_list.insert(&timer4);
active_list.insert(&timer5);
CHECK_FALSE(active_list.empty());
CHECK_TRUE(active_list.first() == &timer2);
etl::timer_config* timer = active_list.first();
// The timers should be in the order of their deltas.
CHECK_EQUAL(&timer2, timer);
timer = active_list.next(timer);
CHECK_EQUAL(&timer3, timer);
timer = active_list.next(timer);
CHECK_EQUAL(&timer5, timer);
timer = active_list.next(timer);
CHECK_EQUAL(&timer1, timer);
timer = active_list.next(timer);
CHECK_EQUAL(&timer4, timer);
timer = active_list.next(timer);
CHECK_EQUAL(ETL_NULLPTR, timer);
CHECK_EQUAL(timer2.get_period(), timer2.get_delta());
CHECK_EQUAL(timer3.get_period() - timer2.get_period(), timer3.get_delta());
CHECK_EQUAL(timer1.get_period() - timer3.get_period(), timer1.get_delta());
CHECK_EQUAL(timer4.get_period() - timer1.get_period(), timer4.get_delta());
CHECK_EQUAL(timer5.get_period() - timer3.get_period(), timer5.get_delta());
// Remove timer3 from the list.
active_list.remove(&timer3);
// Search the list to ensure the other timers are still present.
bool timer1_found = false;
bool timer2_found = false;
bool timer3_found = false;
bool timer4_found = false;
bool timer5_found = false;
timer = active_list.first();
while (timer != ETL_NULLPTR)
{
if (timer == &timer1)
{
timer1_found = true;
}
else if (timer == &timer2)
{
timer2_found = true;
}
else if (timer == &timer3)
{
timer3_found = true;
}
else if (timer == &timer4)
{
timer4_found = true;
}
else if (timer == &timer5)
{
timer5_found = true;
}
timer = active_list.next(timer);
}
CHECK_TRUE(timer1_found);
CHECK_TRUE(timer2_found);
CHECK_FALSE(timer3_found);
CHECK_TRUE(timer4_found);
CHECK_TRUE(timer5_found);
// Check the deltas are still correct.
CHECK_EQUAL(timer2.get_period(), timer2.get_delta());
CHECK_EQUAL(timer1.get_period() - timer3.get_period(), timer1.get_delta());
CHECK_EQUAL(timer4.get_period() - timer1.get_period(), timer4.get_delta());
CHECK_EQUAL(timer5.get_period() - timer2.get_period(), timer5.get_delta());
// Remove a timer that is not in the list.
active_list.remove(&timer6);
// Search the list to ensure the other timers are still present.
timer1_found = false;
timer2_found = false;
timer3_found = false;
timer4_found = false;
timer5_found = false;
timer = active_list.first();
while (timer != ETL_NULLPTR)
{
if (timer == &timer1)
{
timer1_found = true;
}
else if (timer == &timer2)
{
timer2_found = true;
}
else if (timer == &timer3)
{
timer3_found = true;
}
else if (timer == &timer4)
{
timer4_found = true;
}
else if (timer == &timer5)
{
timer5_found = true;
}
timer = active_list.next(timer);
}
// Check the deltas are still correct.
CHECK_EQUAL(timer2.get_period(), timer2.get_delta());
CHECK_EQUAL(timer1.get_period() - timer3.get_period(), timer1.get_delta());
CHECK_EQUAL(timer4.get_period() - timer1.get_period(), timer4.get_delta());
CHECK_EQUAL(timer5.get_period() - timer2.get_period(), timer5.get_delta());
// Clear the list.
active_list.clear();
CHECK_TRUE(active_list.empty());
CHECK_TRUE(active_list.first() == ETL_NULLPTR);
}
//*************************************************************************
TEST(test_triggered_list)
{
etl::timer_config timer1;
etl::timer_config timer2;
etl::timer_config timer3;
etl::timer_config timer4;
etl::timer_config timer5;
etl::timer_triggered_list triggered_list;
CHECK_TRUE(triggered_list.empty());
CHECK_TRUE(triggered_list.first() == ETL_NULLPTR);
// Add the timers to the list.
triggered_list.insert(&timer1);
triggered_list.insert(&timer2);
triggered_list.insert(&timer3);
triggered_list.insert(&timer4);
triggered_list.insert(&timer5);
CHECK_FALSE(triggered_list.empty());
CHECK_TRUE(triggered_list.first() == &timer1);
// The timers should be in the order they were added.
etl::timer_config* timer = triggered_list.first();
CHECK_EQUAL(&timer1, timer);
timer = triggered_list.next(timer);
CHECK_EQUAL(&timer2, timer);
timer = triggered_list.next(timer);
CHECK_EQUAL(&timer3, timer);
timer = triggered_list.next(timer);
CHECK_EQUAL(&timer4, timer);
timer = triggered_list.next(timer);
CHECK_EQUAL(&timer5, timer);
timer = triggered_list.next(timer);
CHECK_EQUAL(ETL_NULLPTR, timer);
// Clear the list.
triggered_list.clear();
CHECK_TRUE(triggered_list.empty());
CHECK_TRUE(triggered_list.first() == ETL_NULLPTR);
}
}
} // namespace

View File

@ -3559,6 +3559,7 @@
<ClInclude Include="..\..\include\etl\generators\variant_pool_cpp03_generator.h" />
<ClInclude Include="..\..\include\etl\index_of_type.h" />
<ClInclude Include="..\..\include\etl\inplace_function.h" />
<ClInclude Include="..\..\include\etl\timer_config.h" />
<ClInclude Include="..\..\include\etl\invoke.h" />
<ClInclude Include="..\..\include\etl\lcm.h" />
<ClInclude Include="..\..\include\etl\math.h" />
@ -11449,6 +11450,7 @@
<ClCompile Include="..\test_task_scheduler.cpp" />
<ClCompile Include="..\test_threshold.cpp" />
<ClCompile Include="..\test_chrono_time_point.cpp" />
<ClCompile Include="..\test_timer_data.cpp" />
<ClCompile Include="..\test_to_arithmetic.cpp" />
<ClCompile Include="..\test_to_arithmetic_u16.cpp" />
<ClCompile Include="..\test_to_arithmetic_u32.cpp" />

View File

@ -226,6 +226,9 @@
<Filter Include="Documentation">
<UniqueIdentifier>{5b809d95-b37c-4011-af5e-b5ea6dcb354e}</UniqueIdentifier>
</Filter>
<Filter Include="Tests\Timer support">
<UniqueIdentifier>{c3b8f843-f046-4bf2-ab0a-34e9da0dcfbe}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\include\etl\enum_type.h">
@ -1566,6 +1569,9 @@
<ClInclude Include="..\..\include\etl\format.h">
<Filter>UnitTest++\Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\include\etl\timer_config.h">
<Filter>ETL\Frameworks</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\test_string_char.cpp">
@ -3836,6 +3842,9 @@
<ClCompile Include="..\test_vector_member_function_pointer_external_buffer.cpp">
<Filter>Tests\Containers</Filter>
</ClCompile>
<ClCompile Include="..\test_timer_data.cpp">
<Filter>Tests\Timer support</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="..\..\library.properties">