Set 172016
 

In passato, avevamo parlato di LED e di come modulare la luminosità tramite la tecnica PWM (Pulse-width modulation). Brevemente per chi non ricordasse, la tecnica PWM consiste nell’alternare in rapida sequenza, attimi in cui non forniamo tensione ed attimi in cui invece la emettiamo, una sorta di continuo “acceso-spento”. In base alla variazione dei tempi in cui il “segnale” resta acceso o spento, abbiamo una tensione media che si avvicina più allo zero o al 100%.

Il problema

Nel caso del LED l’accensione e spegnimento con diverse tempistiche in rapida sequenza simula la presenza di una tensione più o meno elevata portando il LED dallo spegnimento alla massima luminosità. Continuo a parlare di LED perché questo si adatta bene all’accensione e spegnimento in rapida successione, ma se invece avessimo realmente bisogno di una tensione in uscita di un certo valore che non sia solo la media di un repentino accendi e spegni? Per far ciò dobbiamo usare un filtro RC di cui avevamo accennato in questa lezione della serie di elettrobaby. Lo scopo di oggi è quindi quella di trasformare i segnali che vi ho riportato nel grafico a lato in una tensione lineare di un valore che cambia a seconda delle nostre esigenze.

I filtri RC

Se ricordate, nell’articolo sul condensatore, dicevamo che “maggiore è la tensione che raggiunge il condensatore e minore è il tempo di carica dello stesso“. Avevamo anche detto che per modulare la tensione che arriva al condensatore potevamo utilizzare un resistore a monte e che il resistore (R) ed il condensatore (C) in questa configurazione prendevano appunto il nome di circuito RC. Se ricordate dicevamo che il valore di resistenza del resistore moltiplicato per la capacità del condensatore prende il nome di tau che rappresenta la costante di tempo che è espressa in secondi e rappresenta il tempo necessario a raggiungere il 63% della carica che, ricordiamolo, segue una legge logaritmica e non lineare il che significa che inizialmente si carica molto velocemente per poi rallentare sempre di più. 

Vediamo di partire da un esempio pratico. Facciamo conto di avere un segnale PWM con una frequenza di 488Hz, corrispondente ad un periodo di circa 0.002 secondi, ossia passa questo tempo fra una onda quadra e l’altra. Come potete immaginare l’esempio non è casuale, ma si tratta dell’uscita PWM standard di Arduino nella gran parte dei casi. La prima cosa da sapere è che la costate di tempo del circuito RC deve essere di almeno 100 volte il periodo PWM. Se il periodo PWM è di 0.002 secondi, dovremmo usare una costante di tempo di 0.2 secondi (200ms). Se usiamo un condensatore da 22μF ed un resistore da 10kΩ otteniamo 220ms che vanno più che bene per il nostro test. Proviamo quindi a vedere come si comporta nella pratica. Nella prossima figura ho riportato il circuito di prova con il resistore ed il condensatore. Ho poi aggiunto anche un resistore R2 da 10000KΩ per simulare un piccolo carico. Nel grafico sottostante vi ho riportato il risultato del filtraggio attraverso il filtro RC, non vi ho invece riportato il segnale PWM perchè con la medesima scala è troppo “fitto” e va a colorare tutta l’area rendendo il tutto incomprensibile. All’interno del tracciato vi ho inserito anche un ingrandimento per mostrarvi l’ondulazione residua che tecnicamente è detta “ripple” (ondulazione, increspatura).

filtroRC

Cosa possiamo desumere dal grafico?  La prima cosa che si nota è che il valore di tensione sale lentamente sino a stabilizzarsi, cosa che potevamo prevedere in quanto è una curva caratteristica dei filtri RC che ricalca la curva di carica del condensatore. Essendo la costante di tempo di 200ms a quel punto dovremmo avere il 63% della carica. Facendo una misura infatti troviamo una tensione di circa 1.64V per cui a regime (5t=1sec) dovremmo trovare circa 2.6V (ho misurato circa 2.7). Una considerazione legata alla costante di tempo è che necessitando di circa 1sec per arrivare a regime, questo segnale non potrà essere usato per applicazioni in cui è necessaria una rapida variazione del valore di tensione. In tal caso sarà necessario modificare la frequenza del periodo PWM di partenza, cosa che però con Arduino richiede un piccolo sforzo aggiuntivo per poter essere fatto anche se, ve lo anticipo, alcuni pin delle schede più recenti funzionano già di base a 976Hz anziché 488.

