Display 7 segmenti via BCD

Quale è più veloce? La comparazione è più veloce del calcolo del resto, ma rischi che sfugga l'attimo... Certo, se il ciclo gira a più di 1kHz non dovrebbe accadere.

Siamo fuori del seminato, ma basta mettere in loop con un contatore e vedere il risultato dopo un lasso di tempo.

Il codice del post #53 a parte due punti e virgola errati funziona, LED acceso 250ms e spento 750 (circa perché ad ogni variazione si accumula almeno un ms di ritardo dovuto all'errato aggiornamento di previousMillis).

La funzione blink_LED_SEC viene eseguita più di 152mila volte al secondo (misura stumentale commutando il pin dig2 ad ogni ciclo). Considerano che ogni volta chiama la funzione millis e valuta due condizioni doppie con operandi a 32 bit, contenenenti anche un'operazione aritmetica ciascuna, per un microcontrollerino che esegue un compilato C non mi sembra male ;)

Sono contento di scriverlo giusto, però continuo a non capire la storia su previousMillis e elapsed.

Ma io non posso sapere qual'è il punto oscuro... siamo d'accordo che questo codice fa lampeggiare a 1 Hz il LED on board (collegato al pin 13)? È chiaro che la funzione loop continua a richiamare la funzione elabora che continua a calcolare elapsed e controllare se diventa vera la condizione dell'if? E che questa condizione diventa vera quando la differenza tra il valore ritornato da millis() e il valore di controllo control raggiunge o supera 500?

uint32_t control;


void setup(){
    pinMode(13, OUTPUT);
    control = millis();
}


void elabora(){
    uint32_t elapsed = millis() - control;
    if (elapsed >= 500)
    {
        control += 500;
        digitalWrite(13, !digitalRead(13));
    }
}


void loop(){
    elabora();
}

Ho usato il nome control perché previousMillis potrebbe trarre in inganno... non è affatto detto che li dentro ci sia l'esatto valore di millis precedente, c'è il valore da confrontare con millis per capire se è passato abbastanza tempo oppure no per arrivare allo scadere dei successivi 500ms.

In sostanza è chiaro che i valori delle variabili control ed elapsed nel caso ideale si comportano come nel grafico del post #56 (che ho aggiornato)? Le frecce verticali indicano i momenti in cui il valore di elapsed raggiunge 500 e la condizione dell'if diventa vera.

Ah, tutto questo riguarda il realizzare un ciclo continuo temporizzato in modo preciso.

Se invece si vuole realizzare una temporizzazione "one-shot" è sufficiente caricare con millis() la variabile di controllo al momento di partenza della temporizzazione. Nel codice seguente c'è lo stesso ciclo di 500ms di prima, ma allo scoccare dei 500ms accende il LED e attiva una seconda funzione indipendente, che trascorsi 56ms spegne il LED e si autodisattiva:

uint32_t control;
bool     started = false;
uint32_t control2;


void setup(){
    pinMode(13, OUTPUT);
    control = millis();
}


void elabora2(){
    uint32_t elapsed = millis() - control2;
    if (elapsed >= 56)
    {
        digitalWrite(13, 0);
        started = false;
    }
}


void elabora(){
    uint32_t elapsed = millis() - control;
    if (elapsed >= 500)
    {
        control += 500;
        digitalWrite(13, 2);
        control2 = millis();        
        started = true;
    }
}


void loop(){
    elabora();
    if (started) elabora2();
}

No, intendevo che non capisco quando si comincia a contare millis e come mai elapsed varia fino ad andare sopra a 750 mS e 250 mS (uso i miei valori ma anche 500 mS vanno bene). Insomma non riesco a immaginarmi questa variabile cambiare e soprattutto come. Purtroppo anche la tua immagine non mi è d'aiuto.

Seguendo il codice di prima:

previousMillis = millis() inizia a contare dall'accensione del processore

