Vous êtes sur la page 1sur 24

Les interruptions

Généralement, pour mettre en œuvre un système,


Dans loop(), on va lire des entrées de l’Arduino, qu’elles soient analogiques ou numériques,
exécuter un certain nombre de calculs en fonction de ces entrées puis modifier l’état du
système, ce qui aboutira éventuellement à positionner des sorties.
Ces lectures se font donc de manière répétitive. On appelle ceci de la scrutation ou polling. La
période de scrutation dépend du temps que loop() met à s’exécuter et peut
d’ailleurs être variable si loop() contient des instructions conditionnelles qui font que d’une
itération a l’autre de loop() le nombre d’instructions exécutées n’est pas le même.
Dans certaines circonstances la scrutation n’est pas adaptée car l’entrée qui est scrutée peut
changer plusieurs fois de valeur pendant la période de scrutation. Dans ce cas ces
changements de valeur ne sont pas vus par le logiciel et le système ne fonctionne pas
correctement.

La solution réside dans l’utilisation des interruptions.

Les interruptions permettent au microcontrôleur d’exécuter une fonction lorsqu’un évènement


survient sur une des broches d’interruption. Plutôt que de lire constamment la valeur d’un
capteur par exemple, le programme ne va se déclencher que lorsque la valeur du capteur va
changer. Cela permet de résoudre bon nombre de problèmes d’agencement de tâche.
Principe de fonctionnement des interruptions

Une interruption, comme son nom l’indique, consiste a interrompre momentanement le


programme principal qui s’exécute, pour qu’il effectue un autre travail. Quand cet autre travail
est terminé, il retourne a l’exécution à son programme principal et reprend à l’endroit exact où
il l’avait laissé.
Cet autre travail s’appelle le programme d’interruption ou la routine d’interruption ou
encore une ISR pour Interrupt Service Routine.
Il est très important que les ISR aient un temps d’exécution le plus court possible. On ne
fera donc aucun calcul compliqué et aucun appel à des fonctions longues comme un affichage
sur un écran LCD.
Il existe plusieurs sources d’interruption, on dit également vecteur d’interruption, sur
l’AVR 328 qui équipe l’Arduino Uno, 26 au total. Il s’agit des 5 interruptions liées au
changement de la tension présente sur une broche numérique qui sont donc ce que l’on
appelle des interruptions externes. Les interruptions liées aux timers sont également
intéressantes.

Que se passe-t-il si une nouvelle interruption survient alors que l’ISR déclenchée par la
précédente n’est pas terminée ?

Une ISR n’est pas interrompue par une nouvelle interruption. La nouvelle interruption ne sera
prise en compte que lorsque l’ISR en cours se terminera.
Le corollaire est qu’il ne faut pas appeler de fonctions qui se mettent en attente d’une
interruption à partir d’une ISR. Comme l’interruption attendue ne peut pas déclencher une
nouvelle ISR, la fonction attendra indéfiniment et tout le système se bloquera. C’est ce que
l’on appelle un deadlock.
Les fonctions de Serial qui permettent d’afficher, via la ligne série et l’USB dans le moniteur
série font exactement cela. Leur appel à partir d’une ISR est donc interdit.
Les Interruptions ont chacune une priorité. Par exemple, les interruptions externes sont plus
prioritaires que les interruptions des Timers. L’Arduino exécutera les ISR dans leur ordre de
priorité.

Source Rôle
INT0 Interruption externe sur la broche 2
INT1 Interruption externe sur la broche 3
PCIE0 Interruption externe sur les broches 8 à 13
PCIE1 Interruption externe sur les broches A0 à A5
PCIE2 Interruption externe sur les broches 0 à 7
Relation entre broches et les interruptions pour ATMega 328P

Seuls les vecteurs d'interruption INT0 et INT1 sont


independants.
INT0 ne s'applique qu'a la broche PD2.
INT1 ne s'applique qu'a la broche PD3.
(Les broches PD2 et PD3 peuvent aussi être gérées à
partir du vecteur PCIE2.)
INT1 et INT0 disposent d'un choix multiple pour le
déclenchement : niveau bas,
basculement de niveau, front montant ou
descendant.

