Interrupts unterbrechbar

This site uses cookies. By continuing to browse this site, you are agreeing to our Cookie Policy.

  • Standardgemäß können in BascomAVR Interrupts durch weitere Interrupts nicht unterbrochen werden.

    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.

    BASCOM Source Code

    1. ' Demo Interrupt unterbrechbar
    2. ' Dieses Demo ist ausschließlich für den Simulator konzipiert.
    3. ' Standardgemäß wird bei BascomAVR ein Interrupt-Hander nicht unterbrochen.
    4. ' Bedeutet, wenn während ein Hander gerade abgearbeitet wird, dieser
    5. ' durch auftretende Interrupts nicht unterbrochen wird, sondern der
    6. ' laufende Interrupt zuerst fertig abgearbeitet wird.
    7. ' Dieses Demo zeigt, wie ein laufender Interrupt durch einen weiteren
    8. ' Interrupt unterbrochen werden kann.
    9. ' Diese Technik erfordert allerdings besondere Sorgfalt vom Programmierer,
    10. ' dass weder der Stack überläuft, noch unvorhersehbare Effekte auftreten.
    11. ' Denn der Interrupt kann auch durch sich selbst unterbrochen werden.
    12. $Regfile = "m8def.dat"
    13. $HWStack = 128
    14. $SWStack = 32
    15. $Framesize = 32
    16. $Crystal = 8000000
    17. Config INT0 = Rising
    18. On Timer1 ISR_Timer1
    19. On Timer2 ISR_Timer2
    20. On INT0 ISR_INT0
    21. Enable Timer1 ' Interrupts zulassen
    22. Enable Timer2
    23. Enable INT0
    24. Enable Interrupts ' Generell Interrupts zulassen
    25. Do
    26. Nop
    27. Loop
    28. ISR_Timer1:
    29. Enable Interrupts ' Interrupt unterbrechbar machen
    30. NOP
    31. NOP
    32. NOP
    33. NOP
    34. Return
    35. ISR_Timer2:
    36. Enable Interrupts ' Interrupt unterbrechbar machen
    37. NOP
    38. NOP
    39. NOP
    40. NOP
    41. Return
    42. ISR_INT0:
    43. Enable Interrupts ' Interrupt unterbrechbar machen
    44. NOP
    45. NOP
    46. NOP
    47. NOP
    48. Return
    Display All

    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 Source Code

    1. ' Nested Interrupt mit AVR
    2. ' Nested Interrupts (eingebettete Interrupts) nennt man das Verhalten,
    3. ' wenn eine aktive Interrupt-Service-Routine (ISR) durch einen weiteren Interrupt
    4. ' unterbrochen werden kann.
    5. ' Anschlüsse:
    6. ' Taster an Pin INT0 gegen Masse
    7. ' Status-Ausgabe über UART an PC / Terminal (19200 Baud)
    8. ' Funktionsweise
    9. ' Mit Timer2 wird ein regelmäßiger Interrupt (OC2-Interrupt) mit 1kHz ausgelöst.
    10. ' In der ISR_1ms wird ein Counter inkrementiert, der die ms Zählt.
    11. ' Der Counter kann mit Funktion Millis() ausgelesen werden.
    12. ' Taster löst bei neg. Flanke einen INT-Interrupt aus.
    13. ' Die ISR liest zu Beginn und am Ende mittels Function Millis()
    14. ' den ms-Counter aus. Dazwischen wird 1000ms Mittels Wait gewartet.
    15. ' Die Ausführungsdauer von INT0 beträgt also 1000ms.
    16. ' In der ISR wird noch ein Flag gesetzt, damit in der Hauptschleife
    17. ' Notitz von der ausgeführten ISR_INT0 Routine genommen wird.
    18. ' In der ISR_INT0 wird explizit mit 'Enable Interrupts' die Unterbrechung
    19. ' der ISR zugelassen, wodurch der MS-Counter (Timer-Interrupt) weiterzählen kann.
    20. ' Im Hauptprogramm wird die ausgeführte ISR_INT0 furch das gesetzte Flag erkannt.
    21. ' Dies führt zur Berechnung und Ausgabe per UART der Ausführungsdauer in ms.
    22. ' Wenn AVR-Controller also Nested Interrupts nicht zulassen, dürfte während
    23. ' der Ausführung von ISR_INT0 der msCounter nicht weiter laufen.
    24. ' Die Ausgabe würde dann 'Ausführungsdauer: 0ms' ergeben.
    25. ' Das ISR_INT0 durch 'Enable Interrupts' unterbrechbar gemacht wurde läuft der
    26. ' msCounter weiter und die Ausgabe dürfte einen Wert von ca. 1000ms ergeben.
    27. ' Gegenprobe:
    28. ' Zeile 'Enable Interrupts' in ISR_INT0 auskommentieren.
    29. ' Dann werden keine Interrupts mehr ausgelöst, während ISR_INT0 ausgeführt wird.
    30. $Regfile = "m8def.dat"
    31. $HWStack = 80
    32. $SWStack = 20
    33. $Framesize = 30
    34. $Crystal = 8000000 ' Quarz
    35. $Baud = 19200
    36. Waitms 1000
    37. ' --------------------------------------------------------
    38. ' Variablen
    39. ' --------------------------------------------------------
    40. Dim Flags as Byte
    41. Const FLAG_INT0 = 1 ' Ext. Interrupt wurde ausgelöst
    42. Dim msStart as Long ' Zeitstempel Start-Zeit [in ms]
    43. Dim msEnd as Long ' Zeitstempel End-Zeit [ms]
    44. Dim msDauer as Long ' Berechnete Dauer [ms]
    45. ' interne Variablen
    46. Dim _msCounter as Long ' intern, msCounter
    47. ' --------------------------------------------------------
    48. ' Deklarationen
    49. ' --------------------------------------------------------
    50. Declare Function Millis() as Long ' liest msCounter aus
    51. ' --------------------------------------------------------
    52. ' Initialisierung
    53. ' --------------------------------------------------------
    54. Set PortD.2 ' INT0 PullUp einschalten
    55. ' Timer als RTC-Timer
    56. Config Timer2 = Timer , Prescale = 32 , Clear Timer = 1
    57. Compare2 = 250 - 1 ' 1000Hz einstellen
    58. ' 1ms-ISR aktivieren
    59. On OC2 ISR_1ms
    60. Enable OC2
    61. ' Taster-Interrupt
    62. Config INT0 = Falling
    63. On INT0 ISR_INT0
    64. Enable INT0
    65. Enable Interrupts ' Global Interrupts zulassen (I-Flag setzen)
    66. Print
    67. Print "Testprogramm Nested-Interrupts"
    68. ' --------------------------------------------------------------------------------------
    69. ' Hauptschleife
    70. ' --------------------------------------------------------------------------------------
    71. Do
    72. If Flags.FLAG_INT0 = 1 then ' INT 0 wurde ausgelöst
    73. msDauer = msEnd - msStart ' Bechechnung der Dauer
    74. Print
    75. Print "INT0 wurde ausgelöst"
    76. Print "Start-Zeit: " ; msStart ; "ms"
    77. Print "End-Zeit: " ; msEnd ; "ms"
    78. Print "Ausführungsdauer: " ; msDauer ; "ms"
    79. Print
    80. Reset Flags.FLAG_INT0 '
    81. End If
    82. Loop
    83. ' --------------------------------------------------------
    84. ' Dieser Interrupt wird durch Timer2 (OC2-Interrupt) 1000x pro Sekunde aufgerufen
    85. ' Hier wird die Variable _msCounter alle ms um 1 erhöht.
    86. ' So sind Zeitmessungen im ms-Bereich möglich
    87. ' --------------------------------------------------------
    88. ISR_1ms:
    89. Incr _msCounter
    90. Return
    91. ' --------------------------------------------------------
    92. ' Dieser Interrupt wird mit Taster (Low-Flanke) aktiviert
    93. ' --------------------------------------------------------
    94. ISR_INT0:
    95. msStart = Millis() ' Zeitstempel holen
    96. Enable Interrupts ' INT0 soll unterbrechbar sein
    97. Wait 1
    98. Set Flags.FLAG_INT0 ' Dem Hauptprogramm einen absolvierten INT0 Interrupt mitteilen
    99. msEnd = Millis() ' Zeitstelpel holen
    100. Return
    101. ' --------------------------------------------------------
    102. ' Gibt aktuellen Wert von ms-Counter zurück
    103. ' --------------------------------------------------------
    104. Function Millis() as Long
    105. !PUSH r24 ' Statusregister retten
    106. !IN r24,SREG
    107. !PUSH r24
    108. !CLI ' Interrupts sperren (I-Flag=
    109. Millis = _msCounter ' Statusregister restaurieren (auch I-Flag)
    110. !POP r24
    111. !OUT SREG,r24
    112. !POP r24
    113. End Function
    Display All
    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.

    402 times viewed