Controller radio per effetti sonori - Decodifica del segnale

Per la decodifica del segnale radio (sul ricevitore) ho utilizzato il modulo ricevitore 'gemello' a quello trasmittente, il modulo Qam-rx2. Come la maggior parte di questi moduli, si tratta di un circuito super-rigenerativo. Oltre ai pin di alimentazione e al pin dell'antenna, vi sono non uno ma due pin di uscita. Per quale ragione?

Il secondo pin di uscita, quello ufficiale a livello logico TTL, è l'uscita di un comparatore integrato nel modulo il quale compara il segnale rigenerato con una soglia fissa. In pratica, quando il segnale rigenerato oltrepassa una certa soglia, l'uscita va alta per indicare che si sta ricevendo qualcosa.

Pur funzionando bene, questo meccanismo può non essere il massimo quando il segnale giunge debole al ricevitore, non riuscendo a portare alta l'uscita e causando la perdita dei dati inviati. Nel nostro caso, siccome non utilizziamo il modulo ricevitore ad una distanza di pochi metri dal trasmettitore, dobbiamo tenere conto di questo problema.

Ho condotto varie prove sperimentali su questi parametri, scoprendo che ad una distanza di una trentina di metri (o attraverso un muro) il segnale viene degenerato fino a cadere in questo triste caso.

Ecco che viene in aiuto l'altro pin di uscita: su quel pin è presente il segnale analogico rigenerato. Si tratta in pratica dell'ingresso non invertente del comparatore di uscita.
Ho prelevato l'uscita da questo pin e la ho mandata in un comparatore esterno, su cui ho fissato una soglia modificabile con un trimmer, tarandola in modo da garantire una sensibilità più alta ma non eccessiva. Ho così ottenuto la portata più alta possibile (circa una cinquantina di metri o una trentina con un muro in mezzo).

Il segnale presente su questo pin ha una forma "strana": quando non si riceve niente, è un livello più o meno continuo (qualche centinaio di millivolt), mentre quando si riceve qualcosa i segnali ricevuti si sommano a questo offset. In pratica, quando trasmettiamo un'onda quadra a 3 kHz, abbiamo su questo pin un segnale composto da una componente continua di qualche centinaio di millivolt più un'onda quadra leggermente distorta di ampiezza pari anch'essa a qualche centinaio di millivolt.
La figura seguente è una ricostruzione di questo segnale, in questo caso l'offset vale 0.4 V e l'onda quadra ha ampiezza 0.3V:

