(→Bascom Interrupts (Mega8 und andere)) |
(→Fehlervermeidung) |
||
(19 dazwischenliegende Versionen von 6 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
== Bascom Interrupts (Mega8 und andere) == | == Bascom Interrupts (Mega8 und andere) == | ||
− | + | Was ist das eigentlich und wie wird mit [[Interrupt|Interrupts]] unter Bascom gearbeitet? Die hier gezeigten Beispiele lassen sich eigentlich auf alle [[AVR]]-Boards bzw. Controller übertragen.<br> | |
− | Interrupts werden immer dort verwendet, wo | + | |
+ | === Allgemein === | ||
+ | Interrupts werden immer dort verwendet, wo zeitkritische Anwendungen gefordert sind. | ||
Wenn man z.B. ein ganz kurzes Signal an einem Pin messen/detektieren will, kann man es mit dem normalem Hauptprogramm leicht übersehen. (z.B. durch eine Wait-Anweisung o.ä.) | Wenn man z.B. ein ganz kurzes Signal an einem Pin messen/detektieren will, kann man es mit dem normalem Hauptprogramm leicht übersehen. (z.B. durch eine Wait-Anweisung o.ä.) | ||
− | Bei | + | |
+ | Bei Verwendung eines Interrupts, reagiert der Prozessor, bei auftreten des Signals, unbedingt und verzweigt zu seiner ISR (Interrupt Service Routine), um dort darauf zu reagieren. | ||
+ | (Anschliessend springt er wieder in das Hauptprogramm zurück) | ||
+ | |||
+ | |||
+ | |||
+ | === Etwas genauer === | ||
+ | Microcontroller heissen so, weil sich in ihnen eine Reihe von recht eigenständigen Geräten für Timer, PWM, ADC, etc. befinden. Diese Geräte werden irgendwann mal konfiguriert ("CONFIG device-name"), dann in irgendeiner Form gestartet (z.B. "START ADC"), ja, und dann werden sie natürlich irgendwann mal mit ihrer Aufgabe fertig. Das zeigen sie durch Setzen eines bestimmten Bits in einem I/O Register an. | ||
+ | *Wir kriegen das mit, wenn wir gelegentlich oder öfter diese "FERTIG"-Bits abfragen | ||
+ | *Oder wir erlauben dem Gerät, einen [[Interrupt|Interrupt]] auszulösen ("ENABLE device-name"). | ||
+ | |||
+ | Ganz vorne im Programmspeicher ist eine Tabelle von sogenannten "ISR" (=Interrupt-Service-Routinen), jeder Eintrag davon ist einem bestimmten Gerät zugeordnet. (Die Tabelle ist keine Idee von den Bascom-Leuten, das haben die AVR-Hersteller so festgelegt, und das gilt daher auch für alle anderen Programmiersprachen). Der Ablauf ist nun folgender: | ||
+ | *Ein Gerät wird fertig und setzt sein "fertig" Bit | ||
+ | *Der Controller schaut, ob diese Gerät Interrupts auslösen darf | ||
+ | *Der Controller schaut, ob das "global interrupt enable"-Bit gesetzt ist (eine Art Hauptschalter) | ||
+ | Trifft das alles zusammen, wird zwar noch der gerade laufende Maschinenbefehl fertig gemacht, aber dann gibt es kein Halten mehr: | ||
+ | *Weitere Interrupts werden "disabled", damit dem Controller jetzt keiner in die Suppe spucken kann, denn ein paar Befehle lang steht der Controller jetzt "auf dem linken Bein". | ||
+ | *Die eigentlich nächste fällige Befehlsadresse wird auf den Stack gelegt ("PUSH") | ||
+ | *der Adresse der ISR aus der ob. Tabelle wird in den Befehlszähler geladen | ||
+ | *und ausgeführt. | ||
+ | Die weitere Kontrolle liegt nun vollständig in der Hand der ISR. | ||
+ | |||
+ | Tritt in dieser Zeit nun noch ein anderer Interrupt auf, merkt sich das der | ||
+ | Controller zwar, tut aber weiter nichts, da ja die Interrupts jetzt global "disabled" sind | ||
+ | |||
+ | Erst, wenn die ISR den Maschinen-Befehl "RETI" durchführt, | ||
+ | *wird die ursprüngliche Befehlsadresse vom Stack wieder runtergeholt ("POP") | ||
+ | *in den Befehlszähler geladen | ||
+ | *Interrupts werden wieder global "enabled" | ||
+ | Und es geht wieder "normal" weiter, wenn nicht schon ein weiterer Interrupt lauert, denn dann wiederholt sich das alles. | ||
+ | |||
+ | |||
+ | |||
=== Struktur: === | === Struktur: === | ||
Zeile 40: | Zeile 74: | ||
Man soll darauf achten, dass in der ISR immer nur kurze Anweisungen abgearbeitet | Man soll darauf achten, dass in der ISR immer nur kurze Anweisungen abgearbeitet | ||
werden und der Hauptteil dann im Hauptprogramm bearbeitet wird. | werden und der Hauptteil dann im Hauptprogramm bearbeitet wird. | ||
− | Sonst kann es sein, dass man den Prozessor damit blockiert. | + | Sonst kann es sein, dass man den Prozessor damit blockiert. Insbesondere Befehle wie "Wait" oder "Print" sollte man in der ISR vermeiden. |
+ | Für die ISR muß der Zustand des Hauptprogramms gespeichert werden. Dazu ist zusätzlicher Platz (bis 35 Bytes) auf dem HW-Stack nötig, der entsprechend reserviert werden muß. | ||
=== Interrupt: Int0 === | === Interrupt: Int0 === | ||
<pre> | <pre> | ||
− | + | Config Int0 = Low Level ‘Configuriere Int0 auf Low Level | |
Enable Interrupts ‘einschalten der Interrupts | Enable Interrupts ‘einschalten der Interrupts | ||
Enable Int0 ‘einschalten von Interrupt Int0 | Enable Int0 ‘einschalten von Interrupt Int0 | ||
Zeile 65: | Zeile 100: | ||
Es gibt für diesen Interrupt drei Einstellmöglichkeiten auf die er reagieren kann: | Es gibt für diesen Interrupt drei Einstellmöglichkeiten auf die er reagieren kann: | ||
− | '''Low Level:''' reagiert | + | :'''Low Level:''' reagiert solange der Pin auf Masse gesetzt wird.<br/> |
− | '''Falling:''' reagiert auf eine fallende Flanke<br/> | + | :'''Falling:''' reagiert nur auf eine fallende Flanke<br/> |
− | '''Rising:''' reagiert auf eine steigende Flanke<br/> | + | :'''Rising:''' reagiert nur auf eine steigende Flanke<br/> |
+ | == Fehlervermeidung == | ||
+ | === Zuviel Code im Interrupt === | ||
+ | Oft kommt es vor, daß zu viel Code in einem häufig auftretenden Interrupt ausgeführt wird. In diesem Fall wird der Interrupt sofort nach der Bearbeitung erneut aufgerufen. Für die Hauptschleife bleibt keine Zeit. Halten sie deshalb den Interrupt so kurz wie möglich. | ||
+ | Im Zweifelsfall lässt sich Laufzeit für die ISR in der Simulation kontrollieren. So kann relativ leicht festgestellt werden wie viele Zyklen die ISR benötigt und wie oft die ISR entsprechend aufrufen darf (z.B. als Timer Interrupt). Schon eine "leere" ISR benötigt in BASCOM gut 100 Zyklen. | ||
+ | |||
+ | === Variablen und Interrupts === | ||
+ | Wenn ein Interrupt eine Variable ändert, die aus mehr als einem Byte besteht, kann es passieren, daß dies genau in dem Moment geschieht, wenn die Variable auch in der Hauptschleife zu Berechnungen herangezogen wird. Das hat dann zur Folge das das Low-Byte der Variablen noch dem alten Wert entspricht, während das High-Byte gerade vom Interrupt geändert wurde. Dieser Fall MUSS bei solchen Variablen immer bedacht werden. Eine Möglichkeit dieses Problem zu umgehen besteht darin, daß man den Interrupt für die Dauer des Zugriffs disabled. | ||
+ | |||
+ | ==Autor== | ||
+ | * [[Benutzer:Roberto|Roberto]] | ||
+ | * [[Benutzer:PicNick|PicNick (Ergänzungen)]] | ||
==Siehe auch== | ==Siehe auch== | ||
Zeile 77: | Zeile 123: | ||
[[Kategorie:Microcontroller]] | [[Kategorie:Microcontroller]] | ||
[[Kategorie:Software]] | [[Kategorie:Software]] | ||
+ | [[Kategorie:Praxis]] | ||
[[Kategorie:Quellcode Bascom]] | [[Kategorie:Quellcode Bascom]] |
Aktuelle Version vom 1. Februar 2012, 21:38 Uhr
Inhaltsverzeichnis
Bascom Interrupts (Mega8 und andere)
Was ist das eigentlich und wie wird mit Interrupts unter Bascom gearbeitet? Die hier gezeigten Beispiele lassen sich eigentlich auf alle AVR-Boards bzw. Controller übertragen.
Allgemein
Interrupts werden immer dort verwendet, wo zeitkritische Anwendungen gefordert sind. Wenn man z.B. ein ganz kurzes Signal an einem Pin messen/detektieren will, kann man es mit dem normalem Hauptprogramm leicht übersehen. (z.B. durch eine Wait-Anweisung o.ä.)
Bei Verwendung eines Interrupts, reagiert der Prozessor, bei auftreten des Signals, unbedingt und verzweigt zu seiner ISR (Interrupt Service Routine), um dort darauf zu reagieren. (Anschliessend springt er wieder in das Hauptprogramm zurück)
Etwas genauer
Microcontroller heissen so, weil sich in ihnen eine Reihe von recht eigenständigen Geräten für Timer, PWM, ADC, etc. befinden. Diese Geräte werden irgendwann mal konfiguriert ("CONFIG device-name"), dann in irgendeiner Form gestartet (z.B. "START ADC"), ja, und dann werden sie natürlich irgendwann mal mit ihrer Aufgabe fertig. Das zeigen sie durch Setzen eines bestimmten Bits in einem I/O Register an.
- Wir kriegen das mit, wenn wir gelegentlich oder öfter diese "FERTIG"-Bits abfragen
- Oder wir erlauben dem Gerät, einen Interrupt auszulösen ("ENABLE device-name").
Ganz vorne im Programmspeicher ist eine Tabelle von sogenannten "ISR" (=Interrupt-Service-Routinen), jeder Eintrag davon ist einem bestimmten Gerät zugeordnet. (Die Tabelle ist keine Idee von den Bascom-Leuten, das haben die AVR-Hersteller so festgelegt, und das gilt daher auch für alle anderen Programmiersprachen). Der Ablauf ist nun folgender:
- Ein Gerät wird fertig und setzt sein "fertig" Bit
- Der Controller schaut, ob diese Gerät Interrupts auslösen darf
- Der Controller schaut, ob das "global interrupt enable"-Bit gesetzt ist (eine Art Hauptschalter)
Trifft das alles zusammen, wird zwar noch der gerade laufende Maschinenbefehl fertig gemacht, aber dann gibt es kein Halten mehr:
- Weitere Interrupts werden "disabled", damit dem Controller jetzt keiner in die Suppe spucken kann, denn ein paar Befehle lang steht der Controller jetzt "auf dem linken Bein".
- Die eigentlich nächste fällige Befehlsadresse wird auf den Stack gelegt ("PUSH")
- der Adresse der ISR aus der ob. Tabelle wird in den Befehlszähler geladen
- und ausgeführt.
Die weitere Kontrolle liegt nun vollständig in der Hand der ISR.
Tritt in dieser Zeit nun noch ein anderer Interrupt auf, merkt sich das der Controller zwar, tut aber weiter nichts, da ja die Interrupts jetzt global "disabled" sind
Erst, wenn die ISR den Maschinen-Befehl "RETI" durchführt,
- wird die ursprüngliche Befehlsadresse vom Stack wieder runtergeholt ("POP")
- in den Befehlszähler geladen
- Interrupts werden wieder global "enabled"
Und es geht wieder "normal" weiter, wenn nicht schon ein weiterer Interrupt lauert, denn dann wiederholt sich das alles.
Struktur:
Config InterruptXXX ‘Konfiguriere Interrupt Enable Interrupts ‘generell Interrupts zulassen Enable InterruptXXX ‘schalte speziell den InterruptXXX ein On InterruptXXX SprungXXX ‘verzweige bei InterruptXXX zu SprungXXX Do ....Hauptprogramm ‘Hauptprogramm Loop End SprungXXX: ‘Unterprogramm von InterruptXXX ....Ausführung ‘arbeitet hier etwas ab und springt mit Return Return ‚ wieder zurück, zum Hauptprogramm
Am Anfang muss man den beabsichtigten Interrupt mit „Config“, konfigurieren.
Dann das generelle Einschalten von Interrupts.
Dann mit „Enable“ den bestimmten Interrupt einschalten.
Mit „On“ das Unterprogramm (ISR) definieren, an die der Interrupt springen soll,
wenn dieser ausgelöst wird. (ISR= Interrupt Service Routine)
Grundsätzlich kann man das so betrachten, dass es das Hauptprogramm gibt (mit eventuellen Unterprogrammen) und die Interrupt Service Routinen (ISR).
Bei einem Interrupt, wird das Hauptprogramm unterbrochen und der Prozessor springt in die ISR. Die ISR wird abgearbeitet und mit Return wird wieder an die Stelle zurückgesprungen, wo vorher das Hauptprogramm unterbrochen wurde.
Man soll darauf achten, dass in der ISR immer nur kurze Anweisungen abgearbeitet werden und der Hauptteil dann im Hauptprogramm bearbeitet wird. Sonst kann es sein, dass man den Prozessor damit blockiert. Insbesondere Befehle wie "Wait" oder "Print" sollte man in der ISR vermeiden.
Für die ISR muß der Zustand des Hauptprogramms gespeichert werden. Dazu ist zusätzlicher Platz (bis 35 Bytes) auf dem HW-Stack nötig, der entsprechend reserviert werden muß.
Interrupt: Int0
Config Int0 = Low Level ‘Configuriere Int0 auf Low Level Enable Interrupts ‘einschalten der Interrupts Enable Int0 ‘einschalten von Interrupt Int0 On Int0 Isr_von_Int0 ‘springe zu Isr_von _Int0 Do ....Hauptprogramm Loop End Isr_von_Int0: ‘ISR von Int0 .....Programm-Code Return
Int0 ist ein Interrupt, der ausgelöst wird, wenn am INT0 (Pin.d2 beim Mega8), eine Veränderung auftritt. (Int1 bei Pin.d3)
Es gibt für diesen Interrupt drei Einstellmöglichkeiten auf die er reagieren kann:
- Low Level: reagiert solange der Pin auf Masse gesetzt wird.
- Falling: reagiert nur auf eine fallende Flanke
- Rising: reagiert nur auf eine steigende Flanke
Fehlervermeidung
Zuviel Code im Interrupt
Oft kommt es vor, daß zu viel Code in einem häufig auftretenden Interrupt ausgeführt wird. In diesem Fall wird der Interrupt sofort nach der Bearbeitung erneut aufgerufen. Für die Hauptschleife bleibt keine Zeit. Halten sie deshalb den Interrupt so kurz wie möglich. Im Zweifelsfall lässt sich Laufzeit für die ISR in der Simulation kontrollieren. So kann relativ leicht festgestellt werden wie viele Zyklen die ISR benötigt und wie oft die ISR entsprechend aufrufen darf (z.B. als Timer Interrupt). Schon eine "leere" ISR benötigt in BASCOM gut 100 Zyklen.
Variablen und Interrupts
Wenn ein Interrupt eine Variable ändert, die aus mehr als einem Byte besteht, kann es passieren, daß dies genau in dem Moment geschieht, wenn die Variable auch in der Hauptschleife zu Berechnungen herangezogen wird. Das hat dann zur Folge das das Low-Byte der Variablen noch dem alten Wert entspricht, während das High-Byte gerade vom Interrupt geändert wurde. Dieser Fall MUSS bei solchen Variablen immer bedacht werden. Eine Möglichkeit dieses Problem zu umgehen besteht darin, daß man den Interrupt für die Dauer des Zugriffs disabled.