funzione millis

ciao a tutti. per la prima volta uso la funzione millis... ma sto avendo qualche problema.. la funzione mi serve semplicemente per sapere da quanto tempo è acceso arduino e al massimo arrivo a 3 ore.. quindi nessun overflow o simili.. il problema si presenta perche va in ritardo.. mi spiego meglio: dopo qualche minuto mi ritrovo arduino che sta indietro anche di 3/5 secondi rispetto al tempo reale e non ne capisco il motivo. quindi la mia domanda è la seguente: siccome su arduino girano tanti cicli for e varie funzioni è possibile che siccome il codice è pesante arduino non riesca a fornire un valore preciso con la funzione millis? perche per far equivalere un secondo nella velocita reale mills deve arrivare a circa 900 invece dei canonici 1000. leggevo su questo forum che la funzione millis non dovrebbe essere influenzata dal codice... ma è un contatore autonomo. quindi come mai nel mio caso c è questo problema? da precisare che come hardware uso arduino uno.

Purtroppo la millis() NON è affatto precisa per tempi lunghi (vari fattori influenzano il clock tra cui, ad esempio, le variazioni di temperatura) :confused: e, ove vi sia la necessità di avere tempi esatti, occorre necessariamente utilizzare un RTC esterno (... consiglio uno basato su DS3231).

Guglielmo

P.S.: Nota inoltre che il suo avanzamento è effettuato tramite un interrupt legato ad un timer. Ora, se la tua applicazione usa anche essa gli interrupt, ricorda che nelle ISR gli interrupt vengono disabilitati (... sugli AVR a 8 bit NON esistono livelli di priorità degli interrupt) e quindi ... millis() in quel tempo NON può avanzare.

gpb01: Purtroppo la millis() NON è affatto precisa per tempi lunghi (vari fattori influenzano il clock tra cui, ad esempio, le variazioni di temperatura) :confused: e, ove vi sia la necessità di avere tempi esatti, occorre necessariamente utilizzare un RTC esterno (... consiglio uno basato su DS3231).

Guglielmo

P.S.: Nota inoltre che il suo avanzamento è effettuato tramite un interrupt legato ad un timer. Ora, se la tua applicazione usa anche essa gli interrupt, ricorda che nelle ISR gli interrupt vengono disabilitati (... sugli AVR a 8 bit NON esistono livelli di priorità degli interrupt) e quindi ... millis() in quel tempo NON può avanzare.

grazie per la risposta innanzitutto, allora non mi servono tempi esatti, solo che pensavo fosse piu preciso, pensavo che in un range massimo di 3 ore avrei avuto massimo qualche secondo di errore. ma se questi secondi si presentano subito dopo qualche minuto allora mi crea qualche problema.. visto che in giro di 3 ore potrei avere anche 10 min di errore. comunque non so di cosa stai parlando con gli interrupt: il mio programma consiste nella lettura di 2 valori, fare una media e attraverso una scala crearmi 2 bar progression su lcd, poi calcolarne i valori medi e visualizzate tutto sull lcd i2c. contare che la progression bar me la sono dovuta creare io perche le librerie gia pronte non funziovan quindi magari non sono proprio ottimizzate e sto usando la libreria PCF8574+HD44780 LCD I2C per la gestione dell lcd perché è l unica che funzioanva

marcobiondo13: allora non mi servono tempi esatti, solo che pensavo fosse piu preciso, pensavo che in un range massimo di 3 ore avrei avuto massimo qualche secondo di errore. ma se questi secondi si presentano subito dopo qualche minuto allora mi crea qualche problema.. visto che in giro di 3 ore potrei avere anche 10 min di errore.

Non è detto, ma può capitare ... ... ho qualche circuito a bassa precisione che usa millis() e a livello di codice ho previsto io, facendo delle prove di alcuni giorni, dei fattori di correzione software.

Come detto, ti consigli un RTC esterno basato con DS3231 ... si collega anche lui in I2C e ... ormai te li tirano dietro per pochi soldi :D :D :D

Guglielmo

