TeensyTimerTool

Forum keeps timing me out. Lost my post. Got the signal running fast enough for me. Now trying to make the period variable. You advised me to use setPeriod. In the wiki, there is also a setNextPeriod. I tried setPeriod, and there seems to be sort of a delay. Guess that is understandable because the change is asynchronous. Is setNextPeriod supposed to eliminate the delay?

My first attempt at dynamic change of the timer was not good. Forgot to change based on integer periods, and did it based on time. Oops. This resulted in changing the period before the period was up. Not pretty. Maybe tomorrow I will be able to make that work.

My lathe does have linear encoders, but in the beginning, I will only work on spindle position and lead screw position. I want to be able to cut different threads with out changing gears, or to move the carriage very slow for a good finish. If that works out well, I will integrate in the linear encoders. But first, I need to take baby steps.
 
@clinker8 - not had that 'timeout' hit here in some time. After logging back into that thread check the lower left corner of the 'reply' box for "restore" as content seems to be stored on some time period during edits.
 
Seem to be a slow writer :) The restore content got the post before, but not the one I was writing. Oh well, managed to recover!
 
Forum keeps timing me out. Lost my post. Got the signal running fast enough for me. Now trying to make the period variable. You advised me to use setPeriod. In the wiki, there is also a setNextPeriod. I tried setPeriod, and there seems to be sort of a delay. Guess that is understandable because the change is asynchronous. Is setNextPeriod supposed to eliminate the delay?

setPeriod changes the period immediately. I.e., it will end the current period and restart the timer with the new period. setNextPeriod is supposed to set a new period after the current one is done to prevent partial periods. There should be no delay (other than the time needed for executing ) in both functions.

I kind of remember having issues with setNextPeriod some time ago but didn't look into it deeper. So, let me know if setNextPeriod is not working as expected.
 
setPeriod changes the period immediately. I.e., it will end the current period and restart the timer with the new period. setNextPeriod is supposed to set a new period after the current one is done to prevent partial periods. There should be no delay (other than the time needed for executing ) in both functions.

I kind of remember having issues with setNextPeriod some time ago but didn't look into it deeper. So, let me know if setNextPeriod is not working as expected.
setNextPeriod doesn't seem to work at all in my code, but setPeriod seems ok. Ok in the sense that at least there are 4 pulses of the same period, then 4 more at a different period. The last long pulse is sort of strange, but I think it is still "forward". setNextPeriod does not change the waveform, at least not in my code.
Code:
#include "TeensyTimerTool.h"

using namespace TeensyTimerTool;

// for Teensy 4.1 only

PeriodicTimer t1(GPT1);
PeriodicTimer t2(GPT2);

float maxperiod, periodincrement;
int maxcount, gpt2period;
volatile bool forward;
volatile float period;
float initperiod;

#define A 1
#define B 2

// Callbacks

void a_ns( )
{
  static int state=0;
  switch (state & 3) {
    case 0:
    { 
      if (forward) { digitalWriteFast(A, HIGH); digitalWriteFast(B, LOW); }
      else { digitalWriteFast(A, LOW); digitalWriteFast(B, HIGH); }
      break;
    }
    case 1:
    { 
      if (forward) { digitalWriteFast(A, HIGH); digitalWriteFast(B, HIGH); }
      else { digitalWriteFast(A, HIGH); digitalWriteFast(B, HIGH); }
      break;
    }
    case 2:
    {
      if(forward) { digitalWriteFast(A, LOW); digitalWriteFast(B, HIGH); }
      else { digitalWriteFast(A, HIGH); digitalWriteFast(B, LOW); }
      break;
    }
    case 3:
    {
      digitalWriteFast(A, LOW); digitalWriteFast(B, LOW); 
      // same for both fwd and reverse
      break;
    }
  }
  state++;
}

void slowtime()
{
  bool test1;
  test1 = false;

  period += periodincrement;
  if (period > maxperiod) 
  {
    period = initperiod;
    digitalWriteFast(3, HIGH);  // add timing mark to detect beginning
    delayNanoseconds(20);
    digitalWriteFast(3, LOW);
  }
  if (test1)
  {
    t1.setNextPeriod(period);
    t2.setNextPeriod(period*16);
  }
  else
  {
    t1.setPeriod(period);
    t2.setPeriod(period*16);
  }
}

