Timer: Verwendung des Overflow-Interrupt
Der Overflow eines Timers kann wie bereits erwähnt als Interrupt genutzt werden. Wie dies geschieht wird im Folgenden anhand eines Beispiels erläutert.
Im Beispiel wird ein Timer verwendet, der bei jedem Überlauf einen Interrupt erzeugt und das Port Register D inkrementiert. Der Zustand des Registers D wird über die 8 angeschlossenen LEDs wiedergegeben. In diesem Beispiel wird Timer1 verwendet. Dieser basiert auf 16 Bit und zählt daher von 0 bis 2^16-1 = 65535. Der Mikrocontroller des Arduinos verwendet einen 16 MHz Takt. Als Vorteiler wurde 64 gewählt. Im Folgenden wird die Frequenz der Zählerdurchläufe sowie die Dauer eines Zählerdurchlaufes anhand des Beispiels berechnet:
\( Frequenz\ der\ Zählerdurchläufe\ = \frac{Taktfrequenz\ des\ µC\ \{Atmega328P\ \rightarrow\ 16000000\ Hz\}} {Anzahl\ der\ Schritte\ \{8\ Bit\ \rightarrow\ 256;\ 16\ Bit\ \rightarrow\ 65536\} \cdot Vorteiler\ \{1;\ 8;\ 64:\ 256;\ 1024 \} } \)
Berechnung des Beispiels:
µC: Atmega328P, 16 Bit (65536 Schritte), Vorteiler 64
\( f\ = \frac{ 16000000\ Hz} {65536 \cdot 64} = 3,815\ Hz \)
\( Dauer\ eines\ Zählerdurchlaufs\ = \frac{1} {Frequenz\ der\ Zählerdurchläufe} \)
Berechnung des Beispiels:
Frequenz: 3,815 Hz
\( T\ = \frac{1} {3,815\ Hz} = 262,12\ ms \)
In der folgenden Tabelle werden für den 8-Bit-Timer und den 16-Bit-Timer für alle möglichen Vorteiler die Taktfrequenz und die Dauer eines Zählerdurchlaufes aufgezeigt:
Abb.: Taktrequenz und Zählerdurchlauf bei verschiedenen; Quelle: BBS2 Wolfsburg
Achtung: Die Blinkfrequenz ergibt sich aus der Hälfte der Frequenz der Zählerdurchläufe, hier im Beispiel also 3,815 / 2 = 1,907 Hz.
Um die Overflows überhaupt für das menschliche Auge optisch erkennbar darzustellen, ist es notwendig einen Vorteiler zu verwenden (hier 64). Der Hardware-Aufbau sieht wie folgt aus:
Abb.: Aufbau der Schaltung; Quelle: BBS2 Wolfsburg
Quellcode mit Kommentaren:
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
ISR(TIMER1_OVF_vect) //interrupt service routine wird ausgelöst, sobald timer1 einen overflow generiert
{
PORTD++; <span class="hljs-comment" style="color: rgb(136, 136, 136);">// Register PORTD wird inkrementell erhöht</span>
}
int main(void)
{
DDRD = ( <span class="hljs-number" style="color: rgb(136, 0, 0);">1</span> << PORTD7) | (<span class="hljs-number" style="color: rgb(136, 0, 0);">1</span> << PORTD6) | ( <span class="hljs-number" style="color: rgb(136, 0, 0);">1</span> << PORTD5) | (<span class="hljs-number" style="color: rgb(136, 0, 0);">1</span> << PORTD4) | ( <span class="hljs-number" style="color: rgb(136, 0, 0);">1</span> << PORTD3) | (<span class="hljs-number" style="color: rgb(136, 0, 0);">1</span> << PORTD2) | ( <span class="hljs-number" style="color: rgb(136, 0, 0);">1</span> << PORTD1) | (<span class="hljs-number" style="color: rgb(136, 0, 0);">1</span> << PORTD0); <span class="hljs-comment" style="color: rgb(136, 136, 136);">// Ausgägne setzen</span>
PORTD = <span class="hljs-number" style="color: rgb(136, 0, 0);">0</span>; <span class="hljs-comment" style="color: rgb(136, 136, 136);">// Startwert für PORTD auf 0 setzen</span>
TCCR1B = ( <span class="hljs-number" style="color: rgb(136, 0, 0);">0</span><<ICNC1) | (<span class="hljs-number" style="color: rgb(136, 0, 0);">0</span><<ICES1) | (<span class="hljs-number" style="color: rgb(136, 0, 0);">0</span> << WGM13) | (<span class="hljs-number" style="color: rgb(136, 0, 0);">0</span> << WGM12) | ( <span class="hljs-number" style="color: rgb(136, 0, 0);">0</span><<CS12) | (<span class="hljs-number" style="color: rgb(136, 0, 0);">1</span><<CS11) | (<span class="hljs-number" style="color: rgb(136, 0, 0);">1</span> << CS10); <span class="hljs-comment" style="color: rgb(136, 136, 136);">// Timer1 konfigurieren (16 Bit Timer mit Takt/64 durch den Vorteiler)</span>
TIMSK1 |= (<span class="hljs-number" style="color: rgb(136, 0, 0);">1</span><<TOIE1); <span class="hljs-comment" style="color: rgb(136, 136, 136);">// TIMSK - Timer Interrupt Mask Register für Timer1</span>
<span class="hljs-comment" style="color: rgb(136, 136, 136);">// TOIE1 - Timer Overflow Interrupt Enable | kommt es zu einem Overflow, wird ein Interrupt erzeugt und es wird in die interrupt service routine gesprungen</span>
sei(); <span class="hljs-comment" style="color: rgb(136, 136, 136);">// Aktivierung allgmeiner Interrupts</span>
<span class="hljs-keyword" style="font-weight: 700;">while</span> (<span class="hljs-number" style="color: rgb(136, 0, 0);">1</span>)
{
}
}
Zum besseren Verständnis der Timerregister werden im Folgenden Datenblattauszüge gezeigt:
Abb.: TC1 Control Register B; Quelle: Datenblatt Atmega 328P [https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf]
Das TCCR1B-Register ist ein Konfigurationsregister für Timer 1. Anhand der Bits CS10 bis 12 lässt sich der Vorteiler gemäß folgender Tabelle konfigurieren:
Abb.: Clock Select Bit Descriotion; Quelle: Datenblatt Atmega 328P [https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf]
Die Konfiguration des TCCR1B geschieht im Quellcode in Zeile 17.
Des Weiteren wird in Zeile 27 das TIMSK1-Register verwendet. Das Timer-Interruptmaskenregister gibt die Timer1-Interrupts frei. Im Beispiel wird Bit 0 des Registers gesetzt, um bei einem Overflow einen Interrupt zu erzeugen.
Abb.: TIMSK1 Register; Quelle: Datenblatt Atmega 328P [https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-7810-Automotive-Microcontrollers-ATmega328P_Datasheet.pdf]