Una piccola digressione sulla funzione delay()

Salve a tutti, vorrei lanciare un piccolo spunto di riflessione sull'utilizzo della funzione delay() in quanto chiacchierando con alcuni amici programmatori mi hanno fatto riflettere sul fatto che a livello professionale o comunque ad alti livelli questa funzione sia da evitare.

Il principale svantaggio della funzione delay() è che essenzialmente "congela" il codice per il tempo determinato nella funzione, bloccando di fatto tutti i processi che eventualmente avvengono parallelamente. Ora a livello di accendi/spegni led la cosa è accettabile, ma se parliamo di applicazioni un po' più complesse questo svantaggio rischia di diventare una falla pesante nel codice che rallenta di molto l'esecuzione.

Ora la mia domanda è: siccome mi sono trovato spesso ad utilizzare questa funzione mi risulta difficile pensare di programmare senza, e vorrei sentire idee e pareri su come evitarla

Esempio: leggendo un valore di resistenza su un ingresso analogico, io andrei a polarizzare un capo della resistenza e leggerei il valore su un pin analogico collegato all'altro capo, questo processo però necessita di un tempo finito per stabilizzarsi e quindi inserirei un delay di 20 millisecondi alla fine del ciclo..e davvero non saprei come sosituirlo!

Per cui sarebbe bello se chi ne ha voglia condividesse qualche esempio su come evitare di utilizzare la funzione delay, proprio a livello di confronto

Ciao, in effetti il delay è comodo proprio perchè blocca il codice per un tempo x, a volte è utile, altre come ben dici è controproducente se non addirittura dannoso, ora la via più semplice anche se non la più immediata è sfruttare la lettura dei timer interni (ovvero leggi millis che se non ricordo male corrisponde al valore accumulato da timer 0).

Usando Millis o Micros vai appunto a leggere il valore del registro che corrisponde ai millisecondi o microsecondi dall’accensione del micro stesso, tramite un semplice if leggendo questo valore puoi far eseguire una porzione di codice ogni un tot senza dover bloccare l’intero programma…
esempio-> blink whitout delay, è banale ma spiega con semplicità come funzionano i (delay) non bloccanti,se vuoi fare una prova, prendi il programmino detto pocanzi e modificalo in modo da avere due led che lampeggino con tempistiche diverse, li capirai che il processo con millis non è bloccante :slight_smile:

Infatti l'utilizzo del delay è proprio per le applicazioni che indichi tu o per l'attesa di risposte da parte di periferiche. Anzi a volte si usa per alcune applicazioni specifiche delaymicrosecond che in quei casi è insostituibile. Ma stiamo parlando ti tempi piccolissimo. Secondo me un delay non dovrebbe comunque superare i 50 millisecondi. Se deve essere più lungo allora bisogna cambiare algoritmo. Un delay(1000); non ha senso, se non a inizio setup() per evitare problemi con la seriale, ma solo lì. Comunque il delay non congela tutte le periferiche interne: i timer e gli interrupt sono attivissimi anche durante i delay.

TheShredding: ... Il principale svantaggio della funzione delay() è che essenzialmente "congela" il codice per il tempo determinato nella funzione, bloccando di fatto tutti i processi che eventualmente avvengono parallelamente. Ora a livello di accendi/spegni led la cosa è accettabile, ma se parliamo di applicazioni un po' più complesse questo svantaggio rischia di diventare una falla pesante nel codice che rallenta di molto l'esecuzione. ...

Di ai tuoi amici "programmatori" che NON stai lavorando su un sistema multitask/multithread ma su un microprocessore che NON ha un sistema operativo e su cui gira ... un solo task ... quello che scrivi tu ... quindi ... "de che stai a parlà ???" ]:D

NON c'è alcun "processo parallelo" il micro esegue sequenzialmente solo le istruzioni che gli hai scritto tu e, eventualmente, le ISR chiamate (sulla chiamata delle quali la delay() non ha alcun effetto).

