mirror of
https://github.com/ETLCPP/etl.git
synced 2026-04-30 19:09:10 +08:00
Added callback and message timers for interrupts.
This commit is contained in:
parent
a1e3017028
commit
0a163463c2
602
include/etl/callback_timer_interrupt.h
Normal file
602
include/etl/callback_timer_interrupt.h
Normal file
@ -0,0 +1,602 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2022 jwellbelove
|
||||
|
||||
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_INTERRUPT_INCLUDED
|
||||
#define ETL_CALLBACK_TIMER_INTERRUPT_INCLUDED
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "platform.h"
|
||||
|
||||
#include "algorithm.h"
|
||||
#include "nullptr.h"
|
||||
#include "delegate.h"
|
||||
#include "static_assert.h"
|
||||
#include "timer.h"
|
||||
#include "error_handler.h"
|
||||
#include "placement_new.h"
|
||||
|
||||
namespace etl
|
||||
{
|
||||
//***************************************************************************
|
||||
/// Interface for callback timer
|
||||
//***************************************************************************
|
||||
template <typename TInterruptGuard>
|
||||
class icallback_timer_interrupt
|
||||
{
|
||||
public:
|
||||
|
||||
typedef etl::delegate<void(void)> callback_type;
|
||||
|
||||
//*******************************************
|
||||
/// Register a timer.
|
||||
//*******************************************
|
||||
etl::timer::id::type register_timer(const callback_type& callback_,
|
||||
uint32_t period_,
|
||||
bool repeating_)
|
||||
{
|
||||
etl::timer::id::type id = etl::timer::id::NO_TIMER;
|
||||
|
||||
bool is_space = (number_of_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)
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
// Create in-place.
|
||||
new (&timer) timer_data(i, callback_, period_, repeating_);
|
||||
++number_of_registered_timers;
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// 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())
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
active_list.remove(timer.id, false);
|
||||
}
|
||||
|
||||
// Reset in-place.
|
||||
new (&timer) timer_data();
|
||||
--number_of_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()
|
||||
{
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
active_list.clear();
|
||||
number_of_registered_timers = 0;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0U; i < MAX_TIMERS; ++i)
|
||||
{
|
||||
::new (&timer_array[i]) timer_data();
|
||||
}
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
// 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)
|
||||
{
|
||||
// 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);
|
||||
|
||||
if (timer.callback.is_valid())
|
||||
{
|
||||
timer.callback();
|
||||
}
|
||||
|
||||
if (timer.repeating)
|
||||
{
|
||||
// Reinsert the timer.
|
||||
timer.delta = timer.period;
|
||||
active_list.insert(timer.id);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
if (timer.is_active())
|
||||
{
|
||||
active_list.remove(timer.id, false);
|
||||
}
|
||||
|
||||
timer.delta = immediate_ ? 0U : timer.period;
|
||||
active_list.insert(timer.id);
|
||||
|
||||
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())
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
active_list.remove(timer.id, false);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//*************************************************************************
|
||||
/// The configuration of a timer.
|
||||
struct timer_data
|
||||
{
|
||||
//*******************************************
|
||||
timer_data()
|
||||
: callback()
|
||||
, period(0U)
|
||||
, delta(etl::timer::state::INACTIVE)
|
||||
, id(etl::timer::id::NO_TIMER)
|
||||
, previous(etl::timer::id::NO_TIMER)
|
||||
, next(etl::timer::id::NO_TIMER)
|
||||
, repeating(true)
|
||||
{
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// ETL delegate callback
|
||||
//*******************************************
|
||||
timer_data(etl::timer::id::type id_,
|
||||
callback_type callback_,
|
||||
uint32_t period_,
|
||||
bool repeating_)
|
||||
: callback(callback_)
|
||||
, period(period_)
|
||||
, delta(etl::timer::state::INACTIVE)
|
||||
, id(id_)
|
||||
, previous(etl::timer::id::NO_TIMER)
|
||||
, next(etl::timer::id::NO_TIMER)
|
||||
, repeating(repeating_)
|
||||
{
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if the timer is active.
|
||||
//*******************************************
|
||||
bool is_active() const
|
||||
{
|
||||
return delta != etl::timer::state::INACTIVE;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Sets the timer to the inactive state.
|
||||
//*******************************************
|
||||
void set_inactive()
|
||||
{
|
||||
delta = etl::timer::state::INACTIVE;
|
||||
}
|
||||
|
||||
callback_type callback;
|
||||
uint32_t period;
|
||||
uint32_t delta;
|
||||
etl::timer::id::type id;
|
||||
uint_least8_t previous;
|
||||
uint_least8_t next;
|
||||
bool repeating;
|
||||
|
||||
private:
|
||||
|
||||
// Disabled.
|
||||
timer_data(const timer_data& other) ETL_DELETE;
|
||||
timer_data& operator =(const timer_data& other) ETL_DELETE;
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
icallback_timer_interrupt(timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
|
||||
: timer_array(timer_array_),
|
||||
active_list(timer_array_),
|
||||
enabled(false),
|
||||
number_of_registered_timers(0U),
|
||||
MAX_TIMERS(MAX_TIMERS_)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//*************************************************************************
|
||||
/// A specialised intrusive linked list for timer data.
|
||||
//*************************************************************************
|
||||
class timer_list
|
||||
{
|
||||
public:
|
||||
|
||||
//*******************************
|
||||
timer_list(timer_data* ptimers_)
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, current(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
{
|
||||
}
|
||||
|
||||
//*******************************
|
||||
bool empty() const
|
||||
{
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
void insert(etl::timer::id::type id_)
|
||||
{
|
||||
timer_data& timer = ptimers[id_];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
//*******************************
|
||||
etl::timer::id::type begin()
|
||||
{
|
||||
current = head;
|
||||
return current;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
etl::timer::id::type previous(etl::timer::id::type last)
|
||||
{
|
||||
current = ptimers[last].previous;
|
||||
return current;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
etl::timer::id::type next(etl::timer::id::type last)
|
||||
{
|
||||
current = ptimers[last].next;
|
||||
return current;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
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;
|
||||
current = etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
etl::timer::id::type head;
|
||||
etl::timer::id::type tail;
|
||||
etl::timer::id::type current;
|
||||
|
||||
timer_data* const ptimers;
|
||||
};
|
||||
|
||||
// The array of timer data structures.
|
||||
timer_data* const timer_array;
|
||||
|
||||
// The list of active timers.
|
||||
timer_list active_list;
|
||||
|
||||
volatile bool enabled;
|
||||
volatile uint_least8_t number_of_registered_timers;
|
||||
|
||||
public:
|
||||
|
||||
const uint_least8_t MAX_TIMERS;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// The callback timer
|
||||
//***************************************************************************
|
||||
template <uint_least8_t MAX_TIMERS_, typename TInterruptGuard>
|
||||
class callback_timer_interrupt : public etl::icallback_timer_interrupt<TInterruptGuard>
|
||||
{
|
||||
public:
|
||||
|
||||
ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254U, "No more than 254 timers are allowed");
|
||||
|
||||
typedef typename icallback_timer_interrupt<TInterruptGuard>::callback_type callback_type;
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
callback_timer_interrupt()
|
||||
: icallback_timer_interrupt<TInterruptGuard>(timer_array, MAX_TIMERS_)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
typename icallback_timer_interrupt<TInterruptGuard>::timer_data timer_array[MAX_TIMERS_];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
625
include/etl/message_timer_interrupt.h
Normal file
625
include/etl/message_timer_interrupt.h
Normal file
@ -0,0 +1,625 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2022 jwellbelove
|
||||
|
||||
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_MESSAGE_TIMER_INTERRUPT_INCLUDED
|
||||
#define ETL_MESSAGE_TIMER_INTERRUPT_INCLUDED
|
||||
|
||||
#include <stdint.h>
|
||||
#include "algorithm.h"
|
||||
|
||||
#include "platform.h"
|
||||
#include "nullptr.h"
|
||||
#include "message_types.h"
|
||||
#include "message.h"
|
||||
#include "message_router.h"
|
||||
#include "message_bus.h"
|
||||
#include "static_assert.h"
|
||||
#include "timer.h"
|
||||
#include "delegate.h"
|
||||
|
||||
namespace etl
|
||||
{
|
||||
//***************************************************************************
|
||||
/// Interface for message timer
|
||||
//***************************************************************************
|
||||
template <typename TInterruptGuard>
|
||||
class imessage_timer_interrupt
|
||||
{
|
||||
public:
|
||||
|
||||
typedef etl::delegate<void(void)> callback_type;
|
||||
|
||||
public:
|
||||
|
||||
//*******************************************
|
||||
/// Register a timer.
|
||||
//*******************************************
|
||||
etl::timer::id::type register_timer(const etl::imessage& message_,
|
||||
etl::imessage_router& router_,
|
||||
uint32_t period_,
|
||||
bool repeating_,
|
||||
etl::message_router_id_t destination_router_id_ = etl::imessage_router::ALL_MESSAGE_ROUTERS)
|
||||
{
|
||||
etl::timer::id::type id = etl::timer::id::NO_TIMER;
|
||||
|
||||
bool is_space = (number_of_registered_timers < MAX_TIMERS);
|
||||
|
||||
if (is_space)
|
||||
{
|
||||
// There's no point adding null message routers.
|
||||
if (!router_.is_null_router())
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
// Create in-place.
|
||||
new (&timer) timer_data(i, message_, router_, period_, repeating_, destination_router_id_);
|
||||
++number_of_registered_timers;
|
||||
id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// 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())
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
active_list.remove(timer.id, true);
|
||||
}
|
||||
|
||||
// Reset in-place.
|
||||
new (&timer) timer_data();
|
||||
--number_of_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()
|
||||
{
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
active_list.clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAX_TIMERS; ++i)
|
||||
{
|
||||
new (&timer_array[i]) timer_data();
|
||||
}
|
||||
|
||||
number_of_registered_timers = 0U;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
// 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)
|
||||
{
|
||||
// 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);
|
||||
|
||||
if (timer.p_router != ETL_NULLPTR)
|
||||
{
|
||||
timer.p_router->receive(timer.destination_router_id, *(timer.p_message));
|
||||
}
|
||||
|
||||
if (timer.repeating)
|
||||
{
|
||||
// Reinsert the timer.
|
||||
timer.delta = timer.period;
|
||||
active_list.insert(timer.id);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
if (timer.is_active())
|
||||
{
|
||||
active_list.remove(timer.id, false);
|
||||
}
|
||||
|
||||
timer.delta = immediate_ ? 0 : timer.period;
|
||||
active_list.insert(timer.id);
|
||||
|
||||
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())
|
||||
{
|
||||
TInterruptGuard guard;
|
||||
(void)guard; // Silence 'unused variable warnings.
|
||||
|
||||
active_list.remove(timer.id, false);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
//*************************************************************************
|
||||
/// The configuration of a timer.
|
||||
struct timer_data
|
||||
{
|
||||
//*******************************************
|
||||
timer_data()
|
||||
: p_message(ETL_NULLPTR)
|
||||
, p_router(ETL_NULLPTR)
|
||||
, period(0)
|
||||
, delta(etl::timer::state::INACTIVE)
|
||||
, destination_router_id(etl::imessage_bus::ALL_MESSAGE_ROUTERS)
|
||||
, id(etl::timer::id::NO_TIMER)
|
||||
, previous(etl::timer::id::NO_TIMER)
|
||||
, next(etl::timer::id::NO_TIMER)
|
||||
, repeating(true)
|
||||
{
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
timer_data(etl::timer::id::type id_,
|
||||
const etl::imessage& message_,
|
||||
etl::imessage_router& irouter_,
|
||||
uint32_t period_,
|
||||
bool repeating_,
|
||||
etl::message_router_id_t destination_router_id_ = etl::imessage_bus::ALL_MESSAGE_ROUTERS)
|
||||
: p_message(&message_)
|
||||
, p_router(&irouter_)
|
||||
, period(period_)
|
||||
, delta(etl::timer::state::INACTIVE)
|
||||
, destination_router_id(destination_router_id_)
|
||||
, id(id_)
|
||||
, previous(etl::timer::id::NO_TIMER)
|
||||
, next(etl::timer::id::NO_TIMER)
|
||||
, repeating(repeating_)
|
||||
{
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Returns true if the timer is active.
|
||||
//*******************************************
|
||||
bool is_active() const
|
||||
{
|
||||
return delta != etl::timer::state::INACTIVE;
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Sets the timer to the inactive state.
|
||||
//*******************************************
|
||||
void set_inactive()
|
||||
{
|
||||
delta = etl::timer::state::INACTIVE;
|
||||
}
|
||||
|
||||
const etl::imessage* p_message;
|
||||
etl::imessage_router* p_router;
|
||||
uint32_t period;
|
||||
uint32_t delta;
|
||||
etl::message_router_id_t destination_router_id;
|
||||
etl::timer::id::type id;
|
||||
uint_least8_t previous;
|
||||
uint_least8_t next;
|
||||
bool repeating;
|
||||
|
||||
private:
|
||||
|
||||
// Disabled.
|
||||
timer_data(const timer_data& other);
|
||||
timer_data& operator =(const timer_data& other);
|
||||
};
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
imessage_timer_interrupt(timer_data* const timer_array_, const uint_least8_t MAX_TIMERS_)
|
||||
: timer_array(timer_array_)
|
||||
, active_list(timer_array_)
|
||||
, enabled(false)
|
||||
, number_of_registered_timers(0U)
|
||||
, MAX_TIMERS(MAX_TIMERS_)
|
||||
{
|
||||
}
|
||||
|
||||
//*******************************************
|
||||
/// Destructor.
|
||||
//*******************************************
|
||||
~imessage_timer_interrupt()
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
//*************************************************************************
|
||||
/// A specialised intrusive linked list for timer data.
|
||||
//*************************************************************************
|
||||
class timer_list
|
||||
{
|
||||
public:
|
||||
|
||||
//*******************************
|
||||
timer_list(timer_data* ptimers_)
|
||||
: head(etl::timer::id::NO_TIMER)
|
||||
, tail(etl::timer::id::NO_TIMER)
|
||||
, current(etl::timer::id::NO_TIMER)
|
||||
, ptimers(ptimers_)
|
||||
{
|
||||
}
|
||||
|
||||
//*******************************
|
||||
bool empty() const
|
||||
{
|
||||
return head == etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
// Inserts the timer at the correct delta position
|
||||
//*******************************
|
||||
void insert(etl::timer::id::type id_)
|
||||
{
|
||||
timer_data& timer = ptimers[id_];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
//*******************************
|
||||
etl::timer::id::type begin()
|
||||
{
|
||||
current = head;
|
||||
return current;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
etl::timer::id::type previous(etl::timer::id::type last)
|
||||
{
|
||||
current = ptimers[last].previous;
|
||||
return current;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
etl::timer::id::type next(etl::timer::id::type last)
|
||||
{
|
||||
current = ptimers[last].next;
|
||||
return current;
|
||||
}
|
||||
|
||||
//*******************************
|
||||
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;
|
||||
current = etl::timer::id::NO_TIMER;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
etl::timer::id::type head;
|
||||
etl::timer::id::type tail;
|
||||
etl::timer::id::type current;
|
||||
|
||||
timer_data* const ptimers;
|
||||
};
|
||||
|
||||
// The array of timer data structures.
|
||||
timer_data* const timer_array;
|
||||
|
||||
// The list of active timers.
|
||||
timer_list active_list;
|
||||
|
||||
volatile bool enabled;
|
||||
volatile uint_least8_t number_of_registered_timers;
|
||||
|
||||
public:
|
||||
|
||||
const uint_least8_t MAX_TIMERS;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
/// The message timer
|
||||
//***************************************************************************
|
||||
template <uint_least8_t MAX_TIMERS_, typename TInterruptGuard>
|
||||
class message_timer_interrupt : public etl::imessage_timer_interrupt<TInterruptGuard>
|
||||
{
|
||||
public:
|
||||
|
||||
ETL_STATIC_ASSERT(MAX_TIMERS_ <= 254, "No more than 254 timers are allowed");
|
||||
|
||||
typedef typename imessage_timer_interrupt::callback_type callback_type;
|
||||
|
||||
//*******************************************
|
||||
/// Constructor.
|
||||
//*******************************************
|
||||
message_timer_interrupt()
|
||||
: imessage_timer_interrupt<TInterruptGuard>(timer_array, MAX_TIMERS_)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
typename etl::imessage_timer_interrupt<TInterruptGuard>::timer_data timer_array[MAX_TIMERS_];
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
826
test/test_callback_timer_interrupt.cpp
Normal file
826
test/test_callback_timer_interrupt.cpp
Normal file
@ -0,0 +1,826 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2022 jwellbelove
|
||||
|
||||
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/callback_timer_interrupt.h"
|
||||
#include "etl/delegate.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace
|
||||
{
|
||||
uint64_t ticks = 0ULL;
|
||||
|
||||
//***************************************************************************
|
||||
struct ScopedGuard
|
||||
{
|
||||
ScopedGuard()
|
||||
{
|
||||
++guard_count;
|
||||
}
|
||||
|
||||
~ScopedGuard()
|
||||
{
|
||||
--guard_count;
|
||||
}
|
||||
|
||||
volatile static int guard_count;
|
||||
};
|
||||
|
||||
volatile int ScopedGuard::guard_count = 0;
|
||||
|
||||
//***************************************************************************
|
||||
struct TimerLogEntry
|
||||
{
|
||||
etl::timer::id::type id;
|
||||
uint64_t time_called;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// Class callback via etl::delegate
|
||||
//***************************************************************************
|
||||
class Test
|
||||
{
|
||||
public:
|
||||
|
||||
Test()
|
||||
: p_controller(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void callback()
|
||||
{
|
||||
tick_list.push_back(ticks);
|
||||
}
|
||||
|
||||
void callback2()
|
||||
{
|
||||
tick_list.push_back(ticks);
|
||||
|
||||
p_controller->start(2);
|
||||
p_controller->start(1);
|
||||
}
|
||||
|
||||
void set_controller(etl::callback_timer_interrupt<3, ScopedGuard>& controller)
|
||||
{
|
||||
p_controller = &controller;
|
||||
}
|
||||
|
||||
std::vector<uint64_t> tick_list;
|
||||
|
||||
etl::callback_timer_interrupt<3, ScopedGuard>* p_controller;
|
||||
};
|
||||
|
||||
using callback_type = etl::icallback_timer_interrupt<ScopedGuard>::callback_type;
|
||||
|
||||
Test test;
|
||||
callback_type member_callback = callback_type::create<Test, test, &Test::callback>();
|
||||
callback_type member_callback2 = callback_type::create<Test, test, &Test::callback2>();
|
||||
|
||||
//***************************************************************************
|
||||
// Free function callback via etl::function
|
||||
//***************************************************************************
|
||||
std::vector<uint64_t> free_tick_list1;
|
||||
|
||||
void free_callback1()
|
||||
{
|
||||
free_tick_list1.push_back(ticks);
|
||||
}
|
||||
|
||||
callback_type free_function_callback = callback_type::create<free_callback1>();
|
||||
|
||||
//***************************************************************************
|
||||
// Free function callback via function pointer
|
||||
//***************************************************************************
|
||||
std::vector<uint64_t> free_tick_list2;
|
||||
|
||||
void free_callback2()
|
||||
{
|
||||
free_tick_list2.push_back(ticks);
|
||||
}
|
||||
|
||||
callback_type free_function_callback2 = callback_type::create<free_callback2>();
|
||||
|
||||
SUITE(test_callback_timer_interrupt)
|
||||
{
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_too_many_timers)
|
||||
{
|
||||
etl::callback_timer_interrupt<2, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::SINGLE_SHOT);
|
||||
|
||||
CHECK(id1 != etl::timer::id::NO_TIMER);
|
||||
CHECK(id2 != etl::timer::id::NO_TIMER);
|
||||
CHECK(id3 == etl::timer::id::NO_TIMER);
|
||||
|
||||
timer_controller.clear();
|
||||
id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::SINGLE_SHOT);
|
||||
CHECK(id3 != etl::timer::id::NO_TIMER);
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_one_shot)
|
||||
{
|
||||
etl::callback_timer_interrupt<4, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::SINGLE_SHOT);
|
||||
|
||||
test.tick_list.clear();
|
||||
free_tick_list1.clear();
|
||||
free_tick_list2.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1UL;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 37 };
|
||||
std::vector<uint64_t> compare2 = { 23 };
|
||||
std::vector<uint64_t> compare3 = { 11 };
|
||||
|
||||
CHECK(test.tick_list.size() != 0);
|
||||
CHECK(free_tick_list1.size() != 0);
|
||||
CHECK(free_tick_list2.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_one_shot_after_timeout)
|
||||
{
|
||||
etl::callback_timer_interrupt<1, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::SINGLE_SHOT);
|
||||
test.tick_list.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
// Timer should have timed out.
|
||||
|
||||
CHECK(timer_controller.set_period(id1, 50));
|
||||
timer_controller.start(id1);
|
||||
|
||||
test.tick_list.clear();
|
||||
|
||||
ticks = 0;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
// Timer should have timed out.
|
||||
|
||||
CHECK_EQUAL(50U, *test.tick_list.data());
|
||||
|
||||
CHECK(timer_controller.unregister_timer(id1));
|
||||
CHECK(!timer_controller.unregister_timer(id1));
|
||||
CHECK(!timer_controller.start(id1));
|
||||
CHECK(!timer_controller.stop(id1));
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_repeating)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
test.tick_list.clear();
|
||||
free_tick_list1.clear();
|
||||
free_tick_list2.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 37, 74 };
|
||||
std::vector<uint64_t> compare2 = { 23, 46, 69, 92 };
|
||||
std::vector<uint64_t> compare3 = { 11, 22, 33, 44, 55, 66, 77, 88, 99 };
|
||||
|
||||
CHECK(test.tick_list.size() != 0);
|
||||
CHECK(free_tick_list1.size() != 0);
|
||||
CHECK(free_tick_list2.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_repeating_bigger_step)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
test.tick_list.clear();
|
||||
free_tick_list1.clear();
|
||||
free_tick_list2.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
CHECK(!timer_controller.is_running());
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
CHECK(timer_controller.is_running());
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 5U;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 40, 75 };
|
||||
std::vector<uint64_t> compare2 = { 25, 50, 70, 95 };
|
||||
std::vector<uint64_t> compare3 = { 15, 25, 35, 45, 55, 70, 80, 90, 100 };
|
||||
|
||||
CHECK(test.tick_list.size() != 0);
|
||||
CHECK(free_tick_list1.size() != 0);
|
||||
CHECK(free_tick_list2.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_repeating_stop_start)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
test.tick_list.clear();
|
||||
free_tick_list1.clear();
|
||||
free_tick_list2.clear();
|
||||
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
if (ticks == 40)
|
||||
{
|
||||
timer_controller.start(id1);
|
||||
timer_controller.stop(id2);
|
||||
}
|
||||
|
||||
if (ticks == 80)
|
||||
{
|
||||
timer_controller.stop(id1);
|
||||
timer_controller.start(id2);
|
||||
}
|
||||
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 77 };
|
||||
std::vector<uint64_t> compare2 = { 23 };
|
||||
std::vector<uint64_t> compare3 = { 11, 22, 33, 44, 55, 66, 77, 88, 99 };
|
||||
|
||||
CHECK(test.tick_list.size() != 0);
|
||||
CHECK(free_tick_list1.size() != 0);
|
||||
CHECK(free_tick_list2.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_timer_starts_timer_small_step)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback2, 100, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(member_callback, 10, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(member_callback, 22, etl::timer::mode::SINGLE_SHOT);
|
||||
|
||||
(void)id2;
|
||||
(void)id3;
|
||||
|
||||
test.set_controller(timer_controller);
|
||||
|
||||
test.tick_list.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
while (ticks <= 200U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 100, 110, 122 };
|
||||
|
||||
CHECK(test.tick_list.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_timer_starts_timer_big_step)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback2, 100, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(member_callback, 10, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(member_callback, 22, etl::timer::mode::SINGLE_SHOT);
|
||||
|
||||
(void)id2;
|
||||
(void)id3;
|
||||
|
||||
test.set_controller(timer_controller);
|
||||
|
||||
test.tick_list.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 3;
|
||||
|
||||
while (ticks <= 200U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 102, 111, 123 };
|
||||
|
||||
CHECK(test.tick_list.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_repeating_register_unregister)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1;
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
test.tick_list.clear();
|
||||
free_tick_list1.clear();
|
||||
free_tick_list2.clear();
|
||||
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
if (ticks == 40)
|
||||
{
|
||||
timer_controller.unregister_timer(id2);
|
||||
|
||||
id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::REPEATING);
|
||||
timer_controller.start(id1);
|
||||
}
|
||||
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 77 };
|
||||
std::vector<uint64_t> compare2 = { 23 };
|
||||
std::vector<uint64_t> compare3 = { 11, 22, 33, 44, 55, 66, 77, 88, 99 };
|
||||
|
||||
CHECK(test.tick_list.size() != 0);
|
||||
CHECK(free_tick_list1.size() != 0);
|
||||
CHECK(free_tick_list2.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_repeating_clear)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
test.tick_list.clear();
|
||||
free_tick_list1.clear();
|
||||
free_tick_list2.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
|
||||
if (ticks == 40)
|
||||
{
|
||||
timer_controller.clear();
|
||||
}
|
||||
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 37 };
|
||||
std::vector<uint64_t> compare2 = { 23 };
|
||||
std::vector<uint64_t> compare3 = { 11, 22, 33 };
|
||||
|
||||
CHECK(test.tick_list.size() != 0);
|
||||
CHECK(free_tick_list1.size() != 0);
|
||||
CHECK(free_tick_list2.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_delayed_immediate)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(member_callback, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(free_function_callback2, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
test.tick_list.clear();
|
||||
free_tick_list1.clear();
|
||||
free_tick_list2.clear();
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 5;
|
||||
timer_controller.tick(uint32_t(ticks));
|
||||
|
||||
timer_controller.start(id1, etl::timer::start::IMMEDIATE);
|
||||
timer_controller.start(id2, etl::timer::start::IMMEDIATE);
|
||||
timer_controller.start(id3, etl::timer::start::DELAYED);
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 6, 42, 79 };
|
||||
std::vector<uint64_t> compare2 = { 6, 28, 51, 74, 97 };
|
||||
std::vector<uint64_t> compare3 = { 16, 27, 38, 49, 60, 71, 82, 93 };
|
||||
|
||||
CHECK(test.tick_list.size() != 0);
|
||||
CHECK(free_tick_list1.size() != 0);
|
||||
CHECK(free_tick_list2.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), test.tick_list.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list1.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), free_tick_list2.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_one_shot_big_step_short_delay_insert)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(free_function_callback, 15, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(free_function_callback2, 5, etl::timer::mode::REPEATING);
|
||||
|
||||
free_tick_list1.clear();
|
||||
free_tick_list2.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 11U;
|
||||
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
|
||||
std::vector<uint64_t> compare1 = { 22 };
|
||||
std::vector<uint64_t> compare2 = { 11, 11, 22, 22 };
|
||||
|
||||
CHECK(free_tick_list1.size() != 0);
|
||||
CHECK(free_tick_list2.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), free_tick_list1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), free_tick_list2.data(), compare2.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_one_shot_empty_list_huge_tick_before_insert)
|
||||
{
|
||||
etl::callback_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(free_function_callback, 5, etl::timer::mode::SINGLE_SHOT);
|
||||
|
||||
free_tick_list1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 5U;
|
||||
|
||||
for (uint32_t i = 0U; i < step; ++i)
|
||||
{
|
||||
++ticks;
|
||||
timer_controller.tick(1);
|
||||
}
|
||||
|
||||
// Huge tick count.
|
||||
timer_controller.tick(UINT32_MAX - step + 1);
|
||||
|
||||
timer_controller.start(id1);
|
||||
|
||||
for (uint32_t i = 0U; i < step; ++i)
|
||||
{
|
||||
++ticks;
|
||||
timer_controller.tick(1);
|
||||
}
|
||||
std::vector<uint64_t> compare1 = { 5, 10 };
|
||||
|
||||
CHECK(free_tick_list1.size() != 0);
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), free_tick_list1.data(), compare1.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
class test_object
|
||||
{
|
||||
public:
|
||||
|
||||
void call()
|
||||
{
|
||||
++called;
|
||||
}
|
||||
|
||||
size_t called = 0UL;
|
||||
};
|
||||
|
||||
TEST(callback_timer_interrupt_call_etl_delegate)
|
||||
{
|
||||
test_object test_obj;
|
||||
callback_type delegate_callback = callback_type::create<test_object, &test_object::call>(test_obj);
|
||||
|
||||
etl::callback_timer_interrupt<1, ScopedGuard> timer_controller;
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
etl::timer::id::type id = timer_controller.register_timer(delegate_callback, 5, etl::timer::mode::SINGLE_SHOT);
|
||||
timer_controller.start(id);
|
||||
|
||||
timer_controller.tick(4);
|
||||
CHECK(test_obj.called == 0);
|
||||
|
||||
timer_controller.tick(2);
|
||||
CHECK(test_obj.called == 1);
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(callback_timer_interrupt_log_timer_calls)
|
||||
{
|
||||
std::vector<TimerLogEntry> timer_log;
|
||||
|
||||
etl::callback_timer_interrupt<4, ScopedGuard> timer_controller;
|
||||
size_t timer_count = 0U;
|
||||
|
||||
// Create the callbacks.
|
||||
callback_type delegate_callback0([&]()
|
||||
{
|
||||
timer_log.push_back(TimerLogEntry{ 0, timer_count });
|
||||
});
|
||||
|
||||
callback_type delegate_callback1([&]()
|
||||
{
|
||||
timer_log.push_back(TimerLogEntry{ 1, timer_count });
|
||||
});
|
||||
|
||||
callback_type delegate_callback2([&]()
|
||||
{
|
||||
timer_log.push_back(TimerLogEntry{ 2, timer_count });
|
||||
});
|
||||
|
||||
callback_type delegate_callback3([&]()
|
||||
{
|
||||
timer_log.push_back(TimerLogEntry{ 3, timer_count });
|
||||
timer_controller.start(1);
|
||||
});
|
||||
|
||||
timer_log.clear();
|
||||
timer_controller.enable(true);
|
||||
|
||||
constexpr uint32_t T0 = 2U;
|
||||
constexpr uint32_t T1 = 3U;
|
||||
constexpr uint32_t T2 = 4U;
|
||||
constexpr uint32_t T3 = 5U;
|
||||
|
||||
// Register the timers.
|
||||
etl::timer::id::type id0 = timer_controller.register_timer(delegate_callback0, T0, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(delegate_callback1, T1, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(delegate_callback2, T2, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(delegate_callback3, T3, etl::timer::mode::REPEATING);
|
||||
|
||||
// Start the repeating timers.
|
||||
timer_controller.start(id0);
|
||||
timer_controller.start(id2);
|
||||
timer_controller.start(id3);
|
||||
|
||||
// Run the timer.
|
||||
for (int i = 1; i < 50; ++i)
|
||||
{
|
||||
++timer_count;
|
||||
timer_controller.tick(1);
|
||||
}
|
||||
|
||||
// Check the results log.
|
||||
for (auto t : timer_log)
|
||||
{
|
||||
switch (t.id)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
CHECK_EQUAL(0, t.time_called % 2);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
CHECK_EQUAL(0, (t.time_called % 5) % 3);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
CHECK_EQUAL(0, t.time_called % 4);
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
CHECK_EQUAL(0, t.time_called % 5);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
CHECK(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
};
|
||||
}
|
||||
770
test/test_message_timer_interrupt.cpp
Normal file
770
test/test_message_timer_interrupt.cpp
Normal file
@ -0,0 +1,770 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2022 jwellbelove
|
||||
|
||||
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/message_router.h"
|
||||
#include "etl/message_bus.h"
|
||||
#include "etl/message_timer_interrupt.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
//***************************************************************************
|
||||
// The set of messages.
|
||||
//***************************************************************************
|
||||
namespace
|
||||
{
|
||||
uint64_t ticks = 0;
|
||||
|
||||
//***************************************************************************
|
||||
struct ScopedGuard
|
||||
{
|
||||
ScopedGuard()
|
||||
{
|
||||
++guard_count;
|
||||
}
|
||||
|
||||
~ScopedGuard()
|
||||
{
|
||||
--guard_count;
|
||||
}
|
||||
|
||||
volatile static int guard_count;
|
||||
};
|
||||
|
||||
volatile int ScopedGuard::guard_count = 0;
|
||||
|
||||
//***************************************************************************
|
||||
struct TimerLogEntry
|
||||
{
|
||||
etl::timer::id::type id;
|
||||
uint64_t time_called;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
enum
|
||||
{
|
||||
MESSAGE1,
|
||||
MESSAGE2,
|
||||
MESSAGE3,
|
||||
MESSAGE4
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ROUTER1 = 1,
|
||||
};
|
||||
|
||||
struct Message1 : public etl::message<MESSAGE1>
|
||||
{
|
||||
};
|
||||
|
||||
struct Message2 : public etl::message<MESSAGE2>
|
||||
{
|
||||
};
|
||||
|
||||
struct Message3 : public etl::message<MESSAGE3>
|
||||
{
|
||||
};
|
||||
|
||||
struct Message4 : public etl::message<MESSAGE4>
|
||||
{
|
||||
};
|
||||
|
||||
Message1 message1;
|
||||
Message2 message2;
|
||||
Message3 message3;
|
||||
Message4 message4;
|
||||
|
||||
//***************************************************************************
|
||||
// Router that handles messages 1, 2, 3
|
||||
//***************************************************************************
|
||||
class Router1 : public etl::message_router<Router1, Message1, Message2, Message3>
|
||||
{
|
||||
public:
|
||||
|
||||
Router1()
|
||||
: message_router(ROUTER1)
|
||||
{
|
||||
}
|
||||
|
||||
void on_receive(const Message1&)
|
||||
{
|
||||
message1.push_back(ticks);
|
||||
}
|
||||
|
||||
void on_receive(const Message2&)
|
||||
{
|
||||
message2.push_back(ticks);
|
||||
}
|
||||
|
||||
void on_receive(const Message3&)
|
||||
{
|
||||
message3.push_back(ticks);
|
||||
}
|
||||
|
||||
void on_receive_unknown(const etl::imessage&)
|
||||
{
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
message1.clear();
|
||||
message2.clear();
|
||||
message3.clear();
|
||||
}
|
||||
|
||||
std::vector<uint64_t> message1;
|
||||
std::vector<uint64_t> message2;
|
||||
std::vector<uint64_t> message3;
|
||||
};
|
||||
|
||||
//***************************************************************************
|
||||
// Bus that handles messages 1, 2, 3
|
||||
//***************************************************************************
|
||||
class Bus1 : public etl::message_bus<1>
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
//***********************************
|
||||
Router1 router1;
|
||||
Bus1 bus1;
|
||||
|
||||
SUITE(test_message_timer_interrupt)
|
||||
{
|
||||
//*************************************************************************
|
||||
TEST(message_timer_too_many_timers)
|
||||
{
|
||||
etl::message_timer_interrupt<2, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 37, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router1, 23, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, router1, 11, etl::timer::mode::SINGLE_SHOT);
|
||||
|
||||
CHECK(id1 != etl::timer::id::NO_TIMER);
|
||||
CHECK(id2 != etl::timer::id::NO_TIMER);
|
||||
CHECK(id3 == etl::timer::id::NO_TIMER);
|
||||
|
||||
timer_controller.clear();
|
||||
id3 = timer_controller.register_timer(message3, router1, 11, etl::timer::mode::SINGLE_SHOT);
|
||||
CHECK(id3 != etl::timer::id::NO_TIMER);
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_one_shot)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 37, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router1, 23, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, router1, 11, etl::timer::mode::SINGLE_SHOT);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1UL;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 37ULL };
|
||||
std::vector<uint64_t> compare2 = { 23ULL };
|
||||
std::vector<uint64_t> compare3 = { 11ULL };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), router1.message2.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), router1.message3.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_one_shot_after_timeout)
|
||||
{
|
||||
etl::message_timer_interrupt<1, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 37, etl::timer::mode::SINGLE_SHOT);
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
// Timer should have timed out.
|
||||
|
||||
CHECK(timer_controller.set_period(id1, 50));
|
||||
timer_controller.start(id1);
|
||||
|
||||
router1.clear();
|
||||
|
||||
ticks = 0;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
// Timer should have timed out.
|
||||
|
||||
CHECK_EQUAL(50U, *router1.message1.data());
|
||||
|
||||
CHECK(timer_controller.unregister_timer(id1));
|
||||
CHECK(!timer_controller.unregister_timer(id1));
|
||||
CHECK(!timer_controller.start(id1));
|
||||
CHECK(!timer_controller.stop(id1));
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_repeating)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router1, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, router1, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1U;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 37ULL, 74ULL };
|
||||
std::vector<uint64_t> compare2 = { 23ULL, 46ULL, 69ULL, 92ULL };
|
||||
std::vector<uint64_t> compare3 = { 11ULL, 22ULL, 33ULL, 44ULL, 55ULL, 66ULL, 77ULL, 88ULL, 99ULL };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), router1.message2.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), router1.message3.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_repeating_bigger_step)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router1, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, router1, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
CHECK(!timer_controller.is_running());
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
CHECK(timer_controller.is_running());
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 5UL;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 40ULL, 75ULL };
|
||||
std::vector<uint64_t> compare2 = { 25ULL, 50ULL, 70ULL, 95ULL };
|
||||
std::vector<uint64_t> compare3 = { 15ULL, 25ULL, 35ULL, 45ULL, 55ULL, 70ULL, 80ULL, 90ULL, 100ULL };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), router1.message2.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), router1.message3.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_repeating_stop_start)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router1, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, router1, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1UL;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
if (ticks == 40)
|
||||
{
|
||||
timer_controller.start(id1);
|
||||
timer_controller.stop(id2);
|
||||
}
|
||||
|
||||
if (ticks == 80)
|
||||
{
|
||||
timer_controller.stop(id1);
|
||||
timer_controller.start(id2);
|
||||
}
|
||||
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 77ULL };
|
||||
std::vector<uint64_t> compare2 = { 23ULL };
|
||||
std::vector<uint64_t> compare3 = { 11ULL, 22ULL, 33ULL, 44ULL, 55ULL, 66ULL, 77ULL, 88ULL, 99ULL };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), router1.message2.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), router1.message3.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_repeating_register_unregister)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1;
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router1, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, router1, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1UL;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
if (ticks == 40)
|
||||
{
|
||||
timer_controller.unregister_timer(id2);
|
||||
|
||||
id1 = timer_controller.register_timer(message1, router1, 37, etl::timer::mode::REPEATING);
|
||||
timer_controller.start(id1);
|
||||
}
|
||||
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 77ULL };
|
||||
std::vector<uint64_t> compare2 = { 23ULL };
|
||||
std::vector<uint64_t> compare3 = { 11ULL, 22ULL, 33ULL, 44ULL, 55ULL, 66ULL, 77ULL, 88ULL, 99ULL };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), router1.message2.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), router1.message3.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_repeating_clear)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router1, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, router1, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1UL;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
|
||||
if (ticks == 40)
|
||||
{
|
||||
timer_controller.clear();
|
||||
}
|
||||
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 37ULL };
|
||||
std::vector<uint64_t> compare2 = { 23ULL };
|
||||
std::vector<uint64_t> compare3 = { 11ULL, 22ULL, 33ULL };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), router1.message2.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), router1.message3.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_route_through_bus)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, bus1, 37, etl::timer::mode::SINGLE_SHOT, ROUTER1);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, bus1, 23, etl::timer::mode::SINGLE_SHOT, ROUTER1);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, bus1, 11, etl::timer::mode::SINGLE_SHOT, etl::imessage_router::ALL_MESSAGE_ROUTERS);
|
||||
|
||||
bus1.subscribe(router1);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 1UL;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 37ULL };
|
||||
std::vector<uint64_t> compare2 = { 23ULL };
|
||||
std::vector<uint64_t> compare3 = { 11ULL };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), router1.message2.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), router1.message3.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_immediate_delayed)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 37, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router1, 23, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, router1, 11, etl::timer::mode::REPEATING);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 5;
|
||||
timer_controller.tick(uint32_t(ticks));
|
||||
|
||||
timer_controller.start(id1, etl::timer::start::IMMEDIATE);
|
||||
timer_controller.start(id2, etl::timer::start::IMMEDIATE);
|
||||
timer_controller.start(id3, etl::timer::start::DELAYED);
|
||||
|
||||
const uint32_t step = 1UL;
|
||||
|
||||
while (ticks <= 100U)
|
||||
{
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> compare1 = { 6ULL, 42ULL, 79ULL };
|
||||
std::vector<uint64_t> compare2 = { 6ULL, 28ULL, 51ULL, 74ULL, 97ULL };
|
||||
std::vector<uint64_t> compare3 = { 16ULL, 27ULL, 38ULL, 49ULL, 60ULL, 71ULL, 82ULL, 93ULL };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), router1.message2.data(), compare2.size());
|
||||
CHECK_ARRAY_EQUAL(compare3.data(), router1.message3.data(), compare3.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_one_shot_big_step_short_delay_insert)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 15, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router1, 5, etl::timer::mode::REPEATING);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id2);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 11UL;
|
||||
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
|
||||
ticks += step;
|
||||
timer_controller.tick(step);
|
||||
|
||||
std::vector<uint64_t> compare1 = { 22 };
|
||||
std::vector<uint64_t> compare2 = { 11, 11, 22, 22 };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
CHECK_ARRAY_EQUAL(compare2.data(), router1.message2.data(), compare2.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(message_timer_one_shot_empty_list_huge_tick_before_insert)
|
||||
{
|
||||
etl::message_timer_interrupt<3, ScopedGuard> timer_controller;
|
||||
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router1, 5, etl::timer::mode::SINGLE_SHOT);
|
||||
|
||||
router1.clear();
|
||||
|
||||
timer_controller.start(id1);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
ticks = 0;
|
||||
|
||||
const uint32_t step = 5ULL;
|
||||
|
||||
for (uint32_t i = 0UL; i < step; ++i)
|
||||
{
|
||||
++ticks;
|
||||
timer_controller.tick(1);
|
||||
}
|
||||
|
||||
// Huge tick count.
|
||||
timer_controller.tick(UINT32_MAX - step + 1);
|
||||
|
||||
timer_controller.start(id1);
|
||||
|
||||
for (uint32_t i = 0UL; i < step; ++i)
|
||||
{
|
||||
++ticks;
|
||||
timer_controller.tick(1);
|
||||
}
|
||||
std::vector<uint64_t> compare1 = { 5, 10 };
|
||||
|
||||
CHECK_ARRAY_EQUAL(compare1.data(), router1.message1.data(), compare1.size());
|
||||
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
|
||||
|
||||
class RouterLog : public etl::message_router<RouterLog, Message1, Message2, Message3, Message4>
|
||||
{
|
||||
public:
|
||||
|
||||
//*********************************
|
||||
RouterLog(etl::imessage_timer_interrupt<ScopedGuard>& timer_controller_)
|
||||
: message_router(ROUTER1)
|
||||
, timer_count(0)
|
||||
, timer_controller(timer_controller_)
|
||||
{
|
||||
}
|
||||
|
||||
//*********************************
|
||||
void on_receive(const Message1&)
|
||||
{
|
||||
// Id0
|
||||
timer_log.push_back(TimerLogEntry{ 0, timer_count });
|
||||
}
|
||||
|
||||
//*********************************
|
||||
void on_receive(const Message2&)
|
||||
{
|
||||
// Id1
|
||||
timer_log.push_back(TimerLogEntry{ 1, timer_count });
|
||||
}
|
||||
|
||||
//*********************************
|
||||
void on_receive(const Message3&)
|
||||
{
|
||||
// Id2
|
||||
timer_log.push_back(TimerLogEntry{ 2, timer_count });
|
||||
}
|
||||
|
||||
//*********************************
|
||||
void on_receive(const Message4&)
|
||||
{
|
||||
// Id3
|
||||
timer_log.push_back(TimerLogEntry{ 3, timer_count });
|
||||
timer_controller.start(1);
|
||||
}
|
||||
|
||||
//*********************************
|
||||
void on_receive_unknown(const etl::imessage&)
|
||||
{
|
||||
timer_log.push_back(TimerLogEntry{ 99, timer_count });
|
||||
}
|
||||
|
||||
size_t timer_count;
|
||||
std::vector<TimerLogEntry> timer_log;
|
||||
etl::imessage_timer_interrupt<ScopedGuard>& timer_controller;
|
||||
};
|
||||
|
||||
TEST(message_timer_interrupt_log_timer_calls)
|
||||
{
|
||||
etl::message_timer_interrupt<4, ScopedGuard> timer_controller;
|
||||
RouterLog router(timer_controller);
|
||||
|
||||
timer_controller.enable(true);
|
||||
|
||||
constexpr uint32_t T1 = 2U;
|
||||
constexpr uint32_t T2 = 3U;
|
||||
constexpr uint32_t T3 = 4U;
|
||||
constexpr uint32_t T4 = 5U;
|
||||
|
||||
// Register the timers.
|
||||
etl::timer::id::type id1 = timer_controller.register_timer(message1, router, T1, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id2 = timer_controller.register_timer(message2, router, T2, etl::timer::mode::SINGLE_SHOT);
|
||||
etl::timer::id::type id3 = timer_controller.register_timer(message3, router, T3, etl::timer::mode::REPEATING);
|
||||
etl::timer::id::type id4 = timer_controller.register_timer(message4, router, T4, etl::timer::mode::REPEATING);
|
||||
|
||||
// Start the repeating timers.
|
||||
timer_controller.start(id1);
|
||||
timer_controller.start(id3);
|
||||
timer_controller.start(id4);
|
||||
|
||||
// Run the timer.
|
||||
for (int i = 1; i < 50; ++i)
|
||||
{
|
||||
++router.timer_count;
|
||||
timer_controller.tick(1);
|
||||
}
|
||||
|
||||
// Check the results log.
|
||||
for (auto t : router.timer_log)
|
||||
{
|
||||
switch (t.id)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
CHECK_EQUAL(0, t.time_called % 2);
|
||||
break;
|
||||
}
|
||||
|
||||
case 1:
|
||||
{
|
||||
CHECK_EQUAL(0, (t.time_called % 5) % 3);
|
||||
break;
|
||||
}
|
||||
|
||||
case 2:
|
||||
{
|
||||
CHECK_EQUAL(0, t.time_called % 4);
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
{
|
||||
CHECK_EQUAL(0, t.time_called % 5);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
CHECK(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check the
|
||||
CHECK_EQUAL(0U, ScopedGuard::guard_count);
|
||||
}
|
||||
};
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user