funzione(){ elapsed = millis() - previousMillis inizia a contare da quando parte la funzione e contemporaneamente sottrae al conteggio previousMillis

tipo previousMillis è a 1000 mS, la funzione parte e elapsed sarà mettiamo caso 1000 mS - 2000 mS, perché la funzoine è partita quando previousMillis era a 1000 mS, ma dopo 1000 mS contati dentro elapsed, previousMillis sarà a 2000 mS (1000 mS iniziali più altri 1000 mS). In questo modo elapsed sarà una costante perché previousMillis e elapsed avanzeranno con lo stesso passo e la differenza sarà sempre la medesima.

Io ho capito questo usando queste funzioni, per questo non mi è chiaro.

Forse ho capito il tuo dubbio, ricapitomboliamo....

  • C'è un contatore interno che aumenta da solo.
  • La funzione millis non inizia niente, legge e restituisce il valore del contatore nel momento in cui è eseguita.
  • Il valore prevoiusMillis non aumenta da solo, resta fisso fino a modifica manuale.
  • La differenza millis() - previousMillis aumenta sempre (è il valore chiamato elapsed).

Nel grafico abbiamo in rosso il valore ritornato da millis istante per istante, in azzurro il valore previousMillis (o control che dir si voglia) che viene aggiornato manualmente ("inseguendo" millis()) quando la differenza (elapsed) in verde raggiunge la soglia prefissata.

Quindi praticamente io scrivo una sola volta i millisecondi trascorsi dall’accensione del micro nella variabile previousMillis, dopodiché con elapsed conto i millisecondi togliendo quello che ho scritto in previousMillis e quando questo numero è maggiore del valore, faccio quello che devo fare.

previousMillis legge una volta il valore di millis() che può essere per esempio 10

elapsed = dall’inizio della condizione della funzione inizia a contare - 10

quando elapsed è >= di 750, fai l’istruzione e aggiungi a previousMillis 750

Domanda: ma siccome cambio stato e utilizzo “case”, per cui il contatore serve solo per cambiare stato dopo un certo tot di tempo, non mi conviene fare una variabile elapsed = millis() all’interno dell’IF così mi conta i millisecondi dall’inizio della condizione.

Non capisco perché sia così difficile...

#define LED 13

const int TEMPO=500; // in mS, fino a 32 secondi (unsigned int fino a 65, se vuoi...)
unsigned long t1=0;

setup()
{
pinMode(LED,OUTPUT);
}

loop()
{
if(millis()-t1>TEMPO) {t1=millis(); digitalWrite(LED,!digitalRead(LED));}
}

Ogni volta che fra t1 e millis() trascorre TEMPO, pone t1=millis() (quindi azzera la differenza) e inverte lo stato dell'uscita.

Si ok, in questo caso t1 è "fermo" per cui con millis() conta i millisecondi da un punto fermo.

Mentre quello che dicevo io prima era che la variabile previousMillis era uguale a millis() al di fuori della funzione e pensavo non stesse "fermo" ma variasse in continuazione.

Datman: if(millis()-t1 > TEMPO) {t1=millis(); digitalWrite(LED,!digitalRead(LED));}

In questo modo comunque trascorso TEMPO+δ, dove δ è un ritardo casuale incontrollabile di pochi (o molti) ms rispetto al momento ideale esatto dello scadere di TEMPO, si riparte dal valore attuale di millis() contando un altro TEMPO+δ.

Se non si vuole continuare ad accumulare l'errore δ ad ogni scadere di TEMPO basta aggiornare t1 dell'esatto valore TEMPO, così la differenza millis()-t1 parte già da un certo valore corrispondente all'ultimo ritardo casuale δ:

if(millis()-t1 >= TEMPO) {t1+=TEMPO; digitalWrite(LED,!digitalRead(LED));}

Qui rappresentato graficamente con due ritardi esagerati segnati in giallo:

Se invece non interessa l'esatta periodicità dei tempi, allora questa finezza si può ignorare.

Sì, hai ragione. Non mi sono mai posto il problema, perché non ho mai avuto la necessità di una precisione molto elevata. Certo, se il loop gira a molto meno di 1kHz, quando si accorge che TEMPO è trascorso, ormai potrebbe essere trascorso TEMPO+x.

Grazie per avermelo fatto notare.

Credo di aver finalmente capito. La variabile previousMillis salva una sola volta il valore di millis(), che viene comparato in elapsed. Elapsed contiene millis() per cui ogni loop della funzione aumenta il suo valore fino a che non è maggiore del mio intervallo scelto. A quel punto previousMillis aggiunge il valore dell'intervallo... ma per quale motivo? Perché invece non ho un'unica variabile di tempo che se è maggiore dell'intervallo scelto allora fa quello che deve fare e poi si azzera?

Perdonate la stupidità delle domande, ma non riesco a capire bene la situazione, anche con le immagini, malgrado i vostri sforzi, scusatemi.

Questo codice è molto simile a quello scritto da Datman solo che utilizza una variabile per ricordare lo stato del led. Questa variabile credo si chiami "flag".

#define ledPin 13
byte ledState = 0;
unsigned long previousMillis = 0;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void blinkLed() {
  if (millis() - previousMillis > interval) {
    previousMillis = interval;
    ledState ^= 1;
    digitalWrite(ledPin, ledState);
  }
}

void loop() {
  blinkLed();
}

>ricki158: Devi a studiarti bene come si usa la millis(), prima QUI, poi QUI ed infine leggi anche QUI e QUI … vedrai che ti sarà tutto più chiaro :wink:

Guglielmo

I primi due link li ho appena letti ed hanno partorito quel codice, ora leggo gli altri due. Grazie mille!

Infatti elapsed non serve: basta mettere il calcolo all'interno dell'if().

Io dicevo una cosa del tipo

if(millis()>=intervallo){ fai qualcosa }

ricki158: Io dicevo una cosa del tipo...

La sintassi è valida e può servire a controllare che il valore di millis() abbia superato un certo valore ... ovvero che, [u]dal momento del reset di Arduino[/u] (momento in cui millis() viene azzerato) al momento in cui fai quella IF in quel modo, è passato almeno un tempo pari a "intervallo" ... ... ma è cosa [u]ben diversa[/u] dal voler sapere un intervallo tra un certo momento ed un altro !!!

Guglielmo

Non puoi azzerare millis() impunemente... :) quindi devi usare un'altra variabile.

Quello che non capisco è come mai se faccio un:

#define intervallo 500
unsigned long precedente = 0;

void blink(){
  if(millis() - precedente >= intervallo) {
    precedente=intervallo
    funzione
  }
}

La variabile "precedente" si blocca mentre "millis - precedente" continua a contare. Cioè è così che funziona?

Cioè io mi immagino "precedente" uguale a 0, la funzione parte e finché millis() non arriva all' "intervallo", non succede niente. Quando arriva all' "intervallo" fa la funzione, tra le quali scrive in "precedente" l' "intervallo".

millis() è come se si azzerasse perché in quel momento è uguale a "intervallo", sarebbe come dire

 if(intervallo - 0 >= intervallo)

ma dopo a questa funzione sarà

intervallo - intervallo (cioè 0) >= intervallo

Quindi da 0 conta fino a "intervallo", a "precedente" si aggiunge ancora un intervallo e così via.

Quindi "precedente" deve essere uguale a 0 e non a millis()