giovedì 26 febbraio 2015

Arduino a 5V e corretto interfacciamento tra periferiche 3.3V

Ho notato che spesso si trovano esempi di progetti su internet del tutto inaffidabili.
Un esempio classico è il collegamento di periferiche a 3.3V basate su bus SPI a normali arduini a 5V.
Un errore è quello di pensare che sia sufficiente alimentare la periferica a 3.3V, collegando il bus SPI normalmente senza alcun riduttore di tensione.
Sono incappato anch'io in questo errore, e ho scoperto a mie spese che il bus SPI deve lavorare alla stessa tensione dell'alimentazione della periferica, pena il cattivo funzionamento o addirittura il danneggiamento della scheda.

Vi faccio un esempio. Il classico lettore Rfid RC522 disponibile in molte varianti e marche va alimentato a 3.3V.

L'errore è quello di collegare Vdd a 3.3V di arduino, Gnd a gnd, e poi miso/mosi/sck/rst/sda ai rispettivi pin di arduino.

Devo essere sincero che ho fatto anch'io così e a prima vista sembrava funzionare tutto bene.
Poi quando ho smesso di alimentare il tutto via usb e sono passato all'alimentazione esterna, tutto ha smesso di funzionare.

Qualche giornata a dare la caccia ai fantasmi e poi il dubbio e l'intuizione: non è che alimentando il tutto con un alimentatore esterno si generano dei livelli di tensione più alti che alterano il funzionamento?

Se andiamo a leggere le specifiche NXP che trovate qui http://www.nxp.com/documents/data_sheet/MFRC522.pdf noterete che la tolleranza dell'RC522 è di circa mezzo volt rispetto alla tensione nominale di 3.3V.

Io ho provato in condizioni di alimentazione usb a misurare il livello di un'uscita ttl di arduino: a un valore HIGH non corrispondono 5V ma qualcosina di meno, nell'ordine dei 4V.
Ebbene, se alimentiamo l'arduino con un alimentatore esterno tale livello si alza di un qualcosina, diciamo che si porta attorno a 4.1/4.2V, a seconda delle schede e delle tolleranze dei componenti.
Tale differenza è quella che fa si che l'RC522 smetta di funzionare, perchè l'RC522 tollera (forse) un 4V al posto di un 3.3V, ma non riconosce un segnale più alto, anzi questa tolleranza cambia da scheda a scheda.

Ho avuto la fortuna di disporre di 3 schede RC522 identiche e di poterle intercambiare.
Ebbene, tutte e 3 funzionavano se alimentate da usb. Una sola su 3 funzionava se alimentato il tutto dall'esterno.

La soluzione corretta è l'applicazione di un level converter che converta il segnale dei pin miso/mosi/ecc da 5V a 3.3V.
Essendo il bus SPI bidirezionale ho adottato con successo questo level converter della adafruit.
http://www.adafruit.com/product/757 che costa circa €1.50 e svolge egregiamente il suo lavoro: converte da 5V a 3.3V i segnali SPI da arduino all'rfid e viceversa.

E mi viene da dire: culo che andasse senza level converter e che niente si sia guastato nelle prove.





Bus SPI e cavi lunghi

Ho recentemente montato una versione del mio progetto di rilevazione presenze con rfid, adattandolo ad apri porta con validazione lato server.
Non ripropongo qui il progetto perchè è una leggerissima variante di quello già postato in precedenza per il rilevatore presenze.
La cosa interessante su cui volevo soffermarmi è la difficoltà di portare lontano le periferiche dalla board arduino, in particolare quelle basate sul bus SPI.
Nel progetto incriminato utilizzavo una mega 2560 con a cavallo una ethernet shield e un relè, che doveva costituire la centralina base del mio apriporta e posizionata all'interno dell'abitazione.
All'esterno dell'abitazione veniva posizionato il lettore rfid rc522, un led e un buzzer di segnalazione.
In mezzo, circa 3m di cavo, su cui girava il bus spi e i pin di controllo per led+buzzer.
Sia la ethernet shield che l'rfid utilizzano il bus SPI, alternandosi tramite attivazione via software (pin SDA).
Ho perso circa 2gg a cercare di capire il perchè il tutto funzionasse perfettamente sul tavolo di casa ma appena montato nella sua sede definitiva smettesse di funzionare, in particolare la schede ethernet che pensavo, essendo montata direttamente sopra la board arduino, dovesse essere l'ultima ad avere problemi.
Il problema è che il bus SPI non è fatto per le grandi distanze, anzi è fatto per gestire collegamenti diretti molto molto brevi tra shield.

Dalle specifiche dello standard SPI:

The serial peripheral interface (SPI) bus is an unbalanced or single-ended serial interface designed for short-distance communication between integrated circuits

Appena si introduce un cavo lungo, anche nell'ordine di 1 o pochi metri, si vanno a introdurre disturbi sul bus SPI che alterano in modo sistematico e a volte randomico il funzionamento.
Il bus SPI infatti "gira" a una frequenza tipicamente 1/4 della frequenza del nostro arduino. Su un arduino Uno o Mega 2560, il clock 16MHz va a formare il clock al SPI  con una frequenze pari a 1/4, ovvero 4MHz. Un segnale digitale a 4MHz su un cavo inadatto o in ambiente rumoroso (cavi 220V nelle vicinanze, citofoni, campanelli, ecc) può essere davvero un gran problema.
Uno dei problemi che riscontravo era che nonostante l'rfid fosse scollegato, il solo cavo volante connesso lato arduino al bus SPI portasse gravi disagi all'ethernet shield posta a 1cm dall'arduino board.

Ci sono vari trick&tips per la soluzione del problema.

Consiglio #1

Attenzione al cavo: evitiamo cavi elettrici sfusi o da antifurto. Un cavo di rete cat5/6 può andare benissimo, meglio se schermato (stp). La particolare conformazione del cavo di rete, a coppie twistate, permette scegliendo bene la combinazione di segnali di proteggersi meglio dal rumore.
Ad esempio accoppiando SCK con GND, MISO con Vdd, MOSI con Vdd ho ottenuto ottimi risultati. Tra tutti quandi direi che il segnale di clock SCK è quello più sensibile al rumore.

Consiglio #2

Un'altro fattore importantissimo da non sottovalutare è la frequenza del clock. Più il cavo è lungo, più effetto capacitivo ed induttivo viene introdotto, alterando la forma del segnale digitale. Quello maggiormente sensibile è proprio il segnale di clock (SCK, a volte indicato come CLK).
La frequenza può però essere ridotta via software, impostando un divisore maggiore della frequenza base.

Questa semplice istruzione ad esempio imposta il clock a 16MHz / 128 = 125KHz

SPI.setClockDivider(SPI_CLOCK_DIV128); 

Altri consigli

Ho trovato vari articoli, ma questo mi è stato particolarmente utile:
http://neophob.com/2012/03/lpd6803-spi-and-long-distance
In particolare qui si cita la necessità di introdurre sui pin dati del spi (miso , mosi e anche sul clock sck) una resistenza di basso valore (33-51-100Ohm) al fine di implementare un terminatore per ridurre le reflextion del segnale.
Nel mio caso non ho provato perchè la semplice adozione del cavo twistato e l'abbassamento del clock ha permesso di far funzionare tutto egregiamente, però immagino che nel caso avessi adottato un cavo più lungo tale consiglio sarebbe stato essenziale.
Altri consigliano di mettere una resistenza tra il SCK e la GND, pari a 1K o più.

Parola d'ordine: SPERIMENTARE!