PrettyPrint

lunedì 6 settembre 2010

Timer AVR in 5min: 6) Generare forme d'onda PWM (a correzione fase e freq.)

Ultima revisione: 30/Apr/2012 


< Timer AVR in 5min: 5) Gen. forme d'onda PWM (fast) Timer AVR in 5min: 7) Un foglio di calcolo >
pwm2.zip



Un secondo modo di generare segnali a larghezza di impulso variabile, con il Timer1, è quello "a correzione di frequenza e di fase". Questa modalità, a differenza di quella "fast", al variare del duty cicle dell'onda generata mantiene immutata non solo la frequenza del segnale, ma anche la fase; questa caratteristica è dunque da preferire per il controllo di motori.

In questa modalità il timer conta in avanti fra 0 e il valore massimo N. Raggiunto il massimo valore N continua a contare, ma indietro, verso lo 0, per poi ricominciare di nuovo il conteggio verso N e così via, periodicamente, di periodo T.


Dunque, l'andamento nel tempo del dato contenuto del registro del timer1 (TCNT1) assume una forma triangolare simmetrica, piuttosto che a rampa come nella modalità PWM fast, come schematizzato nella figura successiva:



Il fronte di salita dell'onda triangolare descrive il conteggio in avanti (fra il numero minimo 0 e il numero massimo N), mentre quello di discesa rappresenta il conteggio in direzione opposta (fra N e 0).
 
La forma d'onda uscita rettangolare sopra raffigurata si riferisce alla modalità non invertente: in questo modo di funzionamento, la commutazione a 0V del pin OC1A avviene sul confronto nel periodo del conteggio in avanti, mentre la commutazione al livello alto VH dello stesso avviene sul confronto nel  periodo del conteggio in discesa.


Si nota che il periodo del segnale generato (Ts) è il doppio del periodo T che si ottiene in corrispondenza di N e di questo ne dovremmo tenere conto nei calcoli successivi


Si hanno a disposizione fino a due canali PWM: A (pin OC1A) e B (pin OC1B), quindi possiamo avere due forme d'onda con duty cicle diversi, ma ovviamente entrambe aventi lo stesso periodo dal momento che condividono lo stesso timer.


La commutazione avviene al successivo impulso di clock dopo l'avvenuta uguaglianza fra il valore contenuto nel registro TCNT1 e il valore contenuto nel registro di confronto OCR1A (ovvero OCR1B se stiamo usando il secondo canale B), quando il valore massimo N è stato definito nel registro ICR1  (è possibile definire anche il valore massimo in OCR1A, v. datasheet).

L'aggiornamento del registro di confronto (es. OCR1A) in questo modo di funzionamento avviene sempre in corrispondenza del successivo zero del registro TCNT1; questo per evitare asimmetrie nel segnale rettangolare.

Il valore N, che stabilisce così la risoluzione del Timer1, può assumere un valore compreso fra 3 (risoluzione minima di 2bit) e 65535 (risoluzione massima di 16 bit).

Esempio applicativo

Supponiamo di avere un tipico problemino per il controllo di un servomotore:

Generare con il timer1 di un ATmega168, sul pin OC1A, il sequente segnale rettangolare (Ton=2ms) avente frequenza di 50Hz (T=20ms) usando come clock quello di sistema (fc=1MHz):



Calcoliamo innanzitutto un possibile valore di N in corrispondenza del quale abbiamo un periodo T pari alla metà dell'intero periodo (Ts=20ms) della forma d'onda.

Usando l'equazione già vista in precedenza, in cui pero dobbiamo tener conto che è T=Ts/2, avremo:










Per k=1, 8, 64, 256, 1024, e con Tc=0,25us (fc=1MHz), sostituendo, abbiamo:







k N
1 9.999
8 4.999
64 155,25
256 38,06
1024 28,77


Preferiamo k=1 (nessun prescaler) e quindi N=9999.

Individuiamo ora il numero NA in corrispondenza del quale si deve avere il confronto e quindi la successiva commutazione dell'uscita.

Come si vede dal primo diagramma temporale, fra 0 e NA corrisponde una differenza di tempo pari a Ton/2, dunque:



sostituendo, abbiamo NA=999


Scriviamo il programma

Abbiamo tutti i dati per scrivere un possibile pseudocodice per la configurazione del solo timer

  1. Imposta la modalità PWM a correzione di fase e di frequenza (e valore N definito in ICR1)
  2. Carica N=9999 nel registro ICR1;
  3. Carica NA=999 nel registro OCR1A;
  4. Imposta la polarità della forma d'onda: non invertita;
  5. Imposta prescaler :1 (nessun prescaling, k=1)

