Quantos pinos de interrupção um Uno pode manipular?

16

Estou pensando em usar um receptor RC de 7 canais com o Arduino Uno R3. Na documentação, há menções de no máximo 2 pinos de interrupção, enquanto em outros blogs eu já vi menções de usar até 20 pinos como interrupções, na biblioteca PinChangeInt. Então, quantas interrupções o Arduino pode lidar de forma nativa? E isso é diferente de quantos podem ser tratados com suporte de software, como PinChangeInt?

asheeshr
fonte

Respostas:

13

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:

  • A0 a A5
  • D0 a D7
  • D8 a D13

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

Nick Gammon
fonte
9

Existem dois tipos de interrupções. O que o Arduino Playground disse:

O processador no coração de qualquer Arduino possui dois tipos diferentes de interrupções: "externo" e "troca de pinos". Existem apenas dois pinos de interrupção externos no ATmega168 / 328 (ou seja, no Arduino Uno / Nano / Duemilanove), INT0 e INT1, e eles são mapeados para os pinos 2 e 3. do Arduino. Essas interrupções podem ser configuradas para disparar em RISING ou Bordas do sinal caindo ou em nível baixo. Os gatilhos são interpretados pelo hardware e a interrupção é muito rápida. O Arduino Mega tem mais alguns pinos de interrupção externos disponíveis.

Por outro lado, as interrupções de troca de pinos podem ser ativadas em muitos outros pinos. Para os Arduinos baseados no ATmega168 / 328, eles podem ser ativados em um ou em todos os 20 pinos de sinal do Arduino; nos Arduinos baseados no ATmega, eles podem ser ativados em 24 pinos. Eles são acionados igualmente nas bordas do sinal RISING ou FALLING, portanto, cabe ao código de interrupção definir os pinos adequados para receber interrupções, determinar o que aconteceu (qual pino? ... o sinal aumentou ou diminuiu?) E para lidar com isso corretamente. Além disso, as interrupções de troca de pinos são agrupadas em 3 "portas" no MCU, portanto, existem apenas 3 vetores de interrupção (sub-rotinas) para todo o corpo de pinos. Isso torna o trabalho de resolver a ação em uma única interrupção ainda mais complicado.

Basicamente, as interrupções externas são extremamente rápidas, pois são todas baseadas em hardware. No entanto, também há interrupções na troca de pinos, mas essas parecem ser muito mais lentas porque são baseadas principalmente em software.

tl; dr: os 20 pinos de interrupção juntos são muito mais lentos. Os 2 pinos de interrupção são os mais rápidos e eficientes.


EDIT: Acabei de olhar para a folha de dados e ela diz que uma interrupção na troca de pinos é acionada para qualquer um dos pinos selecionados sem indicação de qual pino foi alterado (embora esteja dividido em três vetores de interrupção).

  • Para interrupções externas, ele diz que o pino 3 acabou de mudar
  • Para a mudança de pino, ele informa que um pino alterado que você estava monitorando!

Como você pode ver, uma interrupção na troca de pinos adiciona muita sobrecarga ao ISR com o qual você precisa lidar com o armazenamento de estados anteriores e ver se é com o que você está preocupado. Pode ser bom para um estado de suspensão, mas é melhor usar as interrupções externas.

Pinguim anônimo
fonte