TeensyTimerTool

It is supposed to do the second.

However, as joepasquariello mentioned, those functions are currently not implemented for the FTM timers. As a first fix I implemented the setNextPeriod() function which changes the next period after you call it. Implementing setPeriod() (i.e. hard stop the current timer and restart with a new period) is kind of difficult since all FTM Channels of one module use the same underlying counter. I'll see if I can work around this on the weekend.

You can find the new code in the "fixFTM" Branch on the gitHub repo.

@luni, it looks to me like the new setPeriod() in your fixFTM branch will do what you want. Because you have FTMEN=0 in the FTMx_MODE registers, and you are using output compare, writes to the CV registers take effect on the next FTM clock, so they "override" the current period. The code in start() does the right thing, i.e. add the new period (in clocks) to the current value of the FTM clock CNT.

Code:
    errorCode FTM_Channel::start()
    {
        ci->chRegs->CV = regs->CNT + ci->reload;     // compare value (current counter + pReload)
        ci->chRegs->SC &= ~FTM_CSC_CHF;              // reset timer flag
        ci->chRegs->SC = FTM_CSC_MSA | FTM_CSC_CHIE; // enable interrupts
        return errorCode::OK;
    }

    errorCode FTM_Channel::stop()
    {
        ci->chRegs->SC &= ~FTM_CSC_CHIE; // enable interrupts
        ci->chRegs->SC &= ~FTM_CSC_CHF;  // reset timer flag

        return errorCode::OK;
    }

    // errorCode FTM_Channel::setPeriod(float us)
    // {
    //     stop();
    //     ci->reload = ticksFromMicros(us);
    //     start();
    //     return errorCode::OK;
    // }

    errorCode FTM_Channel::setNextPeriod(float us)
    {
        ci->reload = ticksFromMicros(us);
        return errorCode::OK;
    }
 
Hi luni,

thanks for the follow up, I see the fixFTM branch and the new commit you pushed up to the repository. I noticed this commented-out block:

Code:
    errorCode FTM_Channel::setPeriod(float us)
    {
        stop();
        ci->reload = ticksFromMicros(us);
        start();
        return errorCode::OK;
    }

This looks like it has the functionality I want (going back to my original desire, to ensure that the period of the purple trace starts perfectly aligned with the pulse shown in the yellow trace)

Previously, I was calling stop() and then start() again, but I realized that this just temporarily pauses the timer and then un-pauses it again (which is not what I wanted).
 
