Getting std::chrono to work with the Teensy

honey_the_codewitch

Active member
I'm not sure where to put this, so here goes. I hope it's useful to someone.

I just figured out how to resolve the linker error with _gettimeofday() and to implement that function such that it will actually make std::chrono be able to do its job properly as well as link properly.

Code:
volatile uint64_t chrono_tick_count;
void chrono_tick() {
    ++chrono_tick_count;
}
IntervalTimer chrono_timer;
...
void setup() {
    chrono_timer.begin(chrono_tick,1);
    ...
}
...
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()
}

After adding this code to your main cpp, your actual std::chrono calls will function now rather than causing linker errors and not working.
 
This could also be done by reading the RTC hardware.

Code:
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;
  }
}
 
Thanks! I'll bear that in mind, though I really only use the STL on IoT sparsely, and when I do use chrono it's because I want a cross platform way to do precision timing. It works on most platforms I've tried out of the box. Teensy is the first one where I've had to do anything significant to cajole it. This technique I used works great for me, although I may take Paul's suggestion and use the RTC for this.
 
honey_the_codewitch said:
"It's a solution, just not a great one." & "It currently works with the Teensy, but the SDA reads are the trick. I don't know how to set that up." & "Teensy is the first one where I've had to do anything significant to cajole it."

Sounds like you need to temporarily forget all of your other platforms, ignore all of your past experiences (hard to do), & just dive into the capabilities provided by & for the Teensy. Paul & others have gone to great lengths to optimize the libraries provided by TeensyDuino so that they take advantage of the best capabilities available with the hardware designed into the Teensy versions & the PJRC peripherals available to go with, as well as a fair collection of popular third-party hardware.

By constantly trying to hand-jam (my impression) your view of the Arduino world (which certainly appears to be based upon a wealth of valuable experience), you will only be cheating yourself of being able to capitalize on the experience of others, & you're not really doing yourself any favors, but rather just developing an incorrect view of what the Teensy family of products are really capable of !! Open your mind & your projects to a wealth of opportunity provided by the Teensy hardware & supporting software & libraries !! Take a look at the examples for each library for detailed info on how to best take advantage of a particular capability.

I hope you'll take this as "constructive criticism & recommendations", rather than just "criticism" (which it is certainly not intended as) !!

Good luck & have some fun !!

Mark J Culross
KD5RXT
 
That's awesome advice for hobbyists, which I am not. I am evaluating the Teensy for the possibility of using Arm Cortex processors in future professional endeavors like I currently do with other platforms.

> , & just dive into the capabilities provided by & for the Teensy.

It sounds like you're asking me to rewrite my libraries? Nah, I don't think I will. It would cost too much to duplicate and retest all of my efforts, and if nothing else I would lose functionality (like if I were to use Teensy's graphics libraries instead of my own)

> By constantly trying to hand-jam (my impression) your view of the Arduino world (which certainly appears to be based upon a wealth of valuable experience)

I didn't even mention Arduino? This is about The STL, which is C++, which has nothing to do with Arduino whatsoever.

> Open your mind & your projects to a wealth of opportunity provided by the Teensy hardware & supporting software & libraries !!

"Tie your projects to the Teensy hardware specifically, and duplicate all your efforts" I guess if I had to, but currently I don't.

As far as my remarks on SDA reads. That's not Teensy specific. Any platform that uses display controllers like ST7789s will be subject to the same thing. Arduino, ESP-IDF, or R-PI or otherwise. It doesn't matter.
 
Last edited:
This technique I used works great for me, although I may take Paul's suggestion and use the RTC for this.

Your _gettimeofday() will give superior precision, the full resolution of struct timeval (1 us). But the cost is CPU overhead of 1 million interrupts per second!

The RTC hardware runs from the 32.768 kHz crystal, so its timing precision is limited to 30.5 us.
 
Paul, thanks for the information! I definitely will use your approach when I don't need it to the microsecond. In the application I wrote for this, the timing is critical, so I'll try your method and compare what I get, see if it's "good enough" - it's hard for me to know offhand because it's for MIDI clocks, but I think 30us may work.
 
I'll tell you now, I use The STL sparingly when I'm working with little machines. Heap fragmentation is an absolute killer, and then there are issues of certain 8-bit procs not fully supporting it for obvious reasons.

To that end I have reimplementations of vectors, hash tables, circular buffers and such that don't whip the heap so hard, and are stripped down besides to handle 80% of cases. (simple_vector can't delete items out of it for example)

However, when it comes to std::chrono, I've seen very little in the alternative for providing precision timing cross platform. So I use std::chrono whenever I need either to do performance benchmarking, or a cooperatively tasked precision timer so I don't have to write it for arbitrary platforms.

As such, std::chrono for me at least, is probably the bit of The STL I lean on the most, in the most places.

Not sure if that helps.

I do know that you will find more complicated libraries out there that probably could run on the teensy that use things like std::map and std::vector, or at least std::function
 
@luni - hopefully with future Teensyduino releases, creating a custom now() function won't be needed as described under "A simple Teensy clock"?
Great! Another step to modern c++ compatibility :) I gave it a quick try, works out of the box.

Here some demo code showing how to use it.

Code:
#include "Arduino.h"
#include <chrono>

// some typedefs to save typing
using namespace std::chrono;
using timePoint = system_clock::time_point;

void setup()
{
    pinMode(LED_BUILTIN, OUTPUT);
    while (!Serial) {}

    timePoint start = system_clock::now();

    // blink for 2.5 seconds
    while (system_clock::now() < start + 2.5s)  
    {
        digitalToggleFast(LED_BUILTIN);
        delay(100);
    }

    // Measure time
    timePoint t0 = system_clock::now();
    delay(123);
    timePoint t1 = system_clock::now();

    nanoseconds delta = t1 - t0;           // system clock measures time in nanoseconds
    Serial.println(delta.count() / 1'000); // print duration in microseconds

    // same without using knowledge of implementation details:
    auto delta_us = duration_cast<microseconds>(t1 - t0);
    Serial.println(delta_us.count());

    // pretty print current time using the standard c_API
    timePoint now   = system_clock::now();          // get current time
    time_t cApiTime = system_clock::to_time_t(now); // convert to time_t
    Serial.printf("%s", std::ctime(&cApiTime));     // pretty print date/time using C-API function
}

void loop()
{
}

which blinks for 2.5s and then prints:
Code:
123017
123017
Sun Aug 28 09:20:53 2022

However, since your implemenation uses the RTC output directly, the precision is only some 30.51µs (as you see in the 123017 result for measuring 123ms). Generating a RTC synced 1/F_CPU (1.66ns) precision chrono::system_clock is quite simple as shown in the link from above, but needs a timer (e.g. the 1s tick timer from the RTC) to handle the cycle counter overflow. This might be too expensive/intrusive for adding it to Teensyduino. If I find some time, I can pack the code for the 1.66ns std::chrono clock into a small lib.

Edit: Sorry, this https://github.com/TeensyUser/doc/wiki/implementing-a-high-resolution-teensy-clock is the correct link for the ns clock
 
Last edited:
Back
Top