Quindi, usa tranquillamente la delay() che non ti cambia nulla. Se invece devi calcolare un tempo prima di fare un qualche cosa, ma nel frattempo vuoi fare un altra cosa, allora usi la millis() per calcolare il passare del tempo :)

Guglielmo

Dire che la delay è da evitare non è del tutto giusto. La delay è un'istruzione che esegue un certo compito, e tale compito può anche essere utile. Basta sapere i pro ed i contro.

gpb01:

TheShredding: ... Il principale svantaggio della funzione delay() è che essenzialmente "congela" il codice per il tempo determinato nella funzione, bloccando di fatto tutti i processi che eventualmente avvengono parallelamente. Ora a livello di accendi/spegni led la cosa è accettabile, ma se parliamo di applicazioni un po' più complesse questo svantaggio rischia di diventare una falla pesante nel codice che rallenta di molto l'esecuzione. ...

Di ai tuoi amici "programmatori" che NON stai lavorando su un sistema multitask/multithread ma su un microprocessore che NON ha un sistema operativo e su cui gira ... un solo task ... quello che scrivi tu ... quindi ... "de che stai a parlà ???" ]:D

NON c'è alcun "processo parallelo" il micro esegue sequenzialmente solo le istruzioni che gli hai scritto tu e, eventualmente, le ISR chiamate (sulla chiamata delle quali la delay() non ha alcun effetto).

Quindi, usa tranquillamente la delay() che non ti cambia nulla. Se invece devi calcolare un tempo prima di fare un qualche cosa, ma nel frattempo vuoi fare un altra cosa, allora usi la millis() per calcolare il passare del tempo :)

Guglielmo

Non sono d'accordo con quanto dici, nel senso che non la vedo così. Certo, Arduino ha una serie di limitazioni per cui la cosa è al limite dell'inifluenza, ma io lo vedo anche come uno strumento didattico che mi porti a lavorare su strutture e architetture più sofisticate. Abituarsi fin dall'inizio ad evitare quelle imperfezioni del codice che in futuro potrebbero diventare problematiche aiuta a non trovarsi a dover reimparare le basi una volta che si va più sul difficile.

è lo stesso motivo per cui quando programmo su Arduino cerco di usare il più possibile funzioni ed interrupt anche se ai fini pratici non sarebbe necessario, perchè poi quando lo diventerà per me necessario, avrò comunque già una mente tarata a lavorare su determinati schemi.

La mia educazione scientifica inoltre mi spinge a cercare sempre e comunque l'eleganza e la perfezione formale di quello che faccio, da cui non deriva sempre un'immediatezza pratica, ma porta ad una solidità di ragionamento che permette di affrontare allo stesso modo il problema molto semplice e il problema più complicato.

Giusto per la cronaca, i miei amici "programmatori" sono effettivamente ingegnieri informatici professionisti che per mestiere programmano microcontrollori per strumentazione digitale, e fino ad ora i loro consigli si sono rivelati sempre di alto livello (forse suona polemico il tono della frase, ma giuro che non lo è :))

Tranquillo, nessuna polemica ... faccio il loro mestiere semplicemente da ... 40 anni ... :grin:

Non fare l'errore che fanno molti ... Arduino è un bello strumento, ma, come tutti i microcontrollori a 8 bit, non è certo nato per fare elaborazioni multitask/multithread e quindi ... vorrei capire quando la delay() ferma il codice ... tu cosa pensi che altro potrebbe girare ???

Poi se non ti piace usare la delay() e vuoi usare la millis() perché ... ti sembra più elegante o perché hai altre ragioni ... fai come vuoi, ma non venire a dire che "rallenta di molto l'esecuzione" perché ... compito della delay, salvo le ISR, è proprio di fermarla l'esecuzione XD

Guglielmo

P.S. : Sono dottore in Fisica con specializzazione in Elettronica e Cibernetica ;)

TheShredding: ... Per cui sarebbe bello se chi ne ha voglia condividesse qualche esempio su come evitare di utilizzare la funzione delay, proprio a livello di confronto

... e per evitare di usare la delay() leggiti quest'articolo (e anche gli altri che troverai li) di Leo : http://www.leonardomiliani.com/2013/programmiamo-i-compiti-con-millis/ :)

