vegetto1624:
qualche esempio di come posso trasformarlo da while/for a if + variabile?
Un while
, con dentro due for
, con dentro dei delay
... un esempio più complicato non lo potevi trovare?
Per scrivere in modo non bloccante bisogna esplicitare le fasi di funzionamento, che adesso invece sono implicite nell'ordine di esecuzione delle istruzioni.
C'è una fase di funzionamento in cui i for non devono essere eseguiti (FERMO), c'è una fase in cui con il primo for
il PWM aumenta (FADEIN), e una in cui con il secondo for diminuisce (FADEOUT).
Bene, il progetto del programma deve partire dalle fasi necessarie scritte in modo esplicito:
if (FERMO == fase)
{
}
else if (FADEIN == fase)
{
}
else if (FADEOUT == fase)
{
}
In ogni fase vanno scritti gli eventi attesi e le azioni da compiere.
Nella fase fermo ci si aspetta solo di ricevere il comando di avvio:
if (FERMO == fase && 'c' == command)
{
fase = FADEIN;
}
Per semplificare possiamo scrivere la condizione di arresto con un if
prima delle fasi:
if ('c' != command) { fase = FERMO; }
if (FERMO == fase && 'c' == command)
{
fase = FADEIN;
}
else if ....
Ora le fasi FADEIN e FADEOUT devono comportarsi come i for. Un generico for
è composto così:
for (INIT; CONDITION; UPDATE)
{
ACTION;
}
Bisogna fare in modo che, ad esempio nella fase FADEIN
, le parti INIT
CONDITION
ACTION
e UPDATE
si svolgano nello stesso ordine:
else if (FADEIN == fase)
{
if (inizio) { INIT; inizio=0; }
if (CONDITION) { ACTION; UPDATE; }
else { fase = FADEOUT; }
}
Si vede che serve una variabile flag 'inizio'
per indicare la condizione di prima esecuzione della fase:
if ('c' != command) { fase = FERMO; }
if (FERMO == fase && 'c' == command)
{
fase = FADEIN;
inizio = 1;
}
else if (FADEIN == fase)
{
if (inizio) { fadeValue=0; inizio=0; } // INIT
if (fadeValue <= 250)
{
analogWrite(motorPin, fadeValue); // ACTION
fadeValue += 20; // UPDATE
}
else
{
fase = FADEOUT; // fine ciclo fadein
inizio = 1;
}
}
else if ...
E abbiamo il nostro for
trasformato in non bloccante, MA... nell'ACTION originale dopo l'analogWrite
c'è anche un delay... che qui non possiamo mettere perché, seppur breve, bloccherebbe la libera esecuzione del loop.
E quindi serve un'altra variabile flag 'attesa'
per indicare che il contenuto della fase "è in pausa", cioè non eseguito, fino a timeout. E i tempi vanno calcolati come valore trascorso da un momento iniziale usando la funzione millis. Il momento iniziale è ovviamente l'istante successivo alla analogWrite
(dove c'era il delay
originale).
if ('c' != command) { fase = FERMO; }
if (FERMO == fase && 'c' == command)
{
fase = FADEIN;
inizio = 1;
}
else if (FADEIN == fase)
{
if (inizio) { fadeValue=0; inizio=0; } // INIT
if (attesa && (millis()-t >= 10)) attesa = 0;
if (!attesa)
{
if (fadeValue <= 250)
{
analogWrite(motorPin, fadeValue); // ACTION
t = millis();
attesa = 1;
fadeValue += 20; // UPDATE
}
else
{
fase = FADEOUT; // fine ciclo fadein
inizio = 1;
}
}
}
else if ...
Questo in generale, poi alcune implementazioni specifiche delle fasi possono semplificare alcune cose e ridurre il numero di righe, per esempio si possono spostare gli INIT nelle azioni delle fasi precedenti, e usare una struttura switch
che consente di interrompere l'esecuzione di un case
con break
.
if ('c' != command) fase = FERMO;
switch (fase)
{
case FERMO:
if ('c' == command) { fase = FADEIN; fadeValue = 0; t = millis(); }
break;
case FADEIN:
analogWrite(motorPin, fadeValue);
if (millis()-t < 10) break;
fadeValue += 20;
if (fadeValue <= 250) t = millis();
else { fase = FADEOUT; fadeValue = 250; t = millis(); }
break;
case FADEOUT:
....
break;
}