Les 3 vecteurs d'interruption PCIE0, PCIE1 et PCIE2


s'appliquent à un port complet, l'identification de la
broche à l'origine de l'interruption est à la charge du
programmeur.
Ces 3 vecteurs d'interruption ne disposent que du
choix basculement de niveau pour le
déclenchement de l'interruption.
Avec un bouton, ils se déclenchent à
l'enclenchement et au relâchement. C'est au
programmeur de différencier ces deux états.
Liste et priorité des interruptions
pour l'atmega 328P

Plus l'adresse d'un vecteur est


basse plus il est prioritaire. La
priorité absolue est celle du
vecteur RESET (adresse 0x0000).
Les interruptions Externes sont déclenchées par les pins INT0, INT1 ou une
quelconque des pins PCINT 0 à 23.
Les interruptions restent fonctionnelles même si les pins INT0, INT1 ou PCINT 0 a 23 sont
configurées en sortie.
L'interruption PCI2 se déclenche si une quelconque des pins PCINT23-16 change d ‘état.
L'interruption PCI1 se déclenche si une quelconque des pins PCINT14-8 change d ‘état.
L'interruption PCI0 se déclenche si une quelconque des pins PCINT7-0 change d ‘état.
Les registres PCMSK2, PCMSK1 et PCMSK0 contrôlent quelle broche est a l'origine du
changement d‘état.
Les changements d‘état sur les broches PCINT023 sont détectes de manière asynchrone.

Les interruptions externes PCIE0, PCIE1 et PCIE2 offrent moins de possibilités que INT0, INT1
Registres généraux nécessaires en cas d’interruption:

La gestion des interruptions peut nécessiter de connaitre deux registres généraux MCUCR
(MCU Control Register)

IVSEL Interrupt Vector Select


• = 0 → les vecteurs d'interrup ons sont places en debut de memoire flash.
• = 1 → les vecteurs d'interrup ons sont places en fin de memoire flash.

IVCE Interrupt Vector Change Enable


• = 0 → interdit la modifica on de la valeur de IVSEL
• = 1 → autorise le changement de valeur de IVSEL

Les bits dont la valeur est repérée par un tiret - sont des bits réservés, leur valeur doit
toujours être égale à 0.
SREG (Avr Status Register)

I → Global Interrupt Enable


• I = 0 → interdit toutes les interrup ons de maniere globale.
• I = 1 → autorise les interrup ons de maniere globale.

Chaque interruption doit être individuellement autorisée et configurée dans son


registre spécialisé.
Dès qu'une interruption se déclenche, elle met automatiquement le bit I à 0 bloquant
ainsi la possibilité d'avoir plusieurs interruptions simultanées. L'instruction RETI
repositionne le bit I à 1 permettant ainsi la prise en compte des interruptions
immédiatement suivantes.
Le bit I peut être directement positionné à 1 avec sei() et à 0 avec cli()

reti() = return from interrupt


Les Interruptions Externes INT0 et INT1

Les interruptions INT0 & INT1 disposent chacune de leur propre vecteur d'interruption
et peuvent être déclenchées sur un front montant, descendant, un niveau bas ou un
basculement de niveau.
La configuration s'effectue dans le registre EICRA.

EICRA External Interrupt control register A

ISC = Interrupt Sense Control

Si les modes de déclenchement sur un front ou un changement d‘état sont sélectionnés,


l'impulsion devra durer plus qu'une période d'horloge pour pouvoir être prise en compte.
o Niveau bas : l’interruption est declenchee quand la broche concernee est au niveau
bas. Comme il s’agit d’un etat et non d’un evenement, l’interruption sera
declenchee tant que la broche est LOW. Par consequent, des que l’ISR aura
terminé son exécution, elle la recommencera. Pendant ce temps, loop() ne
sera pas exécuté.
o Changement de niveau : l’interruption est declenchee quand la broche concernee
change
d’etat, c’est a dire passe de LOW a HIGH ou bien de HIGH a LOW. Il s’agit
d’un evenement.
o Front montant: l’interruption est declenchee quand la broche concernée passe de
LOW a HIGH. Il s’agit egalement d’un evenement.
o Front descendant : l’interruption est declenchee quand la broche concernee passe de
HIGH a LOW. Il s’agit encore d’un evenement.
Pour que l'interruption externe soit active il faut que le masque EIMSK soit également
configuré.

