millis (crystal) accuracy & thoughts

I’m trying to make a sports timer with an Arduino and I’m finding the crystals appear to be wildly inaccurate. Comparing photos of the (3 different) Arduino vs. (several) handheld stopwatches, I get results like this over a period of 10 minutes:

Delta-s      delta-ms
-1.11       -1110
-1.22       -1220
-1.36       -1360
-1.21       -1210
-1.03       -1030
-0.48       -480
-0.31       -310
-0.72       -720
-0.06       -60
0.09        90
0.27        270

The timer code on the Arduino is simply reading millis() and displaying via SPI interface to a Sparkfun 4-digit 7-segment display. Relevant code below:

//declare globals 
volatile unsigned long current_time, *display_time;

void SpiTransfer(volatile char data) { 
  SPDR = data;                    // start the transmission
  while (!(SPSR & (1<<SPIF)));    // wait for the end
} 

void DisplayTime(void) {
  PORTB &= ~(1<<PORTB2);                   //set SPI_SS low
  SpiTransfer( (*display_time/600)%10 );   // thousands digit
  SpiTransfer( (*display_time/100)%6 );    // hundreds digit
  SpiTransfer( (*display_time/10)%10 );    // tens digit
  SpiTransfer( *display_time%10 );         // ones digit
  PORTB |= (1<<PORTB2);                    //set SPI_SS high
}

void setup(void) { 
  uint8_t junk;

  //setup spi in order to talk to 4-digit 7-segment display_time
  pinMode(SPI_MISO, INPUT);
  pinMode(SPI_MOSI, OUTPUT);
  pinMode(SPI_CLOCK, OUTPUT);
  pinMode(SPI_SS, OUTPUT);
  digitalWrite(SPI_SS, HIGH);
  //spi enabled, msb 1st, master, clk low when idle,
  //sample on leading edge of clk, system clock/64
  SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR1);
  junk = SPSR;
  junk = SPDR;

  //display ticking timer.
  display_time = &current_time;
  current_time = 0; 

  //offset between now and system time
  millis_offset = millis();
}

