Pin Change Interrupt

(Hier geht's zum Pin Change Interrupt beim Attiny)

Standardmäßig stellt Arduino mit der Funktion "attachInterrupt()" beim Uno nur 2 Interrupt-Pins (Pin D2 und D3) zu Verfügung. Durch entsprechende Registerprogrammierung kann aber bei jedem Ein-/Ausgangspin ein "Pin Change Interrupt" ausgelöst werden.

Wie der Name "Pin Change Interrupt" bereits sagt, wird bei jeder Zustandsänderung eines Eingangssignals, also sowohl bei steigender als auch bei fallender Flanke ein Interrupt ausgelöst. Die Auswertung, welche Flanke den Interrupt ausgelöst hat, bzw. falls mehr als ein Pin Change Interrupt pro Port programmiert wurde, welcher Pin einen Interrupt ausgelöst hat, muss in der "Interrupt-Serviceroutine" erfolgen.


Verwendete Register:

  • Pin Change Interrupt Control Register: PCICR
  • Pin Change Mask Register 0/1/2: PCMSK0/1/2
  • Status Register: SREG


Vorgangsweise:

  1. Alle Interrupts sperren
  2. Portfreischaltung
  3. Pin(s) für Pin Change Interrupt freigeben
  4. Alle Interrupts freigeben
  5. Interrupt-Serviceroutine erstellen


1. Alle Interrupts sperren

  • Durch Löschen des Global Interrupt Enable Bits (I) im Status Register (SREG) werden alle Interrupts gesperrt. Während der Manipulation der Interrupt-Register soll kein Interrupt ausgelöst werden können.


  • 2. Portfreischaltung

    Im Pin Change Interrupt Control Register (PCICR) wird festgelegt, dass die Pins eines bestimmten Ports (Arduino Uno hat 3 Ports: B, C und D) für Pin Change Interrupts zugelassen sind.

    In Abhängigkeit des gewünschten Pins, bei dem ein Pin Change Interrupt ausgelöst werden soll, ist das entsprechende Pin Change Interrupt Enable Bit (PCIEx) zu setzen. Die Zuordnung des Pins zum PCIEx-Bit ist in nachfolgender Tabelle ersichtlich:


    3. Pin(s) für Pin Change Interrupt freigeben

    Durch Setzen des Pin Change Enable Mask Bit (PCINTx) im entsprechenden Pin Change Enable Mask Register (PCMSKx) wird die Auslösung eines Pin Change Interrupt ermöglicht. Nachfolgende Aufstellung zeigt die Zuordnung der Pin Change Enable Mask Bits (PCINTx) zu den Pins D0 bis D7, D8 bis D13 und A0 bis A5.


    PCMSK0 - Pin Change Mask Register 0:

    Hier werden Pin Change Interrupts für die Pins D8 bis D13 ermöglicht.

    PCMSK1 - Pin Change Mask Register 1:

    Hier werden Pin Change Interrupts für die Pins A0 bis A5 ermöglicht.

    PCMSK2 - Pin Change Mask Register 2:

    Hier werden Pin Change Interrupts für die Pins D0 bis D7 ermöglicht.


    4. Alle Interrupts freigeben

    Durch Setzen des Global Interrupt Enable Bits (I) im Status Register (SREG) werden alle Interrupts freigegeben.


    5. Erstellen der Interrupt-Serviceroutine

    Wird ein Pin Change Interrupt ausgelöst, verzweigt das Programm in eine zu erstellende Serviceroutine ISR(PCINTx_vect). Wurde der Interrupt z.B. durch den Pin Change Mask Register 0 (PCMSK0) freigegeben, so verzweigt das Programm in die Serviceroutine "ISR(PCINT0_vect)". In nachfolgender Tabelle ist die Zuordnung der Interrupt-Serviceroutine zum Pin Change Mask Register ersichtlich:

    Die Namen der Interrupt-Serviceroutinen sind vom System fix vorgegeben und dürfen nicht verändert werden!

    In der aufgerufenen Serviceroutine können nun z.B. folgende Maßnahmen gesetzt werden:

    • Überprüfen, ob der Interrupt nicht wiederholt durch "Prellen" ausgelöst worden ist (bounce-time).
    • Feststellen, welcher Pin einen Interrupt ausgelöst hat, für den Fall, dass mehr als ein Pin für Pin Change Interrupt freigegeben wurde.
    • Extrahieren des interruptauslösenden Bits aus dem Port zur Ermittlung, ob der Interrupt durch eine steigende oder fallende Flanke ausgelöst wurde.
    • Setzen von Merker in Abhängigkeit der Interruptauslösung durch steigende oder fallende Flanke, auf die dann im Hauptprogramm entsprechend reagiert werden kann.


    Die einzelnen Schritte im Programm zusammengefasst:

    Als Beispiel soll der Pin Change Interrupt am Digital-Pin D2 ausgelöst werden:


    void setup()

    {

      //Löschen des Global Interrupt Enable Bits (I) im Status Register (SREG)

      cli();


      //Setzen des PCIE2-Bit im Pin Change Interrupt Control Register (PCICR)

      PCICR |= (1 << PCIE2);


      //Setzen des Pin Change Enable Mask Bit 18 (PCINT18)  ==> Digital-Pin 2
      PCMSK2 |= (1 << PCINT18);


      //Setzen des Global Interrupt Enable Bits (I) im Status Register (SREG)
      SREG |= 0x80; // gleichwertig mit "sei();"

    }


    //Aufruf der Interrupt Serviceroutine

    ISR(PCINT2_vect)

    {

      //Programmcode der Service-Routine

    }


    Beispiel:

    Ein Anwendungsbeispiel ist in meinem Projekt "DCF77-Empfänger zur Uhrzeitsynchronisation" im Abschnitt Sync.-Programm ersichtlich.