Die Timer des ATMega128 – PWM Modus (1)

Timer sind für viele Anwendungen unabdingbar, werfen aber immer wieder Fragen auf – auch bei mir. Aus diesem Grund habe ich bereits etliche Stunden vor dem Rechner verbracht und unzählige Websites besucht um meine Wissenslücken zu schließen. Ich möchte diese Artikelserie dazu nutzen, dass andere “Unwissende” weniger suchen müssen und ich werde an dieser Stelle die verschiedenen Funktionen der Timer beleuchten. Ganz nebenbei gehe ich auch auf die Besonderheiten ein, die es bei den Timer des ATMega128 zu beachten gilt

Bislang habe ich die beiden Methoden „Timer Overflow“ und „Clear Timer on compare (CTC)“ beschrieben. Für ein besseres Verständnis dieses Artikels kann es sinnvoll sein, die beiden Artikel vorab zu lesen. Dieses Mal möchte ich mich dem Thema PWM, also der PulsWeiten-Modulation, widmen.

Die Pulsweite

Bevor ich an dieser Stelle über eine Modulation einer Pulsweite schreiben werde, muss ich den Begriff „Pulsweite“ zunächst erklären. Der Puls in diesem Fall ist ein Signal, dass kurz von Low auf High und wieder auf Low springt – also ein Rechtecksignal. Die Besonderheit ist hier, dass es sich nur im kurze Impulse (da steckt der Puls ja schon drin 😉 ) handelt und nicht um ein andauerndes Signal. Das folgende Bild zeigt gleich drei solcher Pulse.

Die Pulsweite ist nun das Verhältnis zwischen der Einschalt- und Ausschaltdauer der Impulse. Im diesem Beispiel haben die drei Impulse je 50% Einschaltzeit und 50% Ausschaltzeit. Übrigens, „Einschaltzeit“ heißt im Englischen „Duty Cycle“. Dieser Begriff taucht das ein oder andere Mal auf.

Timer - Pulsweite

Timer – Pulsweite

Eine solche Impulsfolge wird später an einem der PWM-Ausgänge herauskommen. Die Frage ist aber erstmal, was damit bezweckt wird, oder nicht? Angenommen, die Zeiteinteilungen entsprechen 1s. Was würde ein Spannungsmesser messen? Klar, zwei Sekunden 5V und die nächsten zwei Sekunden 0V, usw. Und was wäre, wenn die Zeiteinteilung nur noch 1 Millisekunde beträgt? Ein normaler Spannungsmesser wird dem Auf und Ab, zwischen 0V und 5V, nicht mehr folgen können. Nun tritt der eigentlich gewünschte Effekt auf, auf dem Display wird 2,5V angezeigt.

Noch anschaulicher wird das, wenn anstelle des Spannungsmessgeräts eine LED an dieses Signal angeschlossen wird (Vorwiderstand nicht vergessen!). Auch dieses Bauteil wird nun von den Impulsfolgen versorgt. In den ersten 0,5 Millisekunden (50% Einschaltdauer) wird die LED eingeschaltet und die Helligkeit steigt. in den nächsten 0,5 Millisekunden wird die Versorgung abgeschaltet und die Helligkeit sink wieder. Dann wird die LED wieder eingeschaltet und die Helligkeit steigt wieder um anschließend, bei 0V wieder zu sinken. Tatsächlich hat die LED gar nicht genug Zeit um die Helligkeit auf 100% und auch nicht um sie wieder auf 0% zu bringen. Dafür ist sie einfach zu träge, genau wie unser Auge auch.

Somit bleibt unterm Strich, dass als PWM-Spannung das arithmetische Mittel der Ein- und Ausschaltzeit herangezogen werden kann. Da in diesem Fall eine Einschaltzeit von 50% vorliegt, ist auch das PWM-Signal = Vcc * 50% = 2,5V.

Zum Glück ist die Einschaltzeit nicht statisch. Sie kann grundsätzlich jeden Wert zwischen 0% und 100% betragen. Somit wären auch die folgenden Impulsfolgen möglich:

Timer - Pulsweite mit einer Einschaltdauer von 25 Prozent

Timer – Pulsweite mit einer Einschaltdauer von 25 Prozent

