Go Down

Topic: millis (crystal) accuracy & thoughts (Read 9752 times) previous topic - next topic


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


Dec 20, 2010, 09:33 am Last Edit: Dec 20, 2010, 09:33 am by graynomad Reason: 1
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 Gray aka the GRAYnomad www.robgray.com


Dec 20, 2010, 09:38 am Last Edit: Dec 20, 2010, 09:42 am by westfw Reason: 1
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...

Thanks for all of the responses so far.


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?

I am making a split timer, where the timer will run continuously with intervals tripped by an electronic switch.

One individual is timed over several intervals. The overall event could be from 15 minutes to several hours, with the splits lasting in the range of 1-10 minutes long.

I need to display the interval times.

I want at least a tenth-of-second accuracy, hundredths would be better.

Man, it looks like I was having a conversation with myself?


Even my crappy Uno with a resonator is good to 4 seconds a day (72 seconds fast after 20 days) .  As I've said before my Duemilanoves (even my Chinese knockoff) are much better.  The secret is not to count millis between it doing something but to work just with the big number since it was started.  


The secret is not to count millis between it doing something but to work just with the big number since it was started.

Yes, but he's been doing that all along.  From the first message:
Code: [Select]
void loop(void) {
 //main loop
 while(1) {
   //update display
   current_time = (millis() - millis_offset)/100;

Note that the divide by 100 is throwing away relatively important precision, and probably explains some of the non-linearity in the times reported in your first post.


If you need  1/10 or 1/100 accuracy, then you don't want a 1pps time source. You would use the 32KHz output on the ChronoDot or other RTC chips. I think the Arduino hardware interrupt pins would be able to handle it. Or set up one of the timers with the external clock option and set up an overflow at the appropriate time. The only difficulty is that an external RTC designed for timekeeping is not going to have a frequency evenly divisible by 10 or 100, so you'll have to just get close and live with some inaccuracy. For 1/100 you would see maybe +/- 0.3% of one count error.
Unique RGB LED Modules and Arduino shields: http://www.macetech.com/store

Dec 25, 2010, 02:27 am Last Edit: Dec 25, 2010, 02:28 am by JimEli Reason: 1
Running a 7-segment display directly with the following code solved my timing issues.

Code: [Select]

//7 segment timer test
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000
#include <avr/delay.h>

// SBI and CBI to set bits
#define sbi(port_name, pin_number)   (port_name |= 1<<pin_number)
#define cbi(port_name, pin_number)   ((port_name) &= (uint8_t)~(1 << pin_number))

int d[10];
//wiring millis value
extern volatile unsigned long timer0_millis;

void setup() {
 /* 7-segment pin alignment
  LCD - Arduino
   1  -  A0
   2  -  A1
   6  -  A2
   8  -  A3
   13 -  D0
   15 -  D1
   12 -  D2
   3  -  D3
   5  -  D4
   10 -  D5
   14 -  D6
 DDRC = DDRC | 0b0111111;      // Set data direction for port C (DIG1,...DP)
 PORTC = PORTC | 0b0011111;      // Initialize all digits off
 DDRD = DDRD | 0b11111110;      // Set data direction for port D (A,B,...,G)
 PORTD = PORTD | 0b00000000;      // Initialize all digits off

void loop() {
 // Main loop: update display
 int i;
 while(1) {
   for (i=0; i<4; i++) {
     //total time per loop ~5.5ms@16MHz
     display(d[5 - i], i);
     //this delay controls individual segment on-time/brightness, longer = brighter
     //clear display
     PORTC = PORTC | 0b0011111;;
     PORTD = 0;
   //this delay controls all segments off-time/brightness, shorter = brighter

void ExtractDigits(unsigned long num) {
 int i;
 i = 0;
 while(num > 0) {
   d[i++] = (int)num%10;
   num /= 10;

// Output number to digit 0,1,2, or 3, 4 to display dots
void display(int number, int digit) {
 cbi(PORTC, digit);      // Turn on corresponding digit
 switch(number) {      // Set PORTD, display pins, to correct output
   case 0:
     PORTD = 0b01111110;
   case 1:
     PORTD = 0b00001100;
   case 2:
     PORTD = 0b10110110;
   case 3:
     PORTD = 0b10011110;
   case 4:
     PORTD = 0b11001100;
   case 5:
     PORTD = 0b11011010;
   case 6:
     PORTD = 0b11111010;
   case 7:
     PORTD = 0b00001110;
   case 8:
     PORTD = 0b11111110;
   case 9:
     PORTD = 0b11011110;
     PORTD = 0b01111110;  //0

I think I found the issue. It appears to be in the sparkfun serial 7-segment display. The problem is outlined here: http://bleaklow.com/2010/08/28/sparkfun_are_less_than_electrifying.html

thanks for everyone's help.


The reason isn't too mysterious.  

for a 16Mhz clock (assuming it is EXACTLY accurate) you get 64 clocks per timer tick (as set by the arduino's init() code) and 256 ticks per interrupt (overflow).

So thats: 16000000/64/256 = 976.5625 timer interrupts per second

but this number is used in integer math so that comes out to 976.  So even with a perfectly precise clock you get error.  You can see all this by reading wiring.c

This is why many microcontollers accept TWO clocks one CPU clock at say 16 MHz and another at 32768Hz (=2^15) watch crystal.  Checking the datasheet the timer2 can be run by a watch crystal (at least in the mega 2560).

if we do the same math with a 32768Hz clock we get
32768/64/256=2  (or 128 if you don't prescale the clock)
which gives no loss of precision!  

So, if you want good timing you should probably attach a watch crystal and use timer 2.


Dec 25, 2010, 04:34 am Last Edit: Dec 25, 2010, 04:46 am by graynomad Reason: 1
Well researched mspguy, I assumed the millis actually meant millis.

So one may wonder why they didn't /250 by using an output compare interrupt? Or is that used for PWM?


Rob Gray aka the GRAYnomad www.robgray.com


Dec 25, 2010, 04:58 am Last Edit: Dec 25, 2010, 05:05 am by cnt Reason: 1
EDIT:  I totally misread the above post.  

I think /250 can be done with timer 2 using clear timer on compare match mode.

Coding Badly

Or is that used for PWM?

Yes.  Timer 0 is used to drive millis / micros and provide PWM on two pins.


Dec 25, 2010, 09:37 am Last Edit: Dec 25, 2010, 09:51 am by stephen_t Reason: 1
If there is an automatic error of 576ppm (52 seconds a day)( due to the integer maths. why are all my Arduinos so accurate ?.  The worst is the Uno at 4 seconds a day.  

One of my Duemilanoves  made the period between 08:00 24th July and 0800 11th September  (7 weeks, 49 days) to be 4233620357 Milliseconds an error of only 20357 milliseconds giving it better than 5 ppm against the NTP controlled host computer.  It keeps better time than the Casio on my wrist.  

The chinese knockoff is good to just over 2 seconds aday  and my other real Duemilanove good to a second a day. They do vary slightly with temperature as you would expect of a crystal.

Go Up