void setup() {
  // put your setup code here, to run once:
  for(unsigned pin=1; pin<=3; pin++) pinMode(pin, OUTPUT);
  
  Serial.begin(9600);
  //while(!Serial) delay(10);
  
  initperiod = 1.0;
  gpt2period = 16*initperiod;  // interrupt every 4 full pulses
  
  period = initperiod;  // initial start interrupt period us.  Real period is 4x.
  periodincrement = 1.0;
  maxperiod = 100*periodincrement;  // 
  Serial.print("max period = "); Serial.print(maxperiod);
  forward = true;
  t1.begin( a_ns, period); // default time is in us.  Min value is 0.25.
  t2.begin( slowtime, gpt2period);
}

void loop() {
  // put your main code here, to run repeatedly:
  

}
if test1 is true, (using setNextPeriod) the code fails to create the varying waveform. If false, (using setPeriod) the waveform looks ok.

PXL_20220507_152012879_500.jpgPXL_20220507_152100932_500.jpg
 
Thanks, I'll have a look. But this will take some time (working on another project currently...)
Quick Idea, can you change the GPT1/GPT2 to PIT and see if it works then?
 
Thanks, I'll have a look. But this will take some time (working on another project currently...)
Quick Idea, can you change the GPT1/GPT2 to PIT and see if it works then?

Tried using PIT. Ok with setPeriod, follows the pattern. Something is off with setNextPeriod, It stalls for 1.4ms, then behaves as expected when the pulses get longer. It is like it is either blocking somewhere, or just taking too long for a while. After 1.4ms, setNextPeriod seems to work. And it continues to work until the max quarter period is set (100us). Then resets to 1us but stays at 1us for 1.4ms. Do not get this behavior with setPeriod.

First file: setPeriod. Second file: setNextPeriod
SDS01setPeriod.jpgSDS03setNextPeriod.jpg
 
This all looks complicated and requiring knowledge of interrupts and low-level coding. Is there a simple way to do PWM like AnalogWrite? I'm just trying to do 16,17, or 18-bit dimming of LED's. The default max of AnalogWrite is actually 15 bits, even when resolution is set to 16.
 
Sorry, the TimerTool does not implement any PWM functionality. However, the features of the 32bit GPT timers look promising for this.
This all looks complicated and requiring knowledge of interrupts and low-level coding
Yes
 
Hi Luni,

I'm using the TTT in one-shot mode to manage a screen timeout on an attached OLED but it isn't behaving as expected.

I've used the following code (this is just part of my code, not all of it)

Code:
OneShotTimer timeoutTimer(GPT2); // used to determine when to shut the screen off

timeoutTimer.begin(screenTimeoutISR);

uint32_t timeoutMillis[3] = {10000, 1800000, 3600000}; // timeout time in mS, 15mins, 30mins, 60mins (900000, 1800000, 3600000)

void setup() {
  if (timeOutOn) {
    timeoutTimer.trigger(timeoutMillis[timeoutTime] * 1000);
  }
}

void screenTimeoutISR() {
  Serial.println("SCREEN TIMED OUT");
  timeoutTimer.stop();
  screenTimedOut = 1;
  clearTheDisplay();
}

If timeoutTime == 0 then the timeoutTimer will trigger fine after 10 seconds but if i increase timeoutMillis[0] to 900,000 then the timeoutTimer is triggered right after it starts.

So what is the maximum time for a OneShotTimer on GPT2? I assumed that since the GPT2 timer is 32-bit the maximum value would be 4,294,967,296 but using a time of 900,000 * 1000 = 900,000,000 doesn't seem to work properly. I'm assuming that it is because of the time i am setting in the trigger.

Cheers

NM
 
p.s. i've been able to determine that the largest value i can put into timeoutMillis[0] is 178,956 which is 178,956,000 when it is put into the timeoutTimer.trigger(); function. Any values larger than this cause the timeoutTimer to trigger instantly after being called.

