Interrupts: ein Erklärungsversuch für Dummies ;-)

In der Mikrocontroller Programmmierung ist die Nutzung von Interrupts eine sehr effektive Art um auf nicht regelmässig auftretende „Events“ (Ereignisse) zu reagieren. Ein Interrupt (=Unterbrechnung) unterbricht den Prozessor in seiner aktuellen Aufgabe und meldet mittels eines Flags in einem bestimmten Register des Mikrocontrollers das ein Vorfall eingetreten ist, über den man informiert werden wollte.

Welche Ereignisse einen Interrupt auslösen, können wir beim programmieren selbst definieren. Natürlich können Interrupts auch ganz ausgeschaltet werden, wenn wir diese nicht nutzen wollen.

Hier ein Beispiel für einen recht einfachen Interrupt (ob sinnvoll oder nicht, darf jeder für sich entscheiden):

Nehmen wir an wir möchten das unser Programm eine bestimmte Aktion ausführt wenn wir einen Druck-Taster betätigen der an einem Eingang unsere Mikrocontrollers angeschlossen ist. Wir ignorieren jetzt hier der Einfachheit halber mal den Umstand das ein Taster auch entprellt werden muss und gehen von einem per externer Schaltung enprellten Taster aus. Nun haben wir zwei Möglichkeiten abzufragen ob der Taster gerade gedrückt ist oder nicht:

  1. Wir prüfen im Hauptprogramm kontinuierlich den Status des PINs (den wir natürlich vorher als digitalen Eingang konfiguriert haben) ob dort ein bestimtes Signale anliegt (meistens logischer „1“ Wert, also z.B. ob die Leitung gegen die positive VErsorgungsspannung gezogen wurde). Im einfachsten Fall könnte man eine Endlosschleife in der main Methode nutzen, die immer abfragt ob der Wert 1 ist, dann kann der Mikrocotroller aber nichts anderes machen als diesen Wert zu prüfen, das wäre etwas dämlich, dafür kann er dann doch etwas mehr. Natürlich muss man nicht permanent den Eingang prüfen, da wir als menschen im Vergleich zu einem Mikrocontroller uns ja eher im Schneckentempo bewegen. Wenn ein Mikrocontroller also mit sagen wir 4Mhz getaktet (also das Quarz liefert 4 Mio. Takte pro Sekunde) ist und, wie im Falle eines PIC18F, vier Takte pro Befehlt benötigt, dann braucht er für die Abfrage eines Satus an einem PIN 4 Takte (entspricht 1/4.000.000/4 Sekunden = 0,000.001 Sekunden), um diesen Wert jedoch in einer IF-Abfrage oder in einer While Schleife zu prüfen ob er z.b. 1 ist benötigt man weitere 4 Takte. Pro Abfrage des PIN Status vergehen also 0,000.002 Sekunden oder mit anderen Worten wir können den Status pro Sekunde  500.000 mal abfragen. Das ist natürlich ein wenig zu viel des Guten, den wenn ein Mensch einen Taster drückt, dann wird das mit sicherheit länger dauert als 1/500.000 Sekunde. Gehen wir mal davon aus jemand tippt den Taster nur ganz kurz an, dann wird der Taster sicherlich dennoch für 1/20 oder 1/10 Sekunde (ja es gibt bestimmt Leser die meinen Sie könten das schneller, aber das ist ja hier nur ein Beispiel) gedrückt sein. Es würde also reichen 10 mal pro Sekunde abfragen ob der Taste gedrückt ist, gehen wir auf nummer sicher und fragen 20 mal pro Sekunde ab. Das heisst unser Prozessor muss statt 500.000 mal pro Sekunde nur 20 mal pro Sekunde (also alle 50 milliksekunden) prüfen und hat die restliche Zeit frei für andere Aufgaben. Nun muss man jedoch aufpassen das der Prozessor wirklich alle 50 ms prüft da wir sonst Gefahr laufen würden eine Tasterauslösung zu übersehen (wenn der Prozesseor nämlch mehr als 50 ms in einem anderen Programmteil verbringt, prüft er ggf. im entscheidenen Zeitfenster von 50 ms nicht den Zustand des Tasters und bekommt daher nicht mit das dieser gedrückt wurde). Sicherlich kein unlösbares Problem, man muss nur einfach während der gesamten Entwicklung des Hauptprogramms berücksichtigen das dieser Fall nicht eintreten darf. Aber einfache ist doch definitiv durch Benutzung eines Interrupts.
  2. Wir konfigurieren also den PIN am Mikcrocontroller als einen digitalen Eingang und sagen dem Mikcrocontroller das er einen Interrupt auslösen soll sobald eine steigende Flanke an eben diesem PIN erkannt wird. Nun wird uns der Mikrocontroller zu jedem Zeitpunkt immer genau dann informieren wenn der Taster gedrückt wird (wir gehen wie oben erwähnt davon aus das der Taster entprellt ist und keine mehrfachauslösung in einem kurzen Zeitfenster erfolgt). Der Prozessort lässt dann unverzüglich seine aktuelle Aufgabe stehen und liegen und springt direkt zu der definiertern Interrupt-Handler-Routine (Funktion) die dann eben die Aktion ausführt die geschehen soll wenn der Knopf gedrückt wurde, sofern das nur wenige Anweisungen sind oder es sich um eine zeitkritische Aktion handelt die ausgelöst werden soll, kann das direkt in der Routine erfolgen. Idealerweis setzt man jedoch in der Routine nur einen Wert einer Variable den das Hauptproramm dann irgendwann (wir sprechen hier von ehr kurzen Zeiträumen selbst 1 Sekunde für einen entsprechend getakteten Mikrocontroller ja eine Ewigkeit ist) auswertet und entsprechend reagiert. Danach muss nur noch der Interrupt-Flag gelöscht werden und zum normalen Hauptprogramm zurück zu kehren.

 

