Impressum
< Unterprogramme Inhalt

Interrupts

Einführung top

Der Programmfluss eines Assemblerprogramms ist sequentiell, d.h. es wird immer eine Anweisung nach der anderen ausgeführt. Dieser Programmfluss kann nur durch spezielle Befehle verändert werden, den Sprungbefehlen.
Allerdings ermöglichen es uns Interrupts den Programmfluss zu unterbrechen (engl. to interrupt), ein Unterprogramm auszuführen und danach den Programmfluss wieder aufzunehmen als sei nichts gewesen.
Solche Unterprogramme werden Interrupt-Service-Routinen (ISR) genannt. Eine ISR wird nur ausgeführt, wenn ein entsprechendes Ereignis aufgetreten ist, dies kann ein Timer/Counter-Überlauf, einer der zwei externen Interrupts (an P3.2 oder P3.3) oder das empfangen eines ASCII-Zeichen über die Serielle-Schnittstelle sein.
Ist der Mikrocontroller so konfiguriert, dass er auf dieses Ereignis reagieren soll, so wird das Hauptprogramm kurzzeitig unterbrochen und die Kontrolle geht an ein spezielles Code-Fragment über (die ISR). Nach dem Abarbeiten der ISR wird das Hauptprogramm fortgesetzt, ohne das dies jemals mitbekommt, dass es unterborchen wurde.

Interrupt-Quellen top

Als Interrupt-Quelle bezeichnet man ein Ereignis, welches einen Interrupt auslösen kann (also das Programm unterbricht und zur ISR springt).
Damit man die Ereignisse unterscheiden kann, die einen Interrupt auslösen, springt jeder Interrupt zu einer eigenen Interrupt-Service-Routine (ISR). Jede ISR liegt an einer festen Adresse im ROM. Die folgende Tabelle listet alle Interrupt-Quellen und die zugehörigen Adressen der Interrupt-Service-Routinen auf:

Interrupt-Quelle Adresse der ISR
Externer Interrupt 0 003h
Timer0-Überlauf 00Bh
Externer Interrupt 1 013h
Timer1-Überlauf 01Bh
Serieller Interrupt 023h
Timer2-Überlauf 02Bh
Timer2 ist nicht auf jedem 8051 vorhanden!

Interrupts aktivieren top

Eine Unterbrechung eines Assembler-Programms durch einen Interrupt ist nicht immer wünschenswert, deshalb können Interrupts an- und abgeschaltet werden. Standartmäßig sind alle Interrupts abgeschalten.
Zum einen gibt es einen generellen Interrupt-Anschalt-Knopf (das Bit IE.7 oder kurz EA; IE=Interrupt Enable und EA=Enable All interrupts). Ist dieses auf 1 gesetzt, so sind Interrupts möglich, ist es 0 so sind alle Interrupts gesperrt.
Da es fast kein Programm nötig hat alle Interrupts zu behandeln, kann man jeden einzelnen an- oder abschalten. Hierfür sind die restlichen Bit im IE zuständig. Standartmäßig sind diese alle auf 0 (=abgeschalten) gesetzt.
Welches Bit in IE welchen Interrupt an- bzw. abschaltet zeigt die folgende Tabelle:

Bits in IE (=0A8h)
Bit Nr. Name Interrupt
IE.7 EA Enable All Interrupts
IE.6 nicht verwendet
IE.5 ET2 Timer2 Interrupt
IE.4 ES Serieller Interrupt
IE.3 ET1 Timer1 Interrupt
IE.2 EX1 Externer Interrupt 1
IE.1 ET0 Timer0 Interrupt
IE.0 EX0 Externer Interrupt 0

Merke: Um einen Interrupt nutzen zu können (also eine Unterbrechung zuzulassen) muss EA und das betreffende Bit in IE für den betreffenden Interrupt auf 1 gesetzt werden!

Übersichtsbild über das Interruptsystem
Abb.: Übersichtsbild über das Interruptsystem.

Sprung zur ISR - oder was passiert bei einem Interrupt? top

Wenn ein Interrupt ausgelöst wurde führt der Mikrocontroller folgende Schritte vollautomatisch aus:
  1. aktuellen Befehl beenden
  2. Programm-Counter auf dem Stack ablegen
  3. Programm-Counter auf den Wert der ISR setzen
  4. ISR abarbeiten
  5. Programm-Counter vom Stack holen (reti)
  6. Programm normal fortsetzen

Die Externen Interrupts top

Es existieren zwei externe Interrupts EX0 und EX1. Diese werden, wie der Name schon sagt, durch externe Quellen verursacht, nämlich durch einen Tastendruck an den Portpins P3.2 (EX0) und P3.3 (EX1).
Im folgenden wird EX0 beschrieben, die Werte und Adressen für EX1 werden in [] angegeben.
Mittels des Bits IT0=TCON.0 [IT1=TCON.2] kann eingestellt werden, ob der Interrupt bei einer fallenden Flanke (IT0=1) oder bei einem gedrückten (P3.2=0 [P3.3]) Schalter (IT0=0) ausgelöst wird. Der Unterschied ist, dass eine fallende Flanke nur einmal pro Tastendruck, ein niederer Pegel - also P3.2=0 [P3.3] - bis zum Loslassen des Tasters dauerhaft vorhanden ist, der Interrupt also wieder und wieder ausgelöst wird.
Wird der Interrupt aktiviert (setb IE.0 [IE.2] und setb EA) und ausgelöst, so springt der Mikrocontroller zur Interrupt-Service-Routine (ISR) des eXternen Interrupts an der Adresse 03h [0Bh].

