Détecter un front montant ou descendant sur une entrée interruption (attachInterrupt) : codeur
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 à partir de ces informations.
Rappel (voir fonctionnement ici).
- compter le nombre de points (nombre de fronts) permet de déterminer la position angulaire de l'axe du moteur ;
- calculer la variation de ce nombre de points pendant une durée dt, permet de déterminer la vitesse moyenne de rotation de l'axe du moteur en points par seconde (pts/s).
Le nombre de points s'obtient via l'algorithme suivant :
- si, après un front (montant ou descendant) du capteur A, état capteur A = état capteur B, alors : point = point + 1
- si, après un front (montant ou descendant) du capteur A, état capteur A ≠ état capteur B, alors : point = point - 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 canaux, 3 paires de pôles (aimant), associé à un moteur
Un codeur incrémental 2 canaux (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 (canal A et canal B) du montage ci-dessous.
Le disque magnétique central solidaire du rotor comprend 3 aimants, donnant 3 fronts montants et 3 fronts descendants par tour, et par capteur, soit 12 points par tour. Ainsi pour ce codeur incrémental, 1 point = 30°. (NB : on parle bien de tour de rotor moteur, et non l’axe de sortie du réducteur, au quel cas il faut prendre en compte le rapport de réduction du réducteur, ici 52,6).

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 une fonction spécifique. Cette fonction consiste, en l'occurrence, à la mise à jour d'un compteur de points.
Après traitement de la fonction attachée à l’interruption, le programme principal (void loop) reprend.
Rappel : Sur une carte Arduino Uno, seuls les pins 2 et 3 peuvent être utilisés comme pinavec interruption (nommées respectivement 0 et 1).
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 (CanalApin, CanalBpin,) et déclarer en « volatile »
toute variable qui sera modifiée par une fonction interruption (comme par exemple la variable point 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 de points en fonction de l'état des capteurs A et B.
Code pour déterminer une position (compter les points)
int CanalApin=2, CanalBpin=7 ; // déclare les variables CanalApin, CanalBpin comme des entiers de valeurs respectives 2 et 7 (2 et 7 sont les repères des pins utilisés)
volatile int point=0; // déclare la variable point 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(CanalApin,INPUT); //définit le pin CanalApin (ici 2) comme une entrée
pinMode(CanalBpin,INPUT); //définit le pin CanalBpin (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(CanalApin) == digitalRead(CanalBpin)) {
point=point+1;
}
else {
point=point-1;
}
}
Code pour déterminer une vitesse (en points/s)
La vitesse de rotation du moteur en points par seconde (pts/s) est définie comme la variation du compteur de points 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 points), il faut ajouter une boucle de temporisation qui ne bloque pas les interruptions (boucle do while).
int CanalApin=2, CanalBpin=7 ; // déclare les variables CanalApin, CanalBpin comme des entiers de valeurs respectives 2 et 7 (2 et 7 sont les repères des pins utilisés)
volatile int point=0; // déclare la variable point 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 points/seconde
long T0,T1; // variables pour gérer le pas de temps
long dT=100000; // pas de temps en microsecondes
void setup() {
pinMode(CanalApin,INPUT); //définit le pin CanalApin (ici 2) comme une entrée
pinMode(CanalBpin,INPUT); //définit le pin CanalBpin (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() {
point=0; // initialisation du compteur à chaque pas de temps
T0=micros(); // durée de fonctionnement en microsecondes depuis le lancement du programme
à saisir en en fin de la fonction loop// 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 point
} 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 = point/(dT*0.000001); // calcule la vitesse moyenne durant le pas de temps dT, ramenée en points/seconde (compte le nombre de points en dT microsecondes)
Serial.println(vitesse);
}
void fonctionCodeur() { // fonction appelée à chaque interruption. Incrémente ou décrémente point
if (digitalRead(CanalApin) == digitalRead(CanalBpin)) {
point=point+1;
}
else {
point=point-1;
}
}