Kampis Elektroecke

XMega – Interrupt

XMega Blockdiagramm

Die XMega Familie ist von Atmel mit einem umfangreichen Interruptsystem ausgestattet worden, welches es dem Benutzer erlaubt viel feiner auf Interrupts und Ereignisse der Mikrocontrollerperipherie zu reagieren, indem z. B. einzelne Interruptquellen durch Prioritäten in verschiedene Kategorien eingeordnet werden können. 

Das Interruptsystem:

Bevor Interrupts verwendet werden können, müssen diese aktiviert werden, indem das Interrupt Enable-Bit gesetzt wird. Dies geschieht auf identische Weise, wie bei allen anderen AVRs.

Nun müssen nur noch die Interruptlevel im Interruptcontroller 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. Nachfolgend ein Auszug aus dem XMegaA Manual, welcher die dementsprechenden Bitmuster zeigt.


Hinweis:

Die Bitmuster werden für jeden Interrupt im Chip verwendet!


Dazu ein kleines Beispiel:

Ich möchte der Interruptquelle TimerC0 die höchste Priorität zuweisen. Dafür schreibe ich in das INTCTRLA-Register des Timers das Bitmuster 0x03. Das geschieht so:

Nun hat der TimerC0 die höchste Interruptpriorität und wird einer Interruptquelle mit einer niedrigeren 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

Interrupts an einem I/O:

Bevor die Interrupts an den I/O benutzt werden können, müssen auch hier die globalen Interrupts, bzw. der entsprechende Interruptlevel aktiviert werden. 

Für jeden Pin können für bis zu zwei Interrupts deklariert werden. Soll der z.B. der Pin 1 von Port A dem Interrupt0 zugeordnet werden schreibt man folgendes

Und für Interrupt1

Nun muss noch der Interrupt für Pin 1 vom Port A 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 Port A 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!):

7 Kommentare

  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

    1. 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.

      1. 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

        1. 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?

    1. 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.