Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Interrupts

Interrupts unterscheiden sich in vielerlei Hinsicht von Exceptions; ihre Funktionsweise und Verwendung sind jedoch weitgehend ähnlich, und sie werden zudem von demselben Interrupt-Controller verwaltet. Während Exceptions durch die Cortex-M-Architektur definiert sind, handelt es sich bei Interrupts – sowohl hinsichtlich der Benennung als auch der Funktionalität – stets um herstellerspezifische (und oft sogar chip-spezifische) Implementierungen.

Interrupts bieten ein hohes Maß an Flexibilität, das bei einer fortgeschrittenen Nutzung berücksichtigt werden muss. Wir werden diese Anwendungsfälle in diesem Buch zwar nicht behandeln, dennoch ist es ratsam, Folgendes zu beachten:

  • Interrupts verfügen über programmierbare Prioritäten, die die Ausführungsreihenfolge ihrer Behandlungsroutinen bestimmen.
  • Interrupts können geschachtelt werden und einander unterbrechen (Preemption); das heißt, die Ausführung eines Interrupt-Handlers kann durch einen anderen Interrupt mit höherer Priorität unterbrochen werden.
  • Im Allgemeinen muss die Ursache für die Auslösung des Interrupts beseitigt werden, um zu verhindern, dass der Interrupt-Handler endlos erneut aufgerufen wird.

Die allgemeinen Initialisierungsschritte zur Laufzeit sind immer gleich:

  • Konfigurieren Sie die Peripheriekomponente(n) so, dass Interrupt-Anforderungen zu den gewünschten Zeitpunkten ausgelöst werden.
  • Legen Sie die gewünschte Priorität des Interrupt-Handlers im Interrupt-Controller fest.
  • Aktivieren Sie den Interrupt-Handler im Interrupt-Controller.

Ähnlich wie bei Exceptions stellt das cortex-m-rt-Crate ein interrupt-Attribut zur Deklaration von Interrupt-Handlern bereit. Dieses Attribut steht jedoch nur zur Verfügung, wenn das Device-Feature aktiviert ist. Es ist allerdings nicht für die direkte Verwendung vorgesehen; eine solche würde zu einem Kompilierfehler führen.

Stattdessen sollten Sie die vom Device-Crate (das üblicherweise mit svd2rust generiert wird) bereitgestellte, re-exportierte Version des interrupt-Attributs verwenden. Dies stellt sicher, dass der Compiler überprüfen kann, ob der Interrupt auf dem Zielgerät tatsächlich existiert. Die Liste der verfügbaren Interrupts – sowie deren Position in der Interrupt-Vektortabelle – wird typischerweise von svd2rust automatisch aus einer SVD-Datei generiert.

rust
use lm3s6965::interrupt; // Aus dem Device-Crate zurueck-exportiertes Attribut

// Interrupt-Handler fuer den Timer2-Interrupt
#[interrupt]
fn TIMER2A() {
    // ..
    // Eindeutiger Grund fuer die generierte Interrupt-Anforderung
}

Interrupt-Handler ähneln – abgesehen vom Fehlen von Argumenten – gewöhnlichen Funktionen, ganz wie Exception-Handler. Aufgrund spezieller Aufrufkonventionen können sie jedoch nicht direkt von anderen Teilen der Firmware aufgerufen werden. Es ist allerdings möglich, Interrupt-Anforderungen per Software zu erzeugen, um einen Sprung zum Interrupt-Handler auszulösen.

Ähnlich wie bei Exception-Handlern ist es auch hier möglich, static mut-Variablen innerhalb der Interrupt-Handler zu deklarieren, um den Zustand auf sichere Weise zu speichern.

#[interrupt]
fn TIMER2A() {
    static mut COUNT: u32 = 0;

    // `COUNT` hat den Typ `&mut u32` und ist sicher zu verwenden.
    *COUNT += 1;
}

Für eine detailliertere Beschreibung der hier veranschaulichten Mechanismen konsultieren Sie bitte den Abschnitt zu Ausnahmen.