Controllo radio per tabellone segnapunti - Protocollo di comunicazione

La comunicazione dai dispositivi trasmittenti al dispositivo ricevente avviene via radio grazie a dei moduli Aurel che lavorano ad una frequenza di 433.92 MHz.

Questa frequenza, trovandosi in banda radio libera, è molto utilizzata dai dispositivi che lavorano in radiofrequenza (apri-cancelli, telecomandi di autoveicoli, radio-citofoni, ecc.).

Abbiamo dunque dovuto prevedere un solido protocollo che potesse permettere al ricevitore di distinguere i dati inviati dai dispositivi trasmittenti dalle numerose interferenze. Oltre a questo si sono anche dovuti includere dei sistemi di controllo degli errori, per non interpretare in modo sbagliato i dati in arrivo.

I moduli utilizzati, a riguardo dei quali non si trovano molte informazioni né sul datasheet né sul web, dispongono di uno stadio di ingresso accoppiato in alternata. Non possono pertanto trasmettere un livello logico alto per molto tempo, cosa che rende impossibile l’utilizzo di un protocollo di comunicazione standard come, ad esempio, il RS232.

È invece necessario inviare un segnale che preveda numerose transizioni di livello logico per ciascun bit e che abbia dunque componente continua minima.

Un protocollo che per questi motivi è largamente utilizzato in questo contesto è la codifica Manchester in cui ogni bit contiene due transizioni e la cui componente continua è nulla.

Questo protocollo, seppur ampiamente utilizzato nei dispositivi di comando a radio frequenza, nel nostro caso non è adatto perché non presenta una sufficiente immunità ai disturbi e richiederebbe l’installazione di un sistema di ritrasmissione. Si è dunque reso necessario progettare appositamente un protocollo che rispondesse ai requisiti specifici del progetto.

Il protocollo che abbiamo utilizzato prevede che i dati inviati al modulo trasmettitore siano codificati mediante modulazione ASK-OOK.

Per indicare il livello logico alto è inviata al trasmettitore un’onda quadra a frequenza di 3.5 kHz (in accordo col valore massimo riportato nel datasheet); per indicare il livello logico basso non è inviato alcun segnale.

In questo modo, ogni bit contiene numerose transizioni e la componente continua è minima.

La durata di un bit in trasmissione (quindi il tempo per cui questa frequenza viene inviata o meno) deve essere abbastanza lunga da poter essere discriminata rispetto alle brevi interferenze, ma un tempo troppo lungo rischierebbe di allungare eccessivamente il ritardo tra la pressione di un tasto sul dispositivo trasmittente e l’effettiva esecuzione del comando corrispondente, ossia il tempo totale di trasmissione.

Il valore scelto per la durata di un bit è: Tbit = 25 ms.

Dato che il numero di comandi necessari è limitato, per distinguerli è sufficiente inviare 6 bit anziché 8; le combinazioni restano abbastanza per distinguere fra 26 (64) comandi di cui circa i tre quarti utilizzati. Grazie alla riduzione dei bit inviati è stato possibile ridurre il tempo di trasmissione dei comandi.

I dispositivi trasmittenti inviano i dati in questo modo:

1 bit di start, livello logico alto; 6 bit che formano il codice di comando (dall'MSB all'LSB); 6 bit che formano il codice di controllo del comando (sempre dall'MSB all'LSB)

Il bit di start segnala al ricevitore che è iniziata una trasmissione dati, segue l’invio del comando.

Il codice di controllo, introdotto per l’individuazione degli errori, è costituito dalla negazione del codice di comando. Questo permette un controllo degli errori praticamente infallibile e insensibile alle interferenze ripetitive. Tuttavia non si tratta di un codice a correzione d’errore: un eventuale codice errato è semplicemente scartato ed ignorato.Il tempo totale di trasmissione di un comando è quindi di (1+6+6) * 25 = 325 ms.

L’immagine qui di seguito rappresenta il segnale digitale NRZ da inviare e come esso sia inviato al modulo trasmettitore. Conseguentemente dovrebbe essere lo stesso presente all’uscita del modulo ricevitore.

Supponiamo di voler inviare il comando numero 10, in binario 001010.

