Ich zeige hier, wie man Interrupts unterbrechbar macht.
Interrupts können so durch andere Interrupts als auch durch den eigenen Interrupt unterbrochen werden.
Einleitung
Der AVR-Controller unterstützt eine Reihe an Interrupts.
Diese sind von Controller zu Controller unterschiedlich und hängen auch von dessen Peripherie abhängig.
Alle Controller unterstützen aber mindestens einen Timer-Interrupt und einen externen Interrupt.
Beispiel 1
Ich möchte mit folgenden Code-Schnipsel zeigen, wie man Interrupts unterbrechbar machen kann.
Alles anzeigen
Dieses Demo kann im Simulator ausprobiert werden.
Demo in den Bascom-Editor laden und compilieren.
Dann Simulator starten und durchstellen (Einzelschritte) bin der Cursor in der Hauptschleife (Do..Loop) ist.
Dann im Simulator im Reiter Interrupts einen der Interrupts auswählen und weitersteppen.
Der entsprechende Händler wird aufgerufen.
Sobald im Handler der Enable Interrupts ausgeführt wurde, kann der Händler durch den gleichen oder andere Interupts unterbrochen werden.
Mochte man einen bestimmten Handler nicht unterbrechbar haben, wird der Befehl Enable Interrupts im Handler auskommentiert.
Beispiel 2
Hier noch ein weiteres Beispiel, das Aufgrund eines Themas im Forum entstand. Die Beschreibung und Pinbelegung sind dem Code zu entnehmen.
Alles anzeigen
Hinweis
Diese Technik ist nicht für Einsteiger gedacht. Es erfordert eine gute Planung, damit sich die Interrupts nicht gegenseitig stören oder der Stack überläuft.
Mitch64.
Der AVR-Controller unterstützt eine Reihe an Interrupts.
Diese sind von Controller zu Controller unterschiedlich und hängen auch von dessen Peripherie abhängig.
Alle Controller unterstützen aber mindestens einen Timer-Interrupt und einen externen Interrupt.
Beispiel 1
Ich möchte mit folgenden Code-Schnipsel zeigen, wie man Interrupts unterbrechbar machen kann.
BASCOM-Quellcode
- ' Demo Interrupt unterbrechbar
- ' Dieses Demo ist ausschließlich für den Simulator konzipiert.
- ' Standardgemäß wird bei BascomAVR ein Interrupt-Hander nicht unterbrochen.
- ' Bedeutet, wenn während ein Hander gerade abgearbeitet wird, dieser
- ' durch auftretende Interrupts nicht unterbrochen wird, sondern der
- ' laufende Interrupt zuerst fertig abgearbeitet wird.
- ' Dieses Demo zeigt, wie ein laufender Interrupt durch einen weiteren
- ' Interrupt unterbrochen werden kann.
- ' Diese Technik erfordert allerdings besondere Sorgfalt vom Programmierer,
- ' dass weder der Stack überläuft, noch unvorhersehbare Effekte auftreten.
- ' Denn der Interrupt kann auch durch sich selbst unterbrochen werden.
- $Regfile = "m8def.dat"
- $HWStack = 128
- $SWStack = 32
- $Framesize = 32
- $Crystal = 8000000
- Config INT0 = Rising
- On Timer1 ISR_Timer1
- On Timer2 ISR_Timer2
- On INT0 ISR_INT0
- Enable Timer1 ' Interrupts zulassen
- Enable Timer2
- Enable INT0
- Enable Interrupts ' Generell Interrupts zulassen
- Do
- Nop
- Loop
- ISR_Timer1:
- Enable Interrupts ' Interrupt unterbrechbar machen
- NOP
- NOP
- NOP
- NOP
- Return
- ISR_Timer2:
- Enable Interrupts ' Interrupt unterbrechbar machen
- NOP
- NOP
- NOP
- NOP
- Return
- ISR_INT0:
- Enable Interrupts ' Interrupt unterbrechbar machen
- NOP
- NOP
- NOP
- NOP
- Return
Dieses Demo kann im Simulator ausprobiert werden.
Demo in den Bascom-Editor laden und compilieren.
Dann Simulator starten und durchstellen (Einzelschritte) bin der Cursor in der Hauptschleife (Do..Loop) ist.
Dann im Simulator im Reiter Interrupts einen der Interrupts auswählen und weitersteppen.
Der entsprechende Händler wird aufgerufen.
Sobald im Handler der Enable Interrupts ausgeführt wurde, kann der Händler durch den gleichen oder andere Interupts unterbrochen werden.
Mochte man einen bestimmten Handler nicht unterbrechbar haben, wird der Befehl Enable Interrupts im Handler auskommentiert.
Beispiel 2
Hier noch ein weiteres Beispiel, das Aufgrund eines Themas im Forum entstand. Die Beschreibung und Pinbelegung sind dem Code zu entnehmen.
BASCOM-Quellcode
- ' Nested Interrupt mit AVR
- ' Nested Interrupts (eingebettete Interrupts) nennt man das Verhalten,
- ' wenn eine aktive Interrupt-Service-Routine (ISR) durch einen weiteren Interrupt
- ' unterbrochen werden kann.
- ' Anschlüsse:
- ' Taster an Pin INT0 gegen Masse
- ' Status-Ausgabe über UART an PC / Terminal (19200 Baud)
- ' Funktionsweise
- ' Mit Timer2 wird ein regelmäßiger Interrupt (OC2-Interrupt) mit 1kHz ausgelöst.
- ' In der ISR_1ms wird ein Counter inkrementiert, der die ms Zählt.
- ' Der Counter kann mit Funktion Millis() ausgelesen werden.
- ' Taster löst bei neg. Flanke einen INT-Interrupt aus.
- ' Die ISR liest zu Beginn und am Ende mittels Function Millis()
- ' den ms-Counter aus. Dazwischen wird 1000ms Mittels Wait gewartet.
- ' Die Ausführungsdauer von INT0 beträgt also 1000ms.
- ' In der ISR wird noch ein Flag gesetzt, damit in der Hauptschleife
- ' Notitz von der ausgeführten ISR_INT0 Routine genommen wird.
- ' In der ISR_INT0 wird explizit mit 'Enable Interrupts' die Unterbrechung
- ' der ISR zugelassen, wodurch der MS-Counter (Timer-Interrupt) weiterzählen kann.
- ' Im Hauptprogramm wird die ausgeführte ISR_INT0 furch das gesetzte Flag erkannt.
- ' Dies führt zur Berechnung und Ausgabe per UART der Ausführungsdauer in ms.
- ' Wenn AVR-Controller also Nested Interrupts nicht zulassen, dürfte während
- ' der Ausführung von ISR_INT0 der msCounter nicht weiter laufen.
- ' Die Ausgabe würde dann 'Ausführungsdauer: 0ms' ergeben.
- ' Das ISR_INT0 durch 'Enable Interrupts' unterbrechbar gemacht wurde läuft der
- ' msCounter weiter und die Ausgabe dürfte einen Wert von ca. 1000ms ergeben.
- ' Gegenprobe:
- ' Zeile 'Enable Interrupts' in ISR_INT0 auskommentieren.
- ' Dann werden keine Interrupts mehr ausgelöst, während ISR_INT0 ausgeführt wird.
- $Regfile = "m8def.dat"
- $HWStack = 80
- $SWStack = 20
- $Framesize = 30
- $Crystal = 8000000 ' Quarz
- $Baud = 19200
- Waitms 1000
- ' --------------------------------------------------------
- ' Variablen
- ' --------------------------------------------------------
- Dim Flags as Byte
- Const FLAG_INT0 = 1 ' Ext. Interrupt wurde ausgelöst
- Dim msStart as Long ' Zeitstempel Start-Zeit [in ms]
- Dim msEnd as Long ' Zeitstempel End-Zeit [ms]
- Dim msDauer as Long ' Berechnete Dauer [ms]
- ' interne Variablen
- Dim _msCounter as Long ' intern, msCounter
- ' --------------------------------------------------------
- ' Deklarationen
- ' --------------------------------------------------------
- Declare Function Millis() as Long ' liest msCounter aus
- ' --------------------------------------------------------
- ' Initialisierung
- ' --------------------------------------------------------
- Set PortD.2 ' INT0 PullUp einschalten
- ' Timer als RTC-Timer
- Config Timer2 = Timer , Prescale = 32 , Clear Timer = 1
- Compare2 = 250 - 1 ' 1000Hz einstellen
- ' 1ms-ISR aktivieren
- On OC2 ISR_1ms
- Enable OC2
- ' Taster-Interrupt
- Config INT0 = Falling
- On INT0 ISR_INT0
- Enable INT0
- Enable Interrupts ' Global Interrupts zulassen (I-Flag setzen)
- Print "Testprogramm Nested-Interrupts"
- ' --------------------------------------------------------------------------------------
- ' Hauptschleife
- ' --------------------------------------------------------------------------------------
- Do
- If Flags.FLAG_INT0 = 1 then ' INT 0 wurde ausgelöst
- msDauer = msEnd - msStart ' Bechechnung der Dauer
- Print "INT0 wurde ausgelöst"
- Print "Start-Zeit: " ; msStart ; "ms"
- Print "End-Zeit: " ; msEnd ; "ms"
- Print "Ausführungsdauer: " ; msDauer ; "ms"
- Reset Flags.FLAG_INT0 '
- End If
- Loop
- ' --------------------------------------------------------
- ' Dieser Interrupt wird durch Timer2 (OC2-Interrupt) 1000x pro Sekunde aufgerufen
- ' Hier wird die Variable _msCounter alle ms um 1 erhöht.
- ' So sind Zeitmessungen im ms-Bereich möglich
- ' --------------------------------------------------------
- ISR_1ms:
- Incr _msCounter
- Return
- ' --------------------------------------------------------
- ' Dieser Interrupt wird mit Taster (Low-Flanke) aktiviert
- ' --------------------------------------------------------
- ISR_INT0:
- msStart = Millis() ' Zeitstempel holen
- Enable Interrupts ' INT0 soll unterbrechbar sein
- Wait 1
- Set Flags.FLAG_INT0 ' Dem Hauptprogramm einen absolvierten INT0 Interrupt mitteilen
- msEnd = Millis() ' Zeitstelpel holen
- Return
- ' --------------------------------------------------------
- ' Gibt aktuellen Wert von ms-Counter zurück
- ' --------------------------------------------------------
- Function Millis() as Long
- !PUSH r24 ' Statusregister retten
- !IN r24,SREG
- !PUSH r24
- !CLI ' Interrupts sperren (I-Flag=
- Millis = _msCounter ' Statusregister restaurieren (auch I-Flag)
- !POP r24
- !OUT SREG,r24
- !POP r24
- End Function
Diese Technik ist nicht für Einsteiger gedacht. Es erfordert eine gute Planung, damit sich die Interrupts nicht gegenseitig stören oder der Stack überläuft.
Mitch64.
5.244 mal gelesen