Guglielmo

Ho iniziato a lavorare con arduino da poco ma di programmazione ne mastico abbastanza (a breve diventerò collega dei tuoi amici :) ), e mi inserisco nel discorso. Il "problema" dei microcontrollori in generale è il fatto che l'ottimizzazione del software è sempre strettamente legata all'hardware sottostante. Quindi nella stragrande maggioranza dei casi le ottimizzazioni del codice andranno rifatte per ogni tipo di microcontrollore su cui riporterai il tuo eseguibile. Quelle che tu dici potrebbero diventare "imperfezioni", lo diventano nel momento in cui non hai la flessibilità mentale per adattare quanto appreso a nuovi contesti . È un po la differenza che c'è (ed i tuoi amici ingegneri possono confermartelo) tra chi ha studiato senza cognizione di causa l'informatica, e chi ha capito veramente quello che sta facendo. Infatti capendo quello che si sta facendo e non limitarsi solo ad imparare "a memoria" il linguaggio di programmazione, si diventa molto piu flessibili ed adattabili in vari contesti. Ho conosciuto colleghi del corso di ingegneria che nonostante avessero voti altissimi in tutte le materie non informatiche, nel momento in cui sono dovuti passare da un linguaggio OO al C, si sono messi le mani nei capelli.

gpb01: Tranquillo, nessuna polemica ... faccio il loro mestiere semplicemente da ... 40 anni ... :grin:

Non fare l'errore che fanno molti ... Arduino è un bello strumento, ma, come tutti i microcontrollori a 8 bit, non è certo nato per fare elaborazioni multitask/multithread e quindi ... vorrei capire quando la delay() ferma il codice ... tu cosa pensi che altro potrebbe girare ???

Si, capisco l'obiezione, il problema non sussite su arduino, ma su roba più complessa, alla quale punto ad arrivare sussisterà e mi voglio abituare sin da subito.

Per dire, ho un progettino che trasmette dati da un touchpad (forse avevi risposto proprio tu al post in cui chiedevo aiuto) e la continua lettura con un delay impostato nel ciclo mi preoccupa possa creare un "lag" notevole nel momento in cui il progetto andrà ad arricchirsi con altri pezzi che richiederanno dei delay per essere letti e trasmessi via seriale, ma forse sono paranoie da principiante

gpb01: P.S. : Sono dottore in Fisica con specializzazione in Elettronica e Cibernetica ;)

Siamo quasi colleghi, Dottore Magistrale in Chimica Fisica, specializzato in Elettrochimica applicata a sistemi bioinorganici :)

Ho la terza media. Posso scrivere sul forum? ]:D

PaoloP: Ho la terza media. Posso scrivere sul forum? ]:D

...

GRANDE !!!

Guglielmo

PaoloP: Ho la terza media. Posso scrivere sul forum? ]:D

Per rispondere a questa domanda devo valutare necessariamente tutte le possibili risposte e calcolare i vari effetti causali che ciascuna risposta implicherebbe. Con un'equipe di 35 ricercatori, 16 supercomputer e 4 mesi di ricerca posso darti una risposta.

Credo comunque si possa fare. XD

La risposta mi pare l'abbia già trovata Douglas Adams: 42 :grin:

@TheShredding:
se parliamo di estetica del codice, se parliamo di scrivere una funzione in un modo piuttosto che un’altra OK. Posso usare un paio di if al posto di un while, il risultato è identico.

Però su un micro ad 8 bit dove non c’è un SO, dove non ci sono processi paralleli, dove l’unica altra cosa che può girare al posto del loop principale è una ISR, usare un delay è identico ad usare un controllo su millis.
Cioè:

delay(500);

equivale a:

unsigned long tempMillis = millis();
while (millis - tempMillis < 500);

A livello interno del microcontrollore non rallenti nulla nel primo caso, non lo fai neanche nel secondo.
Delay è stata scritta per stare fermo un attimo, ed alle volte serve. Serve ad esempio quando devi dar modo ad un sensore di preparare il dato in risposta. Oppure per mille altri motivi.

