El tiempo de Arduino usando millis () no es exacto o correcto?

9

He estado usando el Arduino para registrar algunos datos. En mi boceto Arduino también utilicé la millis()función para poder hacer un seguimiento del tiempo en el que se toma cada valor que estoy midiendo. Sin embargo, noté que el momento no es correcto. Por ejemplo, 30 segundos en la vida real solo salen como 10 segundos (ejemplo inventado).

¿Estoy en lo cierto al decir que la función de retraso de Arduino afecta el tiempo que se usa millis()? En otras palabras, supongamos que tengo un retraso de 50 ms, ¿eso significa que la millis()función también se detiene durante esa duración y luego continúa y así sucesivamente durante la conexión? Noté esto cuando intenté trazar algunos datos y encontrar que la frecuencia de los picos en mis datos era demasiado frecuente dado el tiempo que había pasado. Entonces, quiero saber si ese es el motivo de esta falta de coincidencia de tiempo y, de ser así, ¿cómo puedo solucionar esto para poder mantener el tiempo en que ocurre cada muestra?

Para dar un poco de contexto aquí está mi boceto:

#include <eHealth.h>    

unsigned long time;
// The setup routine runs once when you press reset:
void setup() {
  Serial.begin(9600);  
}

// The loop routine runs over and over again forever:
void loop() {

  float ECG = eHealth.getECG();
  time = millis();
  Serial.print(time);
  Serial.print(" ");
  Serial.print(ECG, 5); 
  Serial.println("");    

  delay(50);
}
usuario3284376
fuente
¿Estás usando uno de los tableros oficiales de Uno?
Peter Bloomfield
1
El tiempo real en lugar de los valores inventados (un monitor en serie que marca las líneas de tiempo es ideal) probablemente ayudaría a descubrir qué está sucediendo.
Ignacio Vazquez-Abrams
3
El cálculo de millis()es impulsado por interrupción, por lo delay()que no debería afectarlo.
microtherion
Tengo el mismo problema, pero solo cuando lo integro (millis ()) en código complejo. Supongo que la complejidad del código afecta su precisión en la forma en que se demora cada vez más con la complejidad del código. Hay alguna manera de evitar esto? tal vez usando un módulo RTC separado?
Josip7171

Respuestas:

10

millis()es impulsado por interrupción, por lo delay()que no lo afectará, al menos no en una placa basada en ATmega.

Eso no quiere decir que millis()sea ​​totalmente exacto tampoco. Cada tic del temporizador no es exactamente 1 ms, pero es 1.024 ms. Este error se acumula gradualmente hasta que se realiza una corrección. Esto se puede ver en la implementación del controlador de interrupción TIMER0_OVF (desbordamiento del temporizador 0).

Otra fuente de inexactitud es el oscilador / cristal en sí, que no es exactamente 16MHz. Sin embargo, está bastante cerca, y siempre que la temperatura no cambie demasiado, es relativamente estable.

Lo anterior significa que puede estar alrededor de 1 ms cuando lo usa millis(). Esto no suena como tu problema.

Otro problema potencial sería lo que getECG()está haciendo: puede ser muy lento.

float eHealthClass::getECG(void)
    {
        float analog0;
        // Read from analogic in. 
        analog0=analogRead(0);
        // binary to voltage conversion
        return analog0 = (float)analog0 * 5 / 1023.0;   
    }

analogRead() es lento, pero no tan lento como para impactar un ciclo como este.

Otro problema que he visto que la gente tiene es cuando cambian la velocidad del reloj, pero no cambian correctamente boards.txt. Esto significa que las constantes utilizadas en la millis()implementación son incorrectas y los tiempos son incorrectos.

Si realmente desea leer valores cada 50 ms, una forma mucho mejor de implementar esto es hacer lo siguiente

static long lastUpdate;

if (millis() - lastUpdate > 50)
{
    lastUpdate = millis();
    //Do stuff
}

Realmente necesitaríamos ver las marcas de tiempo que está recibiendo. Si en realidad ve 30 años que se muestran como 10, entonces hay algo más en el trabajo.

