Einführung
Ein Interrupt wird entweder durch ein „externes Ereignis“ oder ein „internes Ereignis“ ausgelöst und unterbricht den Ablauf des Programms, um ein Unterprogramm auszuführen. Sobald dieses Unterprogramm abgearbeitet ist, wird die Rücksprungadresse vom Stackpointer geholt und das Hauptprogramm weiter ausgeführt.
Dieses Unterprogramm nennt sich Interrupt Service Routine (ISR).
Ein „externes Ereignis“ ist beispielsweise die Auslösung einer Lichtschranke und ein „internes Ereignis“ wird durch sogenannte Timer erzeugt.
Dieser Beitrag beschränkt sich jedoch lediglich auf die „externen Ereignisse“!
Unser Mikrocontroller besitzt 2 Interrupts, die durch „externe Ereignisse“ ausgelöst werden können:
- Interrupt
- Bezeichnung: INT0
- Port: 3.2
- Einsprungadresse: 0003h
- Interrupt
- Bezeichnung: INT1
- Port: 3.3
- Einsprungadresse: 0013h
Was passiert, wenn ein Interrupt ausgelöst wird?
Sobald ein Interrupt ausgelöst wird, wird zuerst einmal der aktuelle Befehl fertig abgearbeitet. Danach wird der Stackpointer erhöht und die Rücksprungadresse auf dem Stack abgelegt. Somit weiß das Programm später, wo es nach beenden der ISR fortfahren muss. Als nächstes wird der Programm-Counter (PC) auf die Einsprungadresse des ISR gesetzt. Der ISR wird abgearbeitet, die Rücksprungadresse vom Stack geholt, der Stackpointer verringert und die Rücksprungadresse geladen.
Somit befindet sich der Stack bei Aufruf und Verlassen der ISR immer in gleichem Zustand!
Anwendung
Stackpointer setzen
Da das Programm beim Auslösen eines Interrupts in ein Unterprogramm springt, muss eine Rücksprungadresse im Speicher (SFR) abgelegt werden. Damit diese nicht dort abgelegt wird, wo sich möglicherweise wichtige Daten befinden (werden), weist man dem Stackpointer (SP) eine bestimmte (freie) Adresse im Speicher zu.
In diesem Fall weise ich die Adresse 0x80 zu:
mov SP,#0x80
Interrupt-Modus wählen
Anschließend hat man die Wahl zwischen 2 verschiedenen Interrupt-Modi. Dies geschieht durch setzen bzw. entfernen der Bits IT0 für INT0 bzw. IT1 für INT1.
- mit Low-Signal gesteuert
clr IT0
- mit Flanke gesteuert
setb IT0
Interrupt freigeben
Um einen Interrupt verwenden zu können, muss dieser zuerst freigeschaltet werden. Dies geschieht durch setzen der Bits EX0 für INT0 bzw. EX1 für INT1.
setb EX0
Danach muss noch ein Bit gesetzt werden, um alle Interrupts „generell“ zu aktivieren:
setb EA
ISR erstellen
Damit der Interrupt am Ende auch Sinn macht, benötigt man eine Interrupt Service Routine (ISR).
Dazu setzt man cseg auf die jeweilige Einsprungsadresse, erstellt ein Label und beendet die ISR mit dem Schlüsselwort reti, welches dem Assembler mitteilt, dass die ISR beendet ist.
cseg at 0x0003 //cseg auf die Einsprungsadresse setzen isr: //Label setzen //Irgendwelche Befehle reti //ISR beendet
Nun kann der Interrupt INT0 verwendet werden!
Zusammenfassung
- Stackpointer setzen
- Interrupt-Modus wählen
- Ausgewählten Interrupt freigeben
- Alle Interrupts „generell“ freigeben
Quelltext-Beispiel
Folgendes (kommentiertes) Quelltext-Beispiel löst Interrupt 0 aus und setzt ein Bit bei P2.0, sobald ein Low-Signal an Port 3.2 anliegt:
$NOMOD51 //Vordefinitionen vergessen #include <at89c5131.h> //Neue Definitionen einbinden cseg at 0x0000 jmp main //zu Sprungmarke main springen cseg at 0x0003 //vordefinierte Einsprungadresse (siehe Einfuehrung) isr: //Sprungmarke isr setb P2.0 //Bit bei P2.0 setzen reti //ISR beendet - zuruekspringen main: //Sprungmarke main mov SP,#0x80 //Stackpointer auf 0x80 setzen mov P2,#0x00 //P2 auf 0x00 setzen clr IT0 //INT0 soll auf Low-Signale reagieren setb EX0 //INT0 freischalten setb EA //Alle Interrupts generell freischalten loop: //Endlosschleife nop jmp loop end