void loop(void) {
  //main loop
  while(1) { 
    //update display 
    current_time = (millis() - millis_offset)/100;
    DisplayTime();
}

I’ve tried using nonblocking delays to slow the calls to millis(), thinking that was the culprit, but I get similar results. Does the SPI call block the millis() timer interrupt?

I’m thinking of using a DS3231 (like a chronodot) to create an accurate 1Hz interrupt on the Arduino. Any thoughts? I’m open to ideas.

thx.

I have found the Arduino timekeeping to be within a couple of seconds a day. This is just getting the millis() output out to a seperate NTP controlled computer, once a minute, logging it over several months and doing the maths by hand. Present results show the Arduino has made out the last 4days to be 345,593.346 seconds long (it should be 345,600 seconds)(it was restarted after a software change just over 4 days back) this is fairly typical for my 3 Arduinos.

I am, seeing very similar drift. ~5 seconds at 20 min, when compared to a stop watch for start stop events. I am timing a motor powering on and off and need to be accurate to a tenth of a second. Has anyone found a fix for this?

It is hard to manufacture crystals with a beat that is 16.0000000 Mhz and usually the crystals used in the arduino have +- rating of 50 ppm. So if you need more accurate timing, you need to software adjust the millis() with your drift. Heat can throw the beat off, so temperature swing will affect the mhz rate. Crystals change the frequency due to aging also.

A voltage-controlled crystal oscillator (VCXO) is used when the frequency of operation needs to be adjusted only finely. The frequency of a voltage-controlled crystal oscillator can be varied only by typically a few tens of parts per million (ppm), because the high Q factor of the crystals allows "pulling" over only a small range of frequencies.

To the more intressed read wikipedia http://en.wikipedia.org/wiki/Crystal_oscillator

David

Are those Arduinos using actual crystals? Or are any of them controlled with the 3-terminal ceramic resonators? Resonators have a much larger frequency tolerance than crystals.

I am timing a motor powering on and off and need to be accurate to a tenth of a second. Has anyone found a fix for this?

You may want to use a Digital timer rather then trying to get the arduino to handle it. You should be able to integrate a digital timer with the arduino if you need to keep a log of the times. http://www.automationdirect.com/adc/Overview/Catalog/Relays_-z-_Timers

Duemillenova and Mega have crystals at least (I have a mega, there is spk16.000y crystall there)

those small arduinos, and some copies have resonators

David

assuming that the temperature is constant!
find out the drift of your crystal by running a timer loop against some accurate timing device (preferably one that you can start and stop with the arduino) and let it run for a few hours or days.
when you have that data (the drift is a constant value) you make a formula that states how many millisec it drifts pr. millisec
you add or deduct that value from every millisec counted

Voila! you have an accurate counter!

David

And try to use hand stopwatches over a few seconds is just crazy

  1. human reaction time on stopwatch (try starting and stopping two stop clocks at the same time in each hand)
  2. delay from pressing stop on arduino till it writes it to serial to display
  3. same for pressing start (the arduino must process some code)

And try to use hand stopwatches over a few seconds is just crazy

  1. human reaction time on stopwatch (try starting and stopping two stop clocks at the same time in each hand)
  2. delay from pressing stop on arduino till it writes it to serial to display
  3. same for pressing start (the arduino must process some code)

The above timer comparison was accomplished with no human input. I simply started both an arduino (with code posted above) and a stopwatch, placed them side-by-side, and took photos of them at approximately 1-minute intervals over a period of 10-minutes. I repeated this with several different types/models of stopwatches, all with similar results.

The results posted above are the differential between both timers.

Wildly inaccurate.

No pressing of buttons.

I think the problem is your code -- you should try using a timer interrupt rather than millis() if you care about the accuracy of your timing. That will give you a timing accuracy about on par with the crystal itself, which on the order of 100 ppm. You can get a 10 ppm crystal for a couple of bucks and swap it out if that's not good enough.

If you want better then that, be prepared to spend some coin.

I think the problem is more likely to be latency on the SPI display rather than inaccuracy in the crystal or millis routine.

Try the test with an LCD with parallel connection using the LiquidCrystal library.

or for a simple test, write a sketch that blinks an LED every ten minutes (longer would be better) and compare the blink intervalto the time on your stopwatch.

Lets be realistic here. As has already been observed, if we are talking about an application like casual timing for track and field (i.e. not official competition like Olympics, etc.) the crystal in your typical microcontroller (like Arduino) is probably sufficient. Your human error in hitting start/stop is greater than the error from a typical microprocessor crystal over 10 minutes.

I think you ignored looking at my data, because I'm seeing unacceptable timing errors for anything more accurate than an egg timer. ;)

Thanks for the RTC recommendations.

I'll try hooking up an lcd via parallel.

see reply #9.

Being from the future, I assumed you wouldn't need me to explain all this. ;)

A photo was taken of the arduino & stopwatch at approximately 1-minute intervals for 10 minutes. The columns represent the time difference between the 2 clocks. The difference between the clocks at the start had the arduino 1.11 seconds behind the stopwatch. After approx. 10-minutes, the arduino was 0.27 seconds ahead. I hope that explains the data.

I repeated the experiment using 3 different arduinos and several stopwatches with similar results.

It doesn't seem to make any sence to me either.

pushing data on spi and putting things on the serial will need interrupts. interrupts are "robbed time". are you sure your timekeeping is not bothered by these interrupts ?

I can tell you that here in the future, we expect proper data, just as they did back in 2010 (and 1910 and 1810, etc.) Proper data has an explanation for each line. Does your list represent readings at 1min, 2min, etc? Then say so. Even back in 2010, not doing so was called "unlabeled". Proper data is concise. It does not include the same number represented as seconds and milliseconds. Even back in 2010 repeating values was called "redundant".

Your responses have become redundantly useless.

I'll try the same test with an LCD hooked up via parallel (eliminating spi) and report back.

I promise to post concise labeled unredundant proper data in the future.

If you want to include an accurate RTC in your tests you could try this one: http://macetech.com/store/index.php?main_page=product_info&cPath=5&products_id=8&zenid=fbf0c0760af1303010f94d69e70a8661

As I see it the bottom line is that even the crappiest crystal is good for an accuracy of about 100ppm and most are a lot better.

Surely this is accurate enough, and if so the problem must be in the code.

I'm thinking of using a DS3231 (like a chronodot) to create an accurate 1Hz interrupt on the Arduino.

I gather then that you're timing a long event, not a drag race.

The RTCs with inbuilt TXCOs can be calibrated to about 2ppm and even uncalibrated are very good.

A couple of questions.

Is this a start-time-stop application? How many things are being timed? Do you need to display the time while the timer is running? How long is the event being timed? What accuracy do you need?


Rob

The methodology looks OK to me. The internal millis() timer shouldn't be subject to drift because of other code, but it seems that that his arduino are drifting about 1.3 s over 10 minutes (600s.) That's only 0.2% error, not completely "unreasonable" for a resonator, though better would be ... better.

I'm seeing unacceptable timing errors for anything more accurate than an egg timer.

0.2% error is unacceptable? I guess it would be nicer if it looked more regular...