lunedì 21 novembre 2016

Analog repetita iuvant

Il punto di contatto tra il mondo sensoriale che ci circonda, la nostra realtà analogica, e quello digitale dei controllori e processori, è senza dubbio la conversione analogico/digitale (A/D). Questo confine è, nella maggior parte dei casi, considerato solo e soprattutto nella sua natura puramente funzionale o idealizzata, cioè quello di ottenere un numero dalla misura di una grandezza fisica. Non sempre però vengono considerate le implicazioni fisiche dei componenti che realizzano il passaggio e le non-idealità del processo che lo permette.
La realtà però, prima o poi, ti costringe a guardare meglio come la conversione avviene, spesso dopo aver fatto impazzire lo sviluppatore non abbastanza esperto, come me.
L'aver introdotto un sensore di temperatura basato sul notissimo LM35, durante una fase di test di ClEnSensors, mi ha permesso di fare questa esperienza che ritengo possa essere utile da condividere, soprattutto per chi lavora con Arduino per realizzare prototipi di sensori analogici.

Partiamo dal problema. Durante i primi test con LM35, la lettura da questo sensore integrato attraverso la porta A0 di Arduino risultava instabile e con una forte oscillazione.

Come dicevo, la tendenza ad idealizzare tutto quel che riguarda i comportamenti dei controllori digitali, mi spingeva a cercare nell'LM35 il responsabile del comportamento inatteso, escludendo l' "infallibile" Arduino.

La lettura del datasheet dell'integrato non dava però risposte convincenti. Di esso ho solo seguito il consiglio di inserire una resistenza da 1-2 KOhm sulla linea della misura dell'LM35, per evitare di sovraccaricare l'uscita dell'integrato con la grossa capacità del cavo e dello stadio di ingresso del convertitore A/D. 
Tuttavia il problema era ancora là: le letture di A0 da Arduino oscillavano tra valori anche molto diversi tra loro (tra 100 e 15) che non aveva davvero nemmeno l'aria di un rumore. Era proprio un funzionamento sbagliato!
Cavalcando lo strumento principe dei Makers in difficoltà (cioè Google) mi sono imbattuto in un forum in cui un esperto spiegava come è necessario del tempo tra l'inizio di esecuzione di analogRead() e la possibilità di avere una lettura veritiera. Inoltre veniva descritto come la multiplazione dell'A/D sui 6 canali di conversione A0...A5 ha delle implicazioni sulla possibilità di leggere il valore corretto.

Va detta a questo punto una circostanza importante. Arduino dispone di un solo convertitore A/D e ciascuno dei 6 ingressi analogici viene selezionato di volta in volta quando viene eseguita la analogRead() su quell'ingresso analogico. E come se Arduino, nell'eseguire analogRead(A1) debba prima selezionare il canale A1 e solo dopo leggere il valore analogico. Questa "selezione" è una commutazione logica (CMOS per chi conosce la microelettronica) e diventa particolarmente gravosa quando la lettura su di un canale Ax segue quella di un canale diverso Ay.
In questo caso il tempo di selezione può essere più lungo di quanto impiega Arduino a campionare il valore analogico. La conseguenza e che il valore letto può essere anche molto diverso dal valore effettivo presente sul piedino Ax. Questa viene in gergo chiamata "race condition"

A parer mio questo potrebbe essere considerato addirittura un difetto (un bug) di Arduino, perché non è logico aspettarsi che la grande maggioranza dei "tinkerers" e DIY'ers siano in grado di prevedere questo rischio. Ma tant'è.

La soluzione proposta è quella di leggere 2 volte consecutive dal pin analogico Ax se necessita di avere una lettura stabile dopo aver fatto un'altra lettura analogica su Ay. 
Come nel codice che segue, preso da ClEnSensors. 

measures = measures + "00" + String(analogRead(PIN_ANALOG1)) + ":01" + String(analogRead(PIN_ANALOG2));

Quando questa riga viene eseguita da dentro al loop(), si capisce come il convertitore A/D legga alternativamente i due diversi pin analogici nel flusso di esecuzione, rendendo quindi lo sketch soggetto al problema descritto prima.

Ho allora modificato tale riga in questo modo

ams = analogRead(PIN_ANALOG1);
ams = analogRead(PIN_ANALOG1); // Read TWICE to ensure MUX switch is stable
measures = measures + "00" + String(ams);
ams = analogRead(PIN_ANALOG2);
ams = analogRead(PIN_ANALOG2); // Read TWICE to ensure MUX switch is stable  
measures = measures + ":01" + String(ams);

e con grande soddisfazione ho constatato che la lettura analogica a questo punto era corretta e stabile!

Non c'è nulla di originale in questo, la soluzione l'ho trovata a colpi di Google
Tuttavia condividere un'esperienza è sempre utile, soprattutto quando porta un insegnamento: cioè quello di non dimenticare che Arduino è fatto di componenti reali e come tale possono avere tempi di commutazione ben più lunghi di quelli che nel nostro immaginario governerebbero le funzioni digitali. 


Nessun commento:

Posta un commento