Varying IntervalTimer() interval?

Status
Not open for further replies.

neltnerb

Well-known member
I have a project where I am using an IntervalTimer to do high accuracy square wave signal generation. It's a Teensy 3.6 but I'd like it to work on Teensy LC as well.

It works great, but I am wondering if it is possible to change the interval after the IntervalTimer.begin() function has been called. I expect this to be rare.

I looked through the library and don't see something like a UpdateInterval() function. My read is that I'm passing the microsecond interval as a float for improved resolution, which calls beginCycles, which sets some hardware registers.

I see that "channel->LDVAL = cycles" is probably where the actually setting of the timer register happens, but I'm not sure where the channel object is defined.

This was my attempt to take the relevant portion of the library and manually do it inside my function.

Code:
float NewInterval = 500000./StrobeFrequency;
if (NewInterval > 0 && NewInterval <= UINT32_MAX / (F_BUS / 1000000.0)) {
  uint32_t cycles = (float)(F_BUS / 1000000) * NewInterval - 0.5;
  if (cycles >= 36) {
    channel->LDVAL = cycles;
  }
  else Serial.println("ERROR: Interval Cannot be Set.");
}
else Serial.println("ERROR: Interval Cannot be Set.");

Any simple way to do this? I can tolerate a glitch when changing the timer comparator.
 
  1. Since you can tolerate glitches, you can simply call IntervalTimer.begin(...) with the new value again. It will stop the timer and restart it with a the new LDVAL value
  2. Quick and dirty: you can make KINETISK_PIT_CHANNEL_t *channel; public in IntervalTimer.h which gives you direct access to the register block of the PIT (timer->channel->LDVAL, timer->channel->TCTRL etc...)
  3. More clean: You can get a pointer to the register block by:
    Code:
    KINETISK_PIT_CHANNEL_t* channel = KINETISK_PIT_CHANNELS + ((IRQ_NUMBER_t)timer - IRQ_PIT_CH0);
    Note that if you stop the timer and restart it afterwards IntervalTimer might attach to another PIT. So make sure that you update channel after every call to Intervaltimer.begin()
 
Oh, just calling begin again. That seems to work fine, thanks.

I did try Option 3, but this time timer is undefined. I assume that's a reference to the timer that's been assigned to the interval timer automatically, but I've not dug into the KINETISK code so I'm not really sure how this timer allocation magic is supposed to work.

It does seem like it might be nice to be able to do this within the library, ideally with double buffering. But I suppose for most applications watching an elapsedMicros is safer. I just want sub-microsecond accuracy on the periods and not sure how else to count nanosecond (or cycle number) accurate timing without messing around with timer registers manually.
 
If option 1 works for you I strongly recommend to use it.

-------
Sorry for the incomplete code explaining option 3. Here a complete sketch tested on a T3.2. It blinks the LED and slowly increases the blink frequency.

Code:
IntervalTimer timer;
KINETISK_PIT_CHANNEL_t* channel;

//-------------------------------------------------
void ISR()
{
  digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN));  // just toggle the LED
}

//-------------------------------------------------
uint32_t calcReload(uint32_t micros)
{
  return  (F_BUS / 1000000) * micros;   // don't need float precision here
}

//-------------------------------------------------
uint32_t currentInterval = 500000; // 0.5 sec

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

  timer.begin(ISR, currentInterval);                                     // let teensyduino grab a free timer and do all the initialization work...
  channel = KINETISK_PIT_CHANNELS + ((IRQ_NUMBER_t)timer - IRQ_PIT_CH0); // ... and get hold of the register block for timer allocated by teensyduino
}

//-------------------------------------------------
elapsedMillis stopwatch;

void loop()
{
  // decrease the timer interval every 250ms by 10ms.
  if (stopwatch > 250)
  {
    stopwatch = 0;
    if (currentInterval > 10000) // 10ms
    {
      currentInterval -= 10000; // decrease interval by 10ms
    }
    else currentInterval = 500000; // restart

    channel->LDVAL = calcReload(currentInterval); // load the reload register with new value
  }
}
 
Fantastic, thanks for the detailed explanation! It sounds like "timer" was the interval timer object, missed that bit. I didn't notice that you could typecast the IntervalTimer object as an IRQ number, that's fascinating. Someday I'll have to delve deeper to try to understand what's going on there, I suspect the code is slightly over my head.
 
Status
Not open for further replies.
Back
Top