Holzi (Diskussion | Beiträge) |
(→Normaler Modus (Normal Mode)) |
||
Zeile 36: | Zeile 36: | ||
[[Bild:NormalerModus_1_Vorladen.png]] | [[Bild:NormalerModus_1_Vorladen.png]] | ||
− | Dadurch kann | + | Dadurch kann eingestellt werden, wie lange es dauert, bis ein Overflow auftritt. Um zu berechnen, welchen Wert wir vorladen müssen, kann auch ein Java-Applet genutzt werden, siehe unter [[Timer/Counter (Avr)#Weblinks|Weblinks Java Applet]]. |
Natürlich kann das auch "von Hand" berechnet werden. Die Berechnung des Preloader- sowie Prescalerwerts bei Verwendung der Overflow-Interrupts, eines Prescalers von 64 (nicht alle Prescaler können verwendet werden) und eines Quarzes mit der Frequenz von 8 MHz sieht folgendermaßen aus (gesuchte Frequenz beträgt 1000 Hz unter der Verwendung des Timer0 eines ATmega32): | Natürlich kann das auch "von Hand" berechnet werden. Die Berechnung des Preloader- sowie Prescalerwerts bei Verwendung der Overflow-Interrupts, eines Prescalers von 64 (nicht alle Prescaler können verwendet werden) und eines Quarzes mit der Frequenz von 8 MHz sieht folgendermaßen aus (gesuchte Frequenz beträgt 1000 Hz unter der Verwendung des Timer0 eines ATmega32): |
Version vom 14. Februar 2009, 19:49 Uhr
Die Mikrocontroller der AVR-Familie besitzen je nach Typ eine unterschiedliche Anzahl an programmierbaren Timern. Bei den aktuellen ATmegas sind das mindestens ein 8-Bit Timer und ein 16-Bit Timer. Die Timer werden immer Timerx benannt, wobei x für die Timernummer steht (also 0, 1, 2, usw.). Die Konfigurationsmöglichkeiten sind von Timer zu Timer unterschiedlich.
Hinweis: Die folgenden Code-Beispiele sind in C programmiert und wurden für einen ATmega32 entwickelt. Sie lassen sich also ohne große Änderungen auch auf anderen Mikrocontrollern der AVR-Familie einsetzen.
Inhaltsverzeichnis
Allgemeine Funktionsweise
Timer funktionieren nach dem allgemeinen Prinzip, dass sie eine Ganzzahl (im weiteren als Zähler bezeichnet) je nach Betriebsmodus auf- oder abwärtszählen, d.h. inkrementieren bzw. dekrementieren.
Angenommen, der Timer arbeitet im einfachsten Betriebsmodus, dem normalen Modus (siehe Normaler Modus (Normal Mode)). Die Zählrichtung des Timers ist aufsteigend gerichtet. Je nach Auflösung, also 8-Bit oder 16-Bit, erreicht der Zähler irgendwann einen bestimmten Zustand. Möglich wäre, dass er überläuft, wenn z.B. bei einem 8-Bit Timer der Wert 255 inkrementiert wird (siehe Grafik).
Der Prescaler
Der Prescaler (eng. = Vorteiler) kann dazu genutzt werden, den Takt, der den Timern zugeführt wird, zu verkleinern. U.a. kann damit der Timer so konfiguriert werden, damit diese in den unterschiedlichsten Frequenzen takten. Über den Prescaler kann auch der Timer angehalten werden. Hier eine Grafik die den Prescaler veranschaulicht:
Das obere Diagramm zeigt den Betrieb ohne Prescaler, das untere mit Prescaler. Die gestrichelte Linie zeigt, wann der Timer weiterzählt.
Im Teil Die Betriebsmodi wird weiter auf die praktische Verwendung des Prescalers eingegangen.
Die Betriebsmodi
Die AVR-Timer können in unterschiedlichen Betriebsmodi betrieben werden. Diese sind:
- Normaler Modus
- CTC Modus
- PWM
Normaler Modus (Normal Mode)
Der einfachste Betriebsmodus ist der normale Modus. Er funktioniert wie im Abschnitt "Allgemeine Funktionsweise" beschrieben. Die Zählrichtung des Timers ist immer aufsteigend, und irgendwann kommt es zu dem Interrupt Timer-Overflow (welcher in einer passend ISR aufgefangen werden kann). Im einfachsten Fall kann dieser Modus im folgendem Diagramm dargestellt werden:
Der Zähler des Timers (im Diagramm oben, die aufsteigende und dann wieder zurückgesetzte Linie) ist in dem Register TCNTx gespeichert, wobei x für eine Zahl steht. Soll z.B. auf den Timer0 (siehe Datenblatt des jeweiligen Controllers) des Controllers zugegriffen werden, so ist an TCNT eine 0 anzuhängen, also TCNT0. Wie lange es braucht, bis der Zähler einen Overflow auslöst, ist von der Taktfrequenz des Controllers, dem eingestellten Prescaler-Wert und von der Timerauflösung abhängig. Nun wäre es ja sehr unpraktisch, wenn wir den Zähler nicht anpassen könnten. Denn sonst müssten wir unsere Software, die den Timer benutzt, evtl. anpassen und viel rechnen, um z.B. für 1000 ms zu schlafen. Deswegen kann auf den Zähler zugegriffen werden und ihn vorladen, bevor dieser wieder vom eigentlichen Timer hochgezählt wird. Dies veranschaulicht folgendes Diagramm:
Dadurch kann eingestellt werden, wie lange es dauert, bis ein Overflow auftritt. Um zu berechnen, welchen Wert wir vorladen müssen, kann auch ein Java-Applet genutzt werden, siehe unter Weblinks Java Applet.
Natürlich kann das auch "von Hand" berechnet werden. Die Berechnung des Preloader- sowie Prescalerwerts bei Verwendung der Overflow-Interrupts, eines Prescalers von 64 (nicht alle Prescaler können verwendet werden) und eines Quarzes mit der Frequenz von 8 MHz sieht folgendermaßen aus (gesuchte Frequenz beträgt 1000 Hz unter der Verwendung des Timer0 eines ATmega32):
- [math]Prescale = Frequenz * 1000000 [Hz] = 8000000[/math]
- Wir definieren den maximalen Zählerwert. Dieser ist bei einem 8-Bit Timer 256, bei einem 16-Bit Timer 65536. In unserem Fall ist der maximale Zählerwert 256, weil Timer0 verwendet wird.
- Nun wird die Variable Prescale (s.o.) durch den verwendeten Prescaler (64) geteilt ([math]8000000 Hz / 64 = 125000[/math]).
- Als nächstes wird der im dritten Punkt errechnete Wert durch die gesuchte Frequenz geteilt [math]=125000 / 1000Hz = 125[/math].
- Nun wird mathematisch überprüft, ob der errechnete Wert aus dem vierten Punkt kleiner als der maximale Zählerwert ist. Trifft dies zu, so wird der errechneten Wert vom maximalen Zählerwert subtrahiert([math]= 256 - 125 = 131[/math]).
Damit haben wir den Wert errechnet, der bei jedem Interrupt, den der Timer0 auslöst, in TCNTx (in diesem Fall TCNT0) nachgeladen werden muss, damit die Interrupts in dem gewünschten Zeitabstand von einer Millisekunde ausgelöst werden.
Zwischen dem Timer Overflow und dem tatsächlichen Aufrufen der ISR mit dem Nachladen des Timers ergibt sich eine kleine Verzögerung, die nicht einmal immer gleich ist. Besonders bei kleinen Prescalern kann es dadurch zu Fehlern in der Zeit kommen. Wenn möglich wird für die Erzeugung einer konstanten Interruptrate deshalb besser der CTC Moduls benutzt.
Zusammenfassend ein Beispielprogramm:
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz mit 7,3728 MHz betrieben wird. Im Abstand von 0,01 ms erzeugt der Timer einen Interrupt, also eine Frequenz von 100000 Hz (oder 100 kHz). Der Timer wird auf einen Prescaler von 1 und einem Preloader von 183 konfiguriert.*/ volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert // ISR zum auffangen der Interrupts: SIGNAL(SIG_SIG_OVERFLOW2) { countTimer2++; TCNT2 = 183; // Nachladen } // Initialisierung: TCCR2 = (1<<CS22); // Prescaler von 1 TCNT2 = 183; // Vorladen TIMSK |= (1<<TOIE2); // Interrupts aktivieren und damit Timer starten sei(); // Funktionen zum benutzen der Timer: /** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen. Bei t=100 schläft die Funktion 1 ms. */ inline void sleep (uint8_t t) { // countTimer2 wird in der ISR oben inkrementiert countTimer2 = 0; while (countTimer2 < t); } /** Schläft x-Millisekunden. */ inline void sleep_millisec(uint16_t msec) { uint16_t i; for(i=0; i<msec; i++) { sleep(100); } }
Viele Timer haben "Output-Compare" Register: OCRx oder OCRAx,OCRBx. Wenn der Zähler den darin eingestellten Wert erreicht hat, kann ein Interrupts ausgelöst werden.
Input Capture
Die 16 Bit Timer habe eine "Input Capture" Funktion. Dieser Hardwareteil dient zur genauen Zeitmessung. Außer in einigen PWM Betriebsarten, wo das ICP Register als TOP-wert für den Timer benutzt wird, ist die ICP-Funktion immer aktiv. Wenn am ICP Pin die über das Bit "ICESx" eingestellte Flanke auftritt, wird der aktuelle Zählerstand in die ICP Register kopiert. Außerdem kann ein Interrupt ausgelöst werden. Der Interrupt wird, wie die anderen Timer Interrupts, in den Registern TIMSK und TIFR an- oder abgestellt. Man kann zwar die ICP-Funktion selber nicht ohne weiteres abschalten, aber natürlich den dazugehörigen Interrupt. Als eine spezielle Funktion ("Noise Cancler") gibt es die Möglichkeit sehr kurze Pulse (unter 4 Zyklen) zu unterdrücken. In der Regel kann man diese Funktion angestellt lassen, denn so schnell kann man die Daten ohnehin nicht verarbeiten.
Die typische Anwendung ist die Messung von kurze Zeiten, wie z.B. die Zeit für eine Motorumdrehung. Solange die 16 Bit des Timers ausreichen ist die Benutzung ganz einfach: Der Timer wird mit dem gewünschten Vorteiler im normalen Modus gestartet. Im ICP-Interrupt wird die Differenz aus zwei aufeinanderfolgenden Zeiten (Werte in ICP-Register) berechent. Dazu wird jeweils die vorherige Zeit im RAM zwischengespeichert. Wenn man bei der Rechnung eventuelle Überläufe ignoriert, bekommt man die richtige Zeitdifferenz, auch wenn der Timer während der Messzeit einen Überlauf hatte. Das funktioniert so einfach, denn wenn noch weitere (höherwertige) Bytes vorhanden wären, damit es keinen Überlauf gibt, würde man genau so die unteren Bits berechnen.
Etwas komplizierter wird es, wenn die 16 Bit Auflösung nicht mehr ausreicht. Dann kann der Timer-Überlauf benutzt werden, um auch längere Zeiten mit voller Auflösung zu messen. Die wesentliche Schwierigkeit ist es, den Fall zu berücksichtigen, dass ein Überlauf Interrupt und der ICP Interrupt fast gleichzeitig ausgelöst werden. Es kann passieren, dass der ICP-Interrupt aufgerufen wird, obwohl eigentlich erst der Overflow Interrupt dran gewesen wäre. Dieser seltene Fall läßt sich daran erkennen, dass das Overflow-Interrupt Flag gesetzt ist und der Wert im ICP Register klein ist (high Byte < 128, meistens 0).
Beispielpropgramm (für GCC): (Bisher nur im Simulator getestet)
// Beispielprogramm für Zeitmessung mit ICP-Funktion // Erweiterung des Timers auf 32 Bit durch Software // Es wird die Periodendauer am ICP-Eingang gemessen und als ASCII via UART ausgegeben // Code für Mega48 / Mega88 / Mega 168 / ... // mit leichten Anpassungen auch für Tiny2313, Mega16, Mega32,... #include <stdlib.h> // für utoa #include <avr/io.h> #include <avr/interrupt.h> #include <avr/sleep.h> // Unterstützung für sleep mode // #define F_CPU 1000000UL // Definition von Frequenz ist normal in makefile #define BAUD 19200UL #define UBRR_BAUD ((F_CPU/(16UL*BAUD))-1) typedef union { // union erlaubt einen effektiven, seperaten Zugriff auf Teile der Variable unsigned long i32; struct {uint8_t i8l; // low uint8_t i8m; // mid unsigned int high; // high, soft timer }; } convert32to8; volatile unsigned long timestamp; // volatile wegen Zugriff im Interrrupt volatile unsigned int softtimer; volatile unsigned long zeitdifferenz; unsigned long zeit; char puffer[12]; // puffer für Ausgabe als Ascii ISR(TIMER1_OVF_vect) // Timer Überlauf { ++softtimer; // zählen der Überläufe } ISR(TIMER1_CAPT_vect) // Flanke an ICP pin { convert32to8 cap; // Variablendeklaration cap.i8l = ICR1L; // low Byte zuerst, high Byte wird gepuffert cap.i8m = ICR1H; // overflow verpasst, wenn ICR1H klein und wartender Overflow Interrupt if ((cap.i8m < 128) && (TIFR1 & (1<<TOV1))) { // wartenden timer overflow Interrupt vorziehen ++softtimer; TIFR1 = (1<<TOV1); // timer overflow int. löschen, da schon hier ausgeführt } cap.high = softtimer; // obere 16 Bit aus Software Zähler zeitdifferenz = cap.i32 - timestamp; timestamp = cap.i32; // Zeit merken } void uart_init(void) // USART initialisieren (Mega48 etc.) { // Baudrate einstellen (Normaler Modus) // kann bei älteren AVR Typen etwas anders sein (kein UBRR0H, dafür prescaler) UBRR0H = (uint8_t) (UBRR_BAUD>>8); UBRR0L = (uint8_t) (UBRR_BAUD & 0x0ff); UCSR0B = (1<<TXEN0); // Aktivieren des Senders UCSR0C = (1<<UCSZ01)|(1<<UCSZ00)|(1<<USBS0); // Einstellen des Datenformats: 8 Datenbits, 2 Stoppbit: } void putser(char c) // sende ein Byte via UART { while ( !( UCSR0A & (1<<UDRE0)) ) ; // Warten bis der Sendepuffer frei ist UDR0 = c; } void main(void) { unsigned char i; // Datenrichtungen: DDRB = 0; // Alles Eingänge, PB0 ist ICP PORTB = 0xFF - (1<<PB0); // Pullups an Eingängen außer ICP DDRC = 0; // Eingänge PORTC = 0xFF; // Pullups an Eingängen DDRD = (1<<PD1); // Eingänge, außer PD1 = Tx (UART) PORTC = 0xFF- (1<<PD1); // Pullups an alle Eingängen (außer TX) // Timer1 initialisieren: TCCR1A = 0; // normal mode, keine PWM Ausgänge TCCR1B = (1<< ICNC1) + (1<<CS10) // start Timer mit Systemtakt + (1 << ICES1); // steigende Flanke auswählen TIMSK1 = (1<<TOIE1) + (1<<ICIE1); // Input-capture und TIFR1 = (1<<TOIE1) + (1<<ICIE1); // Schon aktive Interrrupts löschen // UART initialisieren: uart_init(); zeitdifferenz = 0; softtimer = 0; // wird für Zeitdifferenzmessung nicht mal gebraucht, // denn Differenz geht auch über Überlauf bei Softtimer set_sleep_mode (SLEEP_MODE_IDLE); // idle Mode: timer läuft weiter, int zum aufwachen sei(); // Interrupts erlauben: Messung startet while (1) { sleep_enable (); // enable sleep sei(); sleep_cpu(); // wartet auf irgendeinen Interrupt, z.B. ICP, timer_ovr,... sleep_disable(); // disable sleep cli(); // Interrupt sperren wegen Zugriff auf volatile Variablen zeit = zeitdifferenz; zeitdifferenz = 0; // als Markierung für ungültigen Wert sei(); if (zeit > 0) { utoa(zeit,puffer,10); // nach ASCII umwandeln i = 0; while (puffer[i]) { putser(puffer[i++]); // Ausgabe } putser(13); // Zeilenumbruch senden putser(10); } } // Ende von While-schleife }
CTC Modus (Clear Timer on Compare Match mode)
Der CTC Modus ist eine Erweiterung des "Output-Compare"-Funktion. Der CTC Modus eignet sich besonders, um einen mit konstanter Frequenz wiederkehrenden Interrupt zu erzeugen. Wie im normalen Modus zählt der Timer hoch. Wenn der Wert im OCRx Register erreicht wird, wird zusätzlich zum möglichen Interrupt der Zähler wieder auf 0 gesetzt. Es kann also die maximalen Zählergrenze selber definiert werden. Dieses Diagramm veranschaulicht den CTC Modus.
Beispielprogramm:
/* Es wird der Timer2 (8-Bit) eines ATmega32 verwendet, der mit einem Quarz mit 7,3728 MHz betrieben wird. Im Abstand von 0,01 ms erzeugt der Timer einen Interrupt, also eine Frequenz von 100000 Hz (oder 100 kHz). Der Timer wird auf einen Prescaler von 1 und einem OCR2-Wert von 73 konfiguriert.*/ volatile uint8_t countTimer2; // Speichert den aktuellen Zählerwert // ISR zum auffangen der Interrupts: SIGNAL(SIG_OUTPUT_COMPARE2) { countTimer2++; } // Initialisierung: TCCR2 = (1<<CS22) | (1<<WGM21); // Prescaler von 1 | CTC-Modus (siehe unten für Beschreibung) OCR2 = 73; // Vergleichswert TIMSK |= (1<<OCIE2); // Interrupts aktivieren und damit Timer starten sei(); // Funktionen zum benutzen der Timer: /** Diese Funktion nicht aufrufen. Wird von sleep_millisec aufgerufen. Bei t=100 schläft die Funktion 1 ms. */ inline void sleep(uint8_t t) { // countTimer2 wird in der ISR oben inkrementiert countTimer2 = 0; while (countTimer2 < t); } /** Schläft x-Millisekunden. */ inline void sleep_millisec(uint16_t msec) { uint16_t i; for(i=0; i<msec; i++) { sleep(100); } }
PWM
Eine häufige Aufgabe für Mikrocontroller ist die Erzeugung von PWM-Signalen, zum Beispiel für Motorsteuerungen. Daher sind in den meisten AVRs PWM-Einheiten als Hardware vorhanden. Sie sind direkt mit den Timern verbunden und nutzen diese als Taktquelle. Die Hardware-PWM-Einheiten haben den Vorteil, sehr wenig Rechenzeit in Anspruch zu nehmen. Es muss nur die PWM aktiviert werden und bei Änderungen den gewünschten Wert in ein Register schreiben. Der Rest läuft automatisch und unabhängig vom restlichen Programm, ohne den AVR ständig zu beschäftigen wie bei einer PWM-Lösung in Software. Allerdings stehen meist nur zwei bis drei solcher PWM-Kanäle zur Verfügung, die außerdem an bestimmte Pins gebunden sind. Für die meisten Roboter mit zwei Antriebsmotoren reicht dies aber für gewöhnlich aus.
nutzbare Pins am AVR
Die Hardware-PWM-Funktion steht nur an bestimmten Pins zur Verfügung. In der Pinbelegungsübersicht im Datenblatt ist erkenntbar, dass als Sonderfunktion in Klammern "OC..." angegeben ist. Beim Mega32 sind dies zb. OC0 an PB0, OC1A an PD5, OC1B an PD4 und OC2 an PD7. Der Mega32 hat also insgesamt vier Hardware-PWM-Kanäle. Die Zahl hinter dem "OC" gibt an, zu welchem der Timer dieser PWM-Kanal gehört. Wenn noch ein Buchstabe dahinter kommt, dann gehören mehrere PWMs zu diesem Timer. Beim Mega32 sind also OC1A und OC1B demselben Timer, nämlich Timer1, zugeordnet.
Zu beachten ist, dass die für die PWM benutzten Pins zuvor explizit als Ausgang konfiguriert werden müssen! Ansonsten gelangt das PWM-Signal nicht nach draußen!
Funktionsprinzip
Das "OC" in den Pinbezeichnungen steht für "Output Compare Unit", also frei übersetzt Ausgangs-Vergleicher-Einheit. Dies beschreibt die Funktionsweise der PWM-Kanäle: der Zählerstand des Timers wird fortlaufend mit einen einstellbaren Referenzwert verglichen, und wenn beide Werte übereinstimmen, kann ein Ausgangspin des AVRs automatisch geschaltet werden (und ein Interrupt ausgelöst werden, was allerdings für die PWM-Funktion nicht relevant ist). Dies entspricht dem Verfahren im Beispiel zur Software-PWM. Es läuft nun allerdings vollautomatisch im Hintergrund, sodass der Controller nicht damit belastet wird.
Die verschiedenen PWM-Modi
Es gibt -je nach AVR und Timer- etliche Betriebsarten, in denen die PWM-Einheit betrieben werden kann. Sie unterscheiden sich vor allem darin, wie schnell und mit welchen Nebeneffekten sich Änderungen des Sollwertes auf das Ausgangssignal auswirken. Für den Anfang sind diese Unterschiede erst einmal nebensächlich, und für eine einfache Motorsteuerung meist auch irrelevant. Daher wird hier zunächst der "Fast PWM Mode" ("Schneller PWM Modus", weil hier die größte Ausgangsfrequenz möglich ist) beschreiben, welcher der einfachste von allen ist.
Hierbei zählt der Timer immer von Null an aufwärts, bis er den Maximalwert (teilweise einstellbar) erreicht hat. Dann läuft er über und fängt von vorne an. Wie schnell dies geschieht, wird, wie im normalen Modus, über den Prescaler eingestellt. Der gewünschte PWM-Ausgangswert wird im "OCRn"-Register abgelegt. Er darf zwischen Null und dem Maximalwert des Timers liegen. Er wird nun mit dem Timer-Wert verglichen. Was dann passiert, regeln die "COM..."-Bits. Sie bestimmen, wie der Ausgang geschaltet wird. Die übliche Konfiguration ist, dass bei Erreichen des Sollwertes die Ausgänge auf high geschaltet werden, und beim Überlauf auf low. Damit ergibt sich ein nichtinvertiertes PWM-Signal. Schließt man (über einem passenden Motortreiber!) einen Motor an, dreht er sich bei einem Sollwert von 0 gar nicht und beim Maximalwert mit voller Geschwindigkeit.
Beispielcode für den Timer1 des Mega16/32 (und vieler anderer AVRs):
#include <avr/io.h> #include <util/delay.h> //Warteschleife für die Demo-Ausgabe. Für die eigentliche PWM nicht benötigt! /* PWM-Beispiel für Mega16/32 (beiden haben den gleichen Timer) Benutzt wird Timer1 im Fast PWM Mode, 8 Bit Auflösung Die PWM-Signale liegen auf PD5/OC1A und PD4/OC1B */ // 1. Den Prescaler einstellen, der die Frequenz festlegt TCCR1B |= (1<<CS12); //Prescaler 256 // 2. Den Timer in den Fast PWM Mode, 8 Bit schalten //ACHTUNG: Die WGM-Bits sind auf beide Konfigurationsregister verteiltt! TCCR1A |= (1<<WGM10); TCCR1B |= (1<<WGM12); // 3. Compare Output mode einstellen: Pin geht auf high bei Compare match, auf low bei Überlauf. Ergibt nichtinvertierte PWM. TCCR1A |= (1<<COM1A1) | (1<<COM1B1) ; // In diesen Registern wird der gwünschte PWM-Wert abgelegt. Erlaubter Bereich: 0 bis 255. OCR1A = 0; OCR1B = 0; // 4. Zuletzt die Pins als Ausgänge konfigurieren. Erst jetzt liegt das PWM-Signal an den Pins an! DDRD |= (1<<PD4) | (1<< PD5); /*Nun ist der PWM-Modus aktiv! Der Ausgangswert kann nun über die Register OCR1A und OCR1B vorgegeben werden. Man könnte ihnen per define noch einen Zweitnamen verpassen, zb */ #define MotorLinks OCR1A #define MotorRechts OCR1B //Und nun kann man per MotorLinks = 127; MotorRechts = 127; //seinen Roboter mit halber Kraft vorwärts fahren lassen. /*PWM-Demo: Die PWM-Werte werden erst bis zum Maximalwert erhöht und dann wieder verringert. Ein angeschlossener Motor wird beschleunigen und dann wieder abbremsen. */ uint8_t wert; while(1) { for (wert=0; wert<255; wert++) { OCR1A = wert; OCR1B = wert; _delay_ms(10); } for (wert=255; wert>0; wert--) { OCR1A = wert; OCR1B = wert; _delay_ms(10); } }
Es sind also vier Einstellungen zu treffen:
- Takt anlegen per Prescaler
- PWM-Modus wählen
- Ausgangs-Aktion festlegen
- Pins als Ausgänge schalten
Diese Schritte müssen bei jedem AVR-PWM-Kanal ausgeführt werden. Die genauen Registernamen und Werte können sich jedoch je nach Timer-Ausführung etwas unterscheiden.
Registerübersicht
Hinweis: Diese Registertabellen wurden für den aktuellen Atmel Controller Mega16 und Mega32 erstellt. Wenn Sie ein anderes Modell verwenden kann es sein, dass ein oder mehrere Register nicht existieren, oder sie eine andere Bezeichnung haben.
TIMSK | ||||||||||||||||||
Mit diesem Register, der von allen Timern verwendet wird, lässt sich die Interruptausführung und Art des jeweiligen Timers bestimmen.
| ||||||||||||||||||
|
Siehe auch