skip navigational linksPJRC
Shopping Cart Download Website
Home Products Teensy Blog Forum
You are here: Teensy Teensyduino Timing IntervalTimer

PJRC Store
Teensy 4.1, $31.50
Teensy 4.0, $23.80
Teensy
Main Page
Hardware
Getting Started
Tutorial
How-To Tips
Code Library
Projects
Teensyduino
Reference

IntervalTimer

IntervalTimer uses interrupts to call a function at a precise timing interval.

Advanced programming is required to properly use IntervalTimer, because your function runs as an interrupt. See below for details.

IntervalTimer is generally recommended for use only in libraries and advanced applications which require highly precise timing. Usually the Metro library or an elapsedMillis variables are easier to use, because they avoid the pitfalls of interrupt programming.

Compatibility

IntervalTimer is supported only on 32 bit boards: Teensy LC, 3.0, 3.1, 3.2, 3.5, 3.6, 4.0 & 4.1.

Up to 4 IntervalTimer objects may be active simultaneuously on Teensy 3.0 - 4.1. Teensy LC has only 2 timers for IntervalTimer.

The tone function and several libraries use an IntervalTimer. Libraries known to use an IntervalTimer include DmxSimple, Adafruit_VS1053, NewPing, FreqCount, RadioHead (only some uses), ShiftPWM, SoftPWM, Talkie, VirtualWire, MsTimer2 & FlexiTimer2.

For Teensy 2.0 and Teensy++ 2.0, the TimerOne & TimerThree and FlexiTimer2 libraries provide similar capability.

IntervalTimer Usage

IntervalTimer myTimer;

Create an IntervalTimer object. You may create as many IntervalTimers as needed, but only a limited number may be active simultaneously. Normally IntervalTimer objects should be created as global variables.

myTimer.begin(function, microseconds);

Begin calling the function. The interval is specified in microseconds, which may be an integer or floating point number, for more highly precise timing. This functions returns true if successful. False is returned if all hardware resources are busy, used by other IntervalTimer objects.

myTimer.priority(number);

Set the interrupt priority level, controlling which other interrupts this timer is allowed to interrupt. Lower numbers are higher priority, with 0 the highest and 255 the lowest. Most other interrupts default to 128. As a general guideline, interrupt routines that run longer should be given lower priority (higher numerical values).

myTimer.update(microseconds);

Change the interval. For an active IntervalTimer, the current timing interval completes as scheduled, then this new interval is automatically used for the next timing interval. To immediately begin a new interval, without completion of the current interval as scheduled, use begin(). For an inactive IntervalTimer, this function does nothing.

myTimer.end();

Stop calling the function. The hardware resource becomes available for use by other IntervalTimer objects.

Example Program

// Create an IntervalTimer object 
IntervalTimer myTimer;

const int ledPin = LED_BUILTIN;  // the pin with a LED

void setup() {
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
  myTimer.begin(blinkLED, 150000);  // blinkLED to run every 0.15 seconds
}

// The interrupt will blink the LED, and keep
// track of how many times it has blinked.
int ledState = LOW;
volatile unsigned long blinkCount = 0; // use volatile for shared variables

// functions called by IntervalTimer should be short, run as quickly as
// possible, and should avoid calling other functions if possible.
void blinkLED() {
  if (ledState == LOW) {
    ledState = HIGH;
    blinkCount = blinkCount + 1;  // increase when LED turns on
  } else {
    ledState = LOW;
  }
  digitalWrite(ledPin, ledState);
}

// The main program will print the blink count
// to the Arduino Serial Monitor
void loop() {
  unsigned long blinkCopy;  // holds a copy of the blinkCount

  // to read a variable which the interrupt code writes, we
  // must temporarily disable interrupts, to be sure it will
  // not change while we are reading.  To minimize the time
  // with interrupts off, just quickly make a copy, and then
  // use the copy while allowing the interrupt to keep working.
  noInterrupts();
  blinkCopy = blinkCount;
  interrupts();

  Serial.print("blinkCount = ");
  Serial.println(blinkCopy);
  delay(100);
}

Interrupt Context Issues

IntervalTimer will call your function from interrupt context. Because it can interrupt your program at any moment, special design is necessary to share data with the rest of your program.

Many ordinary functions are not designed to work properly from interrupt context. String objects should be avoided. A general guideline is to keep your function short and avoid calling other functions if possible.

Variables usually need to be "volatile" types. Volatile tells the compiler to avoid optimizations that assume variable can not spontaneously change. Because your function may change variables while your program is using them, the compiler needs this hint. But volatile alone is often not enough.

When accessing shared variables, usually interrupts must be disabled. Even with volatile, if the interrupt changes a multi-byte variable between a sequence of instructions, it can be read incorrectly. If your data is multiple variables, such as an array and a count, usually interrupts need to be disabled for the entire sequence of your code which accesses the data.

Known end() Issue

A known issue exists with the end() function. If end() is called after the timer has caused another interrupt to become pending, but the interrupt function has not yet run, the function could be called immediately after the next begin() starts the timer again.

If end() is called within the interrupt function, it should be called as soon as possible within that interrupt function.

Future Teensyduino releases are expected to fix this issue. Programs should not depend on the behavior where the next begin() results in the interrupt function immediately called.

Other Libraries

More delays can be implemented on the FTM timers with this library. However, it is limited to much shorter intervals.

The TeensyTimerTool library can be used to access timers on all 32 bit Teensy boards.