Siamo partiti da un segnale PWM a 5V ed abbiamo ottenuto un segnale “analogico” continuo di circa 2.7V. Questo è compatibile con il duty cycle usato del 50% anche se nella realtà ci saremmo aspettati 2.5V, ma dobbiamo considerare non solo il ripple residuo, ma anche il fatto che le onde quadre usate per la simulazione non sono esattamente delle onde quadre per cui il duty non è realmente del 50%. Avrei potuto inserire un generatore di onde quadre che però avrebbe introdotto complessità aggiuntiva che ho voluto risparmare.

Però un problema reale esiste e ve lo mostro con il prossimo grafico in cui ho variato il carico R2. Si vede in modo piuttosto chiaro che al ridursi del valore di resistenza si riduce la tensione in uscita dal filtro RC, viceversa l’aumento dello stesso incrementa la tensione RC. Vi faccio notare che una resistenza piccola fa passare tanta corrente e quindi rappresenta un grosso carico, viceversa una resistenza grande fa passare poca corrente e rappresenta un piccolo carico.

Ciò ci fa però capire che questo tipo di circuito può funzionare correttamente solo ed esclusivamente nel caso il circuito a valle presenti un’alta resistenza di ingresso, ossia un basso carico, cosa che per fortuna si presenta in diverse occasioni nella pratica quotidiana, quantomeno nell’elettronica digitale.  Se invece il carico a valle è alto, dobbiamo cambiare strategia, ma per farlo dobbiamo utilizzare altri componenti.

filtroRC2

Ci sono diverse possibilità, ma in questo articolo useremo un operazionale in configurazione “voltage follower” (buffer). Sul blog non sono stati ancora pubblicati gli articoli sugli operazionali nella sezione Elettrobaby, che però potete trovare nella seconda edizione dell’omonimo libro sul google store. Di fatto l’operazionale agisce come se il carico applicato fosse nullo, la tensione di uscita viene infatti prelevata dalla linea di alimentazione e non dal segnale in ingresso. Con questo piccolo accorgimento di fatto operiamo una separazione fra quello che è il segnale in ingresso e quello in uscita e se applichiamo un carico all’uscita dell’operazionale, questo non si riflette sul segnale in ingresso perchè la tensione / corrente è prelevata dalla linea di alimentazione.

Lo schema di utilizzo è quello qui sotto; come potete vedere l’uso dell’operazionale è quasi banale, basta infatti collegare l’uscita del filtro RC all’ingresso non invertente (+) e cortocircuitare l’uscita all’ingresso invertente (-). Chiaramente non dovete scordarvi di alimentare l’operazionale (l’alimentazione è al di fuori dello schema).

operazionale_filtrorcIl risultato di questo circuito lo possiamo studiare nel prossimo grafico. In particolare nella prima parte in altro troviamo il circuito senza operazionale a cui viene applicato un carico R2 di 110 Ohm. Come vedete la tensione (traccia verde) si stabilizza con oscillazioni triangolariformi fra i 24 e 36 milliVolt, ben diversi dai 2.5V che volevamo ottenere.

operazionale_filtrorc2Nel secondo grafico, in azzurro, vediamo invece la soluzione con l’operazionale. Si nota che ci vuole più di mezzo secondo per raggiungere i 2.5V (nella simulazione ho ottenuto 2.48), per cui come dicevamo agli inizi questa tecnica è indicata solamente per segnali che variano lentamente, anche se ci sono notevoli spazi per migliorare questo aspetto, in particolare possiamo modificare la frequenza del segnale pwm, ma per fare ciò dobbiamo andare a modificare in maniera diretta i registri di Arduino. Qui le cose si complicano perchè ci sono numerose schede Arduino con diverse famiglie di microcontrollori che come tali hanno registri differenti. Non potendo trattare tutte le possibili alternative, ci concentreremo su Arduino UNO e lasceremo per futuri articoli gli approfondimenti su altre piattaforme.

