Skip to content

Commit b484d6d

Browse files
committed
Add section on timer range checking to docs. and add PolledTimer::checkTime() method.
1 parent 333fe56 commit b484d6d

File tree

2 files changed

+70
-8
lines changed

2 files changed

+70
-8
lines changed

Sming/Core/PolledTimer.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -114,32 +114,50 @@ class Timer : public NanoTime::TimeSource<Clock, unit_, TimeType>
114114
* @param interval Time to expire after last call to start()
115115
*/
116116
template <uint64_t timeInterval> __forceinline void IRAM_ATTR reset()
117+
{
118+
auto ticks = checkTime<timeInterval>();
119+
resetTicks(ticks);
120+
}
121+
122+
/**
123+
* @brief Check the given time interval is valid and return the corresponding tick count
124+
* @tparam timeInterval
125+
* @retval uint32_t
126+
* @note If time interval is invalid fails compilation
127+
*/
128+
template <uint64_t timeInterval> constexpr uint32_t checkTime()
117129
{
118130
auto time = this->template timeConst<timeInterval>();
119131
time.check();
120132
constexpr auto ticks = time.ticks();
121133
static_assert(ticks < maxInterval(), "Polled time interval too long");
122-
resetTicks(ticks);
134+
return ticks;
123135
}
124136

125137
/**
126138
* @brief Start the timer with a new expiry interval
127139
* @param interval Time to expire after last call to start()
140+
* @retval bool true on success, false on failure
141+
* @see See `resetTicks()`
128142
*/
129-
__forceinline void IRAM_ATTR reset(const TimeType& timeInterval)
143+
__forceinline bool IRAM_ATTR reset(const TimeType& timeInterval)
130144
{
131-
resetTicks(this->template timeToTicks(timeInterval));
145+
return resetTicks(this->template timeToTicks(timeInterval));
132146
}
133147

134148
/**
135149
* @brief Start the timer with a new expiry interval
136150
* @param interval Clock ticks to expire after last call to start()
151+
* @retval bool true on success, false if interval is out of range
152+
* @note If time interval is 0, timer will expire immediately, and if it
153+
* exceeds the maximum interval the timer will never expire.
137154
*/
138-
__forceinline void IRAM_ATTR resetTicks(const TimeType& interval)
155+
__forceinline bool IRAM_ATTR resetTicks(const TimeType& interval)
139156
{
140157
start();
141158
this->interval = interval;
142-
neverExpires = (interval >= Clock::maxTicks());
159+
neverExpires = (interval > maxInterval());
160+
return !neverExpires;
143161
}
144162

145163
/**

docs/source/information/timers.rst

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,50 @@ Here's the output from the :source:`BenchmarkPolledTimer <tests/HostTests/app/te
102102
Using micros(), managed 145441 iterations, average loop time = 688ns (55 CPU cycles)
103103
Using PolledTimer, managed 266653 iterations, average loop time = 375ns (30 CPU cycles)
104104
105+
Timer range
106+
-----------
107+
108+
The maximum interval for a timer varies depending on the clock source and the selected prescaler.
109+
The count may be further restricted by hardware. For example, Timer1 only provides a 23-bit count
110+
which means with a /16 prescaler (the default for HardwareTimer) it overflows after only 1.67 seconds.
111+
112+
It's therefore important to check that timers are being used within their valid range. There are generally
113+
two ways to do this:
114+
115+
Runtime checks
116+
This means checking function/method return values and acting accordingly.
117+
This often gets omitted because it can lead to cluttered code, which then leads to undiagnosed
118+
bugs creeping in which can be very difficult to track down later on.
119+
120+
With a polled timer, you'd use one of these methods to set the time interval::
121+
122+
bool reset(const TimeType& timeInterval);
123+
bool resetTicks(const TimeType& interval);
124+
125+
They both return true on success.
126+
127+
Static checks
128+
These checks are performed during code compilation, so if a check fails the code won't compile.
129+
In regular 'C' code you'd do this using #if statements, but C++ offers a much better way using
130+
`static_assert <https://en.cppreference.com/w/cpp/language/static_assert>`__.
131+
132+
To reset a polled timer and incorporate a static check, use this method::
133+
134+
template <uint64_t timeInterval> void reset();
135+
136+
Note that timeInterval cannot be a variable (even if it's *const*) as the compiler must be
137+
able to determine its value. It must therefore be *constexpr compatible*.
138+
139+
You can use static checking to pre-validate the range for a timer before using it::
140+
141+
timer.checkTime<10>();
142+
timer.checkTime<10000>();
143+
144+
This will throw a compile-time error if the timer is not capable of using intervals in the
145+
range 10 - 10000 microseconds (or whichever time unit you've selected). It doesn't add any
146+
code to the application. If the code compiles, then you can be confident that the timer
147+
will function as expected and you don't need to check return values.
148+
105149
Clocks
106150
------
107151

@@ -111,12 +155,12 @@ also consider the CPU cycle counter to have a selectable prescaler of 1 or 2, de
111155
whether it's running at 80MHz or 160MHz.
112156

113157
A *Clock* definition is a class template which allows us to query timer properties and perform time
114-
conversions for a specific timer configuration. These definitions can be found in `Platform/Clocks.h`.
158+
conversions for a specific timer configuration. These definitions can be found in :source:`Sming/Platform/Clocks.h`.
115159

116160
.. note:: A Clock is a purely virtual construct and does not provide any means to configure the hardware,
117161
although it does provide the *ticks()* method to obtain the current timer value.
118162

119-
Clocks are made more useful by *TimeSource*, a generic class template defined in *NanoTime.h*.
163+
Clocks are made more useful by *TimeSource*, a generic class template defined in :source:`Sming/Core/NanoTime.h`.
120164
This provides methods to convert between time values and tick values for a specific time unit.
121165

122166
Let's say we want a microsecond source using Timer2::
@@ -146,4 +190,4 @@ For debugging purposes you can print a description::
146190

147191
Serial.println(t2source.toString()); // "Timer2Clock/5MHz/32-bit/microseconds"
148192

149-
See *NanoTime.h* for further details.
193+
See :source:`Sming/Core/NanoTime.h` for further details.

0 commit comments

Comments
 (0)