Interrupts gehören zu den wichtigsten Instrumenten, die in einem Mikrocontroller zur Verfügung stehen, um auf Ereignisse zu reagiern ohne explizit in einer Warteschleife auf das Ereignis warten zu müssen. Die Quellen für Interrupts sind in der Architektur des Prozessors festgelegt und können zum Teil selektiv an- oder abgeschaltet werden. Wenn der Mikrocontroller einen Interrupt erkennt, wird der aktuelle sequentielle Programmfluss unterbrochen und ein definiertes Unterprogramm, die Interrupt-Service-Routine, ausgeführt. Insgesamt verfügt der MSP430 über 16 verschiedene Interruptquellen.
Generell kann man drei grundsätzliche Typen von Interrupts unterscheiden:
Diese unterscheiden sich neben ihrer physischen Verdrahtung in der Prozessorarchitektur im Wesentlichen durch die Priorität mit der sie Zugriff auf die Ressource CPU erhalten. Mit höchster Priorität ist der Hardware-System-Reset ausgestattet, der an einem externen Pin aber auch durch die Überwachung der Betriebsspannung ausgelöst wird. Hier lässt sich kein spezifisches Verhalten definieren. Es ist im Design genau festgelegt, welche Schritte durchlaufen werden und wie das Programm im Prozessor neu gestartet wird. Als Nächstes in der Hierarchie sind die so genannten NMI-Interrupts. Diese werden im Gegensatz zu den ”normalen” Interrupts nicht durch das globale ”Interrupt-enable-Bit (GIE)” gesteuert, sondern haben ein eigenes Steuerbit und eine eigene Leitung zum Interrupt-Prioritätsdekoder. Beispiele hierfür sind der ”Flash-Violation-” Interrupt (ausgelöst bei Schreibfehlern auf dem Flash) oder der Oscillator-Fault-Interrupt. Die letzte Kategorie umfasst alle übrigen Interruptquellen, wobei die Interrupts sowohl durch ein Peripheriemodul ausgelöst werden können als auch durch einen Software-Befehl.
Während der Prozessor beim System-Reset quasi ”über Los” geht und neu die Programmabarbeitung beginnt, kehren die übrigen Interrupts nach der Bearbeitung zu dem Punkt im Programm zurück, an dem der Interrupt den normalen Programmablauf unterbrochen hat. Natürlich muss man aufpassen, dass in der Interrupt-Routine nicht Daten manipuliert werden, auf die gleichzeitig im Programmablauf zugegriffen wird. Solche Fehler sind oftmals extrem schwierig zu finden. Das Buch von Simon [Sim99] gibt einige wertvolle Tipps, wie man genau solche Probleme vermeiden und lösen kann.
Als exemplarisches und einfaches Beispiel beschäftigen wir uns in diesem Unterkapitel zunächst mit der Aktivierung eines Interrupts für einen Digitalport. Die anderen Interrupts werden in den folgenden Kapiteln mit den zugehörigen Peripheriemodulen eingeführt.
Ein Digitalport, der als Interrupteingang arbeitet, ist zunächst einmal ein ganz normaler digitaler Eingang. Softwaretechnisch muss man den Status des Digitalports jedoch nicht durch eine Endlosschleife, wie sie in Beispiel 2.4 gezeigt wurde, überwachen (im Fachjargon “Polling” genannt). Wenn man einen Eingang als Interrupteingang definiert, kann man sich die Abfrage sparen. Stattdessen muss das Interrupt-Enable-Register (Interruptfreigabe) sowie das Interrupt-Edge-Select-Register (Interrupttriggerflanke) des Byteports entsprechend gesetzt werden.
Die Interrupteingänge des MSP430 sind flankengetriggert. Das heißt, nicht der logische Pegel eines Eingangs löst einen Interrupt aus, sondern der Signalflankenwechsel. Als erkennbare Signalflankenwechsel kommen zwei Möglichkeiten infrage. Mit dem Interrupt-Edge-Select- Register kann man festlegen, ob ein Interrupt bei einer fallenden oder steigenden Signalflanke für den jeweiligen Digitalport ausgelöst werden soll. Um eine steigende Flanke auszuwählen, muss an die der Digitalportnummer entsprechenden Bitstelle des Interrupt-Edge-Select-Registers eine logische 0 und bei einer fallenden Flanke eine 1 gesetzt werden.
Die Interruptvektortabelle (IVT) ist eine Tabelle im Speicher, in der jeder Interruptquelle (z.B. den digitalen Eingängen, aber auch dem A/D-Wandler) eine Speicherstelle zugewiesen ist. In dieser Speicherstelle steht dann die Adresse der Interrupt-Routine (Interrupt-Service-Routine, ISR), das heißt die Adresse derjenigen Funktion, die aufgerufen werden soll, wenn der Interrupt ausgelöst wurde. Die verschiedenen C-Compiler kennen unterschiedliche Methoden Interrupt-Funktionen zu registrieren und an die richtige Stelle der Interruptvektortabelle zu schreiben. Da dies nicht im ANSI-C-Standard beschrieben ist, muss man hier im Einzelfall herausfinden, wie der eigene Compiler dies gelöst hat. Für die Code Composer Essentials 4 aber auch für den IAR-Compiler erfolgt der Eintrag in die Interruptvektortabelle gemäß Listing 2.5. Die Namen der Vektoren (z.B.PORT1_VECTOR) entsprechen der Adresse in der IVT und sind wieder einmal in der prozessorspezifischen Headerdatei (z.B. msp430x16x.h) definiert.
Listing 2.5: | Definition der Interrupt-Routine für Port 1 |
Nachdem wir nun kennengelernt haben, wie wir Interrupts verwenden können, soll die Benutzung in einem kleinen Beispiel anschaulich gemacht werden. An Byteport 1, Pin 7 (PIN1.7) löst ein angeschlossener Schalter einen Interrupt aus, wenn er geschlossen wird. Die Beschaltung sei so wie in Bild 2.3.
Für einen Byteport gibt es immer nur eine gemeinsame Interruptfunktion, jeder Pin des Byteports kann diese auslösen. Um nun zu erkennen, welcher Pin den Interrupt ausgelöst hat, kann man das Interrupt-Flag-Register des entsprechenden Ports (zum Beispiel P1IFG-Byteport P1) auslesen. Mit einer logischen Maskierung in einer IF-Abfrage kann nun das gewünschte Bit selektiert werden.
Listing 2.6: | Interrupt-Routine für einen Schalter an P1.7 |
Das Hauptprogramm ist eine Funktion in C/C++, die vom Mikrocontroller beim Start ausgeführt wird. Hier muss der Interrupteingang initialisiert werden, bevor der erste Interrupt abgehandelt werden kann. Prinzipiell ist eine Initialisierung (auch eine erneute Initialisierung und Modifizierung der aktuellen Einstellungen eines Interrupteingangs) in jedem Abschnitt des Programms möglich.
Listing 2.7: | Interrupt-Initialisierung |
Wie man sieht, passiert nach der Initialisierung nichts mehr, und das Programm wartet in einer Endlosschleife darauf, dass der Interrupt endlich ausgelöst wird.