Ich möchte hier nochmal auf das Thema 'Nested Interrupts' eingehen.
Das Thema kam bei einem anderen Thema auf.
Hier der Link: PCINT Attiny Flankenerkennung
Was sind nested Interrupts. Das sind eingebettete Interrupts, oder auch verschachtelte Interrupts genannt.
Das bedeutet, wenn ein Interrupt ausgelöst wird und die entsprechende ISR-Routine in Abarbeitung ist, dass diese unterbrechbar ist durch einen anderen Interrupt. Ist diese ISR abgearbeitet, wird zur vorherigen ISR zurückgekehrt und diese weiterbearbeitet.
Ich habe dieses Thema bereits im Lexikon schon einmal beschrieben (Interrupts Unterbrechbar)
Es wurde teilweise behauptet, dass AVR's aus aus Gründen seiner Architektur verschachtelte Interrupts (nested Interrupts) nicht ausführen können. Das ist jedoch aus meiner Sicht schlichtweg falsch.
Der AVR kann nested Interrupts ausführen, egal ob man in C, C++, Assembler oder in Bascom programmiert. Das hat nichts mit der Programmiersprache zu tun. Die werden genauso unterstützt oder nicht unterstützt wie normale Interrupts. Der Assembler-Programmierer weiß, das bei normalen Interrupts (nicht nested Interrupts) die Registersicherung selber gemacht werden muss. Nichts anderes ist es bei Nested Interrupts.
Bascom übernimmt die Sicherung und Restaurieruung der Register in den ISR_Routinen. Außer man gibt bei der Konfiguration des Interrupt-Vectors explizit NoSave an (On Interrupt Routine [NoSave]).
Ob ein Interrupt Unterbrechbar ist oder nicht, hängt nur davon ab, ob man in der ISR_Routine, die unterbrechbar sein soll, explizit angibt, ob Interrupts zugelassen werden oder nicht.
Und jetzt werde ich auch den Beweis antreten (also keine Behauptung aufstellen), dass der AVR Ebenso in Basic Nested Interrupts unterstützt.
Hier zunächst das Programm das explizit getestet wurde und funktioniert:
Alles anzeigen
Die Funktionsweise ist im Code erklärt.
Aber Kurz zusammengefasst:
Ein Timer löst alle 1ms einen Interrupt aus, der die ISR_1ms (Interrupt-Service-Routine) aufruft.
Darin wird ein ms-Zähler hochgezählt.
Ein Taster (Low-Aktiv mit aktiviertem PullUp) am INT0 Eingang löst den INT0-Interrupt aus und ruft die ISR_INT0 auf.
Darin ist ein Waitms 1000. Bedeutet die ISR_INT0 benötigt mindestens 1000ms zur Abarbeitung.
Wenn Nestet Interrupts nicht funktionieren, kann während die ISR_INT0 in Bearbeitung ist, der ms-Zähler nicht weiter laufen.
Dann würde in der Hauptschleife als Dauer 0ms heraus kommen.
Der entscheidende Unterschied, ob nested oder nicht ist die Zeile 130. Wird "Enable Interrupts" auskommentiert, hat man "normale" Interrupts.
In der ISR_INT0 wird bei Eintritt der ms-Counter ausgelesen und vor dem beenden auch.
In der Hauptschleife werden dann per UART die ms-Couter-Werte und die Dauer ausgegeben.
Die Ausgabe im Terminal siht dann etwa so aus:
Alles anzeigen
Ich hoffe nun, dass das Thema, ob Nested Interrupts beim AVR funktionieren oder nicht, nun gegessen sein dürfte.
Das Thema kam bei einem anderen Thema auf.
Hier der Link: PCINT Attiny Flankenerkennung
Was sind nested Interrupts. Das sind eingebettete Interrupts, oder auch verschachtelte Interrupts genannt.
Das bedeutet, wenn ein Interrupt ausgelöst wird und die entsprechende ISR-Routine in Abarbeitung ist, dass diese unterbrechbar ist durch einen anderen Interrupt. Ist diese ISR abgearbeitet, wird zur vorherigen ISR zurückgekehrt und diese weiterbearbeitet.
Ich habe dieses Thema bereits im Lexikon schon einmal beschrieben (Interrupts Unterbrechbar)
Es wurde teilweise behauptet, dass AVR's aus aus Gründen seiner Architektur verschachtelte Interrupts (nested Interrupts) nicht ausführen können. Das ist jedoch aus meiner Sicht schlichtweg falsch.
Der AVR kann nested Interrupts ausführen, egal ob man in C, C++, Assembler oder in Bascom programmiert. Das hat nichts mit der Programmiersprache zu tun. Die werden genauso unterstützt oder nicht unterstützt wie normale Interrupts. Der Assembler-Programmierer weiß, das bei normalen Interrupts (nicht nested Interrupts) die Registersicherung selber gemacht werden muss. Nichts anderes ist es bei Nested Interrupts.
Bascom übernimmt die Sicherung und Restaurieruung der Register in den ISR_Routinen. Außer man gibt bei der Konfiguration des Interrupt-Vectors explizit NoSave an (On Interrupt Routine [NoSave]).
Ob ein Interrupt Unterbrechbar ist oder nicht, hängt nur davon ab, ob man in der ISR_Routine, die unterbrechbar sein soll, explizit angibt, ob Interrupts zugelassen werden oder nicht.
Und jetzt werde ich auch den Beweis antreten (also keine Behauptung aufstellen), dass der AVR Ebenso in Basic Nested Interrupts unterstützt.
Hier zunächst das Programm das explizit getestet wurde und funktioniert:
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.
- ' Einige sind der Meinung, dass AVR-Controller aus Gründen der Architektur
- ' nested Interrupts nicht ausführen können. Dies ist jedoch ein Trugschluss.
- ' 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
Die Funktionsweise ist im Code erklärt.
Aber Kurz zusammengefasst:
Ein Timer löst alle 1ms einen Interrupt aus, der die ISR_1ms (Interrupt-Service-Routine) aufruft.
Darin wird ein ms-Zähler hochgezählt.
Ein Taster (Low-Aktiv mit aktiviertem PullUp) am INT0 Eingang löst den INT0-Interrupt aus und ruft die ISR_INT0 auf.
Darin ist ein Waitms 1000. Bedeutet die ISR_INT0 benötigt mindestens 1000ms zur Abarbeitung.
Wenn Nestet Interrupts nicht funktionieren, kann während die ISR_INT0 in Bearbeitung ist, der ms-Zähler nicht weiter laufen.
Dann würde in der Hauptschleife als Dauer 0ms heraus kommen.
Der entscheidende Unterschied, ob nested oder nicht ist die Zeile 130. Wird "Enable Interrupts" auskommentiert, hat man "normale" Interrupts.
In der ISR_INT0 wird bei Eintritt der ms-Counter ausgelesen und vor dem beenden auch.
In der Hauptschleife werden dann per UART die ms-Couter-Werte und die Dauer ausgegeben.
Die Ausgabe im Terminal siht dann etwa so aus:
Quellcode
- Testprogramm Nested-Interrupts
- INT0 wurde ausgelöst
- Start-Zeit: 4716ms
- End-Zeit: 5735ms
- Ausführungsdauer: 1019ms
- INT0 wurde ausgelöst
- Start-Zeit: 6744ms
- End-Zeit: 7763ms
- Ausführungsdauer: 1019ms
- INT0 wurde ausgelöst
- Start-Zeit: 8663ms
- End-Zeit: 9682ms
- Ausführungsdauer: 1019ms
- INT0 wurde ausgelöst
- Start-Zeit: 10154ms
- End-Zeit: 11173ms
- Ausführungsdauer: 1019ms
Ich hoffe nun, dass das Thema, ob Nested Interrupts beim AVR funktionieren oder nicht, nun gegessen sein dürfte.