Polled timers
Polled timers can be used to measure elapsed time intervals or to check for timeouts within code loops. Traditionally one might do this as follows:
const unsigned TIMEOUT_MS = 100;
unsigned start = millis();
while(millis() - start < TIMEOUT_MS) {
// do some stuff
}
unsigned elapsed = millis() - start;
Serial.print("Elapsed time: ");
Serial.print(elapsed);
Serial.println("ms");
Note
A common source of bugs when hand-coding such loops can be illustrated by this alternative:
unsigned timeout = millis() + TIMEOUT_MS;
while(millis() < timeout) {
// do some stuff
}
At first glance this looks better than the first approach as we don’t have a subtraction within the loop. However, when millis() exceeds MAXINT - TIMEOUT_MS the timeout calculation will wrap and the loop will never get executed. This takes a little under 25 days, but with microseconds it’ll happen in less than an hour. This may not be the desired behaviour.
It’s generally safer and easier to use a PolledTimer
:
OneShotFastMs timer(TIMEOUT_MS);
while(!timer.expired()) {
// do some stuff
}
auto elapsed = timer.elapsedTime(); // Returns a `NanoTime::Time` object, value with time units
Serial.print("Elapsed time: ");
Serial.println(elapsed.toString()); // Includes units in the elapsed time interval
// Show time rounded to nearest whole seconds
Serial.println(elapsed.as<NanoTime::Seconds>().toString());
If you prefer to use microseconds, use OneShotFastUs
instead or specify the units directly:
OneShotElapseTimer<NanoTime::Microseconds> timer;
Another advantage of polled timers is speed. Every call to millis (or micros) requires a calculation from clock ticks into milliseconds (or microseconds). It’s also a function call.
Polled timers measure time using hardware clock ticks, and query the hardware timer register directly without any function calls or calculations. This makes them a much better choice for tight timing loops.
Here’s the output from the BenchmarkPolledTimer module:
How many loop iterations can we achieve in 100 ms ?
Using millis(), managed 55984 iterations, average loop time = 1786ns (143 CPU cycles)
Using micros(), managed 145441 iterations, average loop time = 688ns (55 CPU cycles)
Using PolledTimer, managed 266653 iterations, average loop time = 375ns (30 CPU cycles)
API Documentation
- group polled_timer
Polled interval timers.
Unnamed Group
-
template<NanoTime::Unit unit>
using OneShotElapseTimer = PolledTimer::OneShot<PolledTimerClock, unit>
-
template<NanoTime::Unit unit>
using PeriodicElapseTimer = PolledTimer::Periodic<PolledTimerClock, unit>
-
using OneShotFastMs = OneShotElapseTimer<NanoTime::Milliseconds>
-
using PeriodicFastMs = PeriodicElapseTimer<NanoTime::Milliseconds>
-
using OneShotFastUs = OneShotElapseTimer<NanoTime::Microseconds>
-
using PeriodicFastUs = PeriodicElapseTimer<NanoTime::Microseconds>
-
using ElapseTimer = OneShotFastUs
-
template<NanoTime::Unit units>
using OneShotCpuCycleTimer = PolledTimer::OneShot<CpuCycleClockNormal, units>
-
template<NanoTime::Unit units>
using PeriodicCpuCycleTimer = PolledTimer::Periodic<CpuCycleClockNormal, units>
-
template<NanoTime::Unit units>
using OneShotCpuCycleTimerFast = PolledTimer::OneShot<CpuCycleClockFast, units>
-
template<NanoTime::Unit units>
using PeriodicCpuCycleTimerFast = PolledTimer::Periodic<CpuCycleClockFast, units>
-
using CpuCycleTimer = OneShotCpuCycleTimer<NanoTime::Nanoseconds>
-
using CpuCycleTimerFast = OneShotCpuCycleTimerFast<NanoTime::Nanoseconds>
-
typedef OneShotFastMs oneShotFastMs
-
typedef PeriodicFastMs periodicFastMs
-
typedef OneShotFastUs oneShotFastUs
-
typedef PeriodicFastUs periodicFastUs
Defines
-
namespace PolledTimer
Typedefs
-
template<class Clock, NanoTime::Unit unit_, bool IsPeriodic, typename TimeType>
class Timer : public NanoTime::TimeSource<Clock, unit_, TimeType> - #include <PolledTimer.h>
Template class to implement a polled timer.
If the interval is set to 0, the timer will expire immediately, and remain so:
canWait()
will return false. Callcancel()
to prevent the timer from ever expiring:canExpire()
will return false. Callstart()
to set the timer going with the previously set interval. Callreset()
to set the timer with a new interval.Note
Intervals and expiry use ‘tick’ values which are very efficient, whereas ‘time’ values must be computed so are very slow (~100+ cycles for uint32_t, considerably more for uint64_t). The class keeps a note of a ‘start’ time (in ticks) used to determine the
elapsed()
return value. An ‘interval’ value may be given is specified, then theexpired()
method returns true.- tparam Clock
Clock source
- tparam unit
Time unit for tick/time conversions
- tparam IsPeriodic
- tparam TimeType
Variable type to use (uint32_t or uint64_t)
A periodic timer will automatically restart when expired. A one-shot timer will remain expired until reset.
Public Functions
-
inline Timer(const TimeType &timeInterval = 0)
Create a Timer with optional expiry time.
- Parameters
timeInterval – Relative time until expiry
-
inline void start()
Start the timer.
-
template<uint64_t timeInterval>
inline void reset() Start the timer with a new expiry interval.
- Template Parameters
timeInterval – Time to expire after last call to start()
-
template<uint64_t timeInterval>
inline constexpr uint32_t checkTime() Check the given time interval is valid and return the corresponding tick count.
Note
If time interval is invalid fails compilation
- Template Parameters
timeInterval –
- Returns
uint32_t –
-
inline bool reset(const TimeType &timeInterval)
Start the timer with a new expiry interval.
- See
See
resetTicks()
- Parameters
timeInterval – Time to expire after last call to start()
- Returns
bool – true on success, false on failure
-
inline bool resetTicks(const TimeType &interval)
Start the timer with a new expiry interval.
Note
If time interval is 0, timer will expire immediately, and if it exceeds the maximum interval the timer will never expire.
- Parameters
interval – Clock ticks to expire after last call to start()
- Returns
bool – true on success, false if interval is out of range
-
inline void cancel()
Cancelling a timer means it will never expire.
-
inline NanoTime::Time<TimeType> elapsedTime() const
Get elapsed time since start() was last called.
-
inline TickType remainingTicks() const
Get ticks remaining until expiry.
-
inline bool expired()
Determine if timer has expired.
- Returns
bool –
-
template<class Clock, NanoTime::Unit unit_, bool IsPeriodic, typename TimeType>
-
template<NanoTime::Unit unit>