¿Cuántos pines de interrupción puede manejar un Uno?

16

Estoy mirando el uso de un receptor RC de 7 canales con el Arduino Uno R3. En la documentación, hay menciones de un máximo de 2 pines de interrupción, mientras que en algunos otros blogs he visto menciones de usar hasta 20 pines como interrupciones, con la biblioteca PinChangeInt. Entonces, ¿cuántas interrupciones puede manejar el Arduino de forma nativa? ¿Y esto es diferente de cuántos se pueden manejar con soporte de software como PinChangeInt?

Asheeshr
fuente

Respuestas:

13

Hay dos tipos de interrupciones de tipo "cambio de pin". Las interrupciones externas, de las cuales hay dos en el Uno. Se llaman 0 y 1, sin embargo, se refieren a los pines digitales 2 y 3 en el tablero. Estos se pueden configurar para detectar ascensos, caídas, cambios (subiendo o bajando) o BAJOS.

Además de eso, hay interrupciones de "cambio de pin", que detectan un cambio en el estado del pin en cualquiera de los 20 pines (A0 a A5 y D0 a D13). Estas interrupciones de cambio de pin también están basadas en hardware , por lo que, en sí mismas, serán tan rápidas como las interrupciones externas.

Ambos tipos son un poco complicados de usar en el nivel de registro, pero el IDE estándar incluye attachInterrupt (n) y detachInterrupt (n) que simplifica la interfaz para las interrupciones externas. También puede usar la biblioteca de cambio de pin para simplificar las interrupciones de cambio de pin.

Sin embargo, alejándonos de la biblioteca por un minuto, podemos establecer que las interrupciones de cambio de pin pueden ser tan rápidas o más rápidas que las interrupciones externas. Por un lado, aunque las interrupciones de cambio de pin funcionan en lotes de pin, no tiene que habilitar todo el lote. Por ejemplo, si desea detectar cambios en el pin D4, esto será suficiente:

Bosquejo de ejemplo:

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 ()
  {
  }

Mi prueba indica que el pin de "prueba" (pin 5) tardó 1.6 µs en reaccionar ante un cambio en el pin de interrupción (pin 4).


Ahora, si adopta el enfoque simple (¿perezoso?) Y utiliza attachInterrupt (), encontrará que los resultados son más lentos, no más rápidos.

Código de ejemplo:

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 ()
  {
  }

Esto toma 3.7 µs para cambiar el pin de prueba, mucho más que los 1.6 µs anteriores. ¿Por qué? Debido a que el código que el compilador tiene que generar para el manejador de interrupciones "genérico" tiene que guardar todos los registros imaginables (empujarlos) en la entrada al ISR y luego restaurarlos (abrirlos) antes de regresar. Además, está la sobrecarga de otra llamada de función.


Ahora podemos evitarlo evitando attachInterrupt () y haciéndolo nosotros mismos:

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 ()
  {
  }

Esa es la más rápida de todas con 1,52 µs: parece que se guardó un ciclo de reloj en alguna parte.


Sin embargo, hay una advertencia para las interrupciones de cambio de pin. Están agrupados, por lo que si desea tener interrupciones en muchos pines, debe probar dentro de la interrupción cuál cambió. Puede hacerlo guardando el estado del pin anterior y comparándolo con el nuevo estado del pin. Esto no es necesariamente particularmente lento, pero cuantos más pines necesite verificar, más lento será.

Los lotes son:

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

Si solo desea un par de pines de interrupción más, puede evitar cualquier prueba simplemente eligiendo usar pines de diferentes lotes (por ejemplo, D4 y D8).


Más detalles en http://www.gammon.com.au/interrupts

Nick Gammon
fuente
9

Hay dos tipos de interrupciones. Lo que dijo el Arduino Playground :

El procesador en el corazón de cualquier Arduino tiene dos tipos diferentes de interrupciones: "externo" y "cambio de pin". Solo hay dos pines de interrupción externos en el ATmega168 / 328 (es decir, en el Arduino Uno / Nano / Duemilanove), INT0 e INT1, y se asignan a los pines Arduino 2 y 3. Estas interrupciones se pueden configurar para activarse en RISING o FALLING bordes de señal, o en nivel bajo. Los disparadores son interpretados por hardware, y la interrupción es muy rápida. El Arduino Mega tiene algunos pines de interrupción externos más disponibles.

Por otro lado, las interrupciones de cambio de pin se pueden habilitar en muchos más pines. Para los Arduinos basados ​​en ATmega168 / 328, se pueden habilitar en cualquiera de los 20 pines de señal del Arduino; en los Arduinos basados ​​en ATmega se pueden habilitar en 24 pines. Se activan por igual en los bordes de la señal RISING o FALLING, por lo que depende del código de interrupción establecer los pines adecuados para recibir las interrupciones, determinar qué sucedió (¿qué pin? ... ¿subió o bajó la señal?), Y para manejarlo adecuadamente. Además, las interrupciones de cambio de pin se agrupan en 3 "puertos" en la MCU, por lo que solo hay 3 vectores de interrupción (subrutinas) para todo el cuerpo de pines. Esto hace que el trabajo de resolver la acción en una sola interrupción sea aún más complicado.

Básicamente, las interrupciones externas son extremadamente rápidas ya que todas están basadas en hardware. Sin embargo, también están las interrupciones de cambio de pin, pero parecen ser mucho más lentas porque están basadas principalmente en software.

tl; dr: los 20 pines de interrupción juntos son mucho más lentos. Los 2 pines de interrupción son los más rápidos y eficientes.


EDITAR: Acabo de mirar la hoja de datos y dice que se activa una interrupción de cambio de pin para cualquiera de los pines seleccionados sin indicación de qué pin ha cambiado (aunque se divide en tres vectores de interrupción).

  • Para interrupciones externas, le dirá que el pin 3 acaba de cambiar
  • ¡Para el cambio de pin, le dirá un pin cambiado que estaba monitoreando!

Como puede ver, una interrupción de cambio de pin agrega una gran sobrecarga al ISR que debe manejar almacenando estados anteriores y ver si es el pin lo que le preocupa. Puede estar bien para un estado de reposo, pero es mejor usar las interrupciones externas.

Pingüino anónimo
fuente