Come noto, l'istruzione di definizione del prescaler va sempre messa per ultima, in quanto dopo la sua esecuzione viene avviato il timer.

Vediamo come codificare i vari passi in GNU AVR-libc:

1. Imposta la modalità PWM a correzione di fase e di frequenza (e valore N definito in ICR1)

Per quanto riguarda l'impostazione della modalità PWM a correzione di fase e di frequenza, con valore massimo N (TOP secondo la convenzione del datasheet) definito in ICR1, basta guardare la Tabella 16-4 del datasheet dell'ATmega168:


 
Il modo è il numero 8, per il quale bisogna attivare il solo bit WGM13 del registro B di configurazione del timer1. In termini di istruzione :

TCCR1B |= (1 << WGM13); // PWM a corr. di fase e freq. con ICR1=N (Tab.16-4) 


2. Carica N=9999 nel registro ICR1
 
All'istruzione  di caricamento:

ICR1 = N;

dobbiamo far precedere la definizione dell'etichetta simbolica N, sempre opportunamente commentata:

#define N 9999   // @ Ts=20ms , fc=1MHz (:1)

 
3. Carica NA=999 nel registro OCR1A

OCR1A = NA; 

#define N 999   // @ Ton=2ms , fc=1MHz (:1)

 
4. Imposta la polarità della forma d'onda: non invertita

La forma d'onda desiderata deve essere coerente con quella di definizione di duty cicle, ossia durante il tempo Ton l'uscita deve essere a livello alto (es. +5V) e durante il tempo Toff l'uscita deve essere a livello 0V

La Tabella 16-3 del d/sheet definisce il comportamento dei pin OC1A e OC1B allorquando il valore del registro OCR1A (per OC1A) e/o del registro OCR1B (per OC1B) eguaglia quello del registro del timer TCNT1, attraverso la combinazione dei bit COM1A1/COM1A0 (per configurare la forma d'onda sul pin OC1A) e COM1B1/COM1B0 (per configurare la forma d'onda sul pin OC1B) del registro TCCR1A:



Dunque, volendo una forma d'onda non invertita, sul solo pin OC1A, il codice sarà:

TCCR1A |= (1 << COM1A1); // Forma d'onda non inv. (Tab.16-3)


5. Imposta prescaler :1 (nessun prescaling, k=1) 

Per impostare un prescaler unitario (ossia nessun prescaler) bisogna impostare il solo bit CS10 del registro di configurazione B, come indicato nella tabella 16-5 del d/sheet:




TCCR1B |= (1 << CS10); // Nessun prescaler (Tab.16-5) 



Combinando fra loro tutti i vari pezzi  e ricordandosi di impostare il pin PB1/OC1A come uscita, siamo in grado di stilare il sorgente completo:

#include <avr/io.h>

#define N = 9999;  // @ Ts=20ms , fc=1MHz (:1)
#define NA = 999;  // @ Ton=2ms , fc=1MHz (:1)

int main (void)

{

  DDRB |= (1 << PB1);  // imposta pin PB1(OC1A) come uscita
  TCCR1B |= (1 << WGM13); // PWM a corr. di fase e freq. con ICR1=N (Tab.16-4) 
  ICR1 = N;  // Conta da 0 fino a N 
  OCR1A = NA;  // Commuta pin OC1A, quando TCNT1=NA
  TCCR1A |= (1 << COM1A1);  // Forma d'onda non inv. (Tab.16-3)
  TCCR1B |= (1 << CS10); // Nessun prescaler (Tab.16-5)

  while(1)

  {
    /* il compito viene svolto completamente via hardware dal Timer1.. */
  }

}


Simulazione con VMLAB

I risultati della simulazione con VMLAB, sono riportati di seguito.

La misura di Ton, come differenza tra i tempi misurati dai due cursori:




E infine, la misura della frequenza, che conferma Ts=20ms (ovvero fs=50Hz):




Nel file pwm2.zip sono contenuti il sorgente main.c, i file makefile e config e il file progetto pwm2.prj per VMLAB.





I contenuti di questo documento, sono stati resi possibili grazie ai seguenti strumenti gratuiti:
- Datasheet completo dell'ATmega48/88/168/328
- LaTex online equation editor
- FidoCADJ (disegno grafici)
- Google Code Prettify (sorgenti con sintassi evidenziata)
- Libreoffice Calc (calcolo valori prescaler ris. fissa)



< Timer AVR in 5min: 5) Gen. forme d'onda PWM (fast) Timer AVR in 5min: 7) Un foglio di calcolo >

Nessun commento:

Posta un commento