Unterrichts- und Lernmaterial für Mikrocontroller
Unterrichts- und Lernmaterial fürMikrocontroller

timer0 und Interrupt

Die Blinkschaltung wird in diesem Abschnitt über eine andere Methode verwirklicht. Ausgenutzt wird, dass der Zähler, sobald er 255 erreicht hat, wieder auf 0 zurückspringt. In dem Moment springt das Programm in eine sogenannte Interrupt Service Routine (ISR) und führt das dort festgelegte Programm aus (zum Beispiel kann es die Zustände von LEDs umkehren. LEDs, die leuchten, werden ausgeschaltet oder umgekehrt). Anschließend kehrt das Programm in den Hauptteil des Programms zurück und setzt seine Arbeit fort.

Um das folgende Programm zu verstehen, sind vorher ein paar neue Begriffe zu erklären.

Interruptvektor - Interrupt Vektortabelle

Je nach verwendetem Prozessor stellt AVR unterschiedliche Interrupt-Quellen zur Verfügung. Bei einem ATmega 8515 gibt es insgesamt 17 sogenannte Interruptvektoren, das sind schlicht Speicherplätze im Programmspeicherraum, die als Einsprungadressen reserviert sind und im Datenblatt in einer Interrupt-Vektortabelle dokumentiert sind. Dabei ist die niedrigste Adresse, sie hat auch die höchste Priorität, dem RESET-Vektor zugeordnet, während die nachfolgenden 16 weiteren Interruptvektoren sich nach oben anschließen. Es gilt:

  • Je niedriger eine Adresse, desto höher seine Priorität.

Der ATmega 8515 verfügt z. B. über vier Interruptvektoren zum timer1 und einem zum timer0 mit der Adresse $007, die über TIM0OVF angesprochen werden kann.

Im Datenblatt jedes Controllers finden sich unter dem Abschnitt "Interrupts" die spezifischen Angaben. Deshalb wird hier darauf nicht weiter eingegangen.

Übung 1 - Blinkerschaltung mit Timer0 und Interrupt

Für den Einsatz von Timer0 und Interrupt benötigt man die Register TCCR0, TIFR und TIMSK.

Abbildung 1 - Das Register TIMSK (timer/counter0 interrupt mask register)
  • Ist Bit1 im Register TIMSK auf 1 und das I-Flag im S-Register gesetzt (global interrupt), dann ist ein  Timer/Counter0 Overflow Interrupt möglich.

Sobald beim Timer/Counter0 ein Überlauf stattfindet (der Zähler springt von 255 zurück auf 0 und fängt von vorne an zu zählen), wird im Register TIFR das TOV0-Flag gesetzt, der zugehörige Interrupt aus der Interrupt-Vektortabelle ($007) ausgeführt und das TOV0-Flag wieder gelöscht.

Bit 1 (TOIE0) - "Timer/Counter0 Overflow Interrupt Enable einschalten" im Register TIMSK

Bit1 (TOV0-Flag) im Register TIFR wird vom Controller gesetzt, sobald im timer0 ein Überlauf stattfindet und wieder gelöscht, sobald die ISR ausgeführt wird. Der Rücksprung aus einer ISR ist ein Rücksprung von einem Interrupt (return from Interrupt command) und muss deshalb mit RETI abgeschlossen werden.

Blinkerschaltung mit timer0 und Interrupt
Material
  • 1x  STK200 mit ATmega 8515-16PU
  • 1x  Schaltnetzteil 9V DC, 1000 mA
  • 1x  ISP2 Programmer
  • 1x  AVR Studio 4.19 auf PC
  • 1x  USB-Oszilloskop (optional)
Aufgaben
  • Teil 1: LED0 - LED7 sollen im Takt blinken.
  • Lege ein neues Projekt led_timer0OV_1 an
  • Gib das Assemblerprogramm led_timer0OV_1.asm in den Editor ein und speichere es ab.
  • Brenne das Programm in den Controller und starte es anschließend.
  • Überprüfe, ob LED0 - LED7 blinken.
  • Bestimme mit einem USB-Oszilloskop die Blinkfrequenz fblink1.
  • Teil 2: Verändere jetzt das Assemblerprogramm so, dass der Vorteiler auf 256 eingestellt ist. Mit welcher Frequenz fblink2 blinken die LEDs jetzt? Miss mit einem USB-Oszilloskop nach.
  • Teil 3: Schreibe das Assemblerprogramm so um, dass nur LED4 blinkt und der Vorteiler auf 1024 eingestellt ist.

