Releases: luni64/TeensyTimerTool
v1.3.0 internals and bug fixes
New (streamlined) callback system
This version replaces the heavy std::function based callback system with a smaller implementation (https://github.com/luni64/staticFunctional). The change should be transparent to users, so please report any observed incompatibilities or peculiarities.
You can still configure the library to use a traditional function pointer based void(*callback)(void)
callback system. But, with the new smaller staticFunctional::function based system there should be no reason to do so.
Please note: staticFunctional requires the new toolchain introduced with Teensyduino 1.58 beta. Older teensyduino versions will fall back to the function pointer based 'void(*callback)()' system automatically.
Compatibility to new Teensyduino toolchain (gcc 11.3)
To be compatible to the new gcc 11.3 toolchain I needed to remove the frequency uitility (timers accepted 3.45_MHz , 17kHz etc as period parameter). The corresponding header (frequency.h) was a wild hack which is not accepted anymore by the new gcc. Lets hope that future implementations of allow frequency types in addition to duration types.
v0.4.3
v0.4.1
New features:
- Added a new 64bit timer (TCK_RTC) which is based on the built in real time clock instead of the cycle counter which is used for the other TCK timers. The TCK_RTC might be useful for applications where smaller drift of the RTC crystal is important. (e.g. https://forum.pjrc.com/threads/68062-Teensy-4-1-RTC-get-milliseconds-correctly)
Resolution of the timer is 1/32.678 kHz = 30,052µs
The example shows how to use this timer to print out the relative drift between the 32kHz RTC crystal the the main 24MHz crystal.
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;
PeriodicTimer t1(TCK_RTC);
constexpr uint32_t period = 500'000; //µs
void callback()
{
static uint32_t start = micros();
static uint32_t idx = 0;
uint32_t now = micros() - start;
uint32_t expected = idx++ * period;
int32_t delta = now - expected;
float drift = 1E6 * delta / expected; // ppm
Serial.printf("t: %d µs, rel. drift: %.2f ppm\n", now, drift);
}
void setup(){
t1.begin(callback, period);
}
void loop(){
}
-
Added setPeriod and setNextPeriod to the TCK, PIT, GPT and TMR timers.
- setPeriod tries to change the current period to a new value. This of course only works if the new period is longer than the old period or the
change request comes early enough in the current period. In case the change request comes 'too late' the current period will end, the
callback will be called and the next period has the correct period - setNextPeriod sets the new period after the current period. This never generates a wrong period in between (as can happen with setPeriod)
- setPeriod tries to change the current period to a new value. This of course only works if the new period is longer than the old period or the
-
Destructors should work correctly now. I.e. you can use the timers as local variables. Leaving the scope while the timer runs will gracefully stop it and release allocated hardware resources.
Misc
- Large restructuring of the code. This might have generated bugs. PLEASE report any unusual behavior in the issues section.
Frequency literals
Just a quick update to add frequency literals to the list of possible arguments for the begin()
functions of periodic timers. (Please note the required underscore prefix to the literals)
timer.begin(isr, 100_kHz); // instead of 10 (µs)
timer.begin(isr, 0.25_MHz); // instead of 4 (µs)
timer.begin(isr, 5_kHz + 10_Hz); // instead of 100'200 (µs)
Compatibility to c++14 std::chrono_literals
This is just a small update to be compatible to the std::chrono::duration
and the corresponding nice user defined literals. I.e. instead of passing in microseconds to begin()
and trigger()
, you can now also use std::chrono::duration
s.
Here some examples:
timer[0].trigger(10ms); // 10 ms
timer[1].trigger(0.5s + 10ms); // 510 ms
timer[2].trigger(2.5 * 0.3s + 20000us / 2); // 760 ms
timer[3].trigger(milliseconds(50) + microseconds(5000)); // 55ms
which makes a much nicer interface.
Direct triggering of OneShotTimers
New Features
The OneShotTimers now provide a possibility to directly trigger them with a precalculated reload value. Since the normal trigger function involves a float calculation to convert the trigger time to the counter reload value it makes sense to precalculate it if you need to repeatedly trigger a signal with high frequency. This helps reducing processor load and timing errors for the slower processors like T3.2 which don't have a floating point unit. For a T4 the savings are quite insignificant.
void setup()
{
pinMode(1, OUTPUT);
OneShotTimer t1(GPT1); // use GPT1 for the test
t1.begin([] { digitalWriteFast(1, LOW); });
// normal triggering -----------------------------------------------------
for (int i = 0; i < 3; i++)
{
digitalWriteFast(1, HIGH); // set pin
t1.trigger(10); // reset after 10µs
delayMicroseconds(50);
}
delayMicroseconds(100);
// direct triggering -----------------------------------------------------
uint32_t reload;
t1.getTriggerReload(10, &reload); // precalculate the reload value for a 10µs delay time
for (int i = 0; i < 3; i++)
{
digitalWriteFast(1, HIGH); // set pin
t1.triggerDirect(reload); // reset after 10µs
delayMicroseconds(50);
}
}
void loop()
{
}
-> For a T3.2 @96mhz the time to trigger reduces by about 2µs which can be significant for high speed applications.
64 bit TCK Timer
The 32bit cycle counter (ARM_DWT_CYCCNT) which is used for the TCK timers overflows quite fast (e.g. every 7s on a T4@600MHz). I therefore added a variant of the TCK timers which extends ARM_DWT_CYCCNT to 64bit (v0.2.0). This allows for periods up to to full 2^32 microseconds (4295 seconds, 71.6 minutes). The new timer works on all T3.x and T4.x boards. I'll add the documentation to the TeensyTimerTool WIKI in the next days.
Here a small T4 sketch which prints out the overflow duration of the timers currently available with the TeensyTimerTool. The sketch was run in standard configuration, i.e., F_CPU=600MHz, GPT & PIT @24MHz, QUAD @150MHz with prescaler: PSC_AUTO.
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;
PeriodicTimer t1(TCK);
PeriodicTimer t2(TCK64); // <-- new
PeriodicTimer t3(GPT1);
PeriodicTimer t4(TMR3);
PeriodicTimer t5(PIT);
PeriodicTimer blink(TCK);
void empty(){}
void setup()
{
while (!Serial) {}
t1.begin(empty, 1'000);
t2.begin(empty, 1'000);
t3.begin(empty, 1'000);
t4.begin(empty, 1'000);
t5.begin(empty, 1'000);
Serial.printf("TCK: %8.3f seconds\n", t1.getMaxPeriod());
Serial.printf("TCK64: %8.3f seconds\n", t2.getMaxPeriod()); // <-- new
Serial.printf("GPT(@24MHz) %8.3f seconds\n", t3.getMaxPeriod());
Serial.printf("QUAD(PSC_AUTO) %8.3f seconds\n", t4.getMaxPeriod());
Serial.printf("PIT(@24MHz) %8.3f seconds\n", t5.getMaxPeriod());
pinMode(LED_BUILTIN, OUTPUT);
blink.begin([] { digitalToggle(LED_BUILTIN); }, 300'000); // heart beat
}
void loop()
{
}
And here the output
TCK: 6.711 seconds
TCK64: 4294.967 seconds
GPT(@24MHz) 178.957 seconds
QUAD(PSC_AUTO) 0.056 seconds
PIT(@24MHz) 178.957 seconds
Added start and stop functionality to TCK, TMR, GPT and PIT
Start / Stop Timers
Quick Example:
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;
PeriodicTimer t1;
void setup()
{
while (!Serial) {}
TeensyTimerTool::attachErrFunc(ErrorHandler(Serial)); // optional, print errors on Serial
pinMode(13, OUTPUT);
t1.begin([] { digitalToggleFast(13); }, 50'000); //Blink
delay(2000); // stop timer after 2s
t1.stop();
delay(2000); // restart after 2s
t1.start();
}
void loop()
{
}
Begin Timer in Stopped mode
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;
PeriodicTimer t1;
void setup()
{
while (!Serial) {}
TeensyTimerTool::attachErrFunc(ErrorHandler(Serial)); // optional, print errors on Serial
pinMode(13, OUTPUT);
t1.begin([] { digitalToggleFast(13); }, 50'000, false); // Prepare timer but dont start (last parameter = false)
delay(2000); // start timer after 2s
t1.start();
}
void loop()
{
}