|
Direct Digital Synthesis (DDS) con XE164
Cos'è il DDS
il Direct Digital Synthesis è una tecnica di generazione di forme d'onda per via digitale. Leggendo Wiki si scopre che "Con Direct Digital Synthesis (DDS) si indica un metodo per generare, usando elettronica digitale, una arbitraria forma d'onda periodica partendo da un singolo oscillatore di riferimento. La sintesi digitale elimina tutti i tempi d'assestamento, i ringing, gli errori dei PLL ed altri sistemi di sintesi ed il solo errore generato è dovuto all'oscillatore di riferimento."
L'idea base si basa sul fatto che ogni forma d'onda quadra contiene sia componenti coseno che seno. Oppure è più facile capire mediante la spegazione opposta: si consideri lo sviluppo in serie di Fourier che ci dimostra come un qualunque segnale periodico possa essere approssimato da uno sviluppo in serie di seni e coseni, come ad esempio:
percui se generassimo un segnale periodico triangolare potremmo estrarne uno sinusoidale da esso mediante filtraggio (anche se a causa della non idealità del filtro avremmo in uscita un segnale "sporco"), ma cerchiamo di procedere per piccoli passi...
Analisi Teorica del progetto
Come si conosce dall'analisi spettrale si sa che un qualsiasi segnale periodico è costituito da una sovrapposizione (convoluzione) di segnali sinusoidali (sen(t) e cos(t)). Per generare un segnale sinusoidale si puó allora agire nel seguente modo: generare un qualsivoglia segnale periodico e mediante un filtro estrarre la sola componente sinusoidale interessata. Ovviamente la qualità del segnale generato sarà tanto migliore tanto quanto il filtro é ben progettato (alta selettività) e tanto quanto la foma d'onda generata è "simile" ad una sinusoide.
Come si capisce questa tecnica è molto complessa per poter essere realizzata in pratica (infatti richiede la realizzazione di un filtro attivo con frequenza di taglio variabile, quindi richiederebbe l'uso di un Voltage Controlled Filter - VCF).
Direct Digital Synthesis (DDS)
Lo schema a blocchi di un DDS è:

(Fig. 1).
Dallo schema a blocchi riconosciamo:
- un segnale di clock comune, chiamato Fclock
- un contatore chiamato Counter che conta gli impulsi di clock e genera quindi in uscita un indirizzo per la sucessiva memoria associativa
- una memoria associativa (look-up table) che ad ogni indirizzo in ingresso (generato dal contatore appunto) fornisce un numero associato ad esso in uscita
- un convertitore Digitale Analogico (Synth) capace di convertire il numero fornito dalla memoria associativa in forma analogica (vedasi anche il disegno della forma d'onda associato alla sua uscita)
- un filtro di tipo passa-banda (oppure un semplice passa-basso) capace di eliminare le componenti sen(t) o cos(t) non interessate.
Un tipo di Synth economico: il 1-bit DAC
Questa tecnica permette di generare forme d'onda periodiche o aperiodiche (come ad esempio il segnale audio) mediante la modulazione di impulso (Pulse Width Modulation - PWM) accoppiata ad un filtro passa-basso. L'idea è quella di caricare un condensatore per un tempo t ogni periodo di generazione T generando perciò una tensione variabile tra 0V e 5V in uscita a piacimento. La tecnica è molto usata negli apparecchi audio economici (riproduttori MP3 o DVD).
Facciamo degli esempi per capire meglio:
- se si desidera generare una tensione pari a 5V allora si modula l'uscita (l'uscita ad un solo bit, appunto) con t=T, cioè si pilota l'uscita a valore alto per il periodo T;
- se si desidera generare una tensione pari a 2.5V si procede a modulare in PWM con t = T/2, cioè con duty cycle D = 50%;
- se si desiderano 0.1V allora t sarà uguale a T/10, cioè D = 10%.
cioè avviene questo:

(la curva Blu rappresenta l'impulso PWM e quella Rossa la tensione di uscita dopo il filtro (valor medio), cioè la tensione alla Porta P10.0 - in regime stazionario).
Come si può intuire questa tecnica è più semplice da realizzare con un comune microcontrollore inquanto tutti i modelli di microcontrollore integrano almeno una periferica dedicata alla modulazione PWM, il microcontrollore Infineon XE164 ne integra 6 nel solo modulo "Capture and Compare Unit CC60" (usato normalmente per pilotare motori sincroni trifase).
Ovviamente per avere una buona risoluzione in uscita al convertitore bisogna aumentare la frequenza della modulazione PWM, il concetto è abbastanza intuitivo: più aumentiamo la frequenza del segnale PWM e più "micro-steps" avremmo a disposizione per variare la tensione di uscita, cioè la risoluzione del convertitore DAC sarà più alta. Vediamo di fare degli esempi confrontando con i normali convertitori DAC di tipo R/2R: un convertitore DAC a 8bit ha una risoluzione pari a 5V / 28 = 20mV, per ottenere una tale precisione con un DAC a 1-bit bisogna che:
- la modulazione del Duty cycle del PWM sia almeno controllabile con 8 bit (cioè si abbia la possibilità di controllare la durata dell'impulso PWM con una precisione pari agli 8 bit almeno)
- la frequenza di generazione del PWM sia almeno 3 volte maggiore (23 = 8 appunto) di quanto si sarebbe usato con il convertitore DAC a 8bit.
Da quanto esposto sopra si può quindi riassumere che la massima frequenza riproducibile sarà almeno 3 volte inferiore se si utilizza un convertitore 1-bit DAC al posto di uno a 8-bit e di 4 volte inferiore se lo si vuole confrontare con uno a 16-bit (24 = 16). Percui ad esempio, usando una frequenza di sovracampionamento paria a 100 KHz, con il convertitore a 1-bit si potranno riprodurre segnali fino a 5KHz con lo stesso sapporto di distorsione che si sarebbe ottenuta con un conertitore a 8-bit capace di riprodurre segnali fino a 15KHz. Quindi concludendo, per i segnali audio l'uso del 1-bit DAC non è assolutamente penalizzante.
Dalla teoria all pratica
Al fine di preparate la tabella necessaria a generare un segnale sinusoidale mediante modulazione PWM (o 1-bit DAC, se preferite) bisogna capire come funziona il modulo "Capture and Compare Unit CC60".
XE164: Periferica di generazione PWM "Capture and Compare Unit CC60"
La periferica è costituita da un contatore e da un comparatore: se il valore del contatore è superiore al valore impostato nel comparatore allora l'uscita associata è a '0', se è inferiore allora l'uscita è a '1'.
Ovviamente il periodo dell'onda PWM sarà pari al periodo di conteggio del contatore (e perciò sará uguale al periodo di clock moltiplicato per il valore massimo del contatore), mentre il duty cycle D sarà modulabile mediante il valore caricato nel comparatore.
Il contatore può essere configurato per contare solo UP o UP/ DOWN, in quest'ultima modalità quando il contatore raggiunge il valore massimo cambia direzione e conta DOWN decrementandosi; quando raggiunge lo zero cambia nuovamente direzione contando UP incrementandosi.
Per chiarirsi le idee mi sono scritto una tabella (usando OpenOffice) e ho generato il seguente grafico esplicativo:
.jpg)
Modulazione sinusoidale PWM
Sempre per verificare i miei calcoli teorici ho esteso la tabella di cui sopra per "simulare" un intero ciclo di una sinusoide a Freq = 10 kHz.
Come si vede il segnale desiderato (la funzione seno in blue, appunto) è la base per il calcolo del duty cycle ad ogni istante di conversione. Il segnale PWM di ampiezza 0-5V (traccia rossa, mostrata in realtà in valore binario tra '0' e '1') viene filtrato (filtro ideale digitale emulato mediante una semplice operazione di media) così da mostrare anche il segnale di uscita presunto sulla porta P10.0 (traccia verde):

(da notare come la curva di uscita dopo al filto ( Porta P10.0) è abbastanza simila al segnale sinusiodale ( Sin(x)) che si desidera generare con la tecnica DDS, mentre la vera forma d'onda generata dal modulo PWM (traccia rossa) è molto diversa).
Realizzazione Hardware
L'hardware è relativamente semplice:
- il microcontrollore Infineon XE164 per generare il segnale DDS
- un filtro PASSA-BASSO in uscita.
Il filtro passa-basso può essere passivo o attivo, tanto per iniziare, scegliamo un filtro passivo semplice semplice:

L'unico calcolo da eseguire è perciò il dimensionamento della R e della C imponendo la frequenza di taglio Ft. La formula è: Ft = 1 / (2 x PI x R x C)
Con R = 330 Ohm e C = 100nF si ottiene una frequenza di taglio di circa Ft = 4.8 kHz, sufficiente per i nostri scopi.
Realizzazione Software
Il file zip allegato contiene il progetto per DAvE percui vi rimando ad esso: aprite "DDS.dav" con DAvE e cercate di capire le configurazione dell'hardware, dovrebbe essere piuttosto semplice capirle. Comunque vi basterà sapere che:
- Real Time Clock (RTC), timer T14 è settato a 1sec per definire lo scheduling del main ()
- Capture and Compare Unit CCU60, timer T12 è settato a 100uSec -> frequenza di sovracampionamento per la generazione 1-bit DAC (PWM)
- la Porta P10.0 è connessa al canale CC60 percui ai sui capi bisognerà connettere il filtro R-C di cui sopra
- il timer T3 e T3 vengono usati per generare la frequenza desiderata, ad esempio 100Hz, 880Hz, 1000Hz, ...
- il display HD44780 è connesso al fine di mostrare utili stringhe di debug (vedi relativo articolo e descrizione libreria)
funzione main():
Il main è costituito da:
void main(void)
{
// USER CODE BEGIN (Main,2)
float x, y;
unsigned int i;
// USER CODE END
MAIN_vInit();
==> semplice inizializzazione delle periferiche così come configurate mediante DAvE
// USER CODE BEGIN (Main,3)
LCDinitialize();
LCDclear();
LCDtest();
==> semplice inizializzazione del display HD44780
LCDgoToPos(2,4);
LCDwriteString("Ready to GO !!");
GPT2_waitXXms(2000);
==> scrittura di debug sul display e ritardo pari a 2000msec ( = 2sec)
// Stop Frequency Timer
GPT1_vStopTmr ( GPT1_TIMER_3 );
==> meglio fermare il timer T3 prima di finire l'inizializzazione
// Load the values into the DDS Buffer
for (i = 0; i < DDS_BUFFER_SIZE; i++)
{
x = i * 3.1415;
x = x / 128.0;
y = 120.0 * sin (x) + 128; // Sinewave in 8-bit scale
==> facendo riferimento alla Fig.1, per realizzare il DDS è necessario creare una tabella "look-up table" precalcolata con i valori di sen(x):
- la dimensione di tale tabella è pari a DDS_BUFFER_SIZE, ossia 256
- per calcolare la tabella seno si procede moltiplicando 'i' per PI GRECO, poi lo si divide per 128 così che la tabella contenga la funzione seno da 0 a 2 pigreco. Poi si calcola il seno di x e lo si moltiplica per 120 ("120.0 * sin(x)"). A tale valore si somma 128 per avere un valore intero tra 0 e 256. A questo punto bisognerà trasformare questo valore in Duty Cycle, per fare ciò basta:
ucDDSbuffer[i] = (unsigned int) (CCU60_T12PR * y ); // CCU60_CC60SR value determination
}
==> ora bisogna adattare la frequenza Fclock generata mediante il timer T3:
// Frequency selection
x = 880.0 * (float)DDS_BUFFER_SIZE; // x = DDS Frequency, multiplier is given by the array size
GPT1_vLoadTmr ( GPT1_TIMER_2, (unsigned int) (1.0 / ( T3_TICK * x ) ) );
GPT1_vLoadTmr ( GPT1_TIMER_3, GPT1_uwReadTmr (GPT1_TIMER_2) );
y = 1.0 / ( T3_TICK * (float)( GPT1_uwReadTmr (GPT1_TIMER_2) ) * (float)DDS_BUFFER_SIZE );
PrintOnDisplay( "DDS = [Hz]", y );
==> si noti come la frequenza Fclock viene scalata alla dimensione del buffer DDS, quuesto risulta chiaro se si considera che il buffer contiene i valori di Duty Cycle per generare un periodo completo della funzione seno (cioè da 0 a 2xPI), mentre si vuole generare una funzione sinusoidale a 880Hz percui il timer che genera Fclock dovrà essere 256 volte più veloce.
==> il timer T2 viene usato solamente per memorizzare il valore della base di conteggio del timer T3 così da poter seguire il reload di T3 dall'hardware stesso (vedi altro tutorial su GPT1).
// Start Frequency Timer
GPT1_vStartTmr ( GPT1_TIMER_3 );
==> è ora di iniziare la generazione facendo partire il timer T3...
Il main() termina con:
while(1)
{
if ( RTC_Tick )
{
// Wait till next Tick
RTC_Tick = 0;
}
}
} // End of function main
==> cioè il main() non fa nulla....
e allora come è possibile generare la funzione d'onda?
Funzione di interrupt del Timer T3
void GPT1_viTmr3(void) interrupt T3INT
{
// Set PWM DutyCycle accordly to the DDS Table
CCU60_CC60SR = ucDDSbuffer[DDS_Pointer];
CCU60_vEnableShadowTransfer ( CCU60_TIMER_12 );
DDS_Pointer ++;
} // End of function GPT1_viTmr3
==> ossia il Timer T3 realizza la funzione descritta nella Fig.1, cioè il "counter" che fornisce l'indirizzo per la "look-up table" (la tabella ucDDSbuffer[DDS_Pointer]) e che si incarica di modificare il Duty Cycle del "Synth" 1-bit DAC (PWM).
Bene ora c'è tutto e si può procedere con le prove sperimentali...
Misure Sperimentali
Questo paragrafo ha loscopo di raccogliere i risultati più salienti.
Generazione di una sinusoide a 100Hz:
(da notare come l'onda generata sia praticamente perfetta se osservata ad occhio nudo, la dinamica è esattamente 0 - 5V).
Se si osserva meglio ingrandendo la forma d'onda si può notare un segnale in altra frequenza sovrapposto che non riesce ad essere filtrato dal semplice filtro R-C, ossia:

(la frequenza del "ripple" è esattamente 100 uSec, cioè pari alla frequenza di sovracampionamento).
Generazione di una sinusoide a 880Hz:

(da notare come l'onda generata sia peggiorata rispetto alla precedente e come la dinamica si sia ridotta a circa 0.5 - 4.4V a causa dell'azione del filtro R-C).
Generazione di una sinusoide a 1000Hz:

(da notare come l'onda generata sia peggiorata ulteriormente e come la dinamica sia ora ridotta a causa del'effetto attenuante del filtro passa-basso passivo).
Misura del Total Harmonic Distortion (THD)
Riprendiamo ora alla sinusoide con frequenza pari a 880Hz e ne misuriamo la distorsione armonica (per spiegazioni vedi THD). Tale distorsione si può misurare facilmente usando il programma VisualAnalyzer (VA), a 880Hz la vera frequenza generata è pari a 869 Hz e la distorsione THD = 0.29%:
(THD = 0.23% mi sembra un risultato ottimo se si considera che si è scelto un filtro passivo di tipo R-C).
Conclusioni
Alla fine è stato più difficilespiegarlo chefarlo,comunque il risultato ottenuto è abbastanza buono e rappresenta una buona base di partenza per altri progetti, ad esempio:
- sintetizzatore musicale
- sintetizzatore vocale
- generatore segnale in modulazione AM
- generatore segnale in modulazione FM
- ...
BUON DIVERTIMENTO !!!
L'utilizzo di tutte le informazioni illustrate e riportate in questo documento sono a proprio rischio e pericolo.
Tutte le informazioni vengono fornite “COSI' COME SONO”, senza nessuna forma di garanzia sulla loro validità.
Il presente documento ha uno scopo puramente informativo e didattico.
Ciascuna persona, nell'applicare le informazioni descritte, sarà pienamente responsabile delle
eventuali conseguenze che ne derivano. Elettronicamente.com e l'autore declinano
qualsiasi responsabilità nell'applicazione di tali informazioni, né saranno responsabili
in modo diretto e/o indiretto su eventuali danni provocati di qualsiasi genere a cose o persone.
E' vietata la pubblicazione totale e/o parziale di questo documento su altri siti
Internet diversi da Elettronicamente.com e in qualsiasi altra forma di diffusione
(esempio su riviste) senza il consenso scritto da parte dell'autore.
|