I risuonatori ceramici esistono in varie classi di precisione, quelli migliori sono al 0.07%, su Arduino è montato un risuonatore con classe di precisione 0.5%, questo significa che la frequenza reale è compresa tra 16 MHZ +/- 80 kHz, purtroppo anche la stabilità oscilla tra questi valori nel tempo, la frequenza non è stabile. In pratica su un'ora di conteggi, al netto degli errori introdotti da eventuali interrupt, hai un errore compreso tra +/- 18 secondi, con un computo dello stesso variabile nel tempo per via dell'instabilità della frequenza. Con un quarzo le cose andrebbero molto meglio, errore minore di +/-2 secondi ogni ora e abbastanza stabile, quindi facilmente compensabile.

gpb01: Non è detto, ma può capitare ... ... ho qualche circuito a bassa precisione che usa millis() e a livello di codice ho previsto io, facendo delle prove di alcuni giorni, dei fattori di correzione software.

Come detto, ti consigli un RTC esterno basato con DS3231 ... si collega anche lui in I2C e ... ormai te li tirano dietro per pochi soldi :D :D :D

Guglielmo

ho anche il DS3231 ma cosi andrei a finire gli ingressi analogichi.. infatti mi servono 2 ingressi analogici per le misurazioni del potenziometro, 2 per l lcd e quindi me ne rimane solo uno... quindi o riesco a calibrare cosi l arduino oppure elimino la funzione o la lascio imprecisa...

astrobeed: I risuonatori ceramici esistono in varie classi di precisione, quelli migliori sono al 0.07%, su Arduino è montato un risuonatore con classe di precisione 0.5%, questo significa che la frequenza reale è compresa tra 16 MHZ +/- 80 kHz, purtroppo anche la stabilità oscilla tra questi valori nel tempo, la frequenza non è stabile. In pratica su un'ora di conteggi, al netto degli errori introdotti da eventuali interrupt, hai un errore compreso tra +/- 18 secondi, con un computo dello stesso variabile nel tempo per via dell'instabilità della frequenza. Con un quarzo le cose andrebbero molto meglio, errore minore di +/-2 secondi ogni ora e abbastanza stabile, quindi facilmente compensabile.

si ma 18 secondi in un ora mi vanno benissimo... esagerando avrei un minuto in piu in 3 ore di conteggio... e mi andrebbe benissimo... pero attualmente è molto piu grande l errore che ottengo...

marcobiondo13: .. pero attualmente è molto piu grande l errore che ottengo...

Che errore misuri ? Hai un Arduino originale oppure un clone/compatibile ? Su quelli cineseria i risuonatori montati sono i più economici possibili con classe di precisione anche del 2%. Altro fattore di errore sono gli interrupt che possono portare a perdite di count sulla millis(), anche questo fattore può incidere non poco sulla precisione.

il mio è un arduino uno originale... comunque no... non uso degli interrupt.. almenoche la libreria che uso non ne abbia qualcuno interno e io non ne sia a conoscenza. la libreria che uso è questa PCF8574+HD44780 LCD I2C

marcobiondo13: ho anche il DS3231 ma cosi andrei a finire gli ingressi analogichi..

... NON ho capito ... :o

Tu [u]stai già usando un device I2C/u quindi già hai impegnato A4 ed A5 ... usi sempre quelli ! Lo sai come funziona un bus I2C ? Eventualmente studia QUI ;)

Guglielmo

P.S.: Confermo che le librerie che gestiscono il bus I2C utilizzano gli interrupt.

ok allora probabilmente è la libreria stessa che mi causa questo ritardo... ma sara un ritardo costante? oppure variabile secondo voi? comunque no, di preciso non so bene come funziona, appena ho un po di tempo guardo bene.. vorrei solo sapere una cosa: se utilizzo 2 moduli i2c il display avrebbe un leggero ritardo visto che arduino ne deve gestire 2 di i2c? oppure posso stare tranquillo e il display avrà sempre la stessa risposta? questa domanda non vuole prendere il considerazione il codice di arduino... diciamo a parità di codice :)

