Il tempo di Arduino nell'usare millis () non è preciso o corretto?


9

Ho usato Arduino per registrare alcuni dati. Nel mio schizzo di Arduino ho anche usato la millis()funzione in modo da poter tenere traccia del tempo in cui viene preso ogni valore che sto misurando. Tuttavia, ho notato che i tempi non sono corretti. Ad esempio, 30 secondi nella vita reale escono solo come 10 secondi (esempio composto).

Ho ragione a dire che la funzione di ritardo di Arduino influisce sul tempo di utilizzo continuo millis()? In altre parole supponiamo che io abbia un ritardo di 50ms, significa che anche la millis()funzione si interrompe per quella durata e poi continua e così via per la durata della connessione? L'ho notato quando ho provato a tracciare alcuni dati e ho scoperto che la frequenza dei picchi nei miei dati era troppo frequente dato il tempo che era passato. Quindi voglio sapere se questo è il ragionamento per questa mancata corrispondenza dei tempi e, in tal caso, come posso risolvere questo problema in modo da poter mantenere il tempo che si verifica ogni campione?

Per dare un po 'di contesto ecco il mio schizzo:

#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);
}

Stai usando una delle schede ufficiali Uno?
Peter Bloomfield,

1
Il tempismo effettivo anziché i valori inventati (un monitor seriale che segna il timestamp delle linee è l'ideale) probabilmente aiuterebbe a capire cosa sta succedendo.
Ignacio Vazquez-Abrams,

3
Il calcolo di millis()è guidato dall'interruzione, quindi delay()non dovrebbe influenzarlo.
microtherion

Ho lo stesso problema, ma solo quando lo integro (millis ()) in un codice complesso. Immagino che la complessità del codice influenzi la sua accuratezza nel modo in cui va sempre più in ritardo con la complessità del codice. C'è un modo per evitarlo? magari usando un modulo RTC separato?
Josip7171,

Risposte:


10

millis()è guidato dall'interruzione quindi delay()non avrà alcun impatto, almeno non su una scheda basata su ATmega.

Ciò non vuol dire che millis()sia totalmente accurato. Ogni tick del timer non è esattamente 1ms, ma è 1.024ms. Questo errore si accumula gradualmente fino a quando non viene effettuata una correzione. Questo può essere visto nell'implementazione del gestore di interrupt TIMER0_OVF (timer 0 overflow).

Un'altra fonte di inesattezza è l'oscillatore / cristallo stesso, che non è esattamente 16MHz. È abbastanza vicino però, e fintanto che la temperatura non cambia troppo, è relativamente stabile.

Quanto sopra significa che potresti essere circa 1ms fuori durante l'utilizzo millis(). Questo non sembra il tuo problema.

Un altro potenziale problema potrebbe essere quello che getECG()sta facendo: potrebbe essere molto 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() è lento, ma non così lento da influire su un loop come questo.

Un altro problema che ho visto è che le persone cambiano la velocità di clock ma non cambiano correttamente boards.txt. Ciò significa che le costanti utilizzate millis()nell'implementazione sono sbagliate e i tempi sono sbagliati.

Se si desidera effettivamente leggere i valori ogni 50 ms, un modo molto migliore per implementare questo è quello di fare quanto segue

static long lastUpdate;

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

Dovremmo davvero vedere i timestamp che stai ricevendo. Se in realtà vedi 30s che mostrano 10s, allora c'è qualcos'altro al lavoro.


2
Si noti che, per Uno, l'orologio non è a cristalli, ma utilizza solo un risonatore in ceramica che è meno preciso di un cristallo.
jfpoilpret

@jfpoilpret Ah buono a sapersi. Guardando lo schema , questo sarebbe il dispositivo CSTCE16M0V53-R0 Murata CERALOCK .
Chris O

I risonatori hanno una scarsa tolleranza iniziale (spesso 0,5-2%) e una scarsa stabilità della temperatura, ma se si calibrano i circuiti di temporizzazione quando li si utilizza, possono andare bene finché la temperatura non si muove.
Cybergibbons

2
Millis () funziona ancora su un timer che passa ogni 1.024 ms, ma ha aggiunto la compensazione dell'errore, sotto forma di incremento ogni volta che una variabile del misuratore di errore diventa troppo alta. Penso che sia l'algoritmo di Roman Black in realtà. Quindi il tempismo dovrebbe essere molto più vicino a 1ms esattamente. github.com/arduino/Arduino/blob/master/hardware/arduino/cores/…
EternityForest

Per quelli ancora interessati, vedi il commento che ho pubblicato sulla risposta di JRobert, non volevo rispondere con la mia risposta poiché non ne ho una, ho appena riformulato il problema.
user3284376

2

Se gli interrupt vengono disattivati ​​per una eHealth.getECG()durata significativa della frazione , millis()il conteggio potrebbe rimanere indietro. Altrimenti, millis()dovrebbe restituire un tempo molto più accurato rispetto agli errori 3x che hai descritto.

Hai detto che il tuo segnale campionato appare in frequenza più alta di quanto ti aspettassi, il che potrebbe accadere se la frequenza di campionamento fosse inferiore al previsto. Supponete una frequenza di campionamento di 20Hz? Il tuo loop potrebbe impiegare un po 'più di 50ms, che vedresti nei tempi stampati, ma quelli dovrebbero comunque tenere traccia dell'orologio. Se non lo spiegassi, ma assumessi 50ms / campione, vedresti un'apparente accelerazione dei dati.

Se questo non è il problema, il passaggio successivo sarebbe attivare / disattivare un'uscita mentre ci si trova loop()e misurare la frequenza dell'onda quadra risultante con un misuratore di frequenza (alcuni DVM economici possono farlo) o un 'ambito. Fai la stessa cosa con un vuoto loop(). Il primo esperimento sarà la frequenza o l'intervallo di campionamento reali; il secondo ti dirà se millis()(ad es., la frequenza timer0) è quello che ti aspettavi.


1
Ho continuato a giocarci e ho capito che il problema non è con Arduino, la funzione millis () per la maggior parte funziona molto bene, alcuni dei valori non sono esattamente 8ms (ritardo) a parte ma da cosa hai detto che è prevedibile. L'errore 3x che ho descritto è vero per il lato Python delle cose che sto usando per ricevere i dati. Qualunque idea di cosa potrebbe essere il risultato, sto usando il fisico pyserial di Python ed è lento come l'inferno.
user3284376

Non so abbastanza sulla tua implementazione per darti più di 1/2 @ 'd supposizione, ma il lato Python è abbastanza lento da far cadere campioni?
JRobert,

0

Anch'io. Posso aggiungere che se le interruzioni sono disattivate, il tempo misurato è "in tempo reale". Ad ogni modo, non capisco perché questo ritardo, perché se il ciclo impiega troppo tempo, comunque i millis () dovrebbero restituire valori in tempo reale (solo con più distanza tra ogni valore)


1
A cosa si riferisce "stesso qui"? Le risposte dovrebbero essere autonome, poiché StackExchange può riordinare le cose (a differenza di un forum). Quindi "stesso qui" potrebbe significare qualsiasi cosa a seconda di quale risposta / domanda appare sotto la tua risposta.
Nick Gammon

Questo post sarebbe più appropriato come commento, anche se (certamente) ti manca la reputazione sufficiente.
Greenonline,

Scusa, però, quando rispondi a qualcosa, è ovvio che si riferisce al post principale, altrimenti sarebbe un commento
user48711
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.