RTC & elapsedMillis

Status
Not open for further replies.

duff

Well-known member
Hi All,

I need to put a time stamp on data coming into the usb or serial port and wanted to get milliseconds resolution so I came up with this sketch as a test case. Does anyone think this would not work as reliable source for millisecond resolution on the RTC?

Code:
/*
  append milli seconds to my time stamp using elapsedMillis whenever a USB packet is recieved.
 */

#include <Time.h>

#define LED   13

elapsedMillis rtcMilliSeconds;

void rtc_seconds_isr() {
  rtcMilliSeconds = 0;// zero out millisecond counter
  digitalWrite(LED, !digitalRead(LED));
}

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(0);
  while(!Serial);
  delay(100);
  setSyncProvider(getTeensy3Time);
}

void loop() {
  if ( Serial.available() ) {
    String packet;
    while ( Serial.available() ) {
      packet += (char)Serial.read();
      delay(1);
    }
    String myTime = digitalClockDisplay(rtcMilliSeconds-1);
    myTime += " | ";
    Serial.print(myTime);// print current time + milliseconds
    Serial.print(packet);// print data packet
  }
}

String digitalClockDisplay(elapsedMillis millis_t) {

  String time;

  if (hour() < 10) time += 0;
  time += hour() ;//< 10 ? '0' + hour() : hour();
  time += ":";

  if (minute() < 10) time += 0;
  time += minute() ;//< 10 ? '0' + minute() : minute();
  time += ":";

  if (second() < 10) time += 0;
  time += second();
  time += ".";

  if (millis_t < 100) time += 0;
  if (millis_t < 10) time += 0;
  time += (int)millis_t;

  return time;
}

time_t getTeensy3Time() {
  RTC_IER |= 0x10;// set rtc seconds interrupt
  rtcMilliSeconds = 0;
  NVIC_ENABLE_IRQ(IRQ_RTC_SECOND);// enable interrupt
  return Teensy3Clock.get();
}

This sketch shows the elaspedMillis count value at interrupt time, I notice it over shoots every once and awhile by 1 millisecond, but works pretty well. I guess I'll have to collect some data to see if tracks fine at different temps.
Code:
/*
  print elapsedMillis at seconds interrupt
 */

#include <Time.h>

#define LED   13

elapsedMillis rtcMilliSeconds;

void rtc_seconds_isr() {
  Serial.println(rtcMilliSeconds);
  //digitalWrite(LED, !digitalRead(LED));
  rtcMilliSeconds = 0;// zero out millisecond counter
}

void setup() {
  pinMode(LED, OUTPUT);
  Serial.begin(0);
  while(!Serial);
  delay(100);
  setSyncProvider(getTeensy3Time);
}

void loop() {
  
}

time_t getTeensy3Time() {
  RTC_IER |= 0x10;// set rtc seconds interrupt
  rtcMilliSeconds = 0;
  NVIC_ENABLE_IRQ(IRQ_RTC_SECOND);// enable interrupt
  return Teensy3Clock.get();
}
 
Last edited:
You could also consider using micros() if that level of precision is important to you.

As for the RTC, the series of experiments I have undertaken suggests that there is a pretty strong impact on the PPM error of the RTC if you stray far from the inversion point of the crystal. For example, on the one CFS206 I tested, the PPM error was about 6.8 at maximum around 25*C, dropping to zero around 40*C and 10*C. Crystals are frequently specified with a rather wide range of inversion temperatures and given that the correction function is to the second power, the impact can be large if your temperature varies a lot.

That said, a series of tests I did with an on-board temperature sensor (good to 0.3*C) and the CFS206 (sample of one, caveat emptor!) resulted in a correction function with an R^2 of 0.999+, which is pretty close to perfect. The key here is having a good temperature on board that stays with the crystal. Then, the correction function is always customized for the two devices.

FWIW, if you want to capture *exactly* when something came in, then I'd suggest using a PPS signal from a GPS to help keep your MCU on its toes. Simply interrupt whenever the PPS signal comes in and then correct the millis() readings for the last elapsed second (or ten seconds) accordingly.
 
Last edited:
unfortunately where this will be, I get no GPS signals, though the datalogger that the teensy talks too has wifi connectivity so I can have it output a timestamp every so often to keep the teensy's rtc in sync. I don't think using elapsedMillis is going to work either, for the simple reason it is not a volatile object. I already have seen the elapsedMillis variable not update right when packets coming in. I think I'm going to use PIT instead which doesn't exhibit this problem since I can declare a variable volatile to use in the RTC_Seconds ISR.

I was wondering in your tests, does the temperature correction factors hold up across different teensy's with the same crystal type? I mean do you have to redo your calibration for each teensy, if so can you compensate on the fly?
 
I was wondering in your tests, does the temperature correction factors hold up across different teensy's with the same crystal type? I mean do you have to redo your calibration for each teensy, if so can you compensate on the fly?

I have only tested one RTC crystal to date. I ended up writing a program that runs a pre-set number of measurements and while I slowly varyied the temperature of the board. Then, using a least-squares approach, the teensy calculates the factors required for the correction equation and finally writes them to EEPROM for later retrieval. You could use a similar approach to customize the program for each sensor and RTC crystal. That is key, IMO, to keep the same temperature measurement device and RTC crystal paired. That way, any offsets are hopefully kept constant and cancel out.

I used a bucket of carbon-dioxide ice (aka dry ice) to go to as low as -40*C and some vents in the home of my in-laws for the temperatures above 20*C. Other options could include a heating pad, hot water bottle, etc. A styrofoam cooler and a blanket / towel (to slow heat transfer) are great accessories. I used a 10 second time base for the GPS measurements, once to measure the speed of the MCU, another to measure the speed of the RTC crystal relative to the MCU.

I plan to compensate on the fly - there are a number of RTC registers that you can use at the lowest level of the machine or Paul's convenient compensation function. The former allow a more customized approach, the latter is much easier to implement (though over a longer time base). I plan on using the former in order to minimize error and the time prescaler.
 
Status
Not open for further replies.
Back
Top