Erzeugen einer Pulsweitenmodulation (PWM)

Der Timer unterstützt auch das Erzeugen einer Pulsweitenmodulation (PWM).

Modi zum Erzeugen einer PWM

Dabei unterscheidet man verschiedene Modi zum Erzeugen einer PWM. Diese sind z.B.:

  • Fast PWM
  • Phasenkorrekte PWM
  • Phasen- und frequenzkorrekte PWM

 

Grundsätzliche Funktionsweise:

Der Timer beginnt z.B. bei 0 zu zählen und schaltet damit gleichzeitig einen Ausgangspin ein.
Wird ein bestimmter Zählwert erreicht, wird der Ausgangspin ausgeschaltet und der Timer zählt weiter bis er die Obergrenze erreicht hat.
Danach wiederholt sich das Ganze. Der Timer schaltet den Ausgangspin bei 0 wieder auf HIGH und bei erreichen des Zählwertes auf LOW.
Durch verändern des Zählwertes kann man so das Verhältnis der EIN- und AUS-Zeit zur Gesamtzeit steuern.

Im folgenden Bild ist eine PWM mit einen Dutycycle von ca. 20% zu erkennen. Die EIN-Zeit beträgt ca. 20% zur Gesamtzeit.

PWM Dutycycle

 

Hier eine PWM mit einen Dutycycle von 50%. Die EIN-Zeit beträgt 50% zur Gesamtzeit.

PWM Dutycycle

 

Im Folgenden ein kleines Programmbeispiel, welches eine PWM mit einen 50% Dutycycle erzeugt:

// Programmbeispiel PWM
// Erzeugen einer PWM mit dem 8-Bit Timer und Ausgabe am Portpin PD3 (OC0A)
// Controller: ATMEL AT90PWM316

#include <avr/io.h>

#define F_CPU 8000000 // CPU-Frequenz = 8MHz (1 Clockzyklus = 1 / 8MHz = 0,000000125s = 0,125us)

int main(void)
{

DDRD |= (1<<PD3); // PD3 (OC0A) = Ausgang

TCCR0A |= (1<<COM0A1); // clear OC0A on compare match
TCCR0A |= (1<<WGM01)|(1<<WGM00); // Fast PWM (mode 3)

TCCR0B |= (1<<CS00); // Kein Vorteiler

OCR0A = 128; // 50% EIN / 50% AUS (Duty Cycle = 50%)

while(1)
{

asm ("NOP");

}
return
0;

}

Erläuterung zum Programmcode:

Ein Clockzyklus beträgt bei einer Taktfrequenz von 8 MHz 125ns (T = 1 / 8000 000).
Eine gesamte Timerperiode (bei einem 8-Bit Timer) somit 255 x 125ns = 31,875us.
Wird der Vergleichswert (compare match) im Output Compare Register 0 A (OCR0A) auf den Wert 128 gesetzt, wird der Ausgangspin OC0A (PD3) nach 128 Zykluszeiten (128 x 125ns = 16us) von HIGH auf LOW geschaltet. Nach dem Timerablauf (bei 255) startet der Timer erneut bei 0, schaltet den Ausgangspin OC0A (PD3) wieder auf HIGH und das Spiel wiederholt sich.

 

Clockzyklus

Hier ist der Ablauf grafisch dargestellt. Der Timer (blaue Kennlinie) beginnt bei 0 und schaltet den Pin OCOA auf High. Wird der Wert des compare match (OC match) von 128 erreicht, wird der PIN OC0A auf Low gesetzt. Wenn der Timer den Maximalwert von 255 (bei einem 8-Bit Timer) erreicht, beginnt der Timer wieder von 0 zu laufen und schaltet gleichzeitig den Pin OC0A auf High.

 

Das Timer Control Register A - TCCR0A

TCCR0A

Dieses Register steuert das Verhalten der Output Compare Pins OC0A und OC0B.
Wie im Programmbeispiel wird das Bit 7 (COM0A1) gesetzt und bewirkt ein clear OC0A on compare match.

Der jeweilige PWM-Mode wird durch das Setzen der Registerbits WGM00, WGM01 und WGM02 bestimmt. Das Registerbit WGM02 befindet sich im Timer Control Register B (TCCR0B).

TCCR0A

 

Um den Mode Fast PWM einzustellen müssen die Registerbits WGM00 und WGM01 gesetzt werden:

Waveform Generation Mode

 

Das Timer Control Register B - TCCR0B

Hier erfolgen die Einstellungen für den Takt. Um den Vorteiler (Prescaler) auf 1 zu stellen (1 = keine Teilung) wird das CS00 Bit gesetzt.

TCCR0B

Möchte man den Vorteiler z.B. auf 1024 stellen, müssen die Bits CS02 und CS00 gesetzt werden.

 

Programmbeispiel automatisches hochdimmen einer LED am Portpin PD3

// Programmbeispiel PWM
// Erzeugen einer PWM mit dem 8-Bit Timer und Ausgabe am Portpin PD3 (OC0A)
// Controller: ATMEL AT90PWM316

#include <avr/io.h>
#include <util/delay.h>

#define F_CPU 8000000 // CPU-Frequenz = 8MHz (1 Clockzyklus = 1 / 8MHz = 0,000000125s = 0,125us)

int main(void)
{

DDRD |= (1<<PD3); // PD3 (OC0A) = Ausgang

TCCR0A |= (1<<COM0A1); // clear OC0A on compare match
TCCR0A |= (1<<WGM01)|(1<<WGM00); // Fast PWM (mode 3)

TCCR0B |= (1<<CS00); // Kein Vorteiler

OCR0A = 0; // compare match zu Beginn auf 0 setzen

while(1)
{

OCR0A ++; // Bei jedem Schleifendurchlauf erhöht sich der compare match um 1

_delay_ms(10); // 10ms warten

}
return
0;

}