Cajoling std::chrono Operations to Link On the Teensy platform

Updated on 2022-08-24

std::chrono doesn't work on the Teensy? Oh no! Here's how to fix it.

Introduction

Gosh I adore the Teensy, especially the 4.1. Who doesn't love that little powerhouse ARM Cortex M7 processor? It's a delightfully capable little monster, and easily makes the Teensy the fastest Arduino compliant platform available.

I'm pretty new to the platform and still finding my way around it. While doing so, I've found that it can be quirky at times.

One quirk is the missing _gettimeofday() function which you probably won't run into until you try to use The STL's timekeeping functionality under std::chrono.

I ran into this recently trying to use a cross platform (or so I thought!) MIDI timer I had implemented as part of my SFX library. I included it into my project and as soon as I started using it, the code failed to link properly.

SFX library

I felt that I should share the solution here so that others can find it down the road.

Background

std::chrono provides a myriad of functions for keeping time, including high resolution timing in a cross platform manner, which makes it attractive to use for IoT because it's available on most IoT platforms, even the lower end AVRs.

That's why it shocked me when I couldn't readily use this functionality on the Teensy. The problem is it lacks an implementation for _gettimeofday() in <sys/time.h> which std::chrono relies on.

We're going to implement enough to make precision timing work.

We do so using one of two methods - either using the Teensy's IntervalTimer, or using the realtime clock. There are a limited number of interval timers available, from 2 to 5 depending on which Teensy, and we need to use one. It requires us to use an ISR, so we have to be careful how we implement it. You can #define HIGH_PRECISION to use the interval timer, or use the much more efficient but slightly less precise (~30usec) RTC to implement it. For most purposes, the latter should be fine.

Using the Code

Put the following in the main .cpp or .ino file for your project:

#include <sys/time.h>
...
#ifdef HIGH_PRECISION
// stuff for making _gettimeofday() and std::chrono work
volatile uint64_t chrono_tick_count;
void chrono_tick() {
    ++chrono_tick_count;
}
IntervalTimer chrono_timer;
#endif // HIGH_PRECISION
...
void setup() {
#ifdef HIGH_PRECISION
    chrono_timer.begin(chrono_tick,1);
#endif // HIGH_PRECISION
    ...
}
...
#ifdef HIGH_PRECISION
extern "C" {
  int _gettimeofday( struct timeval *tv, void *tzvp )
  {
    // uptime in microseconds
    uint64_t t;
    noInterrupts();
    t=chrono_tick_count;
    interrupts();
    tv->tv_sec = t / 1000000;  // convert to seconds
    tv->tv_usec = ( t % 1000000 ) ;  // get remaining microseconds
    return 0;  // return non-zero for error
  } // end _gettimeofday()
}
#else
// from Paul Stoffregen
extern "C" int _gettimeofday(struct timeval *tv, void *ignore) {
  uint32_t hi1 = SNVS_HPRTCMR;
  uint32_t lo1 = SNVS_HPRTCLR;
  while (1) {
    uint32_t hi2 = SNVS_HPRTCMR;  // ref manual 20.3.3.1.3 page 1231
    uint32_t lo2 = SNVS_HPRTCLR;
    if (lo1 == lo2 && hi1 == hi2) {
      tv->tv_sec = (hi2 << 17) | (lo2 >> 15);
      tv->tv_usec = ((lo2 & 0x7FFF) * 15625) >> 9;
      return 0;
    }
    hi1 = hi2;
    lo1 = lo2;
  }
}
#endif

Remember to modify the "..." portions above - that's where your other code goes. Basically, just weave the above into your existing source.

History

  • 23rd August, 2022 - Initial submission
  • 25th August, 2022 - Added lower precision alternative