Schaltungsaufbau

Abbildung 2 - Schaltungsaufbau zu Übung 1

Das Programm led_timer0OV_1.asm

Abbildung 3 - Programm zu Übung 1

Das aufgenommene Oszillogramm am Ausgang PB0 ergibt bei einem Vorteiler von 1024 eine Ausgangsfrequenz von ca. 1,9 Hz.

Abbildung 4 - Oszillogramm des LED-Blinkers. Gemessen wird eine Blinkfrequenz von ca. 1,9 Hz.

Bei einem Vorteiler 1024, einem Zählbereich von 256 und einer Taktfrequenz von 1.000.000 Hz ergibt sich rechnerisch eine Ausgangsfrequenz von

Wir arbeitet das Programm ov_interrupt1.asm?

Dies wird mit Hilfe des Debuggers genauer untersucht. Über Debug - Start Debugging aus dem Hauptmenü von AVR Studio wird der Debugger in Gang gesetzt. Der gelbe Programmzeiger markiert die aktuell zu bearbeitende Stelle im Quellprogramm. Er wird mit mit jedem Tastendruck auf  F11  (Trace Modus) um jeweils eine Befehlszeile weiter gestellt.

Nach Aufruf des Debuggers landet der Programmzeiger nach dem Programmkopf bei der ersten Direktive

  • .ORG 0x0000     mit der nachfolgenden Befehlszeile
  • RJMP main.

Der Taktzähler (Cycle Counter) im Fenster Processor ist  0. Der Zahlenwert des Taktzählers bezieht sich immer auf das Kommando der vorherigen Befehlszeile.

Cycle

Counter

Beschreibung des

Programmablaufs

Aktion
0 .ORG  0x0000 Start des Programms
0 RJMP main Verzweigung zur Einsprungmarke main
2 Main: Einsprungmarke
2 LDI  R16, HIGH(ramend) Highbyte von RAMEND -> R16
3 OUT  SPH, R16 Inhalt von R16 in Register SPH ablegen
4 LDI  R16, LOW(ramend) Lowbyte von Ramend -> R16
5 OUT SPL, R16 R16-Inhalt in Register SPL ablegen
6 LDI  R17, 0xFF 0xFF -> R17
7 OUT  DDRB, R17 DDRB auf Ausgang
8 OUT  PORTB, R17 Alle LEDs OFF (active low)
9 LDI  R16, 0x01 Für den Debugger 0x01 laden
10 OUT  TCCR0, R16 TCNT0 beginnt zu zählen
11 LDI  R16, 0x02 0x02 -> R16
12 OUT TIMSK, R16 T/C0 OV Interrupt einschalten
13 SEI  
14

Loop:

RJMP  loop
Taktzähler wird bei jedem Schleifendurchlauf um 2 erhöht

...

 

...

 

266

...

 

...

 

...
... die Schleife loop: rjmp loop wird solange durchlaufen, bis das TCNT0 Register den Wert 0xFF überschritten hat und wieder bei 0 anfängt. Dann wird das I-Flag gelöscht und die Interrupt Service Routine aufgerufen.
271

.ORG OVF0addr

        RJMP  isr_tim0_ov
ISR wird aufgerufen
273

Isr_tim0_ov:

        OUT  PORTB, R17
R17 -> PORTB
274 COM  R17 Inhalt von R17 wird invertiert
275 RETI Abschluss der ISR
279

Loop:

        RJMP loop
Schleife wird solange durchlaufen, bis ein erneuter Überlauf im TCNT0 Register erfolgt.

Lösung zu Teilaufgabe 2

Mit LDI r16, 0x04 im Programmblock "fo div 1024" wird der Vorteiler auf 256 eingestellt. Das Oszillogramm zeigt eine Frequenz von ca. 7,6 Hz, während der rechnerische Wert nach (1) bei ca. 7,63 Hz liegt.

Abbildung 5 - Oszillogramm des LED-Blinkers mit einem Vorteilerwert von 256.

Lösung zu Teilaufgabe 3