I timers

Penso che tutti voi sappiate cosa sono i timers di un microcontrollore, potremmo definirli come temporizzatori che scandiscono il passaggio del tempo. L’ATMega328P che equipaggia Arduino UNO ne ha tre che prendono il nome di Timer0, Timer1 e Timer2. Ogni timer è dotato di due registri di comparazione (OCRnA e OCRnB) che vengono confrontati con il contatore per decidere quando le relative uscite debbano andare a valore alto o basso. Essendoci due registri di comparazione per ogni timer, ci saranno due uscite fisiche su Arduino per ogni timer, queste condivideranno la medesima frequenza ma possono avere due duty cycle diversi, in base a come sono settati i due registri.

Timer 0: pin 5 e 6
Timer 1: pin 9 e 10
Timer 2 : pin 3 e 11

pin_pwm

Per quanto riguarda la frequenza, questa è determinata da un prescaler che può assumere valori di 1,8,32,64,128,256,1024 (attenzione che il 32 e 128 sono disponibili solo per il Timer1). Questi valori vanno a dividere il clock principale per ottenere le relative frequenze che su un Arduino UNO saranno quelle che vi riporto qui sotto (attenzione che si riferiscono al singolo conteggio e non all’intera l’unghezza di 256 conteggi):

1 : 16MHz
8 : 2MHz
32: 500 KHz
64: 250 KHz
128: 125 KHz
256 : 62.5KHz
1024 : 15.624KHz

Il Timer 0 e 1 condividono il medesimo prescaler quindi le 4 uscite da loro gestite avranno tutte la stessa frequenza. Il Timer2 ha invece un suo prescaler per cui le sue due uscite possono avere una frequenza differente rispetto le altre 4. Poi ogni timer può funzionare in modo “Fast PWM” o “PWM con correzzione di fase”, inoltre si può stabilire se il contatore debba andare da zero a 255 o da zero ad un valore stabilito. Per completare il quadro il timer uno può portare la risoluzione a 16bit riducendo la frequenza di clregistripwm_328ock, e tutte le uscite possono essere invertite. Come vedete solo elencare la caratteristiche base è una bella confusione.

Oltre ai due registri di comparazione a cui abbiamo accennato, ogni Timer è controllato da due registri che prendono il nome di TCCRnA e TCCRnB, dove la n indica il registro (0,1 o 2). Quindi avremo un TCCR0A un TCCR0B ma anche un TCCR2A o TCCR1B. Nella figura qui a lato potete trovare la rappresentazione di questi registri, tratta direttamente dal datasheet dell’ATMega328P.

  • Waveform Generation Mode bits (WGM): Questi controllano complessivamente la modalità di funzionamento del timer. Si noti che alcuni bit sono nel registro TCCR2A, altri nel TCCR2B.
  • Clock Select bits (CS): Questi controllano il prescaler del clock.
  • Compare Match Output A Mode bits (COMnA): questi attivano / disattivano / invertono l’output A
  • Compare Match Output B Mode bits (COMnB): questi attivano / disattivano / invertono l’output B

Modalità FastPWM

registripwm_328_dutyQuesta è la modalità più semplice. In un primo momento l’uscità è posta a HIGH, il contatore comincia a contare da 0 a 255 e quando raggiunge la soglia impostata nel registro di comparazione fa andare l’uscita a LOW. Il conteggio continua sino al 255 per tornare allo 0 e quindi al punto di partenza. Qui vicono vediamo una rappresentazione grafica di quanto avviene.

