Temporizzazione ad alta precisione su Arduino per la comunicazione seriale


11

Sto usando un Arduino Uno per inviare informazioni sul tempo e sulla tensione tramite la porta seriale a Python per tracciare. Tuttavia, i tempi degli intervalli tra i timestamp successivi sembrano aumentare nel tempo, influenzando la mia rappresentazione. Ciò è particolarmente vero quando il baud rate è impostato su 9600, dove le mie differenze di tempo iniziali forse sono 1320 e aumentano a 16400 dopo un periodo di tempo relativamente breve. Quando questa velocità è impostata a un massimo di 115200 bps, la variazione è più lenta e meno evidente, da circa 1340 a 1500 anche dopo un periodo relativamente lungo di invio. Tutti i tempi sono indicati in microsecondi.

Vorrei sapere se posso ridurre o eliminare questo effetto e se non capisco perché esiste. Ho letto cose su interruzioni e ritardi che causano questo, ma non apprezzo pienamente la complessità dell'elettronica a portata di mano e vorrei sapere:

  1. Posso ottenere una maggiore precisione nei tempi?
  2. Cosa causa questo cambiamento nei tempi?

Ecco quello che ho attualmente:

#include <eHealth.h>

extern volatile unsigned long timer0_overflow_count;
float fanalog0;
int analog0;
unsigned long time;    

byte serialByte;
void setup() {
  Serial.begin(9600);
}

void loop() { 
  while (Serial.available()>0){  
    serialByte=Serial.read();
    if (serialByte=='S'){        
      while(1){
        fanalog0=eHealth.getECG();  
        // Use the timer0 => 1 tick every 4 us
        time=(timer0_overflow_count << 8) + TCNT0;        
        // Microseconds conversion.
        time=(time*4);   
        //Print in a file for simulation
        //Serial.print(time);
        //Serial.print(" ");
        Serial.print(fanalog0,5);
        Serial.print("\n");

        if (Serial.available()>0){
          serialByte=Serial.read();
          if (serialByte=='F')  break;
        }
      }
    }
  }
}

Cosa intendi con "preciso"? I tempi indicati dal contatore saranno abbastanza precisi, accurati e con una buona risoluzione. Vuoi che i tempi siano deterministici (cioè sempre gli stessi)?
Cybergibbons,

Ci scusiamo, suppongo che sia quello che intendevo, per la differenza tra loro essere coerenti e, in caso contrario, il motivo per cui non lo sono
user3284376

Aggiungi il timestamp alla fine del PC piuttosto che alla fine di Arduino, oppure usa un modulo RTC (real time clock). I moduli RTC sono piuttosto economici da trovare su vari webstore, assicurati solo che il negozio si colleghi al foglio dati. Un altro metodo è programmare un timer e utilizzare una routine di servizio di interruzione per ottenere un tempismo ragionevolmente accurato.
jippie,

Cosa fa eHealth.getECG()? Quella chiamata dura sempre la stessa quantità di tempo?
jfpoilpret,

Puoi specificare quanto dura un "periodo di tempo relativamente breve"? È sempre lo stesso dopo aver riavviato Arduino?
jfpoilpret,

Risposte:


4

Utilizzare un timer e un ISR (routine di servizio di interruzione) per rendere i tempi più precisi.

Dai un'occhiata al mio Proms of Concept interrotto a tempo . L'idea è di avere un "battito cardiaco" di 1 ms ragionevolmente accurato nel sistema che possa essere utilizzato per innescare altri eventi. Nel PoC viene utilizzato per far lampeggiare un LED a ½Hz, ma avere accesso alle nuove variabili millisecondCountere secondCounterconsente di attivare eventi nel loop principale in momenti arbitrari (ma con tempismo accurato).


