Combien de broches d'interruption un Uno peut-il gérer?

16

Je cherche à utiliser un récepteur RC à 7 canaux avec l'Arduino Uno R3. Dans la documentation, il y a des mentions d'un maximum de 2 broches d'interruption, alors que sur certains autres blogs, j'ai vu des mentions d'utiliser jusqu'à 20 broches comme interruptions, avec la bibliothèque PinChangeInt. Alors, combien d'interruptions l'Arduino peut-il gérer nativement? Et est-ce différent du nombre qui peut être géré avec un support logiciel tel que PinChangeInt?

asheeshr
la source

Réponses:

13

Il existe deux types d'interruptions de type "changement de broche". Les interruptions externes, dont il y en a deux sur l'Uno. Ils sont appelés 0 et 1, mais ils se réfèrent aux broches numériques 2 et 3 de la carte. Ceux-ci peuvent être configurés pour détecter une hausse, une baisse, un changement (ascendant ou descendant) ou BAS.

En plus de cela, des interruptions de "changement de broche", qui détectent un changement de l'état de broche dans l'une des 20 broches (A0 à A5 et D0 à D13). Ces interruptions de changement de broches sont également basées sur le matériel , elles seront donc aussi rapides que les interruptions externes.

Les deux types sont légèrement difficiles à utiliser au niveau du registre, mais l'IDE standard comprend attachInterrupt (n) et detachInterrupt (n) qui simplifie l'interface avec les interruptions externes. Vous pouvez également utiliser la bibliothèque de changements de broches pour simplifier les interruptions de changement de broches.

Cependant, en évitant la bibliothèque pendant une minute, nous pouvons établir que les interruptions de changement de broche peuvent être aussi rapides, ou plus rapides, que les interruptions externes. D'une part, bien que les interruptions de changement de broches fonctionnent sur des lots de broches, vous n'avez pas besoin d'activer le lot entier. Par exemple, si vous souhaitez détecter des modifications sur la broche D4, cela suffira:

Exemple d'esquisse:

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

Mes tests indiquent qu'il a fallu 1,6 µs pour que la broche "test" (broche 5) réagisse à un changement sur la broche d'interruption (broche 4).


Maintenant, si vous adoptez l'approche simple (paresseux?) Et utilisez attachInterrupt (), vous constaterez que les résultats sont plus lents et non plus rapides.

Exemple de code:

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

Cela prend 3,7 µs pour changer la broche de test, beaucoup plus que les 1,6 µs ci-dessus. Pourquoi? Parce que le code que le compilateur doit générer pour le gestionnaire d'interruption "générique" doit enregistrer tous les registres imaginables (les pousser) à l'entrée dans l'ISR, puis les restaurer (les faire éclater) avant de revenir. De plus, il y a la surcharge d'un autre appel de fonction.


Maintenant, nous pouvons contourner cela en évitant attachInterrupt () et en le faisant nous-mêmes:

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

C'est le plus rapide de tous à 1,52 µs - il semble qu'un cycle d'horloge ait été enregistré quelque part.


Il y a cependant une mise en garde pour les interruptions de changement de broche. Ils sont groupés, donc si vous voulez avoir des interruptions sur beaucoup de broches, vous devez tester à l'intérieur de l'interruption laquelle a changé. Vous pouvez le faire en enregistrant le statut de broche précédent et en le comparant au nouveau statut de broche. Ce n'est pas nécessairement particulièrement lent, mais plus il vous faudra vérifier de broches, plus ce serait lent.

Les lots sont:

  • A0 à A5
  • D0 à D7
  • D8 à D13

Si vous voulez juste quelques broches d'interruption supplémentaires, vous pouvez éviter tout test en choisissant simplement d'utiliser des broches de différents lots (par exemple, D4 ​​et D8).


Plus de détails sur http://www.gammon.com.au/interrupts

Nick Gammon
la source
9

Il existe deux types d'interruptions. Ce que l' Arduino Playground a dit:

Le processeur au cœur de tout Arduino a deux types d'interruptions différents: «externe» et «changement de broche». Il n'y a que deux broches d'interruption externes sur l'ATmega168 / 328 (c.-à-d. Dans l'Arduino Uno / Nano / Duemilanove), INT0 et INT1, et elles sont mappées sur les broches Arduino 2 et 3. Ces interruptions peuvent être définies pour se déclencher sur RISING ou Chute des fronts du signal, ou à faible niveau. Les déclencheurs sont interprétés par le matériel et l'interruption est très rapide. L'Arduino Mega dispose de quelques broches d'interruption externes supplémentaires.

D'un autre côté, les interruptions de changement de broches peuvent être activées sur de nombreuses autres broches. Pour les Arduinos basés sur ATmega168 / 328, ils peuvent être activés sur l'une ou l'ensemble des 20 broches de signal de l'Arduino; sur les Arduinos basés sur ATmega, ils peuvent être activés sur 24 broches. Ils sont déclenchés de la même manière sur les fronts de signal EN HAUSSE ou EN BAISSE, il appartient donc au code d'interruption de définir les broches appropriées pour recevoir les interruptions, de déterminer ce qui s'est passé (quelle broche? ... le signal a-t-il augmenté ou diminué?), Et pour le manipuler correctement. De plus, les interruptions de changement de broches sont regroupées en 3 «ports» sur le MCU, il n'y a donc que 3 vecteurs d'interruption (sous-programmes) pour le corps entier des broches. Cela rend le travail de résolution de l'action sur une seule interruption encore plus compliqué.

Fondamentalement, les interruptions externes sont extrêmement rapides car elles sont toutes basées sur le matériel. Cependant, il y a aussi les interruptions de changement de broche, mais celles-ci semblent être beaucoup plus lentes car elles sont principalement basées sur un logiciel.

tl; dr: les 20 broches d'interruption sont beaucoup plus lentes. Les 2 broches d'interruption sont les plus rapides et les plus efficaces.


ÉDITER: Je viens de regarder la fiche technique et il dit qu'une interruption de changement de broche est déclenchée pour l' une des broches sélectionnées sans indication de la broche qui a changé (bien qu'elle soit divisée en trois vecteurs d'interruption).

  • Pour les interruptions externes, il vous dira que la broche 3 vient d'être modifiée
  • Pour le changement de broche, il vous dira une broche modifiée que vous surveilliez!

Comme vous pouvez le voir, une interruption de changement de broche ajoute beaucoup de surcharge dans l'ISR que vous devez gérer en stockant les états précédents et en voyant si c'est la broche qui vous inquiète. Cela peut être bien pour un état de sommeil, mais il est préférable d'utiliser les interruptions externes.

Pingouin anonyme
la source