Das Programm muss im Block "PORTB auf Ausgang" angepasst werden. Da nur die vierte LED blinken soll, wird die folgende Programmanpassung vorgenommen:

Im Datenrichtungsregister B (DDRB) wird Port 4 auf Ausgang geschaltet und auf HIGH gezogen. Damit ist zunächst die LED4 ausgeschaltet, weil active low. Über die ISR wird bei jedem Overflow deren Zustand invertiert.

Übung 2 - Blinker mit Timer0-Interrupt und Preloader

Ohne dies in einem weiteren Programm zu dokumentieren, sei hier nur angemerkt, dass man, wie bereits in Übung 1 beschrieben, auch bei einem timer0-Interrupt den Zähler mit einem Startwert (preload) versehen kann.

Statt dessen werden wir uns jetzt das timer0-compare-match-Interrupt etwas intensiver ansehen. Ohne Interrupt kennen wir dieses Verfahren bereits aus AVR Assembler Teil 4 - Übung 2 - timer0 im CTC-Modus.

Übung 3 - Blinker mit Timer0-Compare-Match-Interrupt

Wie wir das bereits aus dem CTC-Modus kennen, muss der timer0/counter nicht immer von 0 bis 255 zählen. Gibt man einen Vergleichswert in ein Compare-Register ein, dann wird so lange hochgezählt, bis Vergleichs- und Zählwert identisch sind.

Besteht Gleichheit zwischen Compare-Register und Timer0/Counter-Register, wird ein Compare-Match-Interrupt ausgelöst. Anschließend startet der Zählvorgang wieder bei 0.

 

Bei einem Timer0-Compare-Match-Interrupt sind die folgenden Register beteiligt

Abbildung 6 - TCCR0 - Timer/Counter0 Control Register
Abbildung 7 - OCR0 - Output Compare Register
Abbildung 8 - TIMSK - Timer/Counter0 Interrupt Mask Register

Als viertes kommt das TIFR-Register (Timer/Counter0 Interrupt Flag Register) ins Spiel; dort wird vom Controller das OCF0-Bit gesetzt, wenn der Inhalt des Output Compare Registers mit dem Timer/Counter0 Register übereinstimmt. Vielleicht ein bisschen viel auf einmal, aber so läuft es nun mal ab.

Abbildung 9 - Timer/Counter0 Interrupt Flag Register
Blinkerschaltung mit timer0-Compare-Match-Interrupt
Material
  • 1x  STK200 mit ATmega 8515-16PU
  • 1x  Schaltnetzteil 9V DC, 1000 mA
  • 1x  ISP2 Programmer
  • 1x  AVR Studio 4.19 auf PC
  • 1x  USB-Oszilloskop (optional)
Aufgaben
  • Teil 1: LED0 - LED7 sollen im Takt blinken.
  • Lege ein neues Projekt ov_outputcompare_1 an
  • Gib das Assemblerprogramm ov_outputcompare_1.asm in den Editor ein und speichere es ab.
  • Brenne das Programm in den Controller und starte es anschließend.
  • Überprüfe, ob LED0 - LED7 blinken.
  • Bestimme mit einem USB-Oszilloskop die Blinkfrequenz fblink1.
  • Teil 2: Ändere den Wert im Output-Compare-Register ab, so dass sich die Blinkfrequenz auf ca. 3 Hz einstellt. Bestimme auch hier mit einem USB-Oszilloskop die Blinkfrequenz fblink2.

Arbeiten mit dem Datenblatt

  • Informier dich anhand des Datenblattes darüber, welche Bits im
    • Timer/Counter0 Control Register
    • Timer/Counter0 Interrupt Mask Register
  • für einen timer0-compare-match-Interrupt gesetzt werden müssen (S. 93 ff) und vergleiche nachher deine Ergebnisse mit denen im Assemblerprogramm.
  • Finde heraus, unter welcher Adresse der timer/counter0 Compare Match Interrupt Vector angesprochen werden muss (S. 54).

Das Programm led_timer0OV_1.asm

Abbildung 10 - Programm zu Übung 3

Ergebnisse zu Teilaufgabe 1

Nicht alle LEDs blinken. LED1 ist permanent auf HIGH. Die gemessene Blinkfrequenz der restlichen LEDs liegt bei ca. 2 Hz.

Abbildung 11 - Oszillogramm zu Teilaufgabe 1. fblink beträgt ca. 2 Hz.