EIMSK (External Interrupt Mask Register)

INT1 (INT0) :
Conditions :
• INT1(ou INT0) est mis a 1.
• Le mode de déclenchement est défini dans EICRA.

Actions :
Une interruption correspondante au External Interrupt Request 1 est exécuté à partir
du vecteur d’interruption INT1 (ou INT0)
EIFR (External Interrupt Flag Register)

INTF1 (INTF0)
Conditions :
• EIMSK bit INT1(ou INT0) = 1
• le choix du mode de déclenchement est fait sur un front ou un changement de niveau
Action :
• Quand une demande d'interruption apparait sur la broche sélectionnée, le micro se
branche sur le vecteur d'interruption correspondant.
• Le drapeau est remis à 0 à la fin de la routine d'interruption.

Remarque : le drapeau est toujours à 1 si le mode niveau bas est sélectionné.


Les Interruptions PCINT: PCIE0-PCIE1-PCIE2

PCCIR (Pin Change Interrupt Control Register)

PCIEx Pin Change Enable x


PCIEx doit être mis a 1 pour permettre les interruptions.
• PCIE2 gère les interruptions PCINT23 a 16 (PORT D)
• PCIE1 gère les interruptions PCINT14 a 8 (PORT C)
• PCIE0 gère les interruptions PCINT7 a 0 (PORT B)
N'importe quel changement de niveau sur les broches gérées provoquera une interruption.
Les broches prévues pour générer une interruption doivent être configurées individuellement
avec le registre PCMSKx.
L'identification de la broche du port à l'origine de l'interruption est à la charge du
programmeur

Remarque : Contrairement aux interruptions int0 et int1, il n'est pas possible de choisir le
mode de déclenchement, le seul mode disponible est sur basculement de niveau.
PCIFR (Pin Change Flag Register)

PCIFx Pin Change Interrupt Flag x


Quand un changement se produit sur une des broches
• PCIE2 gere les interruptions 23 a 16
• PCIE1 gere les interruptions 14 a 8
• PCIE0 gere les interruptions 7 a 0

PCIFx est mis a 1, si PCIEx de PCICR =1 le micro se branche sur le vecteur d'interruption
corespondant. Le drapeau est remis à 0 à la sortie de la routine d'interruption.
PCMSKx (Pin change Mask Register x)
2) Configurer en C/Arduino l’IT Pin Change
sur le Port C (si changement sur PC6 ou void setup( )
PC3) et le Port D (si changement sur PD5 {
ou PD2) avec appel des 2 ISR.

PCICR :Activation des ITPinChange


pour 1 groupe
PCMSKi : Activation à l’intérieur d’1 }
groupe, soit PCMSK détermine quelles ISR ( ) {….}
broches du groupe sont prises en compte ISR ( ) {….}
par l’interruption PinChange
Les fonctions Arduino pour:
Les interruptions externes INT0 et INT1
Pour accrocher une routine d’interruption à un état ou un changement d’état de l’une de ces
broches, on va utiliser la
fonction attachInterrupt(...).
Cette fonction prend 3 arguments : le numéro d’interruption externe, la fonction à appeler
quand l’interruption survient et enfin la condition selon laquelle l’interruption survient. Son
prototype est le suivant :

attachInterrupt(numéro, ISR, mode);