Timer - Pulsweite mit einer Einschaltdauer von 75 Prozent

Timer – Pulsweite mit einer Einschaltdauer von 75 Prozent

In den drei letzten Impulsfolgen war die Einschaltdauer recht einfach zu erkennen. Aber natürlich gibt es hier auch eine Formel, mit der die genaue Einschaltdauer ausgerechnet werden kann. Ich hab diese Berechnung mal anhand des letzten Bildes durchgeführt und pro Teilstrich 0,25ms angenommen.

Timer - Berechnung der Einschaltdauer

Die Erzeugung des PWM Signals

Das PWM-Signal wird durch den Vergleich eines laufenden Timerwerts und einem Vergleichswerts erzeugt. Wenn der ansteigende Timerwert einen Vergleichswert erreicht hat, dann schaltet sich der PWM-Ausgang an. Der Timer läuft weiter bis zum Maximalwert (bei 8-Bit Timer bis 255) und fällt dann auf 0 ab. Das wiederum setzt den PWM-Ausgang wieder auf 5V. Als Grafik sieht das wie folgt aus, wenn als Vergleichswert 96 verwendet wird:

Timer - Erzeugung des PWM-Signals

Timer – Erzeugung des PWM-Signals

Die oben angezeigten 37,5% Einschaltdauer können natürlich wieder mit einer netten Formel berechnet werden:

Timer - Berechnung der Einschaltdauer mit Timerschritten

Fast PWM

Der zuvor gezeigte Signalverlauf ist das sogenannte „Fast PWM“. Er wird mit einem Sägezahn-Timer-Signal erzeugt. Und damit ist jetzt ein guter Zeitpunkt gekommen, an dem ich die Timer-Register und deren Bits aufzeigen und erklären kann.

Um den PWM-Modus zu initialisieren sind vor allem die Register TCCR0A und TCCR0B (Timer/Counter Control Register A & B). Der Unterschied zum beliebten ATMega16 oder ATMega32  liegt darin, dass der 128er zwei TCCR-Register besitzt und sich dadurch die Programme der verwanden Controller in einigen Details unterscheiden.

Timer - Das Register TCCR0A und TCCR0B

Timer – Das Register TCCR0A und TCCR0B

Die blau markierten Bits sind für den Timer-Kanal B. Den werde ich jetzt nicht explizit beschreiben, wird aber grundsätzlich so initiiert wie der Kanal A.

Waveform Generation Mode

Da ich zuerst über das Fast PWM schreiben möchte, sollte ich auch zuerst zeigen, wie dieser Modus aktiviert wird. Im oben gezeigten Register TCCR0A befinden sich die Bits WGM00 und WGM01. In der folgenden Tabelle ist zu erkennen, dass der Fast PWM Modus eine 1 bei beiden Bits benötigt. Einfach, oder nicht?

Eine weitere Unterscheidung beim ATMega128 stellt das Bit WGM02 im Register TCCR0B dar. Dessen Wirkung und was damit sonst noch zusammen hängt, werde ich in einem separaten Artikel beleuchten. Hier und jetzt geht es mir darum, die üblichen PWM Modi zu erklären – als Grundlage sozusagen.

Timer - Tabelle Waveform Generation Mode

Timer – Tabelle Waveform Generation Mode

Compare Output Mode

Die beiden Bits COM0A1 und COM0A0, im TCCR0A, sind zuständig für das Verhalten des Pins OC0A am Pin PC3. An diesem Pin wird das PWM-Signal ausgegeben! Die folgende Übersicht zeigt die Einstellmöglichkeiten im Fast PWM Modus.

Timer - Compare Match Output für Fast PWM

Timer – Compare Match Output für Fast PWM

Vor allem die beiden unteren Fälle sind interessant. Bei COM0A1 = 1 ist das PWM-Signal nicht-invertiert und verhält sich so wie in den weiter oben gezeigten Fällen. Sind jedoch COM0A0 und COM0A1 gesetzt erfolgt eine Spiegelung des PWM-Signals.

Timer - PWM-Signal invertiert

Timer – PWM-Signal invertiert

Fall 2 (nur COM0A0 = High) werde ich an dieser Stelle nicht weiter vertiefen. Denn Einstellmöglichkeiten mit gesetztem WGM2-Bit, die hier nicht näher erläutert werden, widme ich später einen separaten Artikel.