In alto vediamo in nero il contatore che va da 0 a 255 per poi riazzerarsi e ripartire. La soglia verde (OCRnB) rappresenta una delle due soglie impostate, la rossa è la seconda: ricordiamo che le soglie sono due per il semplice motivo che ogni timer ha due uscite, in basso vediamo infatti come si comporta il segnale in uscita dai due pin controllati dallo specifico timer. Si intuisce perciò che modificando le soglie si modifica il duty cycle del segnale pwm, un po’ come se avessimo usato la funzione AnalogWrite dell’IDE di Arduino. Vediamo ora un semplice esempio copiato pari pari dalla documuntazione ufficiale sul sito Arduino.cc.

pinMode(3, OUTPUT);
pinMode(11, OUTPUT);
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(CS22);
OCR2A = 180;
OCR2B = 50;

Intanto si osservi che “la n” dei registri di configurazione e comparazione corrisponde a 2 (TCCR2A) per cui questo pezzo di listato configura il Timer 2 a sua volta collegato ai pin 3 ed 11. Non a caso le prime due istruzioni settano come output proprio i pin 3 ed 11. L’istruzione successiva riporta:

TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);

Tanto per cominciare _BV è una macro così definita:

#define _BV(bit) (1 << (bit))

Perciò l’istruzione di prima setta a 1 i bit COM2A1, COM2B2, WGM21 e WGM20 del registro TCCR2A. Vediamo di analizzarla con calma. Abbiamo detto che i bit WGM controllano la modalità di funzionamento del timer, nel nostro caso abbiamo detto che vogliamo usare la modalità “Fast PWM” e per farlo quei bit devono essere impostati a 011, ossia il bit 2 impostato a 0 ed i bit 1 e 0 impostati a 1. Il bit 2 si trova nell’altro registro, il TCCR2B (WGM22), mentre i restanti due sono su questo registro e perciò vengono entrambi impostati a 1. Nella riga qui sotto metto in evidenza i due bit settati:

TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);

Per finire ci sono i bit COMnA e B che dicevamo servire ad abilitare / disabilitare / invertire il segnale sulle due uscite. Impostando i bit COM2A e B a 10 selezioniamo l’uscita non invertita sui canali A e B. Attenzione a non far confusione. Riguardiamo lo schema sopra. I Bit COM, in questo caso COM2 perchè si tratta del timer 2, sono due per il canale A e due per il canale B, quindi A1, A0, B1, B0. Nell’istruzione sopra vengono settati A1 e B1, quindi i corrispettivi A0 e B0 restano settati a 0. Quindi se A1=1 e A0=0 i bit sono settati come “10”, come detto poco sopra, lo stesso dicasi per B1 e B0.

L’istruzione successiva è:

TCCR2B = _BV(CS22);

che va a settare nel registro TCCR2B il bit CS22. CS come dicevamo determina il fattore di prescaler, nella fattispecie viene impostato un prescaler di 64. Qui sotto vi riporto la tabella del datasheet ufficiale.

bit_cs_pwmPiù sopra avevo riportato una tabellina con le frequenze ottenute con i diversi prescaler, avevo inoltre specificato che si trattava la frequenza del singolo conteggio e non dell’intera onda. Questo perchè è vero che adesso stiamo valutando delle serie di conteggi che vanno da 0 a 255, ma vi ho anche detto che è possibile ridurre questo valore per cui ho preferito lasciare le “frequenze grezze” e qui sotto vi riporto quelle per l’intero periodo da 0 a 255 (basta dividere per 256)

1 : 62.5 KHz
8 : 7,8125 KHz
32: 1,953 KHz
64: 976,5625 Hz
128: 488,28125 Hz
256 : 244,14 Hz
1024 : 61,03125 Hz

Dalla tabellina qui sopra capite che la frequenza standard utilizzata da Arduino, ha un prescaler di 128 per cui la frequenza può essere notevolmente aumentata e possiamo del tutto annullare il problema temporale che abbiamo descritto nel raggiungimento della tensione analogica desiderata. Nota: è sconsigliato modificare la frequenza del timer0 in quanto esso è deputato anche ad altre funzioni, fra queste le millis() e delay().

Per ultimo vengono settate le due soglie OCR A e B che abbiamo visto dal grafico come si comportano. Sono semplici valori compresi fra 0 e 255 e appunto rappresentano la soglia di inversione dell’onda e quindi controllano il duty cycle.

