Millis() atrasa

gepd:
Tengo curiosidad de saber cómo llegaste a la conclusión que se atrasa, qué herramientas usaste para hacer las mediciones.

Herramientas... el tiempo es solo una estimación; para darte un valor exacto tendría que saber cuántas instrucciones máquina ejecuta el CPU antes, durante y después de la interrupción que incrementa el contador de millis. Hablo de 50 microsegundos asumiendo una frecuencia de reloj de 16 MHz (la típica en Arduinos con AVR)

Lo de la inexactitud en la interrupción, se puede demostrar matemáticamente; no sin antes definir qué es un "timer":

Un timer es una pieza de hardware en el microcontrolador, cuya función principal es de simplemente contar pulsos sin la intervención del CPU. Por defecto los pulsos que cuenta provienen de la misma señal que gobierna la CPU; por lo tanto, si la frecuencia es de 16 MHz, eso quiere decir que el timer puede incrementar su valor hasta 16 millones de veces por segundo.

Teniendo claro qué es un "timer", procederé con la explicación:

El contador de millis depende de una interrupción que ocurre cuando se desborda el contador del timer0 (uno de los tres que tiene el clásico ATMega328P). Ese desbordamiento ocurre cuando el contador llega a su valor máximo.

El contador del timer0 es de 8 bits; lo que quiere decir que se desbordará cada 256 conteos. Ahora, sí la frecuencia de reloj es de 16 MHz (0.0625 microsegundos entre pulsos) y un timer cuenta a dicha velocidad, entonces el desbordamiento ocurriría cada 0.0625 * 256 = 16 microsegundos.

Espera un momento... ¡debería ser cada milisegundo, no en una fracción de este! Ahhhh quizá sea porque hay otro factor que no mencioné: el "prescaler", es un divisor que efectivamente ralentiza el conteo. El timer0 utiliza uno de factor 64; lo que quiere decir que cada 64 pulsos incrementa el contador.

Ahora la fórmula sería la siguiente: con un divisor de 64, el desbordamiento ocurre cada 0.0625 * 256 * 64 = 1024 microsegundos.
1024 microsegundos equivale a 1.024 milisegundos (frecuencia de 976.56 Hz); ¡ya está! ¡Ya tenemos el milisegundo que buscabamos!

Pero... ¿los decimales sobrantes harán que a la larga el cronómetro pierda basta precisión? Es correcto, sin embargo los desarrolladores de Arduino ya lo sabían. ¿Cómo lo solucionaron? Simple: existe en segundo contador "oculto" que se utiliza para indicar cuándo compensar la desviación que producen esas cifras decimales. Dicha compensación consiste en incrementar dos veces en vez de una.

La solución es bastante aceptable, sin embargo no es perfecta. 24 no es divisible entre 1000, así que en un lapso muuuuuuuuuy largo (incluso más allá de lo que unsigned long puede registrar), la imprecisión todavía se notaría.

El "atraso" que provoca hasta el simple hecho de entrar en una ISR: lo explican en este artículo de Nick Gammon (en inglés).

Variación en la frecuencia del reloj: lo acaba de explicar surbyte:

surbyte:
Si tienes un Arduino Original entonces tendras un cristal pero no es compensado en temperatura de modo que igual atrasará.
Si tienes un CLON puede que ni siquiera tengas un cristal y apenas tengas un Resonador, con lo cual tendras algo que mas o menos se comporta.