Go Down

Topic: Arduino as a chronometer ? Know what you are doing ! (Read 3 times) previous topic - next topic

jmkymoe

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.



robtillaart

Thanks for this information,

It would be very informative if you could post your whole code and the device setting so anyone interested can redo these measurements e.g. with a MEGA.

Rob
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Senso

So, you dont have volatile variables, thats a no no.

pluggy

Depends on what time frame you do the measurement, how you measure it and the type of Arduino, the modern ones have oscillators rather than crystals.  I have had a Duemilanove running a system for around 18 months. Part of that is it keeps track of it's millis() versus an NTP aligned computer. It varies slightly with temperature but generally gains around 500 milliseconds per day (~6 ppm).  It keeps better time than my Casio watch.   A Uno I've tested in the long term is generally within 15 seconds a day.  

JimEli

#4
Mar 01, 2011, 09:47 pm Last Edit: Mar 01, 2011, 09:54 pm by JimEli Reason: 1
Here's a test for you. Insert the following code, which replaces the TIMER0 ISR, and recheck your results. The arduino TIMER0 ticks off at a 1.024 ms (vs. an implied 1ms) which creates some nasty timer/math issues. Admittedly, this code will break millis(), micros() and some PWM stuff too, but disregard that for now ;-)

Just call MyMicros() instead of micros().

Code: [Select]


//millisecond counter
volatile unsigned long my_timer0_millis;

ISR(TIMER0_COMPA_vect) {
 //incremented every 1ms
 my_timer0_millis++;
}

//returns current microsecond [us] count
unsigned long MyMicros(void) {
 unsigned long m;
 uint8_t t;

 m = my_timer0_millis;
 t = TCNT0;
 if ((TIFR0 & _BV(TOV0)) && (t < 249))
   m++;
 return ((m*250) + t)*4);
}

void setup(void) {
 ...

 //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); // F_CPU/64
 TIMSK0 |= (1<<OCIE0A);           // enable CTC A interrupt
 OCR0A = 249;                     // 249 results in a 250 count rollover

 sei();

 ...

}



Go Up