La prima metà del segnale è formata dal bit di start e dai 6 bit del comando, ordinati dal più significativo a quello meno significativo.

La seconda metà è costituita dal comando di controllo (il complemento del comando inviato), sempre dal bit più significativo al meno significativo.

Il segnale in uscita dal modulo ricevitore, teoricamente uguale al segnale in entrata al modulo trasmettitore, è quindi composto da una frequenza di 3.5 kHz per indicare il livello logico alto e dall‘assenza di segnale per rappresentare il livello logico basso.

In ricezione, da tale segnale dobbiamo riottenere il segnale binario NRZ, convertendo quindi la frequenza di 3.5 kHz in una tensione continua.

Una volta che si è riottenuto il segnale originale è possibile inviarlo al microcontrollore ricevente perché sia letto ed elaborato.

È quindi necessario un circuito di conversione frequenza - tensione che soddisfi i seguenti requisiti:

  • Velocità di risposta, sia sul fronte di salita sia su quello di discesa;
  • La più alta immunità ai disturbi possibile.

Abbiamo preferito progettare un circuito su misura piuttosto che utilizzarne uno standard presente in commercio per disporre di una maggiore conformità ai requisiti specifici del nostro progetto e di una maggiore conoscenza del suo funzionamento.

Lo schema del circuito progettato, visibile in seguito nello schema elettrico del ricevitore, è il seguente. Lo analizzeremo ora nel dettaglio.

La prima sezione, costituita dal filtro HP di primo ordine R1 - C1, ha la funzione di trasformare il segnale a onda quadra in ingresso in un segnale impulsivo, grazie alla capacità di fare da derivatore che tale filtro possiede quando vi è applicata in entrata un segnale digitale ad onda quadra.

Il diodo D1 è utilizzato per rimuovere, o comunque limitare all’ampiezza di -Vd (-0.7V), i picchi negativi di tensione generati dal circuito rc, mantenendo soltanto quelli positivi.

Il motivo per cui si è resa necessaria una tale trasformazione del segnale sarà spiegato in seguito, nella parte riguardante la reiezione ai disturbi.

In condizioni di riposo (ingresso a livello logico basso) il condensatore C2 è carico, il transistor Q2 ha così 5V sulla base ed è in saturazione, portando l’uscita a livello logico basso.

Quando in ingresso è presente un’onda quadra, che viene trasformata in un treno d’impulsi dal filtro R1 - C1 – D1, si verifica che ognuno di tali impulsi riesca a portare in saturazione il transistor Q1 per un breve istante, comunque sufficiente a scaricare il condensatore C2, portando in interdizione il transistor Q2 e alzando, di conseguenza, il livello logico dell’uscita.

Quando l’impulso si esaurisce il transistor Q1 torna in interdizione, permettendo al condensatore C2 di iniziare a caricarsi con una costante di tempo dipendente dal valore R3 e di C2. Fino a quando la tensione sul condensatore C2 non supera la tensione necessaria a polarizzare il transistor Q2 attraverso R4, tale transistor è in interdizione e l’uscita rimane alta. Al superamento di tale soglia l’uscita torna a livello logico basso.

Il principio di funzionamento di questo circuito si basa sul fatto che la distanza temporale tra un impulso e l’altro non sia tale da permettere al condensatore di caricarsi abbastanza da portare l’uscita a massa. È possibile osservare questo funzionamento dal grafico seguente.

È perciò necessario dimensionare in modo opportuno più parametri del circuito:

  • La costante di tempo R3-C2;
  • La resistenza di polarizzazione R4;
  • La frequenza che indica il livello logico alto.

È opportuno che tali valori non siano soltanto il risultato di una progettazione teorica, ma che vengano corretti da osservazioni pratiche, per compensare alle immancabili tolleranze dei componenti. Queste sono particolarmente rilevanti nel caso dei condensatori, che ricoprono un ruolo importante in questo circuito, e della differente tensione di soglia Vbe che i transistor utilizzati possiedono.

Nel nostro caso i valori finali sono stati ottenuti mediante calcolo preventivo con l’aiuto di programmi di simulazione e corretti da prove pratiche grazie all’uso di oscilloscopi a memoria.

