Skip to content

Timer

starlightViewModes.switchTo

This content is not available in your language yet.

Der Timer ist ein sehr nützliches Instrument in der hardwarenahen Programmierung und eignet sich besonders gut für zeitbasierte Vorgänge, wie zum Beispiel:

  • Delay (Programmverzögerung) um bestimmte Anzahl an Sekunden
  • regelmäßig ADC triggern
  • DC-Motor drehzahlgesteuert zu betreiben

Theorie

Der ATmega 328p hat 3 verschiedene Timer:

  • TC0: 8-bit
  • TC1: 16-bit
  • TC2: 8-bit; async

Jeder Timer verfügt über ein eigenes Zählregister TCNTn, welches einmal pro Systemtakt hoch zählt. Wenn das Zählregister den TOP-Wert bzw. den OCRnx-Wert (CTC Mode) erreicht hat, passiert ein Überlauf. Das ist der Fachausdruck für den Prozess, bei welchem TCNTn wieder auf den BOTTOM-Wert gesetzt wird.

Der Prozess des Überlaufs ist periodisch und wird hier grafisch dargestellt:

Timer Überlauf

Prescaler

Der Prescaler verlangsamt den Zähltakt. Auf Hardware-Ebene ist der Prescaler nämlich ein Bauteil vorm Timer, welcher den Systemtakt als Input bekommt und den manipulierten neuen Takt an den Timer weitergibt. Standardmäßig ist der Prescaler nicht gesetzt, was bedeutet, dass der Timer gar keinen Takt bekommt und somit gestoppt ist.

Prescalerneuer Takt neue Periodendauer 8-bit / 256 16-bit / 65536
116MHz0.0000625ms0.016ms4.096ms
82MHz0.0005ms0.1275ms32.76ms
64250kHz0.004ms1.02ms262.14ms
25662.5kHz0.016ms4.08ms1048.5ms
102415.625kHz0.064ms16.32ms4194.24ms

Modes of Operation

Dabei hat jeder Timer 3 verschiedene Modi, welche in der Tabelle kurz erklärt werden:

Normal ModeCTC ModePWM Mode
Im normalen Modus zählt der Timer von BOTTOM bis TOP und wird bei TOP wieder auf BOTTOM gesetzt. Es gibt die Möglichkeit den Timer mit einem bestimmten Wert vorzuladen - also quasi den BOTTOM-Wert zu erhöhen -, indem man einfach TCNTn immer direkt nach dem Überlauf setzt, weshalb man die Zeitspanne bis zum Überlauf verringert.Bei der Clear Timer on Compare Match setzt man anfangs einmal den OCRnA- bzw. den OCRnB-Wert. Erreicht der Timer (also TCNTn) diesen Wert, wird er geleert, was bedeutet, dass TCNTn wieder auf 0 gesetzt wird. Die Timer unterstützen dabei zwei unabhängige Output Compare Register (OCRnA und OCRnB).Mithilfe der Pulse Width Modulation kann man die Spannung auf einem bestimmten PIN variable verändern. Somit lässt sich z. B. ein DC-Motor steuern, welcher sich umso schneller dreht, je mehr Spannung er bekommt.

Normal Mode

Um einen bestimmten Programmcode in gewünschten Zeitabständen auszulösen, können wir also nun einen Timer verwenden, welcher konstant hoch zählt und überläuft. Da die Zeitspanne bis zum Überlauf allerdings nicht immer exakt der gewünschten Zeitspanne entsprechen wird, müssen wir den BOTTOM-Wert / Vorladewert so verändern (also erhöhen), dass die zeitliche Differenz zwischen dem Vorladewert und dem TOP-Wert genau der gewünschten Zeitspanne entspricht. Wie man dies programmtechnisch umsetzt, wird unten beim Code erklärt. Um Ihnen die schwierigen Berechnungen zu ersparen, gibt es hier eine einfache Formel zur Berechnung des Vorladewertes:

ü

Zur Visualisierung, was wir eigentlich gerade gemacht haben, eignet sich dieses Bild:

Timer mit bestimmten Wert vorlade

CTC - Clear Timer on Compare Match

Der Clear Timer on Compare Match Mode erleichtert uns die exakte regelmäßige Ausführung eines Programmcodes. Man setzt anfangs einmalig den OCRnx-Wert (Output Compare Register) als Obergrenze. Man kann sich das so vorstellen, als würde man den TOP-Wert senken. Somit ist der CTC Mode das Gegenteil des Vorladens, erfüllt allerdings den gleichen Zweck.

