PrettyPrint

domenica 5 settembre 2010

Timer AVR in 5min: 5) Gen. forme d'onda PWM (fast)

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


Ultima revisione: 26/Apr/2012 

pwm1.zip


La modalità CTC consente di generare forme d'onda rettangolari, ma soltanto simmetriche (tempo di on uguale a quello di off ossia duty cicle del 50%).

Forme d'onda rettangolari con duty cicle diverso dal 50% possono essere generate con un'altra modalità di funzionamento del timer: la PWM (Pulse Width Modulation: modulazione a larghezza d'impulso).

Sul Timer1 dell'ATmega8 si possono usare tre tipologie di PWM:

1) Fast
2) A correzione di fase
3) A correzione di fase e di frequenza.

La 1) è la più veloce, consente di raggiungere frequenze più alte delle restanti due; al variare del duty cicle il periodo (ovvero la frequenza) rimane costante, ma varia la fase;
la 2) e la 3), invece, non comportano una variazione della fase al variare del duty cicle e dunque si prestano per applicazioni quali il pilotaggio di motori, laddove non sono richieste elevate frequenze di funzionamento ma la fase deve mantenersi costante.

Un possibile compito, che vede impiegata la FastPWM, potrebbe essere il seguente:

Generare, con il timer1 da 16bit, il sequente segnale rettangolare (d.c.=25%) avente frequenza di 1 kHz, usando come clock quello di sistema (fc=4MHz):

Nella modalità Fast PWM, come già detto, il periodo Ts rimane costante al variare del duty cicle ed è determinato dalla risoluzione del timer.

La risoluzione può essere scelta fra alcuni valori fissi (8bit, 9 bit o 10 bit) oppure variabile, da 2 bit fino alla risoluzione massima del timer1: 16bit.

Nella risoluzione fissa, il timer dunque conta da 0 fino a un numero N pari a 2n-1 :

N=255 (8bit),
N=511 (9bit)
N=1023 (10bit)

La durata dell'intero periodo Ts dipende da questi valori, dal periodo di clock, dal prescaler, secondo la nota formula già vista:



Con la nostra frequenza di clock fc di 4MHz (Tc=0,25us), per i vari valori di prescaler, si ottengono i seguenti valori di Ts (espressi in ms) per le varie risoluzioni fisse del timer:


Come si può facilmente constatare, non abbiamo nessun valore di Ts=1ms.

Solo con N=511 (9bit) e prescaler :8 ci avviciniamo a 1ms (1,024ms). Se non è richiesta una precisione spinta, potremmo allora "accontentarci" di un valore praticamente uguale a quello assegnato e configurare il timer1 in modo FastPWM con risoluzione fissa da 9bit.

Supponendo, invece, di volere esattamente un valore del periodo del segnale pari a 1ms, dobbiamo allora ricorrere alla risoluzione variabile. Impostando il modo PWM con risoluzione variabile bisogna determinare una risoluzione compresa fra 2 bit (quindi il timer conta fino a N=3) e quella massima consentita dal timer: 16bit nel caso del Timer1 (e quindi conta fino a N=65535).

Quindi, in definitiva, se impostiamo la risoluzione variabile il valore N può essere compreso fra 3 e 65535.

Quale sia un possibile valore di N per avere Ts=1ms, lo ricaviamo dalla formula inversa:



Per k=1, 8, 64, 256, 1024, e con Ts=0,25 us (fc=4MHz)ricaviamo:








k N
1 3.999
8 499
64 61,5
256 14,63
1024 2,906



Sono utilizzabili solo i prescaler 1 e 8, perché danno come risultati dei numeri, come appena detto, compresi fra 3 e 65535.

Usiamo k=1 (nessun prescaler) e dunque N=3999

Tale numero N va caricato nel registro ICR1

Definito il numero necessario per avere l'intero periodo Ts, rimane ora da determinare il numero (compreso nel nostro caso fra 0 e 3999) in corrispondenza del quale si ha la commutazione del pin (OC1A oppure OC1B) quando Ton=0,25ms.

Se vogliamo generare la nostra forma d'onda usando il canale A, dunque segnale in uscita sul pin OC1A, caricheremo questo valore nel registro OCR1A; viceversa, se vogliamo usare il canale B, dunque forma d'onda sul pin OC1B, caricheremo questo valore nel registro OCR1B.

Supponendo di volere usare il canale A (pin OC1A), la situazione è raffigurata di seguito:



Finché il dato del Timer1 (ovvero il valore del registro TCNT1) è inferiore al numero NA, l'uscita OC1A assume valore alto (es. 5V). Allorquando il valore di TCNT1 eguaglia quello contenuto nel registro OCR1A, al successivo impulso di clock, si ha una commutazione del pin OC1A a 0V.

Infine, al raggiungimento del successivo valore N, il pin OC1A commuta a VH (es. 5V)

Il registro di conffronto (OCR1A nel nostro caso) viene sincronizzato a ogni successivo valore 0 del registro TCNT1, questo per evitare asimmetrie nella forma d'onda generata.

