Détecter un front montant ou descendant sur une entrée de la carte Arduino : utilisation des entrées interruptions (attachInterrupt)
Sur une carte Arduino Uno, seuls les pins 2 et 3 peuvent détecter des fronts montants (passage de l'état 0 à 1) et descendants (passage de l'état 1 à 0). Ces 2 pins sont nommés pins avec interruption (nommées respectivement 0 et 1).
Fonctionnement d'un codeur incrémental
Un codeur incrémental permet de mesurer la position et la vitesse réelles de l'axe d'un moteur. Il est utile si l'on souhaite réaliser un asservissement de position ou de vitesse.
Rappel (voir fonctionnement ici).
- compter le nombre inc (nombre de fronts) permet de déterminer la position angulaire de l'axe du moteur ;
- calculer la variation de ce nombre inc pendant une durée dt, permet de déterminer la vitesse moyenne de rotation de l'axe du moteur en incréments par seconde (inc/s).
Le nombre inc s'obtient via l'algorithme suivant :
- si, après un front (montant ou descendant) du capteur A, état capteur A = état capteur B, alors : inc = inc + 1
- si, après un front (montant ou descendant) du capteur A, état capteur A ≠ état capteur B, alors : inc = inc - 1
Ainsi, il est nécessaire de connaître l'instant où il existe un front du capteur A afin d'exécuter l'algorithme ci-dessus.
Exemple de montage : codeur incrémental 2 voies associé à un moteur
Un codeur incrémental 2 voies (constitué de 2 capteurs à effet hall décalés angulairement de 90°) est associé à un moteur.
Afin de détecter les fronts (montant ou descendant) du capteur A, il est nécessaire de le brancher sur une entrée avec interruption (pin 2 ou 3) de la carte Arduino, alors que le capteur B sera quant à lui branché sur une entrée numérique quelconque.
Si cela n'a pas été fait, brancher les entrées des capteurs A et B du montage ci-dessous.
NB : pour le codeur incrémental de ce moteur, 1 inc = 180°.
Structure du code
L'intérêt d'un pin interruption est qu'il permet, comme son nom l'indique, d'interrompre le déroulement du programme (void loop) aux changements de son état, pour effectuer un traitement spécifique (à l'aide d'une fonction qui consiste, en l'occurrence, à la mise à jour d'un compteur d'incréments).
Après traitement, le programme principal (void loop) reprend.
Ainsi, il faut :
• dans l'entête déclarative : déclarer les variables associées aux numéros des pins où sont branchés les entrées (VoieApin, VoieBpin,) et déclarer en « volatile »
toute variable qui sera modifiée par une fonction interruption (comme par exemple la variable inc d'un compteur d'impulsion) ;
• dans la fonction void setup() :
- définir les pins utilisés, comme des entrées, à l'aide de la fonction « pinMode »,
- activer l'écoute du pin interruption (attachInterrupt) en définissant le mode de fonctionnement : appel sur front montant (RISING), ou descendant (FALLING) ou les deux (CHANGE).
• en dehors des fonctions setup et loop : définir la fonction appelée à chaque interruption. Celle-ci modifiera le compteur d'incréments en fonction de l'état des capteurs A et B.
Code pour déterminer une position (compter les incréments)
int VoieApin=2, VoieBpin=7 ; // déclare les variables VoieApin, VoieBpin comme des entiers de valeurs respectives 2 et 7 (2 et 7 sont les repères des pins utilisés)
volatile int inc=0; // déclare la variable inc comme un entier de valeur 0, volatile car il s'agit d'une variable qui sera modifiée par une fonction interruption
void setup() {
pinMode(VoieApin,INPUT); //définit le pin VoieApin (ici 2) comme une entrée
pinMode(VoieBpin,INPUT); //définit le pin VoieBpin (ici 7) comme une entrée
attachInterrupt (0, fonctionCodeur, CHANGE); // active l'écoute du pin interruption 0 (qui correspond au pin 2). Chaque changement d'état (front montant ou descendant) de ce pin interrompt le programme « void loop » et lance la fonction fonctionCodeur ()
}
void fonctionCodeur() { // fonction appelée à chaque interruption. Incrémente ou décrémente inc
if (digitalRead(VoieApin) == digitalRead(VoieBpin)) {
inc=inc+1;
}
else {
inc=inc-1;
}
}
Code pour déterminer une vitesse
La vitesse de rotation du moteur en incréments par seconde (inc/s) est définie comme la variation du compteur d'incréments pendant une durée dT. Il s'agit donc d'une valeur moyenne sur le pas de temps dT.
Au code précédent (qui compte les incréments), il faut ajouter une boucle de temporisation qui ne bloque pas les interruptions (boucle do while).
int VoieApin=2, VoieBpin=7 ; // déclare les variables VoieApin, VoieBpin comme des entiers de valeurs respectives 2 et 7 (2 et 7 sont les repères des pins utilisés)
volatile int inc=0; // déclare la variable inc comme un entier de valeur 0, volatile car il s'agit d'une variable qui sera modifiée par une fonction interruption
int vitesse; // vitesse en incréments/seconde
long T0,T1; // variables pour gérer le pas de temps
long dT=100000; // pas de temps en microsecondes
void setup() {
pinMode(VoieApin,INPUT); //définit le pin VoieApin (ici 2) comme une entrée
pinMode(VoieBpin,INPUT); //définit le pin VoieBpin (ici 7) comme une entrée
attachInterrupt (0, fonctionCodeur, CHANGE); // active l'écoute du pin interruption 0 (qui correspond au pin 2). Chaque changement d'état (front montant ou descendant) de ce pin interrompt le programme « void loop » et lance la fonction fonctionCodeur ()
Serial.begin(115200);
}
void loop() {
inc=0; // initialisation du compteur à chaque pas de temps
T0=micros(); // durée de fonctionnement en microsecondes depuis le lancement du programme
// attente pendant la durée du pas de temps dT (sans blocage des interruptions)
do {
T1=micros(); //T1 augmente petit à petit (car il est dans la boucle do while), pendant ce temps les interruptions ne sont pas bloquées et la fonction codeur fonctionne, en incrémentant ou décrémentant inc
} while (T1-T0<dT); //dès que le pas de temps dT est atteint, le programme sort de la boucle do while, et exécute la ligne suivante qui calcule la vitesse
vitesse = inc/(dT*0.000001); // calcule la vitesse moyenne durant le pas de temps dT, ramenée en incréments/secondes (compte inc incréments en dT microsecondes)
Serial.println(vitesse);
}
void fonctionCodeur() { // fonction appelée à chaque interruption. Incrémente ou décrémente inc
if (digitalRead(VoieApin) == digitalRead(VoieBpin)) {
inc=inc+1;
}
else {
inc=inc-1;
}
}