Die Formel zur Berechnung des Output Compare Register-Wertes ist ziemlich ähnlich. Beachten Sie dabei wieder die gleichen Definitionsmengen wie bei obiger Formel!

ü

PWM - Pulse Width Modulation

Mithilfe des PWM Modes kann man an bestimmten PINs eine bestimmte Spannung zwischen 0V und 5V anlegen. Durch ständiges, in bestimmten Intervallen auftretendes Ein- und Ausschalten der Spannung, stellt sich diese nach kurzer Zeit automatisch auf den Mittelwert der zeitlich auftretenden Spannungen ein.

Diese Vorgang passiert in Realität so schnell, dass man das ständigen Ein- und Ausschalten nicht mitbekommt. Hier ist der Prozess dargestellt:

Duty Cycle stellt sich langsam auf den Mittelwert ein

Der PWM Mode ermöglicht uns also das exakte Einstellen der Spannung bei Laufzeit.

Duty Cycle

Eventuell ist Ihnen aufgefallen, dass im obigen Bild in den Bereichen steht, wo die Spannung high ist. Genau dieses Zeitintervall in Prozent ist der sogenannte Duty Cycle und die Berechnung für diesen ist hier zu sehen:

Konfigurieren kann man den Duty Cycle mittels dem OCRnx Register. Dieses bestimmt - genau wie beim CTC Mode - den Vergleichswert (Output Compare Register).

PWM Mode Compare Register


Der PWM Mode kann invertiert oder nicht invertiert betrieben werden:

Invertierter Modus

Beim invertierten Modus wird beim Erreichen des Vergleichswertes (TCNTn OCRnx) die Spannung auf high gesetzt und beim Überlauf auf low.

PWM Mode Invertierter Modus

Nicht Invertierter Modus

Beim nicht invertierten Modus ist es genau umgekehrt. Beim Erreichen des Vergleichswertes (TCNTn OCRnx) wird die Spannung auf low gesetzt und beim Überlauf auf high.

PWM Mode Nicht Invertierter Modus

Interrupts

Code

Übersichtliche Tabelle für die wichtigsten Konfigurationen (alle Konfiguration finden Sie hier: TC0: Table 14-8, TC1: Table 15-5, TC2: Table 17-8):

Normal ModeCTC ModeFast
PWM Mode
Phase Correct
PWM Mode
TC0WGM00 == 0
WGM01 == 0
WGM02 == 0
WGM00 == 0
WGM01 == 1
WGM02 == 0
WGM00 == 1
WGM01 == 1
WGM02 == 0
WGM00 == 1
WGM01 == 0
WGM02 == 0
TC1WGM10 == 0
WGM11 == 0
WGM12 == 0
WGM13 == 0
WGM10 == 0
WGM11 == 0
WGM12 == 1
WGM13 == 0
WGM10 == 1
WGM11 == 0
WGM12 == 1
WGM13 == 0
WGM10 == 1
WGM11 == 0
WGM12 == 0
WGM13 == 0
TC2WGM20 == 0
WGM21 == 0
WGM22 == 0
WGM20 == 0
WGM21 == 1
WGM22 == 0
WGM20 == 1
WGM21 == 1
WGM22 == 0
WGM20 == 1
WGM21 == 0
WGM22 == 0

Normal Mode

Für den normalen Modus beim Timer können folgende Konfigurationen getroffen werden:

  • WGMn0, WGMn1 und WGMn2 (und WGM13) auf 0 setzen (siehe Table 14-8, Table 15-5 oder Table 17-8)

    // Timer Modus auf Normal Mode setzen
    TCCRnA &= ~((1<<WGMn0) | (1<<WGMn1));
    TCCRnB &= ~((1<<WGMn2));
  • CSn0, CSn1 und CSn2 einstellen (siehe Table 14-9, Table 15-6 oder Table 17-9)

    // Prescaler einstellen - Beispiel 1024
    TCCRnB |= (1<<CSn0) | (1<<CSn2);
    TCCRnB &= ~(1<<CSn1);

    Nutzen Sie diese Tabelle, um herauszufinden, welcher Prescaler Wert in Ihrem Program am meisten Sinn macht.

TCNTn vorladen

Um diese Aufgabe zu lösen, verwenden wir die Formel, um uns den Vorladewert von auszurechnen.

