funzione millis

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.

Claudio_F: 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);
    }

Non capisco per cosa servono questi 3 pezzi di codice :( :( :(

Comunque il problema strano che ho notato anche adesso è il seguente: Ad inizio come viene acceso ( contate che posso vedere i valori solo da quando parte il loop ) il valore dei secondi parte veloce... Leggermenti piu veloci della realta... Per poi dopo 10 secondi o giu di li rallentare un tantino di piu rispetto alla realta... Non so se vi serve a voi come dato per le vostre discussioni visto che io non ci ho capito gran che... :)

marcobiondo13:
Non capisco per cosa servono questi 3 pezzi di codice

Sono tre modi di usare millis(), il primo è esattamente quello che hai postato tu, che ha sempre come minimo un millisecondo di ritardo che si accumula ad ogni “timeout” (ma anche molto di più, e in modo variabile, se il programma fa altre cose). Quello che non genera questo tipo di errore è solo il terzo (che nel mio esempio commuta un’uscita ogni 10ms ottenendo 50Hz precisi).

Riguardo al comportamento evidenziato… le variabili che contengono il tempo le hai dichiarate di tipo uint32_t? Le inizializzi a zero millis() prima di usarle?

ok quindi la terza porzione di codice la devo semplicemente prendere ed incollare nel mio programma e ottengo un tempo piu preciso? oppure devo dichiare qualche altra variabile e modificare il mio algoritmo per quanto riguarda secondi ore e minuti? perche non l ho capito bene...

non capisco a cosa serve il digital write e quella variabile val ^... e perche la differenza deve essere solo di 10...

comunque le variabili sono tutte long int... e tutte inizializzate a 0... cambia qualcosa tra long int e uint32_t? non l avevo mai sentita quest ultimo tipo di variabile...

Questo è il codice che hai postato:

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

Questo è il codice da usare:

if ((mills_attuali - mills_vecchi) >= 1000){
  secondi++;
  mills_vecchi += 1000;
}

minuti e ore vanno bene

l'inizializzazione va fatta a millis() e non a zero (mi ero sbagliato prima)

uint32_t su Arduino è come unsigned long

Ah ok... Ma io avevo messo il valore 900 e qualcosa perche stavo provando vari valori con cui riuscivo ad ottenere valori piu vicini alla realta... Con 935 mi avvicino abbastanza... Sto facendo varie prove con diversi valori su un ora di tempo...

ps: e long int è lo stesso uguale come variabile?

A mio avviso sarebbe meglio se invece di mantenere una variabile secs aggiornata in quel modo, te la calcolassi al volo ogni volta che ti serve, facendo (millis() - millis_vecchi) / 1000. In questo modo sei sicuro che l’errore non si accumula.

Ciao

ho avuto pure io il bisogno di creare pure io un cronometro (che uso come orologio) e ho avuto diverse disavventure con la millis e alla fine ho risolto definitivamente facendo fare il lavoro di conto ad un altro arduino.

Però fino a quando non mi sono deciso a prendere una nuova scheda avevo tamponato il problema cosi:

void loop(){
tempo=millis(); 

//...codice...

delay(/*sincronizzazione*/);
Serial.println(millis()-tempo);
}

dove tempo è una variabile che ti serve solo per misurare quanto tempo ci sta arduino ad eseguire il tuo codice, e alla fine lo stampa sul seriale. Ovviamente dipende dal tuo codice e dalla sua complessità, aspettati risultati molto bassi (io con arduino mega e un programma complessivo di 1700 righe avevo sui 115 millisecondi)

Scoperto questo numero fai 1000 - tempo_di_esecuzione e metti questo valore al delay

Che ha però i suoi enormi difetti, visto che arduino su 1000 millisecondi ne passa 885 a girarsi i pollici

Potresti migliorarlo con un ciclo for che ripete l’intero main per il resto degli 885 secondi cosi da ridurre al minimo il tempo di inattività di arduino.

In teoria credo tu possa pure fare una cosa tipo:

void loop(){
tempo=millis(); 

//...codice...

while((millis()-tempo)<1000);

}

Ma non l’ho mai testato sinceramente, e inoltre avresti lo stesso problema che ti dicevo sopra

marcobiondo13: Ah ok... Ma io avevo messo il valore 900 e qualcosa perche stavo provando vari valori con cui riuscivo ad ottenere valori piu vicini alla realta...

Lo so che il 900 era per quello, ma il codice "corretto" che ho postato non ha bisogno di alcuna taratura come quelle di cui si sta discutendo, non perde colpi (salvo che li perda la funzione millis stessa), e funziona anche dopo l'overflow di millis (che tra parentesi avviene ogni 1193 ore). Addirittura continuerebbe a funzionare anche usando variabili uint16_t (unsigned int) al posto delle uint32_t (unsigned long), l'overflow in questo caso avverrebbe ogni 65.5 secondi, ma ugualmente non darebbe fastidio.

Nota che l'enorme differenza presente nel codice "corretto" non è aver scritto 1000 invece di 900, ma il modo in cui viene aggiornata la variabile mills_vecchi, il trucco è tutto li (invece il '>=' al posto di '>' è una finezza marginale che non cambia praticamente nulla, a meno che non si realizzi un ciclo molto veloce di pochi millisecondi).

e long int è lo stesso uguale come variabile?

No, int (int16_t) e long (int32_t) sono variabili con segno.

oallora provero il tuo codice e ti farò sapere come va... :) grazie mille... :)

Claudio_F: Questo è il codice che hai postato:

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

Questo è il codice da usare:

if ((mills_attuali - mills_vecchi) >= 1000){
  secondi++;
  mills_vecchi += 1000;
}

minuti e ore vanno bene

l'inizializzazione va fatta a millis() e non a zero (mi ero sbagliato prima)

uint32_t su Arduino è come unsigned long

con questo algoritmo oltre che i primi 10 secondi partono lampo ( ma non mi interessa), va bene.. infatti ottengo un errore di qualche secondo in 1 ora e mi va benissimo cosi,...

marcobiondo13: 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... 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...

bisogna vedere bene che si intendono per 18 secondi, probabilmente se la temperatura varia saranno molti di piu, e poi e una variazione in percentuale magari il tuo arduino va a quasi 17 mhz e non te ne accorgi

meglio verificare l a sincronizzazione con l orologio del pc o un esterno e correggere via software se anticipa o ritarda