Arduino Uno millis() function timing accuracy?

Thread Starter

MikeML

Joined Oct 2, 2009
5,444
I have a periodic stream of pulses roughly 10ms between rising edges. I want to measure the period of the pulses. I am willing to take data for up to 10s (average of ~1000 periods) before computing the answer. What absolute accuracy can I expect considering the untrimmed 16MHz resonator and the millis() function in the Uno?
 

theonewho

Joined Jul 9, 2015
17
I'm just getting started with Arduino, but I have plenty of experience with programming in general. For what it's worth I feel confident in saying that the accuracy of the millis() function will likely be fine for your purposes assuming that you aren't using a bunch of time and processor cycle wasting methods like delay() and loops that eat up a bunch of cpu time.

Even then, looking at the source for millis(), it indirectly uses an ISR for timer0 which will be serviced rather quickly and reliably based on my experience with other processors.

So you can pretty much count on the millis() function having accurate timing data available whenever it gets called, but if you do something in your code that delays millis() being called when it needs to be, then the result you get from your call to millis() will be inaccurate.

C:
// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
// the overflow handler is called every 256 ticks.
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))

// the whole number of milliseconds per timer0 overflow
#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)

// the fractional number of milliseconds per timer0 overflow. we shift right
// by three to fit these numbers into a byte. (for the clock speeds we care
// about - 8 and 16 MHz - this doesn't lose precision.)
#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
#define FRACT_MAX (1000 >> 3)

volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
    // copy these to local variables so they can be stored in registers
    // (volatile variables must be read from memory on every access)
    unsigned long m = timer0_millis;
    unsigned char f = timer0_fract;

    m += MILLIS_INC;
    f += FRACT_INC;
    if (f >= FRACT_MAX) {
        f -= FRACT_MAX;
        m += 1;
    }

    timer0_fract = f;
    timer0_millis = m;
    timer0_overflow_count++;
}

unsigned long millis()
{
    unsigned long m;
    uint8_t oldSREG = SREG;

    // disable interrupts while we read timer0_millis or we might get an
    // inconsistent value (e.g. in the middle of a write to timer0_millis)
    cli();
    m = timer0_millis;
    SREG = oldSREG;

    return m;
}
 
Top