(clicca sull'immagine per vederla più grande)

Com'è evidente, la soglia in questo caso dovrebbe essere impostata ad un valore maggiore di 0.4V e minore di 0.7V. In realtà, la presenza di rumore in alta frequenza è molto incisiva, perciò la soglia va posizionata più in "alto" rispetto al valore della tensione continua a riposo.
Incuriosito dall'origine di questo rumore (il dispositivo, vedremo, è alimentato dalla presa USB) ho impostato l'oscilloscopio in modalità AC (blocca la componente continua permettendomi di vedere solo le componenti a frequenza maggiore), il trigger in modalità LF reject (ignora il trigger alle basse frequenze), il canale MATH su FFT e posto una sonda sulla linea di alimentazione. Quello che ho scoperto è un rumore consistente alla frequenza di 48 MHz, un altro picco più basso a 96 MHz, ecc..., casualmente la frequenza di lavoro dell'USB 2.0 (e multipli). Ecco una ricostruzione (le frequenze componenti il disturbo NON sono in scala con i disturbi realmente osservati, ma le ampiezze sì) dell'onda disturbata:

(clicca sull'immagine per vederla più grande)

Provando a collegare il dispositivo ad un PC portatile (prima era collegato ad un PC fisso) questo rumore si riduceva drasticamente. Probabilmente la differenza risiede nel tipo di alimentazione: un PC fisso ha un alimentatore switching (un dispositivo elettricamente molto rumoroso, che dovrebbe essere ben schermato ma non lo è sempre effettivamente) vicino al controllore usb, mentre il portatile dispone di un alimentatore meno potente e più lontano.

Ho comunque effettuato la taratura della soglia nel primo caso, quello più rumoroso, in modo da garantire un corretto funzionamento anche nel caso il dispositivo venisse collegato ad un PC fisso.

Tornando alla decodifica, secondo quanto detto finora, trasmettendo un'onda quadra a 3 kHz ci aspettiamo di vedere uscire da questo pin un segnale come quello visto poco fa. Mandando questo segnale in un comparatore (un LM311 in questo caso) e comparandolo con una soglia impostata secondo il modo appena visto, otteniamo in uscita l'onda quadra di partenza.
Questa onda quadra, la cui presenza (ricordiamolo) significa un uno logico e la cui assenza significa uno zero logico, viene inviata direttamente al microcontrollore ricevente che controlla il valore più volte per ogni Tbit (10 ms), circa 130 volte ad intervalli regolari. Ogni volta che questo valore è alto, viene incrementato un contatore. Per ogni bit, finiti i 130 controlli, se il totale degli 1 supera una soglia minima allora il bit è considerato un 1, altrimenti uno 0. Questo valore minimo è circa 30, cioè se su 130 letture almeno 30 danno un "1", allora il bit è considerato alto.

Questo numero (il 30) deve essere ben calibrato, perchè se fosse troppo basso non scarterebbe le interferenze più pesanti, se fosse molto alto rischierebbe di scartare dei bit validi. Questo 30 è un valore ottenuto sperimentalmente attraverso varie prove e misure.

Tutto questo riguarda i bit del dato e i bit del codice di controllo, per quanto riguarda il bit di start il discorso è differente: quando s’inizia a ricevere un segnale, per verificare il bit di start si fanno ben 350 letture ad intervalli regolari a distanza di qualche microsecondo. Invece di contare il numero finale delle letture a "1” quando si sono letti un certo numero di zeri (120 nell'ultima versione del firmware) s’ignora il bit e lo si considera come un’interferenza, tornando immediatamente al ciclo principale.

Ho preso quest’accorgimento per evitare che ogni interferenza faccia perdere 10ms (Tbit) al ricevitore, durante i quali potrebbe per esempio arrivare un vero bit di start che andrebbe quindi perso.

Il segnale da decodificare (OOK), per semplificare il lavoro di lettura e decodifica al PIC, viene prima mandato in una semplice rete chiamata "rivelatore di inviluppo", una rete comunemente usata nella modulazione di ampiezza. Se cercate informazioni in rete su questa rete troverete molta documentazione e delle formule canoniche, in questo caso le formule non servono particolarmente e vi spiego come ho calcolato i valori dei componenti mostrandovi innanzitutto lo schema (semplicissimo):

Quando in ingresso si ha il livello alto dell'onda quadra, questo attraverso il diodo carica il condensatore in un tempo teoricamente pari a zero (nessuna resistenza di carica). Quando il livello in ingresso è basso, a causa della presenza del diodo il condensatore può scaricarsi solo attraverso la resistenza. Se i valori di R e C sono ben proporzionati rispetto alla frequenza del segnale da demodulare (considerando anche il duty cycle), si avrà in uscita una tensione continua (con un certo ripple, eliminabile con un filtro passa-basso in quanto il ripple è una componente in frequenza più alta) quando in ingresso è presente un'onda quadra, mentre quando in ingresso non abbiamo niente il condensatore si scarica completamente sulla sua resistenza portando l'uscita bassa.

Come dicevo, per calcolare i valori di R e C si può utilizzare la formula canonica reperibile in rete o ragionare di testa propria. Io ho scelto quest'ultima opzione e dimensionato i componenti in questo modo:

Consultando il datasheet del PIC che ho usato nel ricevitore, ho appreso che l'ingresso TTL considera "alto" un valore superiore ai 2 V, basso un segnale inferiore agli 0.8V e non è definito il comportamento nella fascia intermedia. Ciò significa che la costante di tempo R*C dovrà essere dimensionata in modo che nel tempo in cui l'onda quadra è a livello basso, la tensione sul condensatore non scenda sotto i 2V.

Basterebbe impostare una costante di tempo enorme, ma questo impedirebbe la scarica del condensatore e porterebbe il PIC ad interpretare tutti i bit a 0 come bit a 1, con conseguenze catastrofiche. Occorre dunque scegliere la costante di tempo più bassa possibile (per non incorrere in questo problema) che non permetta alla tensione sul condensatore di scendere sotto i due volt nel tempo di Off dell'onda in ingresso.

Procediamo: la frequenza del segnale OOK è 3 kHz, quindi il periodo vale T = 1/F = 333 uS. Il tempo di off sarà quindi 333/2 = 167 uS.
La formula che definisce la tensione ai capi di un condensatore è:

Che, isolando il termine esponenziale, diventa:

Sottoponendo a logaritmo naturale entrambi i membri, otteniamo:

Isoliamo l'incognita RC moltiplicando ambo i membri per RC e dividendo ambo i membri per il logaritmo naturale col suo contenuto:

Secondo quanto detto, la tensione ai capi del condensatore non deve scendere sotto i 2 volt in 167 uS, quindi troviamo la costante di tempo RC minima ponendo Vc=2 V e T=167 uS. Inoltre la scarica parte da una tensione di 5 V per terminare a 0 V, abbiamo quindi Vi=5V e Vf=0V. Sostituendo e risolvendo otteniamo:

Ed ecco finalmente il valore cercato:

Conciliando il risultato di questo calcolo con la mia disponibilità di componenti, ho utilizzato un condensatore da 19pF e una resistenza da 10MOhm ottenendo una costante di tempo RC = 190 uS.

Il valore è molto vicino a quello teorico ed il margine è ristretto, ma come già detto è così che deve essere per garantire una buona ricezione anche nei bit a livello basso (più la costante di tempo è bassa, prima si scaricherà il condensatore e prima il livello logico in uscita tornerà basso). Se anche il PIC dovesse leggere un valore basso invece di uno alto per qualche microsecondo, troverebbe comunque un software implementato in modo da garantire la giusta tolleranza.
Il valore del condensatore potrebbe sembrare molto piccolo, ma in questo modo riesco ad ottenere una carica in un tempo ridottissimo ottenendo una maggiore velocità di risposta ai fronti di salita del segnale OOK.

Grazie a questa rete, il PIC riesce a distinguere meglio tra bit bassi e bit alti perchè deve leggere sostanzialmente un livello logico piuttosto che determinare l'assenza o la presenza di una frequenza. La figura seguente mostra come il segnale (sempre quello di prima, per inviare il comando 5) venga trasformato da questa rete:

(clicca sull'immagine per vederla più grande)

Riassumendo il tutto, l'intero processo di decodifica è schematizzabile in questo modo:

  • Quando un comando viene trasmesso, il segnale rigenerato internamente al modulo ricevitore assume la forma mostrata in precedenza (un 1 logico perchè il bit di start è sempre un uno);
  • Questo segnale viene inviato nel comparatore esterno che, opportunamente tarato, dà in uscita l'onda quadra trasmessa originariamente risquadrata e con ampiezza 0-5 V;
  • Quest'onda quadra entra nel rivelatore di inviluppo, venendo trasformata in tensione approssimativamente continua;
  • Il tutto dura per 10 ms (un Tbit, il bit di start);
  • Il microcontrollore ricevente legge questo segnale e interpreta il bit come bit di start;
  • Il microcontrollore allora legge i 4 bit seguenti (quindi legge 4 volte per 10 ms) per ottenere il codice del comando;
  • Il microcontrollore legge i 4 bit rimanenti (ancora 4 volte per 10 ms) per ottenere il codice di controllo;
  • A questo punto, il microcontrollore compara il codice del comando col codice di controllo negato; se i due corrispondono, il comando è valido e viene inviato via USB al computer.

Il tutto sarà più chiaro vedendo nel dettaglio la struttura nel ricevitore presentata nel prossimo capitolo.