Das Programm

Mit dem folgenden Programm soll eine Leuchtdiode mit dem PWM-Signal versorgt werden. Dieses Signal wird nacheinander mit verschiedenen Einschaltdauern generiert um den Effekt auf die LED zu demonstrieren. Hierfür kommt das ATMega128 Extension zum Einsatz, da dort alle Pins direkt auf Klemmen herausgeführt werden. Der Pin PB3 wird dort direkt abgegriffen und an eine LED weitergeleitet. Diese könnte ich mit ihrem Vorwiderstand auf einem Breadboard aufbauen, da das ATMega128 Extension keine LED direkt am Pin PB3 angeschlossen hat. ABER, das ATMega128 Extension hat noch durchkontaktierte Lötpads für eigene kleine Schaltungen – ideal für einen Widerstand (200 Ohm) und eine Leuchtdiode. Daher habe ich diese kleine Schaltung eben aufgebaut und mit einem Draht an die Klemme angeschlossen. Wer nicht löten möchte, für den ist das Breadboard ebenfalls völlig OK!

Hier ein Bild, wie ich die Schaltung mit einer LED und einem Widerstand aufgebaut habe. Der Pfeil symbolisiert den dünnen Draht, der zum Pin PB3 geht.

Timer - PWM-Schaltung

Timer – PWM-Schaltung

 

#define F_CPU 16000000UL    //muss ganz zu Beginn stehen für <util/delay.h>

#include <avr/io.h>
#include <util/delay.h>
#include "i2clcd.h"
#include "i2cmaster.h"

uint8_t duty;

void pwm_init(){
    // Wellenform - Fast PWM
    TCCR0A |= (1<<WGM00)|(1<<WGM01);

    //OC0 Verhalten
    TCCR0A |= (1<<COM0A1);      //nicht-invert.
    TCCR0B |= (1<<CS00);        //Vorteiler 1

    DDRB |= (1<<PB3);           // OC0 Pin = Ausgang

    OCR0A = 32;                 //Einschaltdauer
}

void main(){                    // Hauptprogramm
    pwm_init();                 // PWM Init.
    i2c_init();                 // Starte I2C Bus
    lcd_init();                 // Starte I2CLCD

    // Begrüßung für das Video
    lcd_command(LCD_CLEAR);     // Leere Display
    lcd_printlc(1,1,"timogruss.de");
    lcd_printlc(2,1,"Timer - Fast PWM");
    lcd_wait_ms(2000);          //Warte
    lcd_command(LCD_CLEAR);     // Leere Display
    lcd_printlc(1,1,"Einschaltdauer:");
    lcd_printlc(2,1,"12,5 %");

    while(1){                   // Endlosschleife
      _delay_ms(1000);          // 1 Sek. warten
      lcd_printlc(2,1,"25,0 %");// LCD Info
      OCR0A = 64;               // Einsch.d. auf 64 setzen

	  _delay_ms(1000);
	  lcd_printlc(2,1,"37,5 %");
	  OCR0A = 96;

	  _delay_ms(1000);
	  lcd_printlc(2,1,"50,0 %");
	  OCR0A = 128;

	  _delay_ms(1000);
	  lcd_printlc(2,1,"62,5 %");
	  OCR0A = 160;

	  _delay_ms(1000);
	  lcd_printlc(2,1,"75,0 %");
	  OCR0A = 192;

	  _delay_ms(1000);
	  lcd_printlc(2,1,"87,5 %");
	  OCR0A = 224;

	  _delay_ms(1000);
	  lcd_printlc(2,1,"100,0%");
      OCR0A = 255;              //Einschaltd. = Max.
    }
}

Im nächsten Timer Artikel werde ich das Thema PWM weiter bearbeiten. Dann wird das phasen-korrekten PWM näher beleuchtet. Natürlich gibt es dazu wieder ein kleines Beispielprogramm und eine ausführliche Erklärung.

Ich hoffe, Ihr konntet mit diesem Artikel etwas anfangen und ich würde mich natürlich sehr freuen, wenn es den Ein oder Anderen in Zukunft wieder hierher verschlägt.

Alles Gute,
Timo

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.