Déterminer quelle broche a déclenché une interruption PCINTn?

9

Ai-je raison de penser que si vous avez deux broches provoquant la même interruption AVR PCINT (par exemple, le vecteur PCINT0 provoqué par les broches PCINT0 ou PCINT1 - je pense que le chevauchement des noms des vecteurs et des broches prête à confusion), la seule façon de déterminer quelle broche (s) a provoqué l'interruption est d'enregistrer leur état après chaque interruption et de comparer les valeurs précédentes et actuelles de toutes les broches qui sont activées dans PCMSKn?

Tom Davies
la source
1
Cela fait un moment que je n'ai pas utilisé un AVR, mais je suis sûr qu'il doit y avoir un indicateur qui doit être déclenché pour la bonne broche. Cet indicateur doit s'effacer après que l'interruption se soit produite, afin que vous n'ayez pas besoin de stocker l'état. Le fait que le drapeau soit réglé devrait suffire
Gustavo Litovsky
@ gl3829 les drapeaux sont par groupe de broches si je comprends bien
Tom Davies

Réponses:

11

Je pense que le chevauchement des noms des vecteurs et des broches est déroutant

C'est!

La raison pour laquelle il existe 8 broches externes différentes pour un vecteur d'interruption est de faciliter la disposition du PCB ou d'utiliser une broche différente en cas de conflit avec une autre fonction de broche.

Ai-je raison de penser ... la seule façon de déterminer quelle (s) broche (s) a causé l'interruption est d'enregistrer leur état après chaque interruption et de comparer les valeurs précédentes et actuelles de toutes les broches qui sont activées dans PCMSKn?

À peu près, disons que vous ne vous souciez que de PB0 (PCINT0) et PB1 (PCINT1). Ainsi, le masque d'activation de changement de broche PCMSK0 serait défini sur 0x03.

// 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
    ...
}

Donc, si pinsc'est 0x01, vous savez que c'était PB0 ... Et si vous avez besoin de savoir ce qui a changé, vous devez le comparer previousPins, à peu près exactement ce que vous pensiez.

Gardez à l'esprit dans certains cas, pinspeut ne pas être précis si la broche a changé d'état depuis l'interruption mais avant pins = (PINB & 0x03).

Une autre option serait d'utiliser des vecteurs d'interruption séparés avec une broche de chaque vecteur pour que vous sachiez lequel est changé. Encore une fois, cela a aussi des problèmes, comme priorité d' interruption et une fois que l'unité centrale de traitement entre ISR, le bit global d' interruption permettent I-biten SREGsera effacé afin que toutes les autres interruptions sont désactivées, mais vous pouvez le mettre à l' intérieur de l'interruption si vous voulez, ce serait être une interruption imbriquée.

Pour plus d'informations, consultez la note d'application d'Atmel Utilisation des interruptions externes pour les appareils megaAVR.

Mise à jour

Voici un exemple de code complet que je viens de trouver ici .

#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
la source
Le méga a trois interruptions de changement de broches, avec les vecteurs PCINT [0-2], mais chacune d'entre elles est déclenchée par un ensemble de broches. Ma question est de savoir comment distinguer les broches de cet ensemble qui ont provoqué l'interruption.
Tom Davies
@ TomDavies vous avez raison, merci, j'ai changé ma réponse mais c'est exactement ce que vous pensiez. Et j'ai lu la fiche technique, il n'y a pas de drapeau pour indiquer quelle broche a changé.
Garrett Fogerlie
@ Garret: Avez-vous reconnu que dans votre exemple d'origine, on peut facilement déterminer si c'est le front descendant ou montant qui a déclenché l'interruption? (enfin, à moins que les deux broches ne changent au même moment ... mais dans ce cas, seule la magie noire aide) (broches précédentes> broches): front descendant (broches précédentes <broches): front montant Peut-être que cela vaut la peine d'être mentionné ci-dessus.
@TomDavies PINB couvre PCINT0-7, PINC couvre PCINT8-15 etc.
EkriirkE
0

Sur la nouvelle série ATTINY, le INTFLAGSregistre vous indiquera quel bit de port a provoqué l'interruption.

Voici un extrait de la fiche technique:

Bits 7: 0 - INT [7: 0]: indicateur de broche d'interruption Le drapeau INT est défini lorsqu'un changement / état de broche correspond à la configuration de détection d'entrée de la broche. L'écriture d'un «1» à l'emplacement du bit d'un drapeau effacera le drapeau.

zmechanic
la source