Interrupt & Eventsystem

XMega Blockdiagramm

Auch am Interruptsystem hat sich einiges geändert. Es ist etwas komplexer geworden und bietet viele verschiedene Möglichkeiten.
Zum einen bietet es die Möglichkeit die Interruptlevel jeder Interruptquelle zu Priorisieren d.h. einem Interrupt kann die Priorität High, Medium oder Low zugeordnet werden. Die Interrupts werden dann auch dementsprechend abgearbeitet.

-> Das Interruptsystem:

Bevor die Interrupts verwendet werden können, müssen diese erst aktiviert werden. Dies wird gemacht indem das Interrupt Enable-Bit auf High gesetzt wird. Wie bei allen anderen Atmel Chips wird das auch hier mit dem Befehl

gemacht. Dadurch wird quasi der Hauptschalter für die Interrupts umgelegt. Damit dieser Befehl genutzt werden kann, muss die Interrupt.h ganz am Anfang des Programmes eingefügt werden:

Nun müssen nur noch die Interruptlevel aktiviert werden. Dies geschieht so:

Jetzt sind die Interruptlevel High, Medium und Low freigegeben und können verwendet werden. Sollen nur der Interruptlevel Low freigegeben werden, schreibt man folgendes:

Auf eine ähnliche Weise können dann natürlich auch alle Low-Level Interruptquellen deaktiviert werden. Es muss nur das LOLVLEN Bit im Low Level Interrupt Enable-Register gelöscht werden.
Die Zuweisung, welche Priorität welcher Interrupt hat, wird dann bei der Konfiguration der Peripherie gemacht.
Hier ein kleines Bildchen aus dem XMega A Manual wie die dementsprechenden Bitmuster aussehen (die Bitmuster können für jede Interruptquelle verwendet werden!):

XMega_Int(1)
Ein kleines Beispiel:
Ich möchte der Interruptquelle TimerC0 die höchste Priorität zuweisen. Dafür schreibe ich in das INTCTRLA das Bitmuster 0x03 hinein.
Das geschieht so:

Nun hat der TimerC0 die höchste Interruptpriorität und wird einer Interruptquelle mit einer anderen priorität vorgezogen.
Das selbe kann natürlich auch für einen I/O Port gemacht werden:

Nun hat der PortX die Interruptpriorität Low.
Des Weiteren erlaubt der XMega nocht die Interruptvektoren zu „verschieben“.
Wird das VSEL-Bit im PMIC Control-Register gelöscht, werden die Interruptvektoren an vor die Startposition des Programms, welches sich im Flash befindet, gesetzt. Wird das Bit allerdings gesetzt, verschieben sich die Interruptvektoren vor die Startadresse des Bootsektors. Das heißt es ist möglich mit einem Interrupt vor den Bootsektor zu springen und das Programm, welches sich im Bootsektor befindet, neu auszuführen. Dadurch kann z.B. der Bootloader im laufenden Betrieb geladen werden und ein Programmupdate durchgeführt werden, ohne das der Controller mittels eines Impulses am Reset-Pin resetet werden muss (so wie es bei anderen Atmel Chips der Fall ist).
Ist das Bit aber gelöscht, verhält sich der Controller ganz „normal“ und führt den Code im Bootsektor erst aus wenn er resetet wird und dadurch sowieso zur ersten Adresse im Flash springt.
Für genauere Infos empfiehlt es sich das Datenblatt zu studieren!

 -> Interrupts an einem I/O:

Bevor die Interrupts an den I/O benutzt werden können, müssen die globalen Interrupts aktiviert werden. Dies geschieht mit diesem Befehl:

Jeder Pin kann für bis zu zwei Interrupts deklariert werden. Soll der z.B. der Pin 1 von PortA dem Interrupt0 zugeordnet werden schreibt man folgendes

und für Interrupt1

Natürlich können auch beide gleichzeitig zugeordnet werden. Nun muss noch der Interrupt für Pin 1 vom PortA aktiviert werden. Vorher muss der Pin aber noch als Eingang eingestellt werden. Dies geschieht so:

Anschließend müssen noch der Pull-Up Widerstand für den Pin aktiviert werden:

Nun kann der Interrupttyp festgelegt werden. In meinem Beispiel verwende ich einen Low-Level Interrupt. Dies geschieht dann so:

Der Interrupt soll außerdem bei einer fallenden Flanke ausgelöst werden.
Dies wird so eingestellt:

Jetzt ist der Interrupt an Pin 1 vom PortA aktiviert, auf Int0 oder Int1 und als ein Low-Level Interrupt eingestellt.
Außerdem wird er bei einer fallenden Flanke ausgelöst.
Die ISR wird dann wie folgt aufgerufen (wichtig ist das der richtige Interrupt für INTx verwendet wird!):

 