Il primo requisito di questo circuito, ovvero la velocità di risposta dell’uscita, è pienamente rispettato: l’uscita passa a livello alto quasi immediatamente alla ricezione del primo impulso.

La velocità di risposta non è invece immediata nel caso del fronte di discesa: l’uscita diventa bassa solo quando non arrivano più impulsi da un tempo variabile a seconda della costante di tempo R3-C2 e della resistenza di polarizzazione del secondo transistor, ossia il tempo necessario al condensatore per caricarsi a una tensione tale da poter mandare in saturazione il transistor Q2.

Da queste considerazioni si evince che il valore della costante di tempo R3-C2 dovrebbe essere abbastanza alto da non causare una prematura caduta dell’uscita a massa ma, più tale valore aumenta, maggiore diventa il tempo impiegato dall’uscita per commutare livello logico. Il valore scelto dovrà quindi rappresentare una via di mezzo fra le due opposte necessità di reattività ed affidabilità.

Il valore dell’intervallo di tempo necessario all’uscita per alternarsi fra il livello logico alto e quello basso è un parametro che deve essere noto a priori per poter sincronizzare correttamente il ricevitore con i trasmettitori.

È possibile calcolarlo matematicamente o mediante simulazione ma, data la complessità dei calcoli e l’alta incidenza della tolleranza dei componenti sul risultato ottenuto, ne risulterebbe un valore che non corrisponderebbe alle reali prestazioni del circuito. È dunque opportuno ricavare tale valore attraverso misurazioni empiriche effettuabili mediante l’ausilio di un oscilloscopio, come abbiamo effettivamente fatto noi.

La seconda caratteristica richiesta a questo circuito è una buona reiezione ai disturbi. Le misure attuate per perseguire tale obiettivo sono:

  • L’invio del codice di controllo per individuare gli errori;
  • Il filtro R1-C1 utilizzato per trasformare l’onda quadra in un treno d’impulsi;
  • Un’apposita routine implementata nel microcontrollore ricevente per leggere più volte ogni bit, diminuendo le probabilità di leggere dati errati.

Il filtro R1-C1 è stato inserito al solo scopo di rendere impulsivo il segnale in ingresso perché ciò conferisce un notevole aiuto nell’eliminazione dei disturbi, per questo motivo:

A seguito di varie osservazioni pratiche mediante oscilloscopi a memoria, si è giunti alla conclusione che i disturbi captati dal ricevitore hanno l’effetto di portare a livello alto l’uscita in momenti imprevedibili e di mantenerla così per tempi relativamente molto lunghi (a volte anche centinaia di millisecondi).

Se non fosse prevista nessuna misura contro tutto ciò, un disturbo in grado di forzare alta l’uscita per un tempo così notevole sarebbe in grado di invalidare completamente un comando.

Il microcontrollore non eseguirebbe comunque azioni indesiderate poiché il codice ad individuazione d’errore gli permetterebbe d’accorgersi del problema, però vi sarebbe comunque la perdita del comando inviato.

Il grafico seguente mostra come la rete di condizionamento trasformerebbe l’onda ricevuta in questo caso (nessuna misura precauzionale):

(il disturbo è evidenziato in rosso; le temporizzazioni sono solo indicative, non in scala)

Come si può vedere, il dato letto dal microcontrollore sarebbe completamente sbagliato.

Lo stesso identico disturbo, elaborato dalla rete di ricezione con il filtro R1-C1, sarebbe derivato; restando a interferire soltanto sui suoi fronti di salita:

(Nota che l’onda trasformata mostra l’effetto di derivazione sul solo disturbo per semplicità di disegno, ma tale effetto viene applicato anche a tutti i fronti di salita del segnale ook).

L’influenza del disturbo, le cui dimensioni rappresentano un interferenza tra le più pesanti e influenti che possiamo avere, viene, come si può notare, drasticamente ridotta applicando il filtro in ingresso.

In questo modo si può proteggere il sistema da eventuali disturbi dovendo, però, accettare una costante di tempo R3-C2 maggiore e incrementando il ritardo dell’uscita nella transizione verso il livello logico basso.