Ein kleines Beispielprogramm:
      ljmp main ; ISR überspringen und im Hauptprogramm (main) beginnen
      
org 03h         ; ISR für EX0 an Adresse 03h im ROM platzieren
      inc  P0   ; ISR erhöht P0 um 1
      reti      ; ISR-Ende (Hauptprogramm fortsetzen)

main: setb IT0  ; Externer Interrupt reagiert auf fallende Flanke an P3.2
      setb EX0  ; Externen Interrupt aktivieren
      setb EA   ; Interrupts generell zulassen
      ; --- ab hier reagiert der µC auf den eXternen Interrupt 0
wait: ljmp wait ; Endlosschleife (Hauptprogramm tut "nichts" mehr)
     

Die Timer Interrupts top

Der 8051 hat zwei Timer/Counter (T/C0 und T/C1). Im folgenden wird T/C0 beschrieben, die Werte von T/C1 werden in [] angegeben.
Timer/Counter0 löst einen Interrupt aus, wenn das Zählregister überläuft. Um dies besser zu verstehen werden wir erst die Funktionsweise des T/C0 betrachten, bevor wir auf die Interrupts eingehen.
Der T/C0 kann als Timer oder Counter verwendet werden. Als Timer zählt er die Maschinenzyklen (MZ), das heißt er erhöht seinen Wert jedem Mikrosekunde. Als Counter zählt er die fallenden Flanken an dem Port-Pin 3.4 [3.5]. T/C0 zählt in den Registern TH0 und TL0 [T/C1 in TH1 und TL0].
Des weiteren kann man, sowohl für den Counter als auch für den Timer, unterschiedliche Modi festlegen. Alle diese Einstellungen werden in dem Register TMod vorgenommen:
Bits in TMod (=089h) nicht bit-adressierbar
Bit Nr. Bedeutung
7 Gate1: uninteressant für uns
6 T/C1 (s. Bit 2)
5 Modus von T/C1 gleich wie die Modi von T/C0 (s. Bit0+1)
4
3 Gate0: uninteressant für uns.
2 T/C0: Mit diesem Bit wird festgelegt, ob T/C0 als Timer oder als Counter operieren soll. Ist dieses Bit 0 so ist T/C0 ein Timer; ist es 1 so ist T/C0 ein Counter.
1
Wert Modus
11 für uns nicht interessant
10 8-Bit Auto-Reload-Modus: In TH0 wird ein Reload-Wert gespeichert. Der T/C0 zählt in TL0. Hierbei beginnt er nicht bei 0 sondern bei dem Wert, welcher in TH0 gespeichert wurde.
Wenn der T/C0 überläuft (wenn in TL0 der Wert 0FFh steht und dieser um 1 erhöht wird) startet der T/C0 läd der er wieder den Wert von TH0 in TL0 und beginnt somit wieder bei diesem Wert.
Da hier immer ein festgelegter Startwert geladen wird heißt der Modus Auto-Reload-Modus.
01 16-Bit Modus: TH0 und TL0 bilden zusammen eine 16-Bit-Variable wobei in TH0 die oberen (höherwertigen) 8 Bits stehen und in TL0 die niederwertigen.
Der T/C0 zählt immer von 0 bis 0FFFFh und beginnt dann wieder bei 0.
00 13-Bit Modus: Die unteren (niederwertigen) 5 Bit von TL0 und die 8 Bit von TH0 bilden eine 13-Bit-Zahl. Die 5 Bits von TL0 sind die hinteren Bits dieser Zahl. Der T/C0 zählt somit von 0 bis 2 hoch 13 und beginnt dann wieder bei 0.
0

Merke: Will man TMod ändern, so muss man den Zähler/Counter zuerst anhalten.

Nachdem für T/C0 der richtigen Modus gesetzt wurde kann er gestartet werden, dies geschieht indem man das Bit TCon.4 [TCon.6] auf 1 setzt (TCon=88h) und man hält ihn an indem man TCon.4 [TCon.6] auf 0 setzt.

Doch wie bekommt das Programm mit, das der T/C0 überläuft (also vom Maximalwert auf 0 springt)? Genau hier kommt der Timer-Interrupt ins Spiel. Falls IE.1 [IE.3] und EA auf 1 gesetzt wurden, löst jeder Überlauf einen Interrupt aus und springt zu der ISR an Adresse 0Bh [01Bh] im Rom. Hier kann im Programm festgelegt werden wie der Überlauf behandelt werden soll.
Den Timer-Interrupt benötigt man nur, wenn man auf Werte größer als 0FFFFh zählen will.
Ein kleines Beispielprogramm, welches die Anzahl der Überläufe in 040h mitzählt:
include reg_51.pdf
    ljmp main       ; ISR überspringen
org 0Bh             ; ISR für Timer0 Überlauf
    call isr_timer
    reti
    
main:
    mov  040h, #0   ; 040h initialisieren
    mov  TMod, #01  ; T/C0 als 16-Bit-Timer
    setb IE.1       ; Timer-Interrupt0 zulassen
    setb EA         ; Interrupts anschalten
    setb TCon.4     ; T/C0 starten
w:  sjmp w          ; sinnlose Endlosschleife

isr_timer:          ; wird bei jedem Überlauf aufgerufen
    inc 040h
    ret
end