#define F_CPU 16000000
#include <avr/io.h>
int main(void)
{
// Timer Modus auf Normal setzen (optional)
TCCR1A &= ~((1<<WGM10) | (1<<WGM11));
TCCR1B &= ~((1<<WGM12) | (1<<WGM13));
// Zählregister vorladen
TCNT1 = 1536;
sei();
while (1);
}
// ISR wird jeden Überlauf getriggert - alle 4ms in diesem Fall
ISR(TIMER1_OVF_vect) {
// Code
// Zählregister vorladen
TCNT1 = 1536;
}

CTC - Clear Timer on Compare Match

Für den CTC Modus beim Timer müssen/können folgende Konfigurationen getroffen werden:

  • WGMnx richtig setzen (TC0 / TC2: WGMn1; TC1: WGM12 (und WGM13))
    (siehe Table 14-8, Table 15-5 oder Table 17-8)

  • OCIEnx auf 1 setzen (siehe 14.9.6, 15.11.8 oder 17.11.6)

    // Interrupts aktivieren - Beispiel Compare Match A
    TIMSKn |= (1<<OCIEnA);
  • CSn0, CSn1 und CSn2 einstellen (siehe Table 14-9, Table 15-6 oder Table 17-9)

    // Prescaler einstellen - Beispiel 256
    TCCRnB |= (1<<CSn2);
    TCCRnB &= ~((1<<CSn0) | (1<<CSn1));

    Nutzen Sie diese Tabelle, um herauszufinden, welcher Prescaler Wert in Ihrem Program am meisten Sinn macht.

Um diese Aufgabe zu lösen, verwenden wir die Formel, um uns den Vorladewert von auszurechnen.

#define F_CPU 16000000
#include <avr/io.h>
int main(void)
{
// Timer Modus auf CTC setzen
TCCR1A &= ~((1<<WGM10) | (1<<WGM11));
TCCR1B |= (1<<WGM12);
TCCR1B &= ~(1<<WGM13);
// Output Compare Register richtig einstellen
OCR1A = 64000;
// Output Compare Register Interrupt aktivieren
TIMSK1 |= (1<<OCIE1A);
sei();
while (1);
}
// ISR wird bei jedem Compare Match getriggert - alle 4ms in diesem Fall
ISR(TIMER1_COMPA_vect) {
// Code
}

PWM - Puls Width Modulation

Für den PWM Modus beim Timer müssen/können folgende Konfigurationen getroffen werden:

Um diese Aufgabe zu lösen, verwenden wir die Formel, um uns den Vorladewert von auszurechnen.

#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
int main(void)
{
// PB1 als Ausgang konfigurieren
DDRB |= (1<<DDB1);
// WGM10 und WGM12: Fast PWM; 8-bit aktivieren (MAX -> 255)
TCCR1A |= (1<<WGM10);
TCCR1B |= (1<<WGM12);
// CS12: 256er Prescaler setzen => PWM mit 244,1 Hz
TCCR1B |= (1<<CS12);
// non-inverting Mode einstellen
TCCR1A |= (1<<COM1A1);
// REFS0: Aufgrund der Beschaltung des ADCs.
// A3: Analoges Signal an PC3 => MUX0 | MUX1
ADMUX |= (1<<REFS0) | (1<<MUX0) | (1<<MUX1);
// ADEN => Enables ADC
// ADPSx => Division Factor to get between 50kHz and 200kHz with our 60MHz Elegoo.
ADCSRA |= (1<<ADEN) | (1<<ADPS0) | (1<<ADPS1) | (1<<ADPS2);
ADCSRA |= (1<<ADIE);
// ADSC => Start Conversion
ADCSRA |= (1<<ADSC);
sei();
/* Replace with your application code */
while (1)
{
//OCR1A = 65; // Pulsweite DC von ca. 25%
//_delay_ms(2000);
//OCR1A = 123; // Pulsweite DC von ca. 50%
//_delay_ms(2000);
//OCR1A = 255; // Pulsweite DC von ca. 100%
//_delay_ms(2000);
}
}
ISR(ADC_vect) {
OCR1A = ADCW / (1023 / 255);
// ADSC => Start Conversion
ADCSRA |= (1<<ADSC);
}

Interrupts

Timer Counter n Überlauf:

ISR(TIMERn_OVF_vect) {
}

Timer Counter n Compare Match x:

// CTC Mode
ISR(TIMERn_COMPx_vect) {
}