Da precisare che questa è la modalità non invertente. E' possibile configurare la generazione della forma d'onda anche in modalità invertente, per la quale, ovviamente, i livelli di commutazione sono invertiti e quindi è invertita anche la forma d'onda.


Resta da determinare il numero NA in corrispondenza del quale si ha che il tempo trascorso dall'ultimo reset del Timer1 è proprio Ton.

E' molto semplice.

Se per impiegare un tempo Ts, il timer deve contare fino a N, evidentemente per un tempo Ton più piccolo il timer dovrà contare fino a un numero NA < N:



Dunque, per calcolare Ton:


mentre per il periodo Ts, già si è visto che:



Facendo il  rapporto Ton/Ts ricaviamo così il duty cicle e da qui il numero NA:


Nel nostro caso, essendo d.c.=0,25 e N=3999, ricaviamo NA=999

Abbiamo tutti i dati per scrivere il software, partendo dal seguente pseudocodice:

  1. Carica NA=999 nel registro OCR1A;
  2. Imposta il modo FastPWM a risoluzione variabile (e valore N definito nel registro ICR1);
  3. Imposta forma d'onda non invertita;
  4. Carica N=3999 nel registro ICR1;
  5. Imposta prescaler :1;


1. Carica NA=999 nel registro OCR1A;


Per il caricamento di questo valore nel registro OCR1A, conviene definire precedentemente una label NA corrispondente al numero 999, con il solito commento indicante il dyty cicle/Ton che si ottiene in corrispondenza di esso:


#define NA 999  // d.c. 25% (Ton=.25ms) @fc=4MHz :1

OCR1A = NA;


2. Imposta il modo FastPWM a risoluzione variabile (e valore N definito nel registro ICR1)



Con l'ausilio del datasheet dell'ATmega168, si va a vedere su quali registri di configurazione del timer1 agire e quali bit settare, per ottenere la modalità PWM desiderata.

Dalla tabella 16-4 del datasheet, si legge che la modalità Fast PWM con risoluzione variabile e con valore N ("TOP" secondo la convenzione del datasheet) specificato in ICR1, viene attuata attivando i bit WGM13, WGM12 e WGM11 (modo 14):







I bit WGM13 e WGM12 sono nel  registro B di configurazione del timer1 (TCCR1B):




mentre il restante bit WGM11 si trova nel  registro A di configurazione (TCCR1A):






Dunque scriveremo:

TCCR1B |= (1 << WGM13) | (1 << WGM12); 
TCCR1A |= (1 << WGM11);



3. Imposta forma d'onda non invertita


Vogliamo che la nostra forma d'onda sia non invertita, cioè così come quella corrispondente alla definizione di duty cicle (livello di tensione alto durante Ton, e livello 0V durante Toff) e per questo bisogna guardare la tabella 16-2, che ci dice come impostare i bit COM1A1 e COM1A0 (ovvero COM1B1 e COM1B0 se ci riferiamo all'uscita OC1B) del registro TCCR1A, per ottenere il comportamento desiderato dell'uscita, nella modalità Fast PWM, quando il timer raggiunge il numero contenuto nel registro di confronto OCR1A:







Quindi, attiveremo il solo bit COM1A1:

TCCR1A |= (1 << COM1A1); 

4. Carica N=3999 nel registro ICR1;


Per questa istruzione seguiamo lo stesso metodo usato per caricare il registro OCR1A, con etichetta e commento relativo al tempo Ts che si ottiene in corrispondenza di esso:

#define N 3999 // Ts=1ms @fc=4MHz :1


5. Imposta prescaler :1


Infine, l'impostazione del prescaler, che è decisa dalla terna di bit CS12, CS11 e CS10 del registro TCCR1B così come definito nella Tab. 16-5 del d/sheet:





Per avere un prescaler :1 (nessun prescaler) bisogna dunque attivare il solo bit CS10:

TCCR1B |= (1 << CS10);



Il sorgente definitivo è dunque:

#include <avr/io.h>

#define N 3999 // Ts=1ms @ fc=4MHz:1
#define NA 999 //  d.c.=25% (Ton=.25ms) @ fc=4MHz:1

int main (void)

{

  DDRB |= (1 << PB1);  // imposta pin PB1(OC1A) come uscita
  OCR1A = NA;  // carica OCR1A con il valore NA
  TCCR1B |= (1 << WGM13) | (1 << WGM12); // FastPWM con ICR1=N (Tab.16-4)
  TCCR1A |= (1 << COM1A1) | (1 << WGM11) ;  // Non inv. (Tab.16-2) | FastPWM con ICR1=N (Tab.16-4)
  ICR1 = N; // carica ICR1 con il valore N
  TCCR1B |= (1 << CS10); // Nessun prescaler (Tab.15-5)

  while(1)
  {
   /* il compito viene svolto completamente via hardware dal Timer1.. */
  }

}


SIMULAZIONE CON VMLAB

I risultati della simulazione sono riportati di seguito. In allegato, lo zip con tutti i file del progetto VMLAB. Per usare VMLAB sotto Linux seguire il relativo tutorial

Misura di Ton (0,25 ms)


Misura di Ts (1ms)






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





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



Ultima revisione: 26/Apr/2012 

Nessun commento:

Posta un commento