Millis() atrasa

Existe la posibilidad de hacer un reloj sin la necesidad de comprar un rtc DS1307 O cualquier otro producto similar?

Hice un reloj solo usando Millis() y algunas variables y funciona, pero atrasa.

Mi pregunta basicamente es: ¿Cuán fidedigno es el contador de milisegundos que viene incluido en la placa Arduino UNO?

Atrasa segun sea tu cristal o resonador.
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.

Quieres evitar un RTC ahi tienes las consecuencias.
Alternativas? Si tienes placa de RED usa un RELOJ NTP
Si tienes un GPS, usa su RELOJ

de lo contrario... no hay mucho que puedas hacer.

¿Cuán fidedigno es el contador de milisegundos que viene incluido en la placa Arduino UNO?

respondiendo tu pregunta, depende de la calidad del cristal que uses.
El DS3231 tiene un cristal compensado en temperatura.

Gio79:
Hice un reloj solo usando Millis() y algunas variables y funciona, pero atrasa.

Hay que admitirlo: es imposible que no lo haga; sin embargo lo más que puede atrasar son 0.05 milisegundos.
No es la gran cosa; por lo tanto, tu definición de "atrasar" está poco clara.

Gio79:
Mi pregunta basicamente es: ¿Cuán fidedigno es el contador de milisegundos que viene incluido en la placa Arduino UNO?

  • Diría que con una precisión de 99%. No puedo garantizar el 100% por tres razones:
  • La interrupción que incrementa el contador no ocurre exactamente cada milisegundo.
  • Incluso la misma interrupción tarda su tiempo en entrar, ejecutar y salir.
  • Aunque teóricamente se incrementara cada milisegundo exacto; todavía no se puede garantizar precisión absoluta ya que la frecuencia del oscilador externo de 16 MHz tiende a variar aunque insignificantemente.

Decir que 99% evidentemente no significa perfección; sin embargo es demasiado aceptable porque tal imprecisión es básicamente imperceptible.

Te lo pondré así: el contador de millis es tan fidedigno como un reloj digital de pulsera.

Tengo curiosidad de saber cómo llegaste a la conclusión que se atrasa, qué herramientas usaste para hacer las mediciones. Como ya te dijeron surbyte y Lucario, la diferencia no debería ser mucha, por lo que existe la posibilidad que tus mediciones puedan ser incorrectas

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.

Madre mía cuánto conocimiento junto!!, jajaja

Gio79:
Hice un reloj solo usando Millis() y algunas variables y funciona, pero atrasa.

Ya sé que la pregunta del compañero no era esa, pero en la intención de ayudar, tal vez debería poner el sketch, lo mismo es que el retraso está en los procesos que ha implementado, verdad?

Lucario448:
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)

Mi error ha sido no citar a Gio79, la pregunta iba para él. Si él nota este retraso quiere decir que no es la cantidad de millisegundos que tu has calculado (teóricamente).
Por supuesto que el tiempo entregado por un arduino es una estimación, pero es una estimación controlada, de otra forma no tendría sentido tener funciones como millis o micros.

Te lo pondré así: el contador de millis es tan fidedigno como un reloj digital de pulsera.

Yo creo que si este fuese el caso, el amigo no estaría planteando su problema. Por eso mismo pregunto como fue que midió ese atraso ya que teóricamente debería ser imperceptible.

Seguir hablando cuando el interesado no se comunica no vale la pena, porque es un tema ya debatido y con los planteos ya expuestos.

albertoG1:
Madre mía cuánto conocimiento junto!!, jajaja

Consecuencias de querer profundizar; ¿Cuál otra razón podría dar?

gepd:
Mi error ha sido no citar a Gio79, la pregunta iba para él.

Pues vaya, y yo hablando de más... :cold_sweat:

surbyte:
Seguir hablando cuando el interesado no se comunica no vale la pena, porque es un tema ya debatido y con los planteos ya expuestos.

De acuerdo, aunque siempre que se me sale una explicación larga, me pasa por la mente registrarla en la sección de documentación; sin embargo no lo hago por temor a generar información redundante :slightly_smiling_face: