Bestimmen, welcher Pin einen PCINTn-Interrupt ausgelöst hat?

9

Habe ich Recht , wenn Sie zwei Pins haben, die denselben AVR-PCINT-Interrupt verursachen (z. B. PCINT0- Vektor , der entweder durch PCINT0- oder PCINT1-Pins verursacht wird - ich denke, die Namensüberlappung von Vektoren und Pins ist verwirrend), ist der einzige Weg, um zu bestimmen, welcher Pin (s) verursacht, dass der Interrupt ihren Status nach jedem Interrupt aufzeichnet und die vorherigen und aktuellen Werte aller Pins vergleicht, die in PCMSKn aktiviert sind?

Tom Davies
quelle
1
Es ist schon eine Weile her, dass ich einen AVR verwendet habe, aber ich bin sicher, dass es eine Flagge geben muss, die für den richtigen Pin ausgelöst wird. Dieses Flag sollte nach dem Auftreten des Interrupts gelöscht werden, damit Sie den Status nicht speichern müssen. Die Tatsache, dass die Flagge gesetzt ist, sollte ausreichen
Gustavo Litovsky
@ gl3829 Die Flags sind pro Gruppe von Pins, wenn ich richtig verstehe
Tom Davies

Antworten:

11

Ich finde die Namensüberlappung von Vektoren und Pins verwirrend

Es ist!

Der Grund, warum es 8 verschiedene externe Pins für einen Interrupt-Vektor gibt, besteht darin, das Layout der Leiterplatte zu vereinfachen oder einen anderen Pin zu verwenden, wenn ein Konflikt mit einer anderen Pin-Funktion besteht.

Bin ich richtig im Denken ... Die einzige Möglichkeit, festzustellen, welche Pins den Interrupt verursacht haben, besteht darin, ihren Status nach jedem Interrupt aufzuzeichnen und die vorherigen und aktuellen Werte aller in PCMSKn aktivierten Pins zu vergleichen.

Nehmen wir an, Sie interessieren sich nur für PB0 (PCINT0) und PB1 (PCINT1). Die Pinwechsel-Aktivierungsmaske PCMSK0 würde also auf 0x03 gesetzt.

// External Interrupt Setup
...

volatile u_int8 previousPins = 0; 
volatile u_int8 pins = 0; 

ISR(SIG_PIN_CHANGE0)
{
    previousPins = pins; // Save the previous state so you can tell what changed
    pins = (PINB & 0x03); // set pins to the value of PB0 and PB1
    ...
}

Wenn pinsalso 0x01 ist, wissen Sie, dass es PB0 war ... Und wenn Sie wissen müssen, was sich geändert hat, müssen Sie es vergleichen previousPins, ziemlich genau das, was Sie dachten.

Beachten Sie in einigen Fällen, dass dies pinsmöglicherweise nicht genau ist, wenn der Status des Pins seit dem Interrupt geändert wurde, jedoch vorher pins = (PINB & 0x03).

Eine andere Möglichkeit wäre, separate Interrupt-Vektoren mit einem Pin von jedem Vektor zu verwenden, damit Sie wissen, welcher geändert wird. Auch dies hat auch einige Probleme, wie Interrupt - Priorität und wenn die CPU die ISR eintritt, der globale Interrupt - Freigabebit I-bitin SREGwird so gelöscht werden , dass alle anderen Interrupts deaktiviert sind, obwohl Sie es innerhalb der Interrupt einstellen können , wenn Sie wollen, das wäre ein verschachtelter Interrupt sein.

Weitere Informationen finden Sie in Atmels App-Hinweis Verwenden externer Interrupts für MegaAVR-Geräte.

Aktualisieren

Hier ist ein vollständiges Codebeispiel, das ich gerade hier gefunden habe .

#include <avr/io.h>
#include <stdint.h>            // has to be added to use uint8_t
#include <avr/interrupt.h>    // Needed to use interrupts
volatile uint8_t portbhistory = 0xFF;     // default is high because the pull-up

int main(void)
{
    DDRB &= ~((1 << DDB0) | (1 << DDB1) | (1 << DDB2)); // Clear the PB0, PB1, PB2 pin
    // PB0,PB1,PB2 (PCINT0, PCINT1, PCINT2 pin) are now inputs

    PORTB |= ((1 << PORTB0) | (1 << PORTB1) | (1 << PORTB2)); // turn On the Pull-up
    // PB0, PB1 and PB2 are now inputs with pull-up enabled

    PCICR |= (1 << PCIE0);     // set PCIE0 to enable PCMSK0 scan
    PCMSK0 |= (1 << PCINT0);   // set PCINT0 to trigger an interrupt on state change 

    sei();                     // turn on interrupts

    while(1)
    {
    /*main program loop here */
    }
}

ISR (PCINT0_vect)
{
    uint8_t changedbits;

    changedbits = PINB ^ portbhistory;
    portbhistory = PINB;

    if(changedbits & (1 << PB0))
    {
    /* PCINT0 changed */
    }

    if(changedbits & (1 << PB1))
    {
    /* PCINT1 changed */
    }

    if(changedbits & (1 << PB2))
    {
    /* PCINT2 changed */
    }
}
Garrett Fogerlie
quelle
Der Mega hat drei Pinwechsel-Interrupts mit Vektoren PCINT [0-2], aber jeder von diesen wird durch einen Satz von Pins ausgelöst. Meine Frage ist, wie man unterscheidet, welcher der Pins in diesem Satz den Interrupt verursacht hat.
Tom Davies
@ TomDavies du bist richtig, danke, ich habe meine Antwort geändert, aber es ist genau das, was du gedacht hast. Und ich habe das Datenblatt durchgelesen, es gibt kein Flag, das angibt, welcher Pin geändert wurde.
Garrett Fogerlie
@ Garret: Wussten Sie, dass man in Ihrem ursprünglichen Beispiel leicht feststellen kann, ob es die fallende oder die steigende Flanke war, die den Interrupt ausgelöst hat? (Nun, es sei denn, beide Pins haben sich genau im selben Moment geändert ... aber in diesem Fall hilft nur schwarze Magie.) (previous_pins> pins): fallende Flanke (vorherige pins <pins): steigende Flanke Vielleicht ist dies oben erwähnt.
@ TomDavies PINB deckt PCINT0-7 ab, PINC deckt PCINT8-15 usw. ab
EkriirkE
0

Im neueren INTFLAGSRegister der ATTINY-Serie erfahren Sie, welches Portbit den Interrupt verursacht hat.

Hier ist ein Auszug aus dem Datenblatt:

Bits 7: 0 - INT [7: 0]: Pin-Flag unterbrechen Das INT-Flag wird gesetzt, wenn ein Pin-Wechsel / -Zustand mit der Eingangserfassungskonfiguration des Pins übereinstimmt. Wenn Sie eine '1' in die Bitposition eines Flags schreiben, wird das Flag gelöscht.

zmechanic
quelle