2
Il tuo PoC è molto interessante ma ha un difetto (facile da correggere) nel fatto che legge un valore di 2 byte mentre gli interrupt sono abilitati (in loop()), questo valore viene modificato da un ISR. Può succedere che loop()legga un valore negativo (nel mezzo di una modifica da parte dell'ISR). Ho pubblicato un commento sul tuo blog al riguardo.
jfpoilpret,

@jfpoilpret punto interessante che fai lì, mai pensato a un interruzione che si verifica a metà strada recuperando il valore dalla RAM. Stasera controllerò lo smontaggio e aggiornerò l'articolo. Forse un buon motivo per scrivere anche un altro articolo: o)
jippie il

Ho creato un campione dal tuo PoC e ho potuto vedere il problema verificarsi almeno una volta ogni 10 secondi sul mio UNO. Ma ovviamente in realtà dipende molto da quello che fai nel tuo loop(): il mio campione ha appena ottenuto il valore in millisecondi, lo confronta con il valore di lettura precedente e se la differenza> 0 (oltre a ripristinare il contatore su 0), visualizza un messaggio.
jfpoilpret,

@jfpoilpret non l'ha mai notato. Lo uso solo come un battito cardiaco per monitorare i secchi di cibo per i miei gatti e fare un LED flash quando i miei gatti saranno potenzialmente delusi ...; o) Certamente cambierà il modo in cui userò gli ISR ​​in futuro.
jippie,

1
Mostra un cristallo collegato a un blocco ATMEGA16U2 e un risonatore collegato a ATMEGA328P-PU. Il 16U2 è per l'interfaccia seriale, il 328P è "The Arduino". È interessante notare che il 16U2 sarebbe in grado di spingere il suo orologio su un altro chip, ad esempio il 328P.
Udo Klein,

3

Posso pensare ad alcune cose che possono influire sulla "coerenza" dei tempi di scrittura seriale:

  • dimensione dei dati da stampare

questa potrebbe essere la cosa più ovvia a cui pensare, ma in effetti più stampi, più ci vorrà per gestirlo.

Soluzione: stampare formattare la stringa in una stringa di lunghezza nota.

  • usando il seriale bufferizzato

su unix puoi accedere alla porta seriale usando un modo bufferizzato o non bufferizzato. L'uso a lungo del buffer può renderlo un po 'più lento mentre il buffer si riempie, di solito succede quando i dati arrivano più velocemente di quanto tu li legga ...

Soluzione: utilizzare la linea seriale senza buffer ( ad esempio : su Darwin / OSX è /dev/cu.usbmodemXXXinvece di /dev/tty.usbmodemXXX)

  • priorità dei timer

sembra che tu stia usando un interrupt TC e gli AVR hanno priorità nel modo in cui vengono gestiti gli interrupt, non conosco l'ordine di priorità per Atmega328 e non è una delle funzionalità più documentate in circolazione, quindi non lo so quanto è sicuro TC0 rispetto all'interruzione UART.

Soluzione: cercare ulteriormente nella documentazione / scheda tecnica sulle priorità di interruzione e, se necessario, modificare il timer; e / o fare un test senza avere in esecuzione l'altro timer.

  • i dati da cui stai leggendo richiedono più tempo per essere letti nel tempo

alcuni driver devono calcolare la media o eseguire alcune operazioni rispetto ai valori precedenti, quindi maggiore è il valore misurato, maggiore è il buffer e maggiore è il tempo necessario per calcolare il valore, fino a quando non si raggiunge la dimensione massima del buffer.

Soluzione: guarda il codice sorgente della libreria che stai utilizzando e ottimizzalo, rimuovi il calcolo se ce n'è uno o prendi in considerazione quel tempo di elaborazione crescente.

  • evitando il sovraccarico del framework arduino

ma se vuoi davvero ottimizzare l'output seriale dall'arduino, dovresti evitare di usare l'overhead di arduino ... Ma è molto meno elegante e comodo da usare.

Sono abbastanza sicuro che ci siano altri punti che mi mancano, ma sono le prime cose che verificherei prima di approfondire.

HTH


2

Il codice include la durata dell'output nelle misurazioni successive. Pertanto, a seconda della lunghezza dell'output, misurerai tempi diversi. Questo può essere risolto formulando output a lunghezza fissa.

Il prossimo numero è che l'ONU ha una base temporale molto scadente. Dai un'occhiata qui per un confronto tra diversi tipi di Arduino rispetto al riferimento temporale DCF77.

Conclusione: se hai bisogno di un tempismo preciso, procurati un Arduino con cristallo o scegli un RTC. Consiglio vivamente i RTC DS3231 / DS3232 poiché questi di solito raggiungono una precisione di 2 ppm.

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.