Existem dois tipos de interrupções do tipo "troca de pinos". As interrupções externas, das quais existem duas no Uno. Eles são chamados 0 e 1, no entanto, referem-se aos pinos digitais 2 e 3 na placa. Eles podem ser configurados para detectar subidas, descidas, alterações (subidas ou descidas) ou BAIXO.
Além disso, há interrupções de "troca de pinos", que detectam uma alteração no estado dos pinos em qualquer um dos 20 pinos (A0 a A5 e D0 a D13). Essas interrupções de troca de pinos também são baseadas em hardware e, por si só, serão tão rápidas quanto as interrupções externas.
Ambos os tipos são um pouco difíceis de usar no nível do registro, mas o IDE padrão inclui attachInterrupt (n) e detachInterrupt (n), o que simplifica a interface para interrupções externas. Você também pode usar a Biblioteca de alteração de pinos para simplificar as interrupções de alteração de pinos.
No entanto, saindo da biblioteca por um minuto, podemos estabelecer que as interrupções de troca de pinos podem ser tão rápidas ou mais rápidas quanto as interrupções externas. Por um lado, embora a troca de pinos interrompa o trabalho em lotes de pinos, você não precisa habilitar o lote inteiro. Por exemplo, se você deseja detectar alterações no pino D4, isso será suficiente:
Esboço de exemplo:
ISR (PCINT2_vect)
{
// handle pin change interrupt for D0 to D7 here
if (PIND & bit (4)) // if it was high
PORTD |= bit (5); // turn on D5
else
PORTD &= ~bit (5); // turn off D5
} // end of PCINT2_vect
void setup ()
{
// pin change interrupt (example for D4)
PCMSK2 |= bit (PCINT20); // want pin 4
PCIFR |= bit (PCIF2); // clear any outstanding interrupts
PCICR |= bit (PCIE2); // enable pin change interrupts for D0 to D7
pinMode (4, INPUT_PULLUP);
pinMode (5, OUTPUT);
} // end of setup
void loop ()
{
}
Meu teste indica que foram necessários 1,6 µs para o pino de "teste" (pino 5) reagir a uma alteração no pino de interrupção (pino 4).
Agora, se você adotar a abordagem simples (preguiçosa?) E usar o attachInterrupt (), verá que os resultados são mais lentos, não mais rápidos.
Código de exemplo:
void myInterrupt ()
{
if (PIND & bit (2)) // if it was high
PORTD |= bit (5); // turn on D5
else
PORTD &= ~bit (5); // turn off D5
} // end of myInterrupt
void setup ()
{
attachInterrupt (0, myInterrupt, CHANGE);
pinMode (2, INPUT_PULLUP);
pinMode (5, OUTPUT);
} // end of setup
void loop ()
{
}
São necessários 3,7 µs para alterar o pino de teste, muito mais do que os 1,6 µs acima. Por quê? Como o código que o compilador deve gerar para o manipulador de interrupção "genérico" deve salvar todos os registros concebíveis (enviá-los) na entrada do ISR e depois restaurá-los (enviá-los) antes de retornar. Além disso, há a sobrecarga de outra chamada de função.
Agora, podemos contornar isso evitando o attachInterrupt () e fazendo isso sozinhos:
ISR (INT0_vect)
{
if (PIND & bit (2)) // if it was high
PORTD |= bit (5); // turn on D5
else
PORTD &= ~bit (5); // turn off D5
} // end of INT0_vect
void setup ()
{
// activate external interrupt 0
EICRA &= ~(bit(ISC00) | bit (ISC01)); // clear existing flags
EICRA |= bit (ISC00); // set wanted flags (any change interrupt)
EIFR = bit (INTF0); // clear flag for interrupt 0
EIMSK |= bit (INT0); // enable it
pinMode (2, INPUT_PULLUP);
pinMode (5, OUTPUT);
} // end of setup
void loop ()
{
}
Essa é a mais rápida de todas, com 1,52 µs - parece que um ciclo de relógio foi salvo em algum lugar.
Há uma ressalva, no entanto, para interrupções de troca de pinos. Eles são agrupados em lotes, portanto, se você deseja interromper muitos pinos, precisa testar dentro da interrupção qual deles foi alterado. Você pode fazer isso salvando o status anterior do pino e comparando-o com o novo status do pino. Isso não é necessariamente particularmente lento, mas quanto mais pinos você precisar verificar, mais lento seria.
Os lotes são:
Se você quiser apenas mais alguns pinos de interrupção, poderá evitar qualquer teste apenas escolhendo usar pinos de diferentes lotes (por exemplo, D4 e D8).
Mais detalhes em http://www.gammon.com.au/interrupts