Questo perché il tempo in cui il condensatore C2 è mantenuto scarico è pari al tempo per cui il segnale in entrata resta alto.

Utilizzando il filtro il segnale viene derivato facendo tendere questo tempo a zero, in questo modo il condensatore C2 ha un tempo per caricarsi maggiore.

Se non ci fosse il filtro il tempo disponibile al condensatore C2 per caricarsi tra gli intervalli alti sarebbe appena la metà di quello attuale (dipenderebbe dal duty cycle dell'onda ricevuta).

Ciò che resta dei disturbi non è sufficiente a causare un errore nella lettura del comando poiché ciascun bit viene letto otto volte a intervalli regolari dal microcontrollore che considera un livello logico alto solo quando almeno cinque delle otto letture risultano tali.

La rete di condizionamento utilizzata dispone così, in associazione alle procedure implementate nel software del microcontrollore ricevente, di una buona protezione contro disturbi ed interferenze.

Il segnale all’uscita del circuito è inviato direttamente al PIC ricevitore e ogni suo primo fronte di salita (il bit di start) genera un interrupt a bassa priorità, costringendo il microcontrollore ad interrompere le eventuali attività in corso per dedicarsi a ricezione e decodifica.

Una volta ricevuto l’interrupt il PIC legge i bit ricevuti e controlla che il codice del comando sia uguale alla negazione del codice di controllo inviato. Se ciò si verifica il comando è eseguito e il tabellone viene aggiornato.

Il motivo per cui il codice di controllo viene inviato negato rispetto al codice di comando è semplice:

Si pensi ad un disturbo in grado di forzare alta l’uscita del modulo ricevitore per molto tempo, facendo leggere al ricevitore il codice binario 111111.

Se si utilizzasse un semplice protocollo di controllo errori a maggioranza anche il codice di controllo risulterebbe 111111 ed il microcontrollore commetterebbe uno sbaglio, validando un dato non corretto.

Utilizzando un codice di controllo negato questo errore non può essere commesso.

Allo stesso modo si potrebbe avere un disturbo in grado di forzare un bit a livello logico alto mentre in realtà è stato trasmesso a livello logico basso.

Se lo stesso disturbo, o un altro, forzasse a uno anche il corrispondente bit del codice di controllo, un semplice codice a maggioranza non individuerebbe l’errore.

Negando il codice di controllo è possibile accorgersi del problema.

Come accennato in precedenza ciascun bit viene letto dal PIC più volte, otto per la precisione. Si tratta di un accorgimento necessario per evitare che una piccola interferenza possa invalidare un intero comando sopravvenendo in contemporanea ad un qualsiasi istante di decisione.

La temporizzazione seguita dal microcontrollore nell’attuazione di questa procedura è descritta nella figura che segue.

Dal grafico si può notare come la durata di un bit sia in realtà di 26 ms mentre quella dichiarata in precedenza ed implementata nei trasmettitori è di 25 ms.

Questo perché, per ottenere una buona sincronizzazione, sono state effettuate delle misurazioni che ci hanno permesso di ricavare sperimentalmente la vera durata di un bit in ricezione, alterata rispetto al valore ideale dalla modalità di funzionamento dei moduli radio e dalle tolleranze dei componenti utilizzati nella rete di condizionamento, nonché dalle inevitabili approssimazioni numeriche compiute nello scrivere il firmware dei trasmettitori.

Il primo fronte di salita del segnale scatena l’interrupt. Il bit di start viene controllato in modo più accurato per essere certi di bloccare eventuali interferenze in già questa fase e non oltre. Questo meticoloso controllo avviene leggendo 20 volte il valore, ogni 1 ms. Terminata questa fase, se il bit è valido, segue una pausa di 10 ms che porta il ricevitore al quarto millisecondo del bit successivo. Qui ha inizio la routine di controllo dei bit che esegue otto letture a distanza di due millisecondi, terminando quindi al 20° millisecondo. Una nuova pausa di 10 ms porta al 4° ms del bit successivo e la routine si ripete così per ogni bit.

Si evita in questo modo di posizionare gli istanti di decisione in prossimità del limite fra un bit ed un altro, come ulteriore misura precauzionale per contrastare eventuali problemi di sincronizzazione.