Millisecond tracking library for AVR

This is a lightweight library for keeping track of time to millisecond accuracy, the data type for storing milliseconds can be easily configured, it supports ‘unsigned long long’ which can keep track of time for 584.9 million years before overflowing! Any clock frequency up to 20MHz can be used, most of the commonly used frequencies have no time keeping error (see table below).
Even though Arduino has its own millis() time keeping, this library may be handy if running at clock frequencies at or below 8MHz or for running longer than 50 days.

Download from GitHub

Brief comparison against the Arduino millis()
Arduino millis()

  • Microsecond support
  • Doesn’t loose or gain time at any clock frequency
  • Loss of resolution at lower frequencies

This library

  • Faster execution
  • Less RAM used
  • Always updates millisecond count every millisecond
  • Support for ‘unsigned long long’ data type, 64 bit integer allows tracking time for up to 584.9 million years, Arduino millis() uses ‘unsigned long’ which goes up to 49.71 days
  • Uses CTC mode for the timer, which might make it a little bit more difficult to use the timer for multiple things

Accuracy at commonly used clock frequencies

Clock This library (TIMER0) This library (TIMER1) This library (TIMER2) Arduino millis()
20MHz Looses 1.6ms each second
(0.16% error)
No error Looses 1.6ms each second
(0.16% error)
No error
16MHz No error No error No error No error
10MHz Looses 1.6ms each second
(0.16% error)
No error Looses 1.6ms each second
(0.16% error)
No error
8MHz No error No error No error Updates every 2ms
4MHz Looses 8ms each second
(0.8% error)
No error No error Updates every 4ms
2MHz No error No error No error Updates every 8ms
1MHz No error No error No error Updates every 16ms
500KHz Looses 8ms each second
(0.8% error)
No error Looses 8ms each second
(0.8% error)
Updates every 8ms

When the table mentions ‘Updates every ##ms’ it means, for example, if it’s 16ms when getting the value of millis() then 10ms later getting the value again, they will both be the same value (unless the 16ms ended during the 10ms delay).
Even when Arduino millis() gets ‘No error’ there is still a small jitter in the length of each millisecond since the interrupt doesn’t fire at exactly every 1ms.

Actual accuracy will ultimately depend on the accuracy of the uC clock source (external crystal, internal RC etc).


Function Description Parameters Returns
millis_init() Initializes library, must be called first! None void
millis_get() Get current milliseconds None millis_t
millis() Alias of millis_get(), only for non-Arduino since millis() is already used None millis_t
millis_resume() Turn on timer and resume time keeping None void
millis_pause() Pause time keeping and turn off timer to save power None void
millis_reset() Reset milliseconds count to 0 None void
millis_t ms
Add time ms – Milliseconds to add void
millis_t ms
Subtract time ms – Milliseconds to subtract void


1 ping

Skip to comment form

    • Carl W on November 27, 2013 at 12:15 am
    • Reply

    Cool library, thanks.

    I guess your accuracy table above is for using TIMER0 or TIMER2? You might want to point out that using TIMER1 is vastly more accurate in the “inaccurate” cases from your table…

    1. The table is for TIMER2 vs Arduino TIMER0. The library only supported TIMER2 when I first released it. I’ll be updating the table with the accuracy of the other timers soon.

    • Sovichea on January 25, 2016 at 3:44 am
    • Reply

    Great library! It’s very convenient for me because 1) it’s fast and 2) the ability to manipulate the timer like resetting the millisecond counts. Thanks for contributing this library.

    1. Glad it’s of use to you! 😀

    • Renato on October 6, 2016 at 7:44 pm
    • Reply

    Why you limit in your library the maximum CPU speed 32640000? What microcontroller uses this speed? I thought the limit was 20000000

    1. It’s actually possible to overclock an ATmega328 up to 30MHz, but really I used 32640000 because that’s the max frequency that can be used with the timer clock dividers that I defined in the library. It wouldn’t have made any difference if I used 20000000 instead.

    • Raul Palav on January 12, 2017 at 1:31 pm
    • Reply

    Is possible to implement the micros () function from millis ()?
    How can I do it?
    Very thanks.

    1. It’s possible by reading the timer counter register, the resolution will very depending on which timer you use and the microcontroller frequency. With 16MHz and Timer2 you will get a resolution of 4us.

      Something like this might work:

      unsigned long microStart = (millis() * 1000) + (TCNT2 * 4);
      // do stuff
      unsigned long microEnd = (millis() * 1000) + (TCNT2 * 4);
      Serial.print("Duration: ");
      Serial.print(microEnd - microStart);
      • Pirx on August 7, 2019 at 10:21 am
      • Reply

      Here is my implementation, for ATtiny13 (9.6MHz) and more.

      #include “millis.h”

      #if F_CPU == 9600000 || F_CPU == 1200000
      #define RET ms * 1000 + ((tc * 427)>>6)
      #elif F_CPU == 4800000 || F_CPU == 600000
      #define RET ms * 1000 + ((tc * 427)>>5)
      #else // 16, 8, 1 etc. MHz
      #define RET ms * 1000 + tc * (PRESCALER * 1000000 / F_CPU)

      #define TC TCNT0
      #define TC TCNT2

      auto micros_get() {
      millis_t ms; uint8_t tc;
      ms = millis_get(), tc = TC;
      return RET;

      #ifndef ARDUINO
      #define micros() micros_get()

  1. […] For more details please go into the source. […]

Leave a Reply

Your email address will not be published.

Are you human? *