I'm also assuming that the value for a OneShotTimer.trigger() is in microseconds.

e.g.
Code:
timeoutTimer.trigger(178,956,000);
 
p.s. i've been able to determine that the largest value i can put into timeoutMillis[0] is 178,956 which is 178,956,000 when it is put into the timeoutTimer.trigger(); function. Any values larger than this cause the timeoutTimer to trigger instantly after being called.

I'm also assuming that the value for a OneShotTimer.trigger() is in microseconds.

The default clock frequency for T4.x GPT and PIT timers in TeensyTimerTool is 24 MHz, so max period in 32 bits is (2^32)/24e6 = 178.95697 seconds. There are both 32- and 64-bit software-based timers (TCK). At 64 bits, the CycleCounter64 timers have max period ~630 years. For non-critical timeouts in the range of minutes to hours to days, you could just use elapsedMillis(), though you would have to poll it, as opposed to having a callback on timeout.
 
The default clock frequency for T4.x GPT and PIT timers in TeensyTimerTool is 24 MHz, so max period in 32 bits is (2^32)/24e6 = 178.95697 seconds. There are both 32- and 64-bit software-based timers (TCK). At 64 bits, the CycleCounter64 timers have max period ~630 years. For non-critical timeouts in the range of minutes to hours to days, you could just use elapsedMillis(), though you would have to poll it, as opposed to having a callback on timeout.

Hi Joe,

i am looking to use a callback routine instead of polling using elapsedMillis if i can. I've been weeding elapsedMillis/Micros out of my project and if i can get this working it'll stay out.. I had been trying to use the TCK64 timer but it acts funny unlike when i try using the GPT or TMR timers.

I am using the timer callback routine to turn off the screen on an attached OLED screen. I can turn the screen back on by pressing any of the buttons on the project. Sometimes when i push a button when the screen is on the screen turns off and sometimes when the screen is off and i push a button the screen turns back on then instantly turns off again. This doesn't happen with the other hardware timers. I'm not sure if there is any specific thing i am doing wrong with my code or extra measures i need to use with the software timers that i am not employing.

Cheers

NM
 
i am looking to use a callback routine instead of polling using elapsedMillis if i can. I've been weeding elapsedMillis/Micros out of my project and if i can get this working it'll stay out.. I had been trying to use the TCK64 timer but it acts funny unlike when i try using the GPT or TMR timers.

I am using the timer callback routine to turn off the screen on an attached OLED screen. I can turn the screen back on by pressing any of the buttons on the project. Sometimes when i push a button when the screen is on the screen turns off and sometimes when the screen is off and i push a button the screen turns back on then instantly turns off again. This doesn't happen with the other hardware timers. I'm not sure if there is any specific thing i am doing wrong with my code or extra measures i need to use with the software timers that i am not employing.

I can understand the desire to have timers with callbacks, but it sounds like you may not have all of the necessary logic to stop/start/reset those timers when events occur, such as pushing a button. The timers are nice because your code appears less cluttered, but the complexity is still there, and is maybe harder to get right and to keep right as your design evolves. If you have a state machine or a loop of some sort to manage things at a high level, having a few elapsedMillis objects might not be so bad. The right tool for the job, as they say.
 
I can understand the desire to have timers with callbacks, but it sounds like you may not have all of the necessary logic to stop/start/reset those timers when events occur, such as pushing a button. The timers are nice because your code appears less cluttered, but the complexity is still there, and is maybe harder to get right and to keep right as your design evolves. If you have a state machine or a loop of some sort to manage things at a high level, having a few elapsedMillis objects might not be so bad. The right tool for the job, as they say.

Yeah, been thinking that myself. Oh well, back to the way it was.

Thanks for your help Joe.

Cheers

NM
 
Hi Luni,
So what is the maximum time for a OneShotTimer on GPT2? I assumed that since the GPT2 timer is 32-bit the maximum value would be 4,294,967,296 but using a time of 900,000 * 1000 = 900,000,000 doesn't seem to work properly. I'm assuming that it is because of the time i am setting in the trigger.