Wann ist der Einsatz von Interrupts sinnvoll?

Welche Ereignisse können typischerweise einen Interrupt auslösen bei einem Mikrocontroller?

Als auslöser für einen Interrupt kommen diverse Komponoenten in Frage, das ist immer abhängig vom Mikrocontroller und dessen hardware.

Bei den meisten PIC Microcontrollern kann (sofern konfiguriert) ein Interrupt z.B. durch folgende Ereignisse ausgelöst werden:

  • Überlauf eines Timers (also wenn einer der Hardware Zähler im Mikrocontroller sein Maximum erreicht und wieder auf Null springt)
  • Eingang einer Neuen Nachricht über eine der Kommunikations Schnittstellen (z.B. I2C, SPI, CAN oder USART)
  • Eine Flanke (= ein Wechsel von High auf Low Pegel, oder umgekehrt) an einem der PINs die einen Interrupt auslösen können (meist konfigurierbar ob steigende oder fallende Flank als Auslöser dienen soll)
  • eintreffen eines Compare Events (wenn ein Timer einen bestimmten Wert hat)

Ein Interrupt wird immer vorranging behandelt

Wenn die Nutzung von Interrupts konfiguriert ist in der Software des Mikcrcontrollers und eine Komponente löst einen Interrupt aus, so hat dieser immer Vorrang vor dem normalen Programmablauf. Der Prozessort springt dann immer an die definierte Zeile im Programm in dem der Interrupt-Handler definiert ist, also eine spezielle Methode im Programm die explizit erstellt wurde um auf die Interrupts reagieren soll. Wenn diese Interrupt-Routine (Methode) ihre arbeit abgeschlossen hat, dann kehrt der Prozessort wieder zum normalen Programmablauf zurück an der Stelle an der er vorher die Verarbeitung unterbrochen hat.

Wenn man mit Interrupts arbeitet, sollte man sich also immer bewusst sein, dass das Hauptprogramm jederzeit in seiner Abarbeitung unterbrochen (pasuiert) werden könnte. Würde man also im Hauptprogramm mit zeitkritischen Aufgaben arbeiten, so besteht die Möglichkeit das durch einen Interrupt der Programmablauf empfindlich gestört wird und ggf. dann fehl Schlägt.

Aus diesem und anderen Gründen sollte man versuchen die abarbeitung von Interrupt-Routinen immer so kurz wie möglich zu halten. Das heisst es sollten so wenige Prozessortakte wie möglich verwendet werden zur Abarbeitung aller Aufgaben in der Routine.

Der Aufruf von anderen Methoden (wohlmöglich aus irgendwelchen Bibliotheken) innerhalb der Interrupt Routine sollte wenn möglich auch vermieden werden, da man hier zum einen Gefahr läuft das nicht abschätzen kann wie lange diese Methoden benötigen und ob diese ggf. wieder andere Methoden aufrufen. Noch kritischer ist es jedoch wenn man in der defintion der Interrupt Routine dem Compiler nicht explizit sagt welche Variablen, Speicherbereich und Werte benötigt werden beim aufruf einer anderen Methode, dann geht der Comipler vom schlimmsten Fall aus und muss erst alle Variablen und Speicherbereiche die das normale Programm nutzt auch im Context der Interrupt Routine bekannt machen, das führt dann dazu das sehr viel Zeit vergeht bis überhaupt einmal die erste Zeile Code der Interrupt Routine ausgeführt wird. Wenn man jedoch innerhalb der Interrupt Routine keine anderen Methoden aufruft, dann weisse der Compiler genau welche Variablen benötigt werden und kann den Kontext sehr schnell bereitstelen für die Interrupt Routine.

Weitere Infos zum effektiven Aufbau einer Interrupt-Routine und was beachtet werden muss wenn man andere Funktionen in dieser aufruft finden sich hier: Grundlagen effekiver Interrupt-Routinen

Verschiedene Prioritäten bei Interrupts

TBD

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.