Fast PWM su Arduino: una valida alternativa all’istruzione analogWrite().

Arduino, essendo basato sul core Atmega328, supporta nativamente la periferica di modulazione PWM utile in tutte le situazioni in cui si cerca di emulare, con un microcontrollore, la variazione continua di una grandezza, tipicamente una tensione.

Associando alla variazione di tensione la variazione di una qualunque altra grandezza fisica (adoperando dei trasduttori), con la modulazione PWM si può ottenere il controllo della stessa con un’accuratezza che dipende dal numero di bit con i quali, all’interno del micro, è possibile fissare il duty-cycle. Non mi dilungo ulteriormente sulla teoria della modulazione PWM ma mi accingo ad illustrare due possibili tecniche firmware che permettono, con differente difficoltà e richiesta di dettaglio dell’architettura, di utilizzare la tecnica PWM su Arduino.

Modulazione PWM versione semplice: analogWrite().

La tecnica più semplice per gestire una modulazione PWM tramite Arduino consiste nell’avvalersi della funzione analogWrite(pin, dutyCycle) dove il parametro dutyCycle è un valore intero compreso tra 0 e 255 (risoluzione a 8bit). Attenzione perché il nome della funzione è forviante, l’uscita del pin indicato come primo parametro è in realtà digitale.

La funzione analogWrite() regola la larghezza dell’impulso dello stato on rispetto al periodo dell’onda ma non permette nessun controllo sulla frequenza, motivo questo per transitare verso una tecnica un po’ più prolissa ma altrettanto efficace e flessibile.

Uso diretto dei registri dell’Atmega328.
Faccio riferimento all’Atmega328 perché è il core delle versioni di Arduino più diffuse in circolazione (10000, 2009 e UNO), anche sa valgono i medesimi discorsi per l’Atmega168.

Per il controllo della periferica PWM, l’Atmega328 dispone di tre timer ognuno dei quali regola le tempistiche di 2 uscite PWM col risultato di poter contare su massimo 6 canali, configurabili per lavorare in modo indipendente o in coppia e complementati. Sul datasheet del componente, ovviamente, è possibile recuperare tutti i dettagli riguardanti la configurazione dei registri attinenti alle varie periferiche e nel caso specifico alla periferica PWM.

Per evitare particolari perdite di tempo a chi tempo non ha o non è così avvezzo alla lettura dei datasheet (usa Arduino anche colui che nulla sa di elettronica ma che riesce in breve tempo a mettere insieme le funzioni di libreria vedendo funzionare la propria applicazione il successo di Arduino, se vogliamo è anche e soprattutto questo!!) riporto di seguito un minimo di teoria e l’implementazione della tecnica fast PWM.

FastPWM: un minimo di sana teoria!!

In virtù di quanto detto nella breve premessa fatta sopra,i timer dedicati al funzionamento della periferica PWM sono tre e vanno sotto il nome di Timer 0, Timer 1, e Timer 2. Ad ogni timer sono associati due registri con i quali viene confrontato il valore di conteggio, potendo regolare di conseguenza la larghezza dell’impulso PWM. Ad ogni comparatore è associata un’uscita che va alta a seconda del risultato della comparazione, quindi ad ogni comparatore è associata un’uscita PWM, e quindi ancora ad ogni Timer sono associate due uscite PWM.

Ad ogni timer è associabile un fattore di prescaler (divisione in frequenza) tra i possibili che si possono impostare: 1, 8, 64, 256 o 1024. Arduino ha un clock di sistema di 16MHz e la frequenza di clock dei vari timer PWM (un clock uguale per tutti i timer) è pari alla frequenza di sistema diviso il fattore di prescaler. L’unico a disporre di un fattore di prescaler differente dagli altri timer è il Timer 2, potendo quindi impostare una frequenza di PWM differente dalla frequenza dei restanti 4 canali (ricordando che ad ogni timer si fanno corrispondere due canali PWM).

I timer possono inoltre lavorare in diverse modalità, e le principali sono la “Fast PWM” e la “PWM con correzione di fase”. Inoltre per i timer si può decidere di far fare loro il conteggio da 0 a 255 (risoluzione PWM di 8bit) oppure da 0 ad un valore fissato (minore di 255) al fine di aumentare la frequenza PWM pur rinunciando a qualcosa in termine di risoluzione sull’impostazione della larghezza di impulso. Solo al timer 1 è associata la possibilità di estendere la risoluzione da 8 a 16 bit riducendo però sensibilmente la frequenza di clock. Ogni uscita può essere inoltre invertita.

Giusto per completezza, ogni timer può generare un segnale di interrupt di overflow o di matching ma questo approfondimento non serve ai fini della semplice generazione della modulazione PWM.

Dettaglio sui registri.
Ogni timer associato a due canali PWM ha a disposizione i registri di configurazione TCCRnA e TCCRnB, dove la lettera n generalizza il numero del timer corrispondente (n=0 per il Timer 0, n=1 per il Timer 1, ecc…). Di questi registri sono essenziali i seguenti bit di configurazione:

      I bit WGM (Waveform Generation Control) che configurano il timer nelle diverse modalità di funzionamento;

 

      I bit CS (Clock Select) controllano il fattore di prescaler associato al clock;

 

      COMnA: bit che permettono di attivare, disattivare e invertire l’output A;

 

      COMnB: bit che permettono di attivare, disattivare e invertire l’output B.

 

    I registri OCRnA e OCRnB settano i livelli di comparazione delle due uscite, A e B e quindi i corrispettivi duty-cycle.