See here https://github.com/luni64/TeensyTimerTool/wiki/Basic-Usage#finding-out-the-maximum-timer-period for information how to find out the max supported timer period for the various hard/software timers available.
Here the results for a T4.x @600MHz
Code:
TCK:               5.00 seconds
TCK64:           634.20 years
TCK_RTC:         634.20 years
GPT(@24MHz)      178.96 seconds
TMR(PSC_AUTO)     55.92 milliseconds
PIT(@24MHz)      178.96 seconds

... i am looking to use a callback routine instead of polling using elapsedMillis if i can. I've been weeding elapsedMillis/Micros out of my project and if i can get this working it'll stay out.. I had been trying to use the TCK64 timer but it acts funny unlike when i try using the GPT or TMR timers.

What exactly do you mean with "acts funny"? I'm always interested in improving my libraries, so, can you post some ideally small code showing the effect? As long as you don't have code which blocks for very long times (seconds) they should work without issues.


sometimes when i push a button when the screen is on the screen turns off and sometimes when the screen is off and i push a button the screen turns back on then instantly turns off again.
As joepasquariello also mentioned, this sounds like some general logic issue in your code? Again, if you want me to look into it I'd need some compileable code.

Yeah, been thinking that myself. Oh well, back to the way it was.
Actually, simply switching off a screen after a given time does sound like a perfect use case for a oneShot timer. Basically, the TCK versions are doing the same as you would do with polling a elapsedMillis, they are just polling the cycleCounter right before loop is called (actually whenever yield() is called) and invoke your callback when the count is larger than the precalculated target. So, nothing mystical about them.
 
What exactly do you mean with "acts funny"? I'm always interested in improving my libraries, so, can you post some ideally small code showing the effect? As long as you don't have code which blocks for very long times (seconds) they should work without issues.

As joepasquariello also mentioned, this sounds like some general logic issue in your code? Again, if you want me to look into it I'd need some compileable code.

It's probably my code. I have about 3k lines of code and i'm using 5 or 6 timers so i'm probably doing something wrong somewhere. I just went back to elapsedMillis for this as, based on your comments, it seemed to not be any different than using a TCK timer.

Basically, the TCK versions are doing the same as you would do with polling a elapsedMillis, they are just polling the cycleCounter right before loop is called (actually whenever yield() is called) and invoke your callback when the count is larger than the precalculated target.

What is the yield() you mention here?

Cheers

NM
 
yield() is the one 'runtime' feature that goes with Arduino.

When called directly, and called with calls to delay() and on each exit of loop() before it is called again.

What yield does is check an existing SerialEvent() functions in a non-interrupt fashion in those cases.

