Determinando qual pino acionou uma interrupção do PCINTn?

9

Estou correto ao pensar que, se você tiver dois pinos causando a mesma interrupção AVR PCINT (por exemplo, vetor PCINT0 causado por pinos PCINT0 ou PCINT1 - acho que a sobreposição de nomes de vetores e pinos é confusa), a única maneira de determinar qual pino (s) causou a interrupção é registrar seu estado após cada interrupção e comparar os valores anteriores e atuais de todos os pinos que estão habilitados no PCMSKn?

Tom Davies
fonte
11
Já faz um tempo desde que usei um AVR, mas tenho certeza de que deve haver uma bandeira que é acionada para o pino certo. Esse sinalizador deve desaparecer após a interrupção, para que você não precise armazenar o estado. O fato de que o sinalizador está definido deve ser suficiente
Gustavo Litovsky
@ gl3829 as bandeiras são por grupo de pinos se eu estou entendendo corretamente
Tom Davies

Respostas:

11

Eu acho que a sobreposição de nomes de vetores e pinos é confusa

Isto é!

A razão pela qual existem 8 pinos externos diferentes para um vetor de interrupção é facilitar o layout da PCB ou usar um pino diferente se houver um conflito com outra função de pino.

Estou correto ao pensar ... a única maneira de determinar quais pinos causaram a interrupção é registrar seu estado após cada interrupção e comparar os valores anteriores e atuais de todos os pinos ativados no PCMSKn?

Basicamente, digamos que você só se preocupa com PB0 (PCINT0) e PB1 (PCINT1). Portanto, a máscara de habilitação de alteração de pino PCMSK0 seria definida como 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
    ...
}

Então, se pins0x01, você sabe que foi PB0 ... E se você precisa saber o que mudou, precisa compará-lo com previousPins, exatamente o que você pensou.

Lembre-se de que, em alguns casos, pinspode não ser preciso se o pino mudar de estado desde a interrupção, mas antes pins = (PINB & 0x03).

Outra opção seria usar vetores de interrupção separados com um pino de cada vetor, para que você saiba qual deles foi alterado. Mais uma vez, isso também tem alguns problemas, como prioridade de interrupção e uma vez que a CPU entra no ISR, o mundial de interrupção bit de habilitação I-bitem SREGserão apagados de forma que todas as outras interrupções estão desativadas, mas você pode configurá-lo dentro da interrupção, se quiser, que ser uma interrupção aninhada.

Para obter mais informações, consulte a nota do aplicativo da Atmel, Usando interrupções externas para dispositivos megaAVR.

Atualizar

Aqui está um exemplo de código completo que acabei de encontrar aqui .

#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
fonte
O mega possui três interrupções de troca de pinos, com os vetores PCINT [0-2], mas cada uma delas é acionada por um conjunto de pinos. Minha pergunta é sobre como distinguir qual dos pinos nesse conjunto causou a interrupção.
Tom Davies
@ TomDavies você está correto, obrigado, mudei minha resposta, mas é exatamente o que você pensava. E eu li a folha de dados, não há sinalizador para indicar qual pino mudou.
Garrett Fogerlie
@ Garret: Você reconheceu que no seu exemplo original é fácil determinar se foi a queda ou a borda ascendente que desencadeou a interrupção? (bem, a menos que os dois pinos mudem exatamente no mesmo momento ... mas neste caso apenas a magia negra ajuda) (pinos_ anteriores> pinos): borda de queda (pinos anteriores <pinos): borda de elevação Talvez isso mencione acima.
O PINB do TomDavies cobre o PCINT0-7, o PINC do PCINT8-15 etc.
EkriirkE
0

No registro mais recente da série ATTINY, INTFLAGSserá informado qual bit de porta causou a interrupção.

Aqui está um trecho da folha de dados:

Bits 7: 0 - INT [7: 0]: Interromper sinalizador de pino O sinalizador INT é definido quando um pino muda / estado corresponde à configuração do sensor de entrada do pino. Escrever um '1' no local de bit de uma bandeira limpará a bandeira.

zmecânico
fonte