numéro est le numero d’interruption concernée. Il s’agira de 0 ou 1 sur un Arduino Uno, ce qui
correspond respectivement aux broches 2 et 3.
Sur un Arduino Mega, les numeros 0 a 5 seront possibles et correspondent, dans l’ordre, aux
broches 21, 20, 19,18, 2 et 3.
ISR est la routine d’interruption qui sera appelée lorsque l’interruption surviendra. Cette
routine d’interruption est une fonction qui ne renvoit rien et qui ne prend aucun argument.
mode indique ce qui va provoquer l’interruption. Les valeurs possibles pour mode sont :
LOW : l’interruption est declenchee quand la broche concernee est LOW.
CHANGE : l’interruption est declenchee quand la broche concernee change d’etat
RISING : l’interruption est declenchee quand la broche concernée passe de
LOW a HIGH.
FALLING : l’interruption est declenchee quand la broche concernee passe de
HIGH a LOW.
Les modes de déclenchement sont le reflet direct des capacités du matériel et à ce titre
permettent la meilleure réactivité. Il faut savoir qu’il s’écoule presque 3μs entre le
déclenchement de l’interruption et l’exécution de la première instruction de l’ISR.

Une seconde fonction, detachInterrupt(...) permet de déconnecter l’ISR de la source


d’interruption. Son prototype est le suivant :

detachInterrupt(numero);

Où numero est le numero d’interruption, 0 ou 1 sur un Arduino Uno, 0 a 5 sur un


Arduino Mega.

Deux broches d’interruptions externes ne permettent pas de mettre en œuvre des


systèmes de grande taille. Heureusement, toutes les broches numériques de l’Arduino
Uno peuvent servir comme broches d’interruptions externes
Pour les interruptions Pcint: Comme la même ISR est exécutée pour toutes les broches qui
partagent sa source d’interruption, la discrimination de la broche qui a engendré
l’interruption, ou des broches en cas de changements simultanés, doit être faite dans la
routine d’interruption.
Ensuite, la richesse des modes de déclenchement de INT0 et INT1 n’existent pas pour les
sources PCINTx. Le seul mode est l’interruption sur changement d’etat, LOW vers HIGH et
HIGH vers LOW. Par conséquent si seul l’un des changements d’état intéresse le
programme, il faut également discriminer en allant lire l’etat de la broche qui a déclenche
l’interruption.
Heureusement il existe une bibliothèque qui gère toutes ces situations et qui permet,
d’une part, de retrouver une souplesse et une richesse comparable à celle de INT0 et INT1
et, d’autre part, de masquer la complexité du matériel.

Il faut toutefois garder à l’esprit que, comparé aux interruptions INT0 et INT1, l’usage des
interruptions PCINTx conduira à une exécution plus fréquente de l’ISR et a une réactivité
moindre puisque la bibliothèque prendra du temps pour déterminer la broche source et le
type de changement avant d’appeler votre fonction. On privilégiera donc les premières. Le
corollaire est que la fonction que vous écrivez n’est pas directement une ISR mais une
fonction appelée par l’ISR. En effet, il n’y a que 3 ISR, une pour chacune des trois sources
PCINT0, PCINT1 et PCINT2, mais vous pouvez définir autant de fonctions qu’il y a de
broches grâce à la bibliothèque la bibliothèque PinChangeInt.
Exemple

int pin = 13;


volatile int state = LOW; // déclaration d'une variable volatile

void setup()
{
pinMode(pin, OUTPUT);
attachInterrupt(0, blink, CHANGE); // attache l'interruption externe n°0 à la fonction blink
}

void loop()
{
digitalWrite(pin, state); // la LED reflète l'état de la variable
}

void blink() // la fonction appelée par l'interruption externe n°0


{
state = !state; // inverse l'état de la variable
}
Remarque

Quand une interruption est appelée, les autres interruptions sont automatiquement
désactivées par le µcontrôleur

Les variables partagées entre interruption et programme principal doivent être de


type volatile

Les opérations de lecture/écriture sur des variables de plus de 8 bits doivent être
protégées en désactivant les interruptions.

Exemple :
unsigned long c;
cli();
c = PulseCounts;
sei();

Les interruptions permettent à certaines tâches importantes de se produire en arrière-


plan et sont activées par défaut. Certaines fonctions ne fonctionneront pas lorsque les
interruptions sont désactivées et les communications entrantes peuvent être ignorées.
Cependant, les interruptions peuvent légèrement perturber la synchronisation du code
et peuvent être désactivées pour des sections de code particulièrement critiques.

Vous aimerez peut-être aussi