millis() for three years


I wanna solder a hardware countdown with a time range about 3 years to count back all days:Hours:mins:secs on LED segment displays.
What happens when the millis() function overflows? at which value? does it continue seemlesly?


Currently millis() overflows much sooner than you might expect because it is calculated from a timer that ticks every 1024 microseconds.

You could take the overflow interrupt on timer2 which runs at the same rate as the millis() timer and define a 'seconds' counter. That might look something like this...

volatile unsigned long seconds = 0;

  static long microseconds = 0;
  microseconds += 1024;
  if ( microseconds > 1000000L) {
    microseconds -= 1000000L;

unsigned long secondsSinceBoot()
  unsigned long t;
  uint8_t oldSREG = SREG;

  cli();    // disable interrupts
  t = seconds;
  SREG = oldSREG; // restore interrupts
  return t;

Then every 1024 microseconds that SIGNAL function will run and accumulate another 1024 microseconds. When there are more than a million accumulated it increments the second and uses up that million. Then you can check "seconds" to see how long it has been since boot. It isn't quite that easy, because the interrupt could fire in the middle of your code reading "seconds", so I added the function "secondsSinceBoot" to safely get you a copy of "seconds" by briefly disabling interrupts. (they won't be lost, just delayed).

You could stick a call to your function in after seconds gets incremented, but be aware you are running at interrupt level and some things don't work... like delay() will freeze and if you take more than about 900 microseconds you will lose a clock tick and not keep correct time. Probably better to poll seconds from your main loop.

(Crappy code notes: The above code doesn't pay any attention to F_CPU or check that timer2 hasn't been changed from its default function. If you diddle with those sorts of things then you'll know what to do.)

Thanks a lot for the detailed reply!

this sounds good to me to use timer2 in the way you did.
But it is used differently. In wiring.c per default the overflow interrupt isnt enabled and ist is set like timer1 to 8 bit phase correct pwm mode.

why not setting 'microseconds' each time to zero instead subtracting 1000000?

il try out... and post results later.

thanks again


Oh right, I forgot about the phase correct aspect. It might not overflow, and you would certainly have to turn on the interrupt enable bit.

I subtract 1000000 because there will be a few left over each time each time, it will go from 999424 to 1000448 and I don't want to lose that 448. About every other time the while loop while run twice and eat up the accumulated remainders.

Ok it works!

i changed the settings in wiring.c concerning interrupt and phase correct PWM.
since im not really good in programming: can i do this adjustments in the Ardunio code as well e.g.
before the void setup() block:

// enable timer 2 overflow interrupt

#if defined(AVR_ATmega168)
sbi(TIMSK0, TOIE2);
sbi(TIMSK, TOIE2);

and now the main routine().....
or is it just allowed to do this in a library?

Now it is about timing stabilty. because it shall run for 3 years. there are about 90 milion seconds....
i guess i have to do some tests.
does the code of main and subroutines affect the stability of the timer?


You should do your timer reprogramming inside your setup() function. This runs after the init in the libraries.

Your code will not effect the timing unless you disable interrupts for more than about... 1000-2000 microseconds.

I'm told the radio folks that really care about crystal stability make "crystal ovens", little insulated covers for the crystal with a temperature sensor and a resistor inside. Then they pick a temperature above any likely ambient temperature for the device and keep the crystal at that temperature by turning the resistor on and off. That should get you are very stable time source, though you'd have to calibrate it by measuring it over a long time period.

If time is very important, just go get a real time clock (RTC). The DS1306 is an SPI interfaced RTC that has alarms, can operate from a backup battery (and charge a rechargable backup battery), and if it’s super critical get a DS32KHZ 32.768kHz TCXO (temperature compensated crystal oscillator) instead of using a standard crystal.

The DS1307 (I think I’ve seen it here and at Sparkfun) is the same thing with an I2C interface.

Disclaimer: I haven’t used any of these yet, although I do have a couple on my bench that I plan to use.