This looks like it has the functionality I want (going back to my original desire, to ensure that the period of the purple trace starts perfectly aligned with the pulse shown in the yellow trace)
As mentioned above, the setPeriod functionality does not seem to work as intended (at least during my very first tests it didn't stop the period but did the same as SetNextPeriod()). I'll have a closer look on this and Joe's results on the weekend. Anyway, feel free to uncomment and see if it will work for your needs.

Can you elaborate a bit what you want to achieve in the end? Looks like you have got some external 1Hz signal and you want to sync a 10Hz signal to it? If so, I'd rather let an IntervalTimer running and use the external signal to finetune the frequency of it. I.e., something like this (modulo details):
  • Use the timer callback to save a current timestamp (e.g. using the cycle counter)
  • Use the pinInterrupt callback to calculate the time difference to the last timestamp.
  • Use this difference to adjust the Intervaltimer period on the fly. Maybe a simple PID would help to adjust the period.
 
  • Use the timer callback to save a current timestamp (e.g. using the cycle counter)
  • Use the pinInterrupt callback to calculate the time difference to the last timestamp.
  • Use this difference to adjust the Intervaltimer period on the fly. Maybe a simple PID would help to adjust the period.
Hi luni, this is an interesting idea, yeah a PID would be one way to achieve this, thank you!
 
I just published a new release of the TeensyTimerTool (v1.0.1) .

Two main changes:
  • To be compatible with the new toolchain (1.58beta) I needed to remove the capability to enter timer periods in frequency units (e.g. 12.3_kHz etc). Since this was a wild hack anyway I'm not too sorry. Hope that nobody used this feature in production code. The usual time based units (e.g. 42ms, 145.5us, 800ns etc) still work of course.
  • I changed the underlying callback system from std::function (quite heavy) to my new staticFunctional library. staticFunctional has a much smaller memory footprint and doesn't use any dynamic memory. Ideally, library users should not note any difference.
    Please note: The new system is only active if you use the new 1.58beta toolchain (GCC version >= v7). Using the old toolchain (GCC v5.4) the TimerTool will fall back to function based void(*callback)() callbacks automatically.

Please let me know if you find any incompatibilities or other bugs
 
Hello, I'm trying to integrate some other code that uses TeensyTimerTool into my project that uses TeensyThreads. The thread code no longer gets called. The following demonstrates this:

Code:
#include <Arduino.h>
#include "TeensyThreads.h"
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;

PeriodicTimer t1; // generate a timer from the pool (Pool: 2xGPT, 16xTMR(QUAD), 20xTCK)

void blinkthread() {
  while (1) {
    Serial.println("Thread - blinkthread()");
    threads.delay(500);
  }
}

void callback()
{
  Serial.println("Timer - callback()");
}

void setup() {
  delay(1000);
  t1.begin(callback, 50'000);
  threads.addThread(blinkthread);
}

void loop() {
}

What can I do so that they will work together in the same project? I'm running TTT 1.3.0 and TT 1.0.2 on a T4.1/T MicroMod. Many thanks.
 
I have absolutely no experience with TeensyThreads. Can it be that TeensyThreads uses the GPT1 timer for thread switching?

If you use a PeriodicTimer without specifiying the used timer module it uses timers from a pool, the content of which is defined here: https://github.com/luni64/TeensyTimerTool/wiki/Configuration#available-settings I.e. the first timer it uses is GPT1, then GPT2 etc.

Try specifiying the used module by e.g.

Code:
PeriodicTimer t1(PIT); // use one of the 4 PIT timer channels
PeriodicTimer t2(TCK); // use a software based timer
//...

You find a list of available timers here: https://github.com/luni64/TeensyTimerTool/wiki/Supported-Timers
 
I have absolutely no experience with TeensyThreads. Can it be that TeensyThreads uses the GPT1 timer for thread switching?

Yes, that's the reason. I tried the timer definition below, and it worked. However, the OP should be aware that print/println are no re-entrant, so it's not OK to call Serial.print/println from a task and also from an ISR, as the ISR may eventually step on the task. Generally speaking, don't use print/println in ISRs, and if you want to use them within a TeensyThreads task, you should either (a) surround them with lock/unlock statements (or whatever those are called in TeensyThreads), or (b) define a very long time slice for TeensyThreads so that it effectively becomes cooperative, and not preemptive. That has been discussed on some forum threads.

Code:
PeriodicTimer t1(TMR1); // First channel on TMR1 aka QUAD timer module. (TMR1 - TMR4, four 16bit channels each)
 
Hello,

today I updated some tools and libraries on my dev computer. Now projects don't compile anymore (VScode). It's related to TTT. Teensy 3.2 :

Code:
lib/TeensyTimerTool/src/TimerModules/RTC//RTC.cpp: In function 'TeensyTimerTool::ITimerChannel* TeensyTimerTool::RTC_t::getTimer()':
lib/TeensyTimerTool/src/TimerModules/RTC//RTC.cpp:24:35: error: 'IRQ_SNVS_IRQ' was not declared in this scope
   24 |             attachInterruptVector(IRQ_SNVS_IRQ, isr);
      |                                   ^~~~~~~~~~~~
make: *** [makefile:214: .vsteensy/build/lib/TeensyTimerTool/src/TimerModules/RTC//RTC.cpp.o] Error 1
make: *** Waiting for unfinished jobs....

Can someone help me with this please ?
Thank you,
Manu

P.S. : I use Teensyduino 1.58
 
Hello,

today I updated some tools and libraries on my dev computer. Now projects don't compile anymore (VScode). It's related to TTT. Teensy 3.2 :

Code:
lib/TeensyTimerTool/src/TimerModules/RTC//RTC.cpp: In function 'TeensyTimerTool::ITimerChannel* TeensyTimerTool::RTC_t::getTimer()':
lib/TeensyTimerTool/src/TimerModules/RTC//RTC.cpp:24:35: error: 'IRQ_SNVS_IRQ' was not declared in this scope
   24 |             attachInterruptVector(IRQ_SNVS_IRQ, isr);
      |                                   ^~~~~~~~~~~~
make: *** [makefile:214: .vsteensy/build/lib/TeensyTimerTool/src/TimerModules/RTC//RTC.cpp.o] Error 1
make: *** Waiting for unfinished jobs....

Can someone help me with this please ?
Thank you,
Manu

P.S. : I use Teensyduino 1.58

Seems that was a recent T_4.x specific change where IRQ_SNVS_IRQ exists and is used for a new RTC based timer.

@luni will need to #ifdef that - going back a version of TTT will get rid of that conflict, unless that were just edited out until it can be updated.
 
Sorry, as defragster mentioned I forgot to enable the RTC timer for T4x boards only. Should be fixed in 1.4.1. @Manu: can you try if it compiles now?
 
Hey Luni,

Is there a way to control the priority of timer callbacks using this library?
I haven't seen anything in this thread or in the documentation about this.
 
You can set the priority directly using the NVIC. Note that the priority can only be set for a complete timer module, not for the individual channels of a module. For example, if you set the priority of the TMR2 module, all 4 channels of this module will get the same priority. Here is an example of how to set the priority of GPT1, the 4 channels of TMR2 and the 4 channels of PIT:

C++:
#include "Arduino.h"
#include "TeensyTimerTool.h"

using namespace TeensyTimerTool;

PeriodicTimer T1(GPT1);
PeriodicTimer T2(TMR2);
PeriodicTimer T3(PIT);

void setup()
{
    T1.begin([]{Serial.println("GPT1");}, 100ms);
    T2.begin([]{Serial.println("TMR2-CH1");}, 50ms);
    T3.begin([]{Serial.println("PIT-CH1");}, 0.2s);

    NVIC_SET_PRIORITY(IRQ_GPT1, 128);
    NVIC_SET_PRIORITY(IRQ_QTIMER2, 16);
    NVIC_SET_PRIORITY(IRQ_PIT, 32);
}

void loop(){}
 
hi luni,

I've spent quite some days/hours reading much of this thread and your excellent wiki, then fiddling with various timer sketches to get to grips with how it all fits together. I'm in awe at how comprehensive the library & documentation are, much appreciated. This thread is a top example of the Teensy crowd at its best :)

It's a while since anyone posted, but I'm guessing many are still using TTT so hoping for an answer to below:

I was looking at the maximum timer periods when an obvious question arose for me: What are the minimum timer periods ? (i.e. maximum frequency for a toggled O/P).

I started with the simple sketch you posted at #93 that produced a 2MHz output and started winding the frequency up.

As shown in the first photo 1 it accurately produced 3MHz
t4_GPT_demo 3MHz.jpg

However to my surprise things quickly went awry at frequencies above that.
Requesting 4MHz resulted in 4.17 MHz (photo 2)
t4_GPT_demo 4MHz.jpg

Requesting 5MHz resulted in 3.33 MHz (!) and an ugly waveform (photo 3)
t4_GPT_demo 5MHz.jpg

Here is my demo sketch and the associated userConfig.h that it #includes
Code:
#include "Streaming.h"

#define OUTpin 4

#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;

PeriodicTimer t1(GPT1);

//nstexpr float f = 3'000'000;                            // Hz - produces 3 MHz @24 but 1.5MHz @150 !??
//nstexpr float f = 3'600'000;                            // Hz - produces 3.74 MHz !?
constexpr float f = 4'000'000;                            // Hz - produces 4.17 MHz !?
//nstexpr float f = 5'000'000;                            // Hz - produces 3.?? MHz !!

void setup(){
  pinMode(OUTpin,OUTPUT);
  pinMode( 30   ,OUTPUT);

  while (!Serial && millis() < 1000) ;
  Serial << "\n\n----- t4_GPT_demo.b -----\n";
 
  float semi_cycle = 1'000'000/f/2;                       // μs 
  t1.begin(toggle_CB, semi_cycle);                        // callback
 
  Serial << "USE_GPT_PIT_150MHz = ";
  if (USE_GPT_PIT_150MHz) Serial << "true\n";
  else                    Serial << "false\n";
 
  Serial << "frequency : " << _FLOAT(f/1000,0) << " kHz on pin " << OUTpin << '\n';
  Serial << "semi_cycle: " << _FLOAT(semi_cycle,2) << " μs (" << _FLOAT(semi_cycle*1000,0) << " ns)\n";
}

void loop(){}

void toggle_CB(){
  digitalToggleFast(OUTpin);
}

Code:
#pragma once

#include "boardDef.h"

namespace TeensyTimerTool{
  #if defined(ARDUINO_TEENSY_MICROMOD) || defined(ARDUINO_TEENSY40) || defined(ARDUINO_TEENSY41)
      TimerGenerator* const timerPool[] = {GPT1, GPT2, TMR1, TMR2, TMR3, TMR4, TCK};
  #endif

  constexpr unsigned timerCnt = sizeof(timerPool) / sizeof(timerPool[0]);
  //--------------------------------------------------------------------------------------------
  constexpr int TMR_DEFAULT_PSC = PSC_128;  // PSC_1, PSC_2, PSC_4 ... PSC_128, clock = 150MHz

  constexpr bool USE_GPT_PIT_150MHz = true;

  constexpr unsigned NR_OF_TCK_TIMERS = 20; // How many TCK timers shall be available

  #define YIELD_TYPE  YIELD_STANDARD

  #define USE_TIME_LITERALS
}

Here is the Serial output for 3 Mhz then 4 MHz.
I noticed the Serial output was interrupted/didn't complete when 4MHz requested.
Maybe interruprts are overloading somehow?
t4_GPT_demo.b.output.JPG


So it seems there's a hard limit somewhere just above 3MHz.

With USE_GPT_PIT_150MHz = true we know the maximum period is 28.63s (= 2^32/150E6). Theoreticallly the minumum is 6.67ns (1/150E6). That's obviously not going to happen but given a processor running @ 600Mhz I was expecting a maximum frequency of say 20 or 50 MHz, or higher.

How would I generate higher frequencies?
 
At some point the interrupt processing load is going to be too high for the T4 to handle. It's not a "hard" limit, but I think you're bumping up against it. To generate higher frequencies, use the PWM hardware, which can be FlexPWM or QuadTimer, depending on which pin you choose. I'm not sure if both have exactly the same capability. I think of FlexPWM as being the most flexible and capable. The FlexPWM clock frequency is 150 MHz, and I think it works correctly up to at least 25 MHz. Keep in mind that would be 6 clocks per PWM period. The next values would 150/5 = 30 MHz, 150/4 = 37.5 MHz, etc.
 
Thanks Joe, happy with that. I will definitely look into PWM / FlexPWM later.

Meanwhile I believe I've stumbled on an inconsistency in the way the library handles the PIT timer.

With USE_GPT_PIT_150MHz = false and the timer set up with "period", or "semi_cycle" (s) for a toggled output, the resulting frequency f=1/2s as expected.

However if I set USE_GPT_PIT_150MHz = true and repeat, the output frequency is too low at
f = 1/2s/factor where factor = 150Mhz / 24Mhz = 6.25. For example:

frequency requested: 500kHz
frequency observed: 80kHz

If I then set the timer up with an adjusted period x = 6.25s the output frequency is the expected value.

This does not happen with the GPT, as it responds correctly to changes in USE_GPT_PIT_150MHz

Can someone else verify this, please. I'm suggesting it be resolved so the rather un-intuitive compensation factor is no longer required.

Here is my test sketch:
Code:
#include "Streaming.h"

#define OUTpin  4

#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;

PeriodicTimer t1(PIT); // 1 PIT x 4 channels, allocated on use

constexpr float f = 500'000; // 500 kHz

void toggle_CB() { digitalToggleFast(OUTpin); }

void setup(){
  pinMode(OUTpin,OUTPUT);

  while (!Serial && millis() < 1000) ;
  Serial << "\n\n----- t4_PIT_demo.d -----\n";

  Serial << "Clock (fc): ";
  if (USE_GPT_PIT_150MHz) Serial << "150 Mhz [Tc: " << 1E3/150 << " ns]\n";
  else                    Serial << " 24 Mhz [Tc: " << 1E3/24  << " ns]\n";

  Serial << "frequency (f): " << _FLOAT(f/1E3,0) << " kHz\n";

  float semi_cycle = 1E6/f/2;      // μs

  // this compensation factor is only required for PIT, not for GPT
  float factor = 1.00;
  if (USE_GPT_PIT_150MHz) factor = 6.25;   // = 150/24
  float x = semi_cycle*factor; 

  Serial << " semi_cycle: " << _FLOAT(semi_cycle,2) << " μs\n";
  Serial << "[x = s*" << _FLOAT(factor,2) << " μs]\n";
  Serial << "timer begin: period (x) = " <<  _FLOAT(x,2) << "μs\n";

  t1.begin(toggle_CB, x);                        // callback
  }

void loop(){}
and here are the serial outputs for the two cases:
t4_PIT_demo_500kHz_24_150.JPG

In both runs above the output observed on my CRO was 500KHz.
 
Sorry, that was a bug. Can you try version 1.4.3?

The following code correctly generates 500kHz signals on pin 0 and 1 using the PIT and GPT1 modules. It works for 24Mhz and 150MHz clock frequency settings.
C++:
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;

PeriodicTimer pitTimer(PIT);
PeriodicTimer gptTimer(GPT1);

void setup()
{
    for (unsigned pin : {0, 1}) pinMode(pin, OUTPUT);

    auto dT = 1us;
    gptTimer.begin([] { digitalToggleFast(1); }, dT);
    pitTimer.begin([] { digitalToggleFast(0); }, dT);     
}

void loop() {}
 
Last edited:
gptTimer.begin( [] { digitalToggleFast(1); }, dT );
@luni, I gather this syntax is a called a lambda? I will remember it as a clear and simple example of how to use it to define a function "inline" rather than separately. Do you have any insight into why this syntax is called "lambda", or why it uses square brackets "[]"? I'm trying to relate the syntax to something I already know to help me and understand and remember the syntax.
 
@joepasquariello. Yes this is a lambda expression. Here some explanation about the origin of the name: https://www.baeldung.com/cs/lambda-functions .

Actually the c++ syntax for lambdas is quite simple and logical. Here the full (AFAIK) syntax of a lambda expression:
Code:
[capture list](parameters) -> return_type {body}
Inside the square brackets is a list of variables that the lambda captures. That is, it stores its current values along with the function body. The parentheses contain the parameters of the function (as in a normal function definition). The braces contain the function body (as in a normal function definition). The value after the -> is its return type.

If your lamda doesn't use parameters, you can omit the parentheses. If the compiler can infer the return type, you do not need to specify it.

Here an example:
C++:
 int i = 17;
 int j = 42;
 auto myLambda = [i,j](float f){return f*(i+j);};
 Serial.println(myLambda(3));

Which prints: 177.00

Some time ago I wrote something about lambdas as callbacks in the teensy user wiki: https://github.com/TeensyUser/doc/wiki/callbacks
The TeensyTimerTool documentation also contains some information: https://github.com/luni64/TeensyTim...lambda-expressions-and-callbacks-with-context

Hope that helps
 
Last edited:
Back
Top