-> Zurück zum XMega Tutorial

7 thoughts on “Interrupt & Eventsystem
  1. Guten Abend

    Ich habe ein Verständnisproblem bei diesem Tutorial. Und zwar bei folgendem Schritt:

    „PORTA.INTCTRL |= PORT_INT0LVL_LO_gc;
    Jetzt ist der Interrupt an Pin 1 vom PortA aktiviert, auf Int0 oder Int1 eingestellt und reagiert auf Low-Level Pegel.“

    Von mir aus gesehen befindet sich im INTCTRL-Register nur der Interruptlevel (die Priorität -> high, medium, low) und nicht der Pegel / die Flanke auf den der Interrupt reagiert.
    Der Pegel wird dann im PINnCTRL-Register eingestellt mit den drei ISC-Bits.

    Ausserdem bin ich mir nicht sicher, welche Einstellung ich bei den OPC-Bits vornehmen soll für einen Eingang. Hast du hier einen Vorschlag?

    Vielen Dank und Gruss
    Philip

    • Hallo Philip,

      danke für den Kommentar.
      Das ist tatsächlich doof ausgedrückt.
      Paar Zeilen weiter oben schreibe ich noch das es sich um einen Low Level Interrupt handelt und da spreche ich von Low Level Pegel…..
      Da hast du vollkommen recht. Da war ich wohl nicht ganz bei Verstand ;).
      Wird korrigiert!
      Was möchtest du den mit dem Eingang machen? Du könntest es mal mit 010 oder 011, also Pulldown oder Pullup, testen.

      • hi
        can you provide me with any simple book for understanding atxmegaD .and as many as possible the C coding examples to use atxmegaD microcontroller. i have to use it in my project and i could not find much stuff and as i am new to microcontrollers so it is a little difficult for me to understand.
        regards

        • Hey,

          no sorry.
          But you can use the same Code like the XMega128A1.
          Take a look into the Datasheet for more Informations about the register. I think they are a little bit different than the register of the XMegaA.

          Greetings
          Daniel

  2. PORTA.INTCTRL |= PORT_INT0LVL_LO_gc;
    Also wenn der Interruptlevel zuvor High war, dann funktioniert dies wohl nicht?
    Vermutlich muesste es aber so gehen:
    PORTA.INTCTRL = (PORTA.INTCTRL & ~PORT_INT0LVL_gm) | PORT_INT0LVL_LO_gc;
    Eigentlich versuche ich gerade grundsaetzlich zu verstehen wie die Interruptlevels funktionieren. Da habe ich ein Beispiel fuer den Timer gefunden das so aussieht:
    TCC1.INTCTRLA = (TCC1.INTCTRLA & ~(TC1_OVFIF_bm)) | TC_OVFINTLVL_HI_gc;
    Wenn ich allerdings den Wert von TC1_OVFIF_bm nachschaue, dann ist dieser falsch.
    Naemlich 1 statt 3. Ueberhaupt sind anscheinend alle Masken fuer die Interruptlevels falsch. Immer nur 1 Bit gesetzt statt 2 Bits. Sehe ich das richtig oder mache ich einen Ueberlegungsfehler?

    • Hey,

      nein falsch sind die Masken nicht.
      Ich setze halt nur immer ein Bit auf High und das andere steht sowieso auf Low.
      Klar es berücksichtigt so nicht den aktuellen Zustand des Registers, aber da ich diese Routine nach dem Reset des Mikrocontrollers aufrufe haben diese Bits eh den Zustand „Low“ und von daher kann ich recht sicher sein das dort noch nichts drin steht (es funktioniert auch :) jeden Code den ich hier poste habe ich vorher getestet. Wenn etwas nicht klappt und es am Code liegt muss mir ein Fehler beim kopieren passiert sein).
      Aber ganz unrecht hast du nicht. Die sauberste Variante ist es natürlich beide Bits zu setzen statt nur eines.
      Die Interruptlevel geben jedem Interrupt eigentlich nur noch ein zusätzliches Ranking, indem du sie priorisierst.
      Dadurch arbeitet der Controller immer erst die Highlevel Interrupts ab und dann die Lowlevel.
      Ich hoffe ich konnte deine Frage damit beantworten :)
      Falls nicht sag ruhig bescheid :)

      Gruß
      Daniel

  3. Hallo Kampi,

    schöne und interessante Webseite hast du! Ich wollte nur kurz anmerken, dass hier von Events weniger steht. Steht nämlich noch in der Überschrift. :-)

Schreibe einen Kommentar

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

Time limit is exhausted. Please reload CAPTCHA.