Ergebnisse zu Teilaufgabe 2

Die Einstellung im Output Compare Register muss auf 0xA0 geändert werden.

Die Blinkfrequenz stellt sich dann auf ca. 3 Hz ein.

Abbildung 12 - Oszillogramm zu Teilaufgabe 2. fblink beträgt ca. 3 Hz.

Wie arbeitet das Programm led_timer0OV_1.asm?

Zum besseren Verständnis wird das Programm mit dem Debugger Programmzeile für Programmzeile durchlaufen. Dabei werden der Programmzähler (PC), das Statusregister (SReg) und der Stack-Pointer genauer angeschaut. Die einzelnen Schrittfolgen lauten:

  • Ändere im Programmzeilenblock "fo div 1024, CTC-Mode, clear OC0 on Compare Match" den Wert 0x0D in 0x01. Damit wird der Vorteiler auf 1 gesetzt und der erste Compare Match Interrupt schnell erreicht.
  • Build - Build oder F7 (Hauptmenü AVR Studio) compiliert das Programm.
  • Debug - Start Debugging startet den Debugger.
  • Mit F11 wird jede Programmzeile einzeln angesteuert und die Auswirkungen auf den Stackpointer und den Programmzähler betrachtet.
Abbildung 13 - Der Stackpointer zeigt nach der Initialisierung den Wert 0x025F an.
  • Ist der Programmblock "Stackpointer initialisieren" abgeschlossen, steht im Stackpointer die Adresse 0x025F. Im Statusregister ist bisher noch kein Flag gesetzt.

Einschub: Aufbau des SRAM

Die unteren 608 Adressen im Datenspeicherraum sprechen die 32 General Purpose Register, 64 I/O Register und den internen SRAM an. Dabei gilt:

  • Die meiste Zeit hält sich das Programm in der Endlosschleife loop:  rjmp loop auf. Im rechten Fenster "Timer_Counter_0" lässt sich mitverfolgen, wie der Zähler sich bei jedem Tastendruck von F11 um zwei Zähleinheiten weiterbewegt (das liegt daran, dass für den OP-Code rjmp loop zwei Taktzyklen benötigt werden (s. Datenblatt)). Der Vergleichswert, ab dem ein Interrupt erfolgen soll, steht im Register OCR0 und ist auf 0xA0 eingestellt.
Abbildung 14 - TCNT0- und OCR0-Register. Stimmen die Inhalte beider Register überein, wird ein Interrupt ausgelöst.
  • Mit dem Aufruf des Befehls SEI im Hauptprogramm, wird das I-Flag im SREG gesetzt und der Controller für Interrupts "scharf geschaltet". Auf die anderen Flags wird hier nicht eingegangen, da sie keine Relevanz zum Thema haben.
Abbildung 15 - Das I-Flag im SREG ist gesetzt.
  • Mit F11 wird der Zähler (TCNT0) so lange hochgezählt, bis er den Wert 0xA0 im OCR0-Register erreicht hat. Ist das der Fall, wird ein Interrupt ausgelöst.
Abbildung 16 - Ein Interrupt ist ausgelöst. Das I-Flag wird gelöscht und der Stack-Pointer erniedrigt sich um 2.
  • Im Stackpointer ist unter der Adresse [0x025F:0x025E] die Rücksprungadresse hinterlegt, die nach Abarbeitung der Interrupt-Service-Routine (ISR) in den Programmzähler zurückgeladen wird. Gleichzeitig wird das I-Flag wieder gesetzt.
Abbildung 17 - Das I-Flag ist wieder gesetzt. Das Programm verweilt in der Loop-Schleife, bis der PC und das OCR0-Register wieder identsiche Werte aufweisen.

Im diesem Abschnitt wurden die Op-Codes

  • RETI - Return from Interrupt
  • SEI - Set Global Interrupt Flag

und die Direktive

  • .ORG

eingeführt.

Aufgabe

Das Programm lässt die LEDs mit einem festen Takt blinken. Nur LED1 macht nicht mit; sie ist permanent eingeschaltet. Wo muss das Programm verändert werden, damit alle LEDs im Takt blinken?

 

Lösung: Über das TCCR0-Register kann dieser "Fehler" behoben werden.

Druckversion | Sitemap
© Reinhard Rahner - Gettorf