Generic Interface to Teensy Timers
Switches to inplace_function for callbacks. Fixes a namespace issue when used together with the EncoderTool
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.
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
This is just a sync release to trigger an update of the Arduino library manager
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.
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.
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)
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.
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.
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
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()
{
}
#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()
{
}