std::chrono compliant, teensy_clock based on the cycle counter

luni

Well-known member
The last days I had a lot of fun exploring the std::chrono namespace which provides a new (well, >=c++11) abstraction of durations, time points and clocks.
As an outcome of all this I implemented a std::chrono compliant teensy_clock with the following features:

  • Internally, the clock tracks time as the number of clock cycles since the usual epoch (0::00h, 1970-01-01).
  • To get reasonable rollover times it extends the cycle counter ARM_DW_CYCNT a 64bit extend cycle counter
  • To correct for rollovers it uses the periodic timer from the SNVS_HPCR module (T=1s)
  • It can be synced to the on board RTC at startup or at any later time

Here a quick example showing what one can do with it. Basically it uses the teensy_clock to time a random delayMicroseconds() and displays the actual delay measured by the clock. After that it pretty prints the actual time/date.
Code:
#include "teensy_clock.h"
using namespace std::chrono;

typedef teensy_clock::time_point timePoint;               // just for the sake of less typing
typedef duration<float, std::micro> micros_f;             // float based microseconds (predefined type 'microseconds' is integer based)

void setup()
{
    teensy_clock::begin();                                // starts the 64bit cycle counter, and syncs the clock to the rtc (works with and without battery)
}

void loop()
{
    // demonstrate clock resolution --------------------------------------------------
    unsigned delay_us = random(10, 5000);
    Serial.printf("delayMicroseconds(%u)\n", delay_us);

    timePoint t_0 = teensy_clock::now();                  // get two timepoints 'delay_us' apart
    delayMicroseconds(delay_us);
    timePoint t_1 = teensy_clock::now();

    auto dt = duration_cast<micros_f>(t_1 - t_0);         // cast delta to microseconds (float)
    Serial.printf("dt = t_1-t_0: %7.2f µs\n", dt.count());

    // convert to C-API ---------------------------------------------------------------
    timePoint currentTime = teensy_clock::now();          // get current time
    time_t ct = teensy_clock::to_time_t(currentTime);     // convert C-API time_t
    Serial.printf("Current Time: %s\n", ctime(&ct));      // pretty print date/time

    delay(1000);
}
Printout:
Code:
delayMicroseconds(3099)
dt = t_1-t_0: 3099.08 µs
Current Time: Fri Oct 23 17:02:34 2020

delayMicroseconds(4945)
dt = t_1-t_0: 4945.08 µs
Current Time: Fri Oct 23 17:02:35 2020

delayMicroseconds(4714)
dt = t_1-t_0: 4714.08 µs
Current Time: Fri Oct 23 17:02:36 2020

delayMicroseconds(4384)
dt = t_1-t_0: 4384.08 µs
Current Time: Fri Oct 23 17:02:37 2020

delayMicroseconds(806)
dt = t_1-t_0:  806.08 µs
Current Time: Fri Oct 23 17:02:38 2020
...

Of course all of this can be done the usual way with ARM_DW_DYCNT and the C-API time functions. But I like the clean and flexible way std::chrono is handling this.

In case anyone is interested in some modern c++ gimmicks: Here the code: https://github.com/luni64/TeensyHelpers
I also did a more detailed description of the new concepts and the working of the clock in the User WIKI: https://github.com/TeensyUser/doc/wiki/fun-with-modern-cpp
 
Back
Top