Con OCR2A = 180; impostiamo il duty cycle del canale A a (180+1)/256=70.7%, mentre con OCR2B = 50; impostiamo il duty cycle del canale B a (50+1)/256=19.9%. Il “+1” è dovuto al fatto che l’uscita viene mantenuta alta per un ciclo oltre il raggiungimento della soglia.

PWM con correzzione di fase

In questa tecnica il contatore va da 0 a 255 e poi ritorna indietro da 255 a 0 e ogni volta che viene oltrepassata la linea di soglia in una direzione o nell’altra, viene invertita l’uscita corrispondente.

pinMode(3, OUTPUT);
pinMode(11, OUTPUT);
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS22);
OCR2A = 180;
OCR2B = 50;

L’unica differenza rispetto a prima è il mancato settaggio di WGM21, infatti anzichè settare WGM a 011, viene settato a 001. Il duty cycle è simile a prima, ma visto che il contatore deve andare “avanti e indietro”, la frequenza è dimezzata per cui è sufficiente dimezzare i valori della tabellina precedente per ottenere la reale frequenza ottenuta con questa tecnica. Per il calcolo preciso del duty cycle non dobbiamo aggiungere il “+1” in quanto viene annullato al “passaggio indietro”, inoltre non dobbiamo dividere per 256 ma per 255. Quindi per il canale A abbiamo 180/255=70.58% e per il canale B abbiamo 19.6%, valori che chiaramente sono molto simili a quelli precedenti.

Modifica della soglia del contatore

Sinora il contatore è stato fatto variare nel range da 0 a 255, ma ques’ultimo valore può essere modificato sia nella tecnica “fast PWM” che quella “con correzione di fase”. Questa modalità però determina lo spegnimento del segnale PWM A mentre resta attivo il solo canale B. Questo è dovuto al fatto che il valore OCRnA viene utilizzato per controllare il valore limite del contatore anche se esiste la possibilità (“Toggle on Compare Match”) di mantenere acceso il segnale PWM sul canale A, nel dettaglio il segnale cambia all’inizio e alla fine di ogni circlo. Ciò genera un segnale PWM con un duty cycle fisso al 50% e con una frequenza che è la metà di quella del canale B. Per attivare questa modalità i bits COM2A devono essere settati a 01.

pinMode(3, OUTPUT);
pinMode(11, OUTPUT);
TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(WGM22) | _BV(CS22);
OCR2A = 180;
OCR2B = 50;

L’esempio è identico al precedente se si eccettua la modifica della modalità, ossia modalità con correzzione di fase, con soglia del contatore impostata a 180, anzichè 255.

Nel caso del canale A come detto il duty cycle è del 50%. La frequenza è invece di:

16MHz/ 64 / 180 / 2 /2 = 347.2 Hz

I 16MHz sono il clock di Arduino, 64 è il prescaler, 180 la soglia, il primo 2 deriva dalla modalità con correzione di fase, il secondo due dipende dalla soglia del clock. Nota: con la tescnica “fast PWM” avremmo dovuto dividere per 181 anzichè 180, esattamente come detto precedentemente.

Sul canale B invece il duty cycle è di 50/180=27.8% (il rapporto fra le soglie). Per quanto riguarda invece la frequenza, sappiamo già essere il doppio di quella calcolata prima (va eliminata l’ultima divisione per 2), per cui è 694.4Hz.

Giusto per fare un ultimo esempio: cosa succede se usiamo poniamo una soglia di 128 ed usiamo un prescaler di 1 con tecnica fastPWM? La frequenza sarà di 16MHz/1/128, ossia 125kHz, ben superiori ai 488Hz da cui siamo partiti. Attenzione però che riduzendo a 128 i “passi” dell’onda quadra, riduciamo la risoluzione temporale dell’uscita PWM, infatti avremo a disposizione solamente 128 diversi possibili valori per impostare il duty cycle, anzichè i 256 iniziali. Certo potevo lasciare i 256 valori, non cambiava molto, ma volevo portarvi un esempio di cosa può fare Arduino UNO portato alle sue massime possibilità contro la configurazione standerd.