marcobiondo13: ... vorrei solo sapere una cosa: se utilizzo 2 moduli i2c il display avrebbe un leggero ritardo visto che arduino ne deve gestire 2 di i2c? oppure posso stare tranquillo e il display avrà sempre la stessa risposta?

... parliamo di millisecondi ... nemmeno te ne accorgi ... ;)

Guglielmo

marcobiondo13: ok allora probabilmente è la libreria stessa che mi causa questo ritardo... ma sara un ritardo costante? oppure variabile secondo voi?

Oltre a imprecisione dell'oscillatore, ed eventuali interrupt che falsano il valore di millis, c'è anche una terza possibilità: aver usato male millis perdendo un po' di tempo ad "ogni giro". In sostanza, il codice che usi riguardo a millis qual'è?

lo spezzone di codice è il seguente:

mills_attuali= millis ();


if ((mills_attuali - mills_vecchi)>900){
  secondi++;
  mills_vecchi= mills_attuali;
}
if (secondi >59){
  minuti++;
  secondi=0;
}

if (minuti >59){
  ore++;
  minuti=0;
}

Claudio_F: ..., c'è anche una terza possibilità: aver usato male millis perdendo un po' di tempo ad "ogni giro".

In che senso ? :o

Salvo non disabilitare gli interrupt, il codice NON ha alcuna influenza sull'avanzamento del contatore ritornato da millis().

Guglielmo

C'è anche un'altra possibilità, più frequente di quanto si possa immaginare, se il tempo di esecuzione di una risposta a interruzione a priorità più alta del timer usato da millis dura più del doppio del periodo del timer usato da millis allora quella risposta a millis viene persa

icio: C'è anche un'altra possibilità, più frequente di quanto si possa immaginare, se il tempo di esecuzione di una risposta a interruzione ....

Si, si, l'avevamo accennato all'inizio ... ;) ... come detto gli AVR a 8 bit NON hanno la possibilità di selezionare i livelli di priorità (... ed i relativi sotto-livelli) di un interrupt, per cui, se si entra in una ISR (in cui gli interrupt vengono disabilitati) e vi si rimane troppo tempo ... ecco qui che il contatore letto da millis() non viene più incrementato, con le ovvie conseguenze.

Guglielmo

OOps, mi era sfuggito, sorry! ::)

gpb01: Salvo non disabilitare gli interrupt, il codice NON ha alcuna influenza sull'avanzamento del contatore ritornato da millis()

Vero, il contatore non subisce alcuna variazione, ma il valore memorizzato nella variabile 'mills_vecchi' può non avanzare come ci si aspetta, infatti dalle misure strumentali:

  mills_attuali = millis();
   if ((mills_attuali - mills_vecchi) > 10){   // 45.3 Hz, il caso in questione
       mills_vecchi = mills_attuali;
       val ^= 1;
       digitalWrite(OUT_PIN, val);
   }
   mills_attuali = millis();
    if ((mills_attuali - mills_vecchi) >= 10){  // 49.8 Hz, i tutorial sull'uso di millis
        mills_vecchi = millis();
        val ^= 1;
        digitalWrite(OUT_PIN, val);
    }
   mills_attuali = millis();
    if ((mills_attuali - mills_vecchi) >= 10){  // 50.0 Hz, errore solo di quarzo+interrupt
        mills_vecchi += 10;
        val ^= 1;
        digitalWrite(OUT_PIN, val);
    }

Il contenuto di 'mills_vecchi' potrà subire uno sfasamento in funzione alle capacità del programmatore di aggiornarla correttamente (il loop deve durare SEMPRE meno di 1mSec) ma comunque millis() restituirà sempre un valore corretto perchè aggiornato in interrupt anche , ad esempio, se nel main c'è una funzione bloccante.

Certo, è normale che ci sia un certo jitter sui "momenti di riconoscimento", e millis() ritorna sempre il valore corretto (salvo interrupt), il problema è che se si vuole un conteggio periodico del tempo la variabile mills_vecchi deve avanzare sempre della stessa quantità ad ogni ciclo (l'ultimo esempio), mentre con i primi due esempi non lo fa, accumula inevitabilmente un ritardo, che ritengo essere quello riscontrato da marcobiondo.