Se poi mi dici che nel frattempo vuoi fare altre cose allora è un altro discorso.

PaoloP:
La risposta mi pare l’abbia già trovata Douglas Adams: 42 :grin:

Gran libro XD

leo72:
@TheShredding:
se parliamo di estetica del codice, se parliamo di scrivere una funzione in un modo piuttosto che un’altra OK. Posso usare un paio di if al posto di un while, il risultato è identico.

Però su un micro ad 8 bit dove non c’è un SO, dove non ci sono processi paralleli, dove l’unica altra cosa che può girare al posto del loop principale è una ISR, usare un delay è identico ad usare un controllo su millis.
Cioè:

delay(500);

equivale a:

unsigned long tempMillis = millis();

while (millis - tempMillis < 500);




A livello interno del microcontrollore non rallenti nulla nel primo caso, non lo fai neanche nel secondo. 
Delay è stata scritta per stare fermo un attimo, ed alle volte serve. Serve ad esempio quando devi dar modo ad un sensore di preparare il dato in risposta. Oppure per mille altri motivi.

Se poi mi dici che nel frattempo vuoi fare altre cose allora è un altro discorso.

Si, diciamo che il mio punto di vista può apparire un po’ filosofico, ma ne è nata una conversazione che mi ha dato degli spunti interessanti. Ad esempio, ho concluso che delay corti sono molto comodi, ma se devo usare delay lunghi, è meglio usare altre funzioni. O cercare altre strade.

Poi, deviazioni metafisiche a parte, mi interessava sentire anche altre opinioni sull’argomento

Scusa se mi inserisco, quale sarebbe il sistema migliore per gestire il controllo del tempo fra due eventi, in modo ricorsivo (resettabile) ? ... ad esempio, poniamo che debba premere una sequenza di tasti, ma in modo che fra un tasto e l'altro non passi mai piu di un certo tempo, diciamo un secondo e mezzo, altrimenti si deve resettare la sequenza ed eseguire un'altra routine ... il delay credo non si possa usare, perche' se blocca l'esecuzione dopo la pressione del primo tasto, non potrebbe rilevarmi i successivi, giusto ? ... quindi, dato che delay blocca, suppongo la millis ... ma come usarla in modo ricorsivo ed autoresettante ? ... in una serie concatenata di IF ? ... o c'e' un modo piu efficente ?

@TheShredding:
in un esempio tipo il Blink, dove il codice non deve fare altro che far lampeggiare un led, a che pro andare a complicarsi la vita per ottimizzare un delay? :wink:

@Etemenanky:
io uso un sistema con un while ed un timeout.
Esempio:

boolean pressed = false;
unsigned long tempMillis = millis();
do {
    if (digitalRead(pin)) { //pulsante premuto
        pressed = true;
        break;
    }
} while ((millis() - tempMillis < 10000) && (!pressed)); //10 secondi di timeout

Esco dal while per timeout oppure perché l’utente ha premuto il pulsante. Dopo il while mi basta verificare lo stato di “pressed”. Se la trovo su “true” l’utente ha premuto il pulsante prima del timeout, altrimenti so che il ciclo while è terminato per tempo scaduto.

leo72: @TheShredding: in un esempio tipo il Blink, dove il codice non deve fare altro che far lampeggiare un led, a che pro andare a complicarsi la vita per ottimizzare un delay? ;)

Beh, ok, ma io parlo di progetti con un livello di complessità un po' più elevato..con degli interrupt, due o tre funzioni

Intanto un interrupt snobba il delay perché una ISR viene eseguito comunque. Se parliamo di progetti più importanti, avevo già specificato che "e poi mi dici che nel frattempo vuoi fare altre cose allora è un altro discorso." Difatti nel mio looper ad esempio, che è uno scheduler basato su millis, ho introdotto un nuovo tipo di delay che ferma l'esecuzione del codice ma nel contempo esegue anche i job che sono stati schedulati, per permettere appunto al programmatore di avere un sistema di attesa che non "congeli" il resto dei compiti del programma.