Fast PWM

Nella modalità fast più semplice, i timer contano ciclicamente da 0 a 255. L’uscita va alta quando il timer è 0 mentre va bassa quando il timer raggiunge il valore contenuto nel registro di comparazione. Più alto è il valore del registro, maggiore è il duty-cycle. La figura che segue mostra l’andamento dei registri OCRnA e OCRnB. Entrambe le uscite hanno la stessa frequenza di PWM.

Per attuare efficacemente la Fast PWM, di seguito riporto uno esempio di codice già funzionante che, sfruttando il timer2, genera sul pin 3 di Arduino2009 un’onda quadra a duty-cycle variabile a frequenza 100kHz (spesso eccessiva per applicazioni switching di potenza, però è giusto per dare un esempio di fin dove ci si riesce a spingere con questa tecnica).

Prima dell’esempio, riporto una piccolissima nota circa il controllo della frequenza attraverso l’utilizzo del livello alto di comparazione OCRnA.
Come si è detto in precedenza, è possibile far contare il singolo timer da 0 a 255 come di default oppure da 0 a ad un valore fissato in modo da aumentare la frequenza pur perdendo qualcosa sulla risoluzione PWM. Per il Timer2, agendo sul registro TCCR2A, è possibile far contare il Timer2 fino al valore contenuto nel registro OCR2A. Naturalmente, questo valore non può più essere utilizzato per regolare in PWM l’uscita OC2A. Esiste però un’opzione ulteriore che permette di fare il toggle dell’uscita OC2A tutte le volte che il timer attraversa la soglia, quindi disponendo di un’onda quadra a duty cycle fisso al 50% e con frequenza pari alla metà della frequenza PWM dell’altro canale.
La frequenza del segnale PWM può essere calcolata con la seguente formula:

f=f_clk/(prescaler*OF_Value)=16MHz/(prescaler*OverFlow_value).

Esempio applicativo.

int variabile=0;
static char mode=0;

void setup()
{
  // Configurazione del Timer2 per 
funzionare in modo 
fast PWM su OC2B (pin 3 di Arduino)
  // set pin high on overflow, clear on compare match with OCR2B
  TCCR2A = 0x23; // imposto “pin high on overflow”, 
e “clear on compare match with OCR2B
  TCCR2B = 0x09;  // seleziono come 
sorgente di clock I 16MHz di sistema senza prescaler
  OCR2A = 159;  // inizializzo 
il top level match a 159 ->f_PWM=100kHz 
  pinMode(3, OUTPUT);  // abilito come uscita il pin 3.
}

void loop()
{
   if(mode==0)
     {
       variabile++;
       if(variabile==160)		
//controllo che il duty sia arrivato al 100%
         {
           mode=1;
         }
     }
   if(mode==1)
     {
       variabile--;
       if(variabile==0)		
//controllo che il duty sia arrivato a 0%
         {
           mode=0;
         }
     }
      OCR2B=variabile;
     delay(10);
 }

Il codice presentato fa il fading di un led posto sul pin digitale 3 di Arduino2009, sia in accensione che in spegnimento, ed è la dimostrazione visiva del funzionamento del codice. Variando tra 0 e 159 il contenuto del registro OCR2B si varia il duty-cycle tra 0 e 100%.

Il dettaglio del registro TCCR2A riportato di seguito mostra il perchè dell’inizializzazione dello stesso a 0x23. Infatti, in binario, il valore inizializzato è pari a 00100011 e quindi, i primi due bit meno significativi (WGM21 – WGM20) impostano la modalità di funzionamento Fast_PWM (per le altre modalità rifarsi al datasheet dell’ATmega328) mentre il quarto e il quinto bit partendo sempre dal meno significativo (COM2B1 – COM2B0) indicano come commuta l’uscita OCB2 (uscita 3) (tutte le possibili combinazioni sono riportate nel datasheet)

Per quanto riguarda il registro TCCR2B, questo è inizializzato a 0x09 (00001001 in binario) cioè si va ad indicare la preferenza di non utilizzare nessun fattore di prescaler ma di considerare come sorgente di clock per il timer i 16MHz di sistema (per ulteriori dettagli rifarsi al datasheet).

Chiudo l’articolo dicendo che se si volesse disattivare completamente l’uscita PWM, basta impostare il relativo pin come ingresso in modo da configurarlo in alta impedenza

pinMode(3, INPUT);  // abilito come ingresso il pin 3.
Scarica subito una copia gratis
Tags:

8 Commenti

  1. Avatar photo electropower 28 Giugno 2011
  2. Avatar photo Ionela 30 Agosto 2011
  3. Avatar photo Antonello74 15 Agosto 2018
    • Avatar photo Maurizio Di Paolo Emilio 19 Agosto 2018
      • Avatar photo Antonello74 20 Agosto 2018
  4. Avatar photo Antonello74 19 Agosto 2018
  5. Avatar photo Raffaele Principi 27 Ottobre 2023

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend