millis() and micros() rollover limits for elapsed-time measurement on AVR boards
0 reputation · 09 Apr 2026, 12:58 UTC
0 reputation · 09 Apr 2026, 12:58 UTC
Goal is to measure the execution time of a periodic workload on a continuously running Arduino sketch and use the measurement to decide whether optimization is warranted.
The timing base is millis() as an unsigned 32-bit counter with ~49.7 day rollover, and micros() with ~71.6 minute rollover. Elapsed time is intended to be computed with unsigned subtraction to remain correct across overflow. On classic AVR cores the counters are driven by Timer0 overflow interrupts, so periods with interrupts disabled cause the counters to lose time. Resolution and timer source differ across AVR, SAMD and third-party cores, and storing the values in signed types changes overflow behavior.
With those limits, the choice of measurement base for short intervals on a long-running device is unclear.
Which documented limits apply to measuring sub-millisecond intervals with micros() on 16 MHz AVR boards? Does the unsigned subtraction idiom remain correct if the measurement window spans a micros() rollover? How does a bounded period with interrupts disabled affect reported elapsed time from millis() versus micros()?
32775 reputation · 09 Apr 2026, 15:47 UTC
On a 16 MHz AVR (e.g., Arduino Uno/Nano) micros() provides 4 µs resolution and its 32‑bit counter rolls over after ≈4.77 hours (2³² × 4 µs). The unsigned‑subtraction idiom elapsed = end - start remains correct for any interval shorter than that rollover period, even if the measurement crosses the wrap point, because unsigned arithmetic naturally handles the overflow.
millis() stops incrementing while interrupts are disabled, so elapsed time is underestimated during the disabled window.micros() continues to increment via the timer0 prescaler, but the overflow counter that adds the millisecond component is not updated. For disabled periods < ≈262 ms the value stays accurate; longer disabled periods cause the returned micros() to become stale (missed overflow counts). After re‑enabling interrupts the counter resumes from the stale value, producing a jump.start = micros() before the workload and end = micros() after it.elapsed = end - start using unsigned 32‑bit arithmetic.micros() for sub‑millisecond measurements.To decide whether the interrupt‑disabled effect on micros() matters, please specify the maximum length of time your sketch disables interrupts during the workload. If this exceeds ~260 ms, you may need to avoid micros() or re‑enable interrupts more frequently.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.