In questa configurazione estremizzata, la costante di tempo del filtro RC che sappiamo essere almeno 100 volte la frequenza pwm, non sarà più di 0.2secondi ma di 0.8ms (milli secondi!). Volete vedere il risultato? Guardate voi stessi qui sotto:

confronto_curve_pwmIn verde la curva vista all’inizio, con Arduino in configurazione standar, in rosso invece la franca differenza che otteniamo con le tecniche spiegate in questo articolo.

Conclusioni

Bene, abbiamo fatto una prima panoramica sui registri PWM, ma non è ovviamente tutto. Ci sono altri modi di funzionamento, ci sarebbero gli interrupt e poi altri chip ATMega utilizzano altri registri PWM perciò già con Arduino Mega2560 le cose cambiano notevolmente, per non parlare delle famiglie di controllori SAM come su Arduino DUE, MK1000, o banalmente i 16 bit del timer 1. Insomma è un campo molto vasto, ma spero che questa prima introduzione risulti chiara a tutti che cercavano una prima infarinatura sull’argomento, poi se si è capito questo articolo sarà facile adattare il tutto alle altre board facendo riferimento ai datasheet dei vari microcontrollori.

  3 Responses to “Trasformare l’uscita PWM in una tensione analogica: i filtri RC e i timers – it”

  1.  

    ottimo tutorial, complimenti. Per semplificare un progetto (un programmatore per vecchie EPROM) costruito con Arduino mi piacerebbe sfruttare le uscite pwm per generare le tensioni di +12 e -5 +26V che mi necessitano e che devo fornire esternamente. Le correnti in gioco molto sono basse, al punto che ipotizzavo di usare dei banali duplicatori di tensione a diodi e condensatori alle uscite o in caso di problemi, usare un banale push-pull a transistor. Userei quindi gli ingressi analogici con dei partitori resistivi per stabilire il feedback necessario alla stabilizzazione PWM delle tensioni d’uscita. Ha approfondito l’argomento o mi può segnalare dei lavori interessanti come questo da cui partire?

    •  

      forse mi sfugge qualcosa. Le tensioni 12 -5 e 26 sono fisse? Nel senso che quei tre valori restano immutati nel tempo? Se la risposta è si non vedo proprio il senso di usare Arduino per generarle, l’uso del PWM ha senso quando il valore in uscita deve variare nel tempo ma se hai bisogno di un valore costante non ha molto senso a meno che non ti interessi semplicemente provare per il gusto e la soddisfazione di farlo. Ti è più comodo partire da un trasformatore sui 30V e da li con dei regolatori di tensione prepararti le varie tensioni, poi se devi fornire con Arduino dei segnali a quelle tensioni utilizzi dei semplici transistor per “dare e togliere” tensione nei momenti opportuni (l’uscita di Arduino tramite resistore comanda il transistor che a sua volta fa passare la tensione desiderata a 12 -5 o 26V). Che ne dici?

  2.  

    Ho fatto un programmatore di EPROM (vecchie eprom 2708) che già funziona con il trasformatore e i regolatori discreti. Poiché il consumo sulle tensioni secondarie di -5V +12 (e +26V per la programmazione è molto basso 15-20mA ) vorrei semplificare il tutto per poter alimentare dalla sola porta USB. Ci sono ottimi step-up, si può fare un generatore con un 555, ma la soluzione più elegante, visto che avanzano porte, mi sembra quella di usare lo stesso Arduino, sia come pilota sia come regolatore con feedback analogico. E’ vero che le tensioni in uscita sono fisse, ma chiaramente senza un regolatore lineare varierebbero in funzione del carico quindi non mi sembra ci sua nulla di sprecato. Al momento non ho visto in giro nulla. Chiaramente ci sarà un push pull a transistor con a valle un moltiplicatore di tensione a diodi e condensatori per raggiungere i 12 e i 26V. Il partitore resistivo per permettere la lettura da un ingresso analogico nel range dei 5V e quindi il feedback software per garantire la stabilità.