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
-
using
OneShotElapseTimer
= PolledTimer::OneShot<Timer2Clock, unit>¶
-
using
PeriodicElapseTimer
= PolledTimer::Periodic<Timer2Clock, unit>¶
-
using
OneShotFastMs
= OneShotElapseTimer<NanoTime::Milliseconds>¶
-
using
OneShotFastUs
= OneShotElapseTimer<NanoTime::Microseconds>¶
-
using
ElapseTimer
= OneShotFastUs¶
-
using
OneShotCpuCycleTimer
= PolledTimer::OneShot<CpuCycleClockNormal, units>¶
-
using
PeriodicCpuCycleTimer
= PolledTimer::Periodic<CpuCycleClockNormal, units>¶
-
using
OneShotCpuCycleTimerFast
= PolledTimer::OneShot<CpuCycleClockFast, units>¶
-
using
PeriodicCpuCycleTimerFast
= PolledTimer::Periodic<CpuCycleClockFast, units>¶
-
using
CpuCycleTimer
= OneShotCpuCycleTimer<NanoTime::Nanoseconds>¶
-
using
CpuCycleTimerFast
= OneShotCpuCycleTimerFast<NanoTime::Nanoseconds>¶
-
typedef OneShotFastMs
oneShotFastMs
¶
-
typedef OneShotFastUs
oneShotFastUs
¶
Defines
-
namespace
PolledTimer
¶ Typedefs
-
using
PolledTimer::OneShot = typedef Timer<Clock, unit, false, uint32_t>
-
using
PolledTimer::Periodic = typedef Timer<Clock, unit, true, uint32_t>
-
template <class Clock, NanoTime::Unit unit_, bool IsPeriodic, typename TimeType>
classTimer
: 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. - Template Parameters
Clock
: Clock sourceunit
: Time unit for tick/time conversionsIsPeriodic
: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 Types
-
template<>
usingTickType
= typename Clock::TickType¶
-
template<>
usingMargin
= NanoTime::TimeConst<Clock, NanoTime::Microseconds, POLLED_TIMER_MARGIN_US>¶
-
template<>
usingTicksConst
= TicksConst<Clock, ticks>¶
-
template<>
usingMaxClockTime
= typename Clock::template MaxTime<unit_>¶ Get the time corresponding to the maximum clock tick value.
-
template<>
usingTimeSource
= TimeSource<Clock, unit, TimeType>
-
template<>
usingTicks
= Ticks<Clock, T>
-
template<>
usingMaxTicks
= TicksConst<maxTicks_>
-
template<>
usingMaxTime
= typename MaxTicks::template TimeConst<unit>
Public Functions
-
Timer
(const TimeType &timeInterval = 0)¶ Create a Timer with optional expiry time.
- Parameters
timeInterval
: Relative time until expiry
-
void
start
()¶ Start the timer.
-
template <uint64_t timeInterval>
voidreset
()¶ Start the timer with a new expiry interval.
- Template Parameters
timeInterval
: Time to expire after last call to start()
-
template <uint64_t timeInterval>
constexpr uint32_tcheckTime
()¶ Check the given time interval is valid and return the corresponding tick count.
- Note
- If time interval is invalid fails compilation
- Template Parameters
timeInterval
:
- Return Value
uint32_t
:
-
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()
- Return Value
bool
: true on success, false on failure
-
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()
- Return Value
bool
: true on success, false if interval is out of range
-
void
cancel
()¶ Cancelling a timer means it will never expire.
-
bool
canExpire
() const¶
-
bool
canWait
() const¶
-
bool
expired
()¶ Determine if timer has expired.
- Return Value
bool
:
Public Static Functions
-
static constexpr TickType
maxInterval
()¶
-
static constexpr Margin
margin
()¶
-
static constexpr BasicRatio32
ticksPerUnit
() Number of clock ticks per unit of time.
- Return Value
BasicRatio32
: Result as a rational fraction
-
static Ratio32
ticksPerUnit
(Unit unit) Get ticks per unit as a Ratio object.
- Return Value
BasicRatio32
:
-
static constexpr MaxClockTime
maxClockTime
()
-
static constexpr TimeConst<time>
timeConst
() Obtain a TimeConst type representing the given time quantity.
- Note
- Use methods of TimeConst to obtain corresponding tick count, etc.
- Template Parameters
time
:
- Return Value
TimeConst
:
-
static constexpr TicksConst<ticks>
ticksConst
() Class template defining a fixed tick quantity.
- Note
- Use methods of TickConst to obtain corresponding time values, etc.
- Template Parameters
ticks
:
- Return Value
TicksConst
:
-
static constexpr Time<TimeType>
maxCalcTime
() The maximum time value supported by timeToTicks without overflowing.
- Return Value
TimeType
: Passing values larger than this totimeToTicks()
will truncate at maximum value
-
static constexpr Ticks<Clock, TimeType>
maxCalcTicks
() The maximum tick value supported by ticksToTime without overflowing.
- Return Value
TimeType
: Passing values larger than this toticksToTime()
will truncate at maximum value
-
static Ticks<Clock, TimeType>
timeToTicks
(TimeType time) Get the number of ticks for a given time.
- Parameters
time
:
- Return Value
TimeType
: Tick count, rounded to the nearest tick
-
static constexpr uint64_t
timeToTicks
() Get the number of ticks for a given time.
- Template Parameters
time
:
- Return Value
uint64_t
: Tick count, rounded to the nearest tick
-
static constexpr uint64_t
ticksToTime
() Get the time for a given number of clock ticks.
- Template Parameters
ticks
:
- Return Value
TimeType
: Time count, rounded to the nearest unit
-
static Time<TimeType>
ticksToTime
(TimeType ticks) Get the time for a given number of clock ticks.
- Parameters
ticks
:
- Return Value
TimeType
: Time count, rounded to the nearest unit
-
static String
toString
()
-
static constexpr const char *
typeName
()
-
static constexpr uint32_t
frequency
()
-
static constexpr MaxTicks
maxTicks
()
-
template <Unit unit>
static constexpr MaxTime<unit>maxTime
()
-
template <Unit unit, typename TimeType>
static constexpr TimeSource<unit, TimeType>timeSource
() Create a Time Source for this Clock.
- Template Parameters
unit
:TimeType
:
-
using
-
using