TCK based timers take advantage of yield() being called regularly (when they are and loop doesn't stall where delay and yield are not called) to update timer status and process requested responses.
 
setPeriod changes the period immediately. I.e., it will end the current period and restart the timer with the new period.

Question: my understanding was that my timer t1 would be reset when setPeriod() is called, but that appears to not work with the FTM timer(s) - or am I doing something wrong?

Context: I want to re-start a PeriodicTimer (1st channel of FTM1, specifically) any time I detect a rising edge on one of my GPIO lines. These rising edges come once per second, see yellow scope trace.
I want my periodicTimer (t1) to go through 10 whole cycles (purple trace) of 100 ms each, and neither "lead" nor "lag" the rising edge on the HW input pin (yellow). i.e. they should be "in sync".

I have set FTM_DEFAULT_PSC to PSC_128 in the defaultConfig.h.

Code:
using namespace TeensyTimerTool;
using namespace std::chrono_literals;

PeriodicTimer t1(FTM1);

constexpr auto period_ms = 50ms;

// some more functions, etc.

void my_hw_interrupt_callback() {
    digitalWriteFast(t1_output_pin, HIGH);  // always start on a rising edge
    t1.setPeriod(period_ms);  // setPeriod causes immediate restart, and sets the specified period (I think?)
        
    count = 0;
    count++;
  }

void my_timer_1_callback() {
    if (count <= 10) {
        bool t1_output_state = digitalReadFast(t1_output_pin);
        if (t1_output_state == HIGH) {
            digitalWriteFast(t1_output_pin, LOW);
          } else {
            digitalWriteFast(t1_output_pin, HIGH);
            count++;  // add to the count each time we output a rising edge
          }
      }
  }

void setup() {
    // some other setup things

    t1.begin(my_timer_1_callback, period_ms, true);  // initialize, and start right away
    attachInterrupt(digitalPinToInterrupt(my_hw_interrupt_pin), my_hw_interrupt_callback, RISING);
}

void loop() {}

not_synced_to_pulse.png
 
Last edited:
With a PeriodicTimer, the timer starts when you call begin(), so you could call t1.begin() in the hw_interrupt function instead of in setup(), and then stop the timer after 10 calls to timer_callback(). I think the issue may be that setPeriod(), doesn't stop/restart the timer, but rather sets the period for the *next* period.
 
Thanks joepasquariello,

With a PeriodicTimer, the timer starts when you call begin(), so you could call t1.begin() in the hw_interrupt function instead of in setup(), and then stop the timer after 10 calls to timer_callback()
...understood, and then I guess the next time the hw_interrupt is triggered, the call to t1.begin() will cause the t1 timer to start from zero again? I'm concerned that t1.begin() might be "slow" (I don't know what it needs to do under the hood to get the timer t1 initialized - maybe it's quick, maybe not ?)

I think the issue may be that setPeriod(), doesn't stop/restart the timer, but rather sets the period for the *next* period.
I think setNextPeriod does this
 
SetPeriod is supposed to change the period of a running timer, i.e. it is supposed to end the current period and start again with the new period (updatePeriod might have been a better name...)
SetNextPeriod is supposed to finish the current period and change to the new period after.

I don't use the FTMs a lot, definitely possible that something is wrong here.I'll have a look later today.

t1.begin() might be "slow" (I don't know what it needs to do under the hood to get the timer t1 initialized - maybe it's quick, maybe not ?)
This of course depends on your definition of slow/quick. begin(...) sets up the timer channel and the callback. I don't have a measurment available but I estimate that this should not take more than a small number of microseconds. You can also setup the timer without starting it (set the optional third parameter to false). You can then call its start() method to actually start it. start() only sets some bits in a register and should be as fast as possible.
 
Hi luni,

SetPeriod is supposed to change the period of a running timer, i.e. it is supposed to end the current period and start again with the new period
...when you say "end the current period", does this mean:
  1. wait for the remaining time in the current period to finish, then start again with the new period
  2. don't wait for the remaining time in the current period to finish, start again with the new period immediately
 
Hi luni,


...when you say "end the current period", does this mean:
  1. wait for the remaining time in the current period to finish, then start again with the new period
  2. don't wait for the remaining time in the current period to finish, start again with the new period immediately

I checked the source code, and it looks like setPeriod() is not implemented for FTM, so that may explain why that function is not doing what you want. It looks like t1.trigger() will do what you want, i.e. stop the current period and start a new one, so please try replacing t1.setPeriod(period) with t1.trigger().
 
Hi luni,
...when you say "end the current period", does this mean:
  1. wait for the remaining time in the current period to finish, then start again with the new period
  2. don't wait for the remaining time in the current period to finish, start again with the new period immediately
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.

Here some test code which will toggle pin0 every 5ms. After 200ms it changes the toggle period to 1ms. Pin 1 marks the time when the switch happened.
Code:
#include "Arduino.h"
#include "TeensyTimerTool.h"
using namespace TeensyTimerTool;

PeriodicTimer t1(FTM0);

void setup()
{
    TeensyTimerTool::attachErrFunc(ErrorHandler(Serial));  // always good to enable error messages when developing
    pinMode(0, OUTPUT);
    pinMode(1, OUTPUT);

    while (!Serial) {}

    t1.begin([] { digitalToggleFast(0); }, 5ms); // isr just toggles pin 0, start period 5ms

    delay(200);
    digitalToggleFast(1);
    t1.setNextPeriod(1ms);

    delay(200);
    t1.stop();
}

void loop()
{
}

Screenshot 2022-08-04 075957.jpg
 
Back
Top