It looks to me like the micros()/millis() functions in wiring.c have a somewhat nasty bug
that could lead to hangs after about 50 days of uptime.
The problem is that the 32 bit overflow_count and timer0_millis that are incremented from the
overflow interrupt handler in wining.c can themselves overflow. Here is some 'bc -l' code for overflow_count:
f_cpu = 16000000
f_timer = f_cpu / 64
f_overflow = f_timer / 256
oos = 2^32 / f_overflow
ood = oos / (60*60*24)
ood
50.90331610074074074074
Where oos is the numbe of seconds until the overflow overflows, and ood the
same value in days
The timer0_millis value has essentially the same problem and overflows
at about the same rate (since f_overflow ~= 1 kHz).
This bug could probably manifest itself in many different codes that uses the
millis() fctn (or others in wiring.c that use the same counter). One example
is in Sd2Card.cpp, where the SD card interface could trigger a hang
in waitNotBusy(), since it just uses a a differential time measurement based on
millis() to figure out when a timeout should happen.
I could be wrong and the counter gets reset somewhere somehow, but I didn't
find any code that does it.
I think the best fix is to just use an explicit 64 bit type for the overflow
counter. This would generate slightly slower code, but nobody should be
depending no millis() or micros() to be that precise anyway.
Also, I don't see any point in doing any time computation math in the handler.
It saves a tiny bit (and therefore increases precision) in millis()
average case, but doesn't do anything for the worst case, since the interrupt
could trigger during a micros() call anyway and produce latency. It would be
better to follow the general design principle of keeping handlers maximally
simple and just do something like:
ISR (TIMER0_OVF_vect)
{
// Note that we don't need to use an atomic block here, as we're inside
// an ordinary ISR block, so interrupts are globally deferred anyway.
timer0_overflow_count++;
}
And then do all the computation in millis()/micros()/etc().