
I tested the accuracy of a crystal on one of my Arduino Duemilanove (ATmega168 version) boards. I compared the crystal against a TXCO (ChronoDot). The following graph shows the results.

Note: I’m not sure about the “3 glitches” present in the data at the 99th, 199th and 291st samples.
After 5 minutes the difference between the arduino timer and the TXCO was 33060 microseconds (us), or 0.033112 seconds. That works out to a 0.397344 second error after one hour. It was averaging a delta of 110.2 per second, or 110.2/16 (MHz) = 6.9 PPM. After 2 minutes of testing, I rested my thumb heavily on the arduino crystal in an attempt to heat it and see if that would have an affect. The data varied by slightly more than 1 PPM.
I’m impressed.

Test Rig: Arduino with ChronoDot connected.
Sample output from arduino program:
#Starting Test
0, 0
1, 112
2, 224
3, 332
4, 444
5, 556
6, 664
7, 776
8, 888
9, 996
10, 1108
11, 1220
12, 1328
...
Arduino Program:
/*
1 Hz Test
Copyright 2012, all rights reserved.
James M. Eli
1/9/2012
project parts:
(1) arduino 16MHz
(1) ChronoDot RTC (DS3231SN)
(1) breadboard
(1) 10 ohm resister (pullup)
(7) wires
DS3231SN TXCO RTC (ChronoDot) pin outs:
CD - Arduino - Port
SQW - D2 - Port D2 (tied thru 10K resister to VCC)
GND - GND - Ground
VCC - VCC - 5V
*/
#include <Wire.h>
//millisecond counter
volatile unsigned long my_timer0_millis;
//ms counters for arduino & TXC0
volatile unsigned long us_t, us_a;
//boolean flag
volatile bool flag;
//this interrupt is called on a TCXO RTC 1Hz SQW
ISR(INT0_vect) { //PD2 or D2 pin
unsigned long m;
uint8_t t;
//assumptions here: arduino 168/328 running @ 16MHz
m = my_timer0_millis;
t = TCNT0;
if ((TIFR0 & _BV(TOV0)) && (t < 249))
m++;
//save current microsecond [us] count
us_a = ((m*250) + t)*(64/clockCyclesPerMicrosecond());
//set flag
flag = true;
}
//timer0 interrupt handler
ISR(TIMER0_COMPA_vect) {
//incremented every 1ms by arduino timer0
my_timer0_millis++;
}
//returns current millis count
unsigned long myMillis(void) {
unsigned long m;
uint8_t oldSREG;
oldSREG = SREG;
cli();
m = my_timer0_millis;
SREG = oldSREG;
return m;
}
void setup(void) {
//init various
us_t = 0;
flag = false;
//turn off interrupts
cli();
//replace arduino timer0 code with our timer
my_timer0_millis = 0;
//16Mhz/64 prescale/250 counts = 16000000/64/250 = 1000us (1ms)
TCCR0A = 0;
TCCR0A |= (1<<WGM01); //CTC mode, top=OCR0A, TOV0 set@max, update immediate
TCCR0B = 0;
TCCR0B |= (1<<CS01) | (1<<CS00); //Fcpu/64
TIMSK0 |= (1<<OCIE0A); //enable CTC A interrupt
OCR0A = 249; //249 results in a 250 count rollover
TCNT0 = 0;
//setup digital #2 external interrupt
EICRA |= (1<<ISC00) | (1<<ISC01);
EIMSK |= (1<<INT0);
//enable interrupts
sei();
//init wire & serial
Wire.begin();
Serial.begin(9600);
//setup square wavew at choice of 0=1Hz/8=1024Hz/16=4096Hz/24=8192Hz
Wire.beginTransmission(104);
//select control register
Wire.send(0x0e);
//set square wave @ 1 Hz
Wire.send(0);
Wire.endTransmission();
}
void loop(void) {
Serial.println("# Starting Test");
while(1) { //endless loop
if (flag) {
if (us_t == 0)
us_t = us_a;
else
us_t += 1000000;
Serial.print(us_t/1000000);
Serial.print(", ");
Serial.println(us_a - us_t);
flag = false;
} //if
} //while
} //loop