ARDUINO
Funzione millis la sintesi
Il contatore
Arduino dispone di un
contatore a 32 bit che incrementa automaticamente ogni
millisecondo e
contiene i millisecondi trascorsi dall’avvio del sistema.
Il contatore
all’accensione parte da zero, dopo circa 1193 ore raggiunge il valore massimo
(4294967295),
poi ritorna a zero e ricomincia ad incrementare.
Annotare il tempo attuale
Il
valore attuale del contatore si legge con la funzione
millis.
Nel momento esatto da cui si vuole iniziare misurare il tempo
il valore ritornato da millis va salvato in una variabile di tipo unsigned long (o uint32_t).
iniziale = millis();
Calcolare il tempo trascorso
Il
tempo trascorso è semplicemente il
valore attuale ritornato da millis
meno il valore di partenza salvato. Anche il valore ‘trascorso’ è di tipo unsigned long:
trascorso = millis() - iniziale;
Il codice seguente usa la funzione millis per simulare esattamente una chiamata
alla funzione delay(10000):
uint32_t iniziale = millis();
while ((millis() - iniziale) < 10000) { }
Nota: questo sopra è solo un esempio, usare millis in questo modo è
del tutto inutile in quanto svolge l’identica funzione di
delay.
Un uso più sensato è in abbinamento con la
programmazione a stati.
Il (falso) problema dell’overflow
Quello riportato qua sopra è l’unico modo corretto per
calcolare il tempo trascorso senza incorrere in alcun problema con
il ritorno a zero del contatore (il famoso e temuto overflow).
Effettuando la differenza tra numeri interi con conteggio circolare a
32 bit, il risultato viene sempre giusto.
Invece
tutti gli altri sistemi di confronto diretto tra il valore di
millis e qualcos’altro possono portare a malfunzionamenti del programma
anche dopo molti giorni di funzionamento impeccabile.
Ad esempio
la riga seguente è errata, perché da per scontato che
il valore ritornato da millis sia sempre crescente senza limiti, ma
questo non è vero. In questi casi il confronto in certi momenti può
generare un risultato molto diverso da quello atteso e
causare malfunzionamenti:
if (millis() > (accensione + 2000)) { .... }
Il problema è ben evidente nel disegno seguente a destra: se il tempo finale
desiderato sono le 3, e noi effettuiamo il confronto tempo_attuale>finale,
la condizione risulta vera anche alle 11, il che è evidentemente un errore.
Effettuando invece una sottrazione circolare, cioè contando indietro di 10 passi,
otteniamo sempre l’esatto tempo trascorso.
Quindi la forma corretta è sempre:
if ((millis() - accensione) > 2000) { .... }
Ottenere intervalli periodici precisi
Per ottenere una
cadenza periodica precisa è necessario usare la forma
seguente. La cosa più importante è
incrementare la variabile tempo ‘t1’
esattamente del valore di ‘periodo’.
Con questa forma ci si può aspettare
un errore di qualche secondo all’ora
dipendente solo dall’imprecisione dell’oscillatore.
if ((millis() - t1) >= periodo)
{
t1 += periodo;
....
}
Il momento in cui viene valutata la condizione potrebbe essere in ritardo rispetto all’esatto istante ideale di scadenza del periodo (ritardo evidenziato in giallo nel disegno seguente). Incrementando la variabile tempo dell’esatto valore del periodo, si “insegue” sempre correttamente l’aumentare di millis(), spostando il prossimo riconoscimento all’esatto scadere del prossimo intervallo:
Invece
la forma seguente è errata. Ogni volta che la condizione viene valutata
in ritardo rispetto al momento ideale, “reimpostando” la variabile
‘inizio’ al tempo attuale, questo ritardo si aggiunge a tutti i ritardi precedenti.
Con questa forma ci si può aspettare un errore di diversi secondi al minuto o
anche peggiore.
if ((millis() - inizio) >= periodo)
{
inizio = millis();
....
}
Il disegno seguente (in basso) mostra visivamente l’accumulo del ritardo (frecce grige) rispetto agli istanti periodici ideali. Il riconoscimento degli istanti ideali avviene invece nel primo caso (anche se il ritardo nella valutazione della condizione introduce comunque un piccolo jitter casuale rispetto agli istanti ideali).