In a recent post I asked about Arduino instruction timing: my goal is to use Arduino to time periodic events (period of 1 to 5 seconds) with a desired accuracy in the range of 10 µs (15 is bearable) and I wanted to know more about how Arduino handles time measurement.
Before diving into the assembly code to look at the nitty-gritty details of the micros() instruction and the timing of interrupts I decided to code a simple timing program and to use it to check the quality of the measurements.
The signal source is a 2ppm, temperature compensated, low frequency oscillator. The signal period ranged from 1 to 16 seconds by 1-second steps, 200 measures were done for each value, the signal being also controlled with a frequency meter, showing a maximum discrepancy of .8 µs between the period displayed by the oscillator and the frequency meter measurement.
The oscillator output is connected to Arduino pin #2 and the Arduino program looks like this :
void setup()
{
…
attachInterrupt(0, Time_signal, RISING);
…
}
void loop()
{
if (sensor_flag) {
sensor_flag=false ;
Process_Time () ;
}
Record () ;
Idle() ;
}
void Time_signal()
{
time_value = micros() ;
sensor_flag = true ;
}
- Process_Time (): process the successive values of time_value to get the value of Period ; takes into account the roll-over of micros().
- Record(): records Period, time of day, temperature to a PC file through a serial connection.
- Idle(): introduces an adjustable delay in the loop (nop based, does not use the delay() instruction).
Here at the results :
1)
Arduino is off, and by a wide margin given my requirements : a 1 second period is measured (averaged over 200 points) as 998,639 µs, ie more than 1,300 ppm error. Over the measurement range, 1 to 16 s, the relationship between reference time (Tr) and measured time (Tm) is precisely:
Tm = 998641 * Tr - 2,2
I have no way of knowing the accuracy of the crystal on my Arduino board. Cheap crystals will usually be in the 50-100 ppm range ; 1,300 ppm is clearly a surprise, even mechanical watches do better than this.
2) For a given value of the period ( 3 s)
the results are scattered over a range of 28 µs around the mean value. For the statistically oriented the standard deviation is 5. These numbers decrease as the idle time provided by Idle() increases. An intuitive explanation is that there are «collisions» that occur in the program between the real-time triggered Time_signal () function and non-interruptible segments of other program processes: in that case Time_signal () is delayed. From the outside of the program black box this looks like a random delay added to the measurement. When Idle() increases the number of nop’s in loop(), the proportion of interruptible versus non-interuptible segments increases and there will be less «collisions».
3)
And of course there’s temperature. Taking 20 °C as a reference temperature I have measured a - 16 ppm/°C drift from 15 °C to 25 °C.
In summary if you want to use Arduino for timing events set your expectations right : unless you have an external reference oscillator a granularity of 0.05 millisecond is a safe bet. And if you are looking at long term time keeping just bear in mind that counting on your Arduino only, next year you run the risk of shouting « Happy New Year » ten hours ahead of anybody else.