¿Determinar qué pin activó una interrupción PCINTn?

9

¿Estoy en lo cierto al pensar que si tiene dos pines que causan la misma interrupción AVR PCINT (por ejemplo, el vector PCINT0 causado por los pines PCINT0 o PCINT1? Creo que la superposición de nombres de vectores y pines es confusa) la única forma de determinar qué pin (s) causó la interrupción es registrar su estado después de cada interrupción y comparar los valores anteriores y actuales de todos los pines que están habilitados en PCMSKn?

Tom Davies
fuente
1
Ha pasado un tiempo desde que usé un AVR, pero estoy seguro de que debe haber una bandera que se active para el pin correcto. Este indicador debe borrarse después de que ocurra la interrupción, para que no necesite almacenar el estado. El hecho de que la bandera esté puesta debería ser suficiente
Gustavo Litovsky
@ gl3829 las banderas son por grupo de pines si lo entiendo correctamente
Tom Davies

Respuestas:

11

Creo que la superposición de nombres de vectores y pines es confusa

¡Está!

La razón por la que hay 8 pines externos diferentes para un vector de interrupción es para facilitar el diseño de la PCB o usar un pin diferente si hay un conflicto con otra función de pin.

¿Estoy en lo correcto al pensar ... la única forma de determinar qué pin (s) causó la interrupción es registrar su estado después de cada interrupción y comparar los valores anteriores y actuales de todos los pines que están habilitados en PCMSKn?

Más o menos, digamos que solo te importan PB0 (PCINT0) y PB1 (PCINT1). Por lo tanto, la máscara de habilitación de cambio de pin PCMSK0 se establecería en 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
    ...
}

Entonces, si pinses 0x01, sabes que era PB0 ... Y si necesitas saber qué cambio necesitas comparar previousPins, más o menos exactamente lo que pensaste.

Tenga en cuenta que en algunos casos, pinspuede no ser preciso si el pin tiene cambios de estado desde la interrupción pero antes pins = (PINB & 0x03).

Otra opción sería usar vectores de interrupción separados con un pin de cada vector para que sepa cuál se cambia. Una vez más, esto también tiene algunos problemas, como la prioridad de interrupción y una vez que la CPU entra en el ISR, el mundial de interrupción bit de habilitación I-biten la SREGque se borrará de manera que el resto de las interrupciones están deshabilitadas, aunque se puede establecer dentro de la interrupción si quieres, que lo haría ser una interrupción anidada

Para obtener más información, eche un vistazo a la nota de la aplicación Atmel Uso de interrupciones externas para dispositivos megaAVR.

Actualizar

Aquí hay un ejemplo de código completo que acabo de encontrar aquí .

#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
fuente
El mega tiene tres interrupciones de cambio de pines, con los vectores PCINT [0-2], pero cada uno de ellos se activa mediante un conjunto de pines. Mi pregunta es sobre cómo distinguir cuál de los pines en ese conjunto causó la interrupción.
Tom Davies
@TomDavies tienes razón, gracias, cambié mi respuesta, sin embargo, es exactamente lo que pensabas. Y leí la hoja de datos, no hay una bandera que indique qué pin cambió.
Garrett Fogerlie
@ Garret: ¿Reconociste que en tu ejemplo original se puede determinar fácilmente si fue el borde descendente o ascendente el que provocó la interrupción? (bueno, a menos que ambos pines cambien exactamente en el mismo momento ... pero en este caso solo la magia negra ayuda) (previous_pins> pines): borde descendente (pines anteriores <pines): borde ascendente Quizás valga la pena mencionarlo anteriormente.
@TomDavies PINB cubre PCINT0-7, PINC cubre PCINT8-15, etc.
EkriirkE
0

En el registro más reciente de la serie ATTINY INTFLAGSle dirá qué bit de puerto ha causado la interrupción.

Aquí hay un extracto de la hoja de datos:

Bits 7: 0 - INT [7: 0]: Interrupción de la bandera del pin La bandera INT se establece cuando un cambio / estado del pin coincide con la configuración de detección de entrada del pin. Escribir un '1' en la ubicación de bit de una bandera borrará la bandera.

zmechanic
fuente