Arduino Uno millis() function timing accuracy?

Discussion in 'Embedded Systems and Microcontrollers' started by MikeML, Jul 20, 2015.

  1. MikeML

    Thread Starter AAC Fanatic!

    Oct 2, 2009
    5,450
    1,066
    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?
     
  2. sirch2

    Well-Known Member

    Jan 21, 2013
    1,008
    351
    If I were you I would write a little test program and run it for say 20 sec and see how good your set up is, it is going to vary from resonator to resonator. As for the millis function Google has loads of stuff, here's one discussion

    http://forum.arduino.cc/index.php?topic=46351.0
     
  3. theonewho

    New Member

    Jul 9, 2015
    17
    2
    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.

    Code (C):
    1. // the prescaler is set so that timer0 ticks every 64 clock cycles, and the
    2. // the overflow handler is called every 256 ticks.
    3. #define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
    4.  
    5. // the whole number of milliseconds per timer0 overflow
    6. #define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000)
    7.  
    8. // the fractional number of milliseconds per timer0 overflow. we shift right
    9. // by three to fit these numbers into a byte. (for the clock speeds we care
    10. // about - 8 and 16 MHz - this doesn't lose precision.)
    11. #define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3)
    12. #define FRACT_MAX (1000 >> 3)
    13.  
    14. volatile unsigned long timer0_overflow_count = 0;
    15. volatile unsigned long timer0_millis = 0;
    16. static unsigned char timer0_fract = 0;
    17.  
    18. #if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    19. ISR(TIM0_OVF_vect)
    20. #else
    21. ISR(TIMER0_OVF_vect)
    22. #endif
    23. {
    24.     // copy these to local variables so they can be stored in registers
    25.     // (volatile variables must be read from memory on every access)
    26.     unsigned long m = timer0_millis;
    27.     unsigned char f = timer0_fract;
    28.  
    29.     m += MILLIS_INC;
    30.     f += FRACT_INC;
    31.     if (f >= FRACT_MAX) {
    32.         f -= FRACT_MAX;
    33.         m += 1;
    34.     }
    35.  
    36.     timer0_fract = f;
    37.     timer0_millis = m;
    38.     timer0_overflow_count++;
    39. }
    40.  
    41. unsigned long millis()
    42. {
    43.     unsigned long m;
    44.     uint8_t oldSREG = SREG;
    45.  
    46.     // disable interrupts while we read timer0_millis or we might get an
    47.     // inconsistent value (e.g. in the middle of a write to timer0_millis)
    48.     cli();
    49.     m = timer0_millis;
    50.     SREG = oldSREG;
    51.  
    52.     return m;
    53. }
    54.  
     
Loading...