Cybergibbons
fuente
2
Tenga en cuenta que, para el Uno, el reloj no funciona con cristales, sino que solo utiliza un resonador de cerámica que es menos preciso que un cristal.
jfpoilpret
@jfpoilpret Ah, es bueno saberlo. Mirando el esquema , este sería el dispositivo CSTCE16M0V53-R0 Murata CERALOCK .
Chris O
Los resonadores tienen una tolerancia inicial pobre (a menudo 0.5-2%) y una estabilidad de temperatura pobre, pero si calibra los bucles de sincronización cuando los usa, pueden estar bien siempre que la temperatura no se mueva.
Cybergibbons
2
Millis () todavía funciona en un temporizador que marca cada 1.024 ms, pero agregaron una compensación de error, en forma de incremento cada vez que una variable del medidor de error es demasiado alta. Creo que en realidad es el algoritmo de Roman Black. Por lo tanto, el tiempo debe estar mucho más cerca de 1 ms exactamente. github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
EternityForest
Para aquellos que todavía están interesados, vea el comentario que publiqué en la respuesta de JRobert, no quería responder con mi propia respuesta, ya que no tengo una, acabo de reformular el problema.
user3284376
2

Si las interrupciones se desactivan por una fracción considerable de la eHealth.getECG()duración de la llamada, millis()el recuento podría retrasarse. De lo contrario, millis()debería devolver un tiempo mucho más preciso que los errores 3x que describió.

Dijiste que tu señal muestreada parece tener una frecuencia más alta de lo que esperabas, lo que podría suceder si tu frecuencia de muestreo es más baja de lo que esperabas. ¿Está asumiendo una frecuencia de muestreo de 20Hz? Su ciclo podría demorar un poco más de 50 ms, lo que vería en los tiempos impresos, pero aún así deberían seguir la hora del reloj. Si no tuvo en cuenta eso, pero asumió 50 ms / muestra, vería una aparente aceleración de los datos.

Si este no es el problema, el siguiente paso sería alternar una salida mientras está dentro loop()y medir la frecuencia de la onda cuadrada resultante con un medidor de frecuencia (algunos DVM económicos pueden hacer esto) o un 'alcance'. Haz lo mismo con un vacío loop(). El primer experimento será su frecuencia o intervalo de muestreo real; el segundo le dirá si millis()(es decir, la frecuencia del temporizador 0) es lo que esperaba.

JRobert
fuente
1
He estado jugando un poco más y me he dado cuenta de que el problema no es con el Arduino, la función millis () en su mayor parte funciona muy bien, algunos de los valores no son exactamente 8 ms (retraso) aparte, sino de qué Has dicho que es de esperar. El error 3x que describí es cierto del lado de Python de las cosas que uso para recibir los datos. Alguna idea de lo que podría ser el resultado, estoy usando la pyserial de Python y es lento como el infierno.
user3284376
No sé lo suficiente sobre su implementación para darle más de 1/2 @ 'adivinar, pero ¿es el lado de Python lo suficientemente lento como para dejar caer muestras?
JRobert
0

Igual que aquí. Puedo agregar que si las interrupciones están desactivadas, el tiempo medido es "en tiempo real". De todos modos, no entiendo por qué este retraso, porque si el bucle tarda demasiado, de todos modos, el millis () debería devolver valores en tiempo real (solo con más distancia entre cada valor)

usuario48711
fuente
1
¿A qué se refiere "lo mismo aquí"? Las respuestas deben ser independientes, ya que StackExchange puede reordenar las cosas (a diferencia de un foro). Así que "lo mismo aquí" podría significar cualquier cosa dependiendo de qué respuesta / pregunta aparezca su respuesta debajo.
Nick Gammon
Esta publicación sería más apropiada como comentario, aunque (ciertamente) carece de suficiente reputación.
Greenonline
Lo siento, aunque cuando respondes algo, es obvio que se refiere a la publicación principal, de lo contrario sería un comentario
user48711