[Arduino] Principe d'utilisation d'un encodeur rotatif
Publié : dim. août 09, 2015 10:12 am
Un encodeur numérique, mais qu'est-ce que c'est que ça encore ?? C'est en fait très simple, et je suis sûr que vous en avez déjà utilisé : extérieurement, ça ressemble à un potentiomètre, mais ça n'a pas de butoir, ça semble aller indéfiniment d'un côté ou de l'autre. Il en existe même qui sont commandés par un moteur et bougent "tout seul" lorsqu'on l'actionne par le biais d'une télécommande, par exemple.
Légende : Encodeur rotatif. Ca ressemble à un potentiomètre, non ?
Dans le F16, concrètement, il s'agit du bouton qui permet de choisir le preset radio lorsqu'on est en mode backup. Vous remarquez que lorsqu'on arrive dans le cockpit, quelle que soit la position du bouton, le preset est toujours le même. Il faut tourner le bouton dans un sens ou dans l'autre pour faire défiler les presets, et cela boucle en permanence.
Donc, même si extérieurement ça ressemble un peu à un potentiomètre (ils ont tous les deux trois broches), son fonctionnement et sa fonction n'ont absolument aucun point commun. En effet, à la différence du potentiomètre, ce composant ne renvoie pas une valeur variable selon sa course, il ne renvoie que des 0 et des 1 : le courant passe ou ne passe pas. On dit qu'il envoie un signal carré.
Imaginez un disque alternant sur sa surface et de façon régulière un conducteur et un isolant. Disons que, par exemple, une rotation entière de l'axe du disque couperait 24 fois un circuit. On obtient alors une succession de courant passé / courant non passée. En binaire, ça donne une succession de 0 et de 1.
Mais comment exploiter ces 0 et ces 1, quel intérêt ? Finalement, pas grand-chose : cela nous permettra de savoir que le codeur est actionné, on pourrait aussi déterminer la vitesse de rotation, mais on ne pourrait pas en revanche savoir si le codeur est actionné dans le sens horaire ou anti horaire.
Légende : Simulation avec une seule entrée : du 5v est appliqué sur l'encodeur (signal haut). Lorsque celui-ci est actionné, le signal s'interrompt brièvement puis revient à son niveau original.
Pour pallier à ça, imaginez que l'axe de rotation de l'encodeur actionne deux disques, et surtout, que ces disques soient décalés. Là, ça va devenir intéressant, car selon l'état (0 ou 1) renvoyé par chaque disque, on pourra connaitre le sens de rotation.
Si on actionne l'encodeur dans le sens horaire.
Légende : Sur ce schéma logique, on voit les deux signaux (et haut et en bas) qui sont décalés (ils démarrent ensemble mais ne finissent pas ensemble).
Lorsqu'on actionne l'encodeur dans le sens horaire, les signaux du bas et du haut passent à 0v en même temps, puis celui du bas revient à 5v un peu avant celui du haut.
Si on actionne l'encodeur dans le sens anti-horaire,
Légende : Sur ce schéma logique, on voit les deux signaux (et haut et en bas) qui sont décalés (ils ne démarrent pas ensemble mais finissent ensemble).
Lorsqu'on actionne l'encodeur dans le sens anti-horaire, le signal du haut passe à 0v avant celui du bas passe, et les deux reviennent à 5v en même temps.
Une mise en garde : tous les encodeurs numériques n'ont pas le même schéma, par exemple celui-ci présente un signal différent. Il semble d’ailleurs que ce soit le mien qui soit « différent ».
Légende : schéma logique de fonctionnement d’un encodeur.
Légende : Analyse logique de l’encodeur que j’utilise (ici, actionné dans le sens horaire).
Ensuite, il faut programmer l’arduino pour exploiter ces signaux, malheureusement les différents essais que j’ai mené ont révélé que ce n’est pas si simple : de manière logique et théorique, mes programmes fonctionnaient, mais ils présentaient à l’usage des ratés de fonctionnement.
La cause de ces bugs venait des signaux que renvoie l’encodeur : parfois des valeurs parasites viennent se glisser dans les signaux renvoyés par l’encodeur.
Légende : anomalies remarqués avec l’analyseur logique.
En zoomant sur ces anomalies, on voit qu’elles sont très brèves (de l’ordre de quelques micro secondes) mais elles suffisent à causer des bugs.
Légende : Zoom sur une anomalie dans un signal.
Pour remédier à ce problème, la solution qui semble la plus propre est d’utiliser des condensateurs. Plusieurs schémas sont donnés sur Internet, je vous propose celui-ci :
Monté avec l’arduino, ça peut donner ça :
Quatre résistances de 10Kohm (valeur nominale pour effectuer un pull up avec arduino http://arduino.cc/en/Tutorial/DigitalPins) et deux condensateurs de 0.1micro farad filtrent le signal.
Mais pourquoi ces valeurs ?
Un condensateur agit comme un réservoir qui va mettre un certain temps pour que la tension à ses bornes s’équilibre. Ce « certain temps » est en rapport avec la résistance qui l’alimente (le couple résistance-condensateur se nomme circuit RC) selon la formule :
Cette « constante de temps » correspond à 63% de la charge totale du condensateur, et vu que la charge du condensateur n’est pas linéaire, il faut 5 constantes de temps pour que le condensateur soit chargé à près de 100%.
Nota : Sur le montage, les résistances qui chargent le condensateur sont en série (deux résistances de 10K?), leur valeur s’additionne.
Ce qui nous donne :
Temps = (10000 ohm x 2) x 0.0000001 farad = 0.002 seconde.
Cela signifie qu’avec deux résistance de 10K? (ou une de 20K?) et un condensateur de 0.1microfarad, il faut 0.002 seconde (2 millisecondes) pour que la tension aux bornes du condensateur soit à 3.15v (63% de 5v).
Dans notre cas, cela signifie surtout que les valeurs parasites de l’ordre de quelques micro secondes n’auront pas le temps d’envoyer un signal à l’arduino.
Maintenant que nous avons un signal « propre », il nous reste à écrire le programme.
Dans un premier temps, voyons le cas où l’encodeur est relié directement sur l’arduino, dans un second temps nous allons le faire fonctionner sur la ligne I2C.
Utilisation en direct sur l’arduino (UNO)
Le principe des interruptions est d'attacher une routine (un sous-programme / une procédure) à un port de l'arduino. Dit d'une autre façon, lorsque la tension d'une broche de l'arduino change, le programme exécute une fonction qu'on lui a désignée.
La commande est : attachInterrupt(int x,fonction,mode).
La séquence dans le sens horaire est 11 00 00 10, dans le sens anti horaire 11 10 00 11. Mais le programme génère une interruption pour chaque changement d’état de chaque ligne et lorsque les deux lignes vont être à 0 en même temps, il verra d’abord la séquence 01 puis 00.
Vu que programme répète et stocke l’état précédent avec l’état actuel, la suite renvoyés par l’encodeur et lue par le programme sera :
Légende : Fonctionnement « parfait » de l’encodeur, du filtre et du programme.
Légende : Test de l’encodeur. Remarquez l’analyseur logique qui nous permet d’avoir un visuel sur les signaux transmis. Celui-ci se branche en parallèle entre l’arduino et l’encodeur.
Utilisation via le bus I2C sur arduino (leonardo)
Reste maintenant à voir comment porter ce code sur l'arduino leonardo, et surtout l'utiliser par le bus IC2.
Pour ça, je vous propose d'utiliser le CI MCP23017, avec lesquels nous avons déjà travaillé.
Comme précédemment, on va tester l'état des deux broches de l'encodeur et les comparer avec les séquences qui correspondent à la rotation de l'encodeur.
Pour le circuit, après plusieurs tests j’ai très considérablement simplifié le schéma précédent : il s’avère qu’un condensateur de 0.1microfarad entre deux broches de l’encodeur donne un très bon résultat. Je ne comprends d’ailleurs pas trop pourquoi j’obtiens un si bon résultat en ne mettant qu’un seul condensateur (si quelqu’un peut me l’expliquer…)
Voilà le brochage :
Coté programmation, ça peut donner ça (utilisez la librairie MCP adafruit et les déclarations telles que nous les avons vues dans l'article « Ajout d'entrées/sorties binaires sur bus I2C ») :
En déclarations :
Dans la boucle du programme :
Et ça fonctionne comme prévu.
On peut d'ailleurs l'utiliser pour des fonctions qui ne sont pas nécessairement liées à un encodeur numérique dans le cockpit. Par exemple, en lui donnant les raccourcis clavier de Audio COM1 volume dec et Audio COM1 volume inc, vous pourrez utiliser l'encodeur comme "potentiomètre" et ajuster le volume du COM1.
Vous pouvez aussi mettre plusieurs encodeurs, ou utiliser l'encodeur pour changer la couleur du HUD (je sais, les puristes vont me haïr).
Blague mise à part, ce qu’il faut surtout retenir dans cet article c’est le principe de base de l’encodeur, et n’oubliez pas que l’encodeur que vous allez acheter n’aura peut-être pas la même séquence que celui que j’ai utilisé (et acheté au hasard chez un commerçant chinois sur ebay.com). D’ailleurs, les deux encodeurs que j’ai acheté au même moment au même fournisseur n’ont pas exactement le même comportement.
L’idéal est d’avoir la notice technique de votre composant et/ou d’utiliser un analyseur logique (une dizaine d’euros sur ebay, ma solution préférée).
Sinon, à défaut, vous pourrez toujours trouver la séquence des sorties par le programme que nous avons utilisé précédemment, c’est d’ailleurs le plus simple (et cela permet d’avoir la séquence exacte vue par le programme).
Légende : Encodeur rotatif. Ca ressemble à un potentiomètre, non ?
Dans le F16, concrètement, il s'agit du bouton qui permet de choisir le preset radio lorsqu'on est en mode backup. Vous remarquez que lorsqu'on arrive dans le cockpit, quelle que soit la position du bouton, le preset est toujours le même. Il faut tourner le bouton dans un sens ou dans l'autre pour faire défiler les presets, et cela boucle en permanence.
Donc, même si extérieurement ça ressemble un peu à un potentiomètre (ils ont tous les deux trois broches), son fonctionnement et sa fonction n'ont absolument aucun point commun. En effet, à la différence du potentiomètre, ce composant ne renvoie pas une valeur variable selon sa course, il ne renvoie que des 0 et des 1 : le courant passe ou ne passe pas. On dit qu'il envoie un signal carré.
Imaginez un disque alternant sur sa surface et de façon régulière un conducteur et un isolant. Disons que, par exemple, une rotation entière de l'axe du disque couperait 24 fois un circuit. On obtient alors une succession de courant passé / courant non passée. En binaire, ça donne une succession de 0 et de 1.
Mais comment exploiter ces 0 et ces 1, quel intérêt ? Finalement, pas grand-chose : cela nous permettra de savoir que le codeur est actionné, on pourrait aussi déterminer la vitesse de rotation, mais on ne pourrait pas en revanche savoir si le codeur est actionné dans le sens horaire ou anti horaire.
Légende : Simulation avec une seule entrée : du 5v est appliqué sur l'encodeur (signal haut). Lorsque celui-ci est actionné, le signal s'interrompt brièvement puis revient à son niveau original.
Pour pallier à ça, imaginez que l'axe de rotation de l'encodeur actionne deux disques, et surtout, que ces disques soient décalés. Là, ça va devenir intéressant, car selon l'état (0 ou 1) renvoyé par chaque disque, on pourra connaitre le sens de rotation.
Si on actionne l'encodeur dans le sens horaire.
Légende : Sur ce schéma logique, on voit les deux signaux (et haut et en bas) qui sont décalés (ils démarrent ensemble mais ne finissent pas ensemble).
Lorsqu'on actionne l'encodeur dans le sens horaire, les signaux du bas et du haut passent à 0v en même temps, puis celui du bas revient à 5v un peu avant celui du haut.
Si on actionne l'encodeur dans le sens anti-horaire,
Légende : Sur ce schéma logique, on voit les deux signaux (et haut et en bas) qui sont décalés (ils ne démarrent pas ensemble mais finissent ensemble).
Lorsqu'on actionne l'encodeur dans le sens anti-horaire, le signal du haut passe à 0v avant celui du bas passe, et les deux reviennent à 5v en même temps.
Une mise en garde : tous les encodeurs numériques n'ont pas le même schéma, par exemple celui-ci présente un signal différent. Il semble d’ailleurs que ce soit le mien qui soit « différent ».
Légende : schéma logique de fonctionnement d’un encodeur.
Légende : Analyse logique de l’encodeur que j’utilise (ici, actionné dans le sens horaire).
Ensuite, il faut programmer l’arduino pour exploiter ces signaux, malheureusement les différents essais que j’ai mené ont révélé que ce n’est pas si simple : de manière logique et théorique, mes programmes fonctionnaient, mais ils présentaient à l’usage des ratés de fonctionnement.
La cause de ces bugs venait des signaux que renvoie l’encodeur : parfois des valeurs parasites viennent se glisser dans les signaux renvoyés par l’encodeur.
Légende : anomalies remarqués avec l’analyseur logique.
En zoomant sur ces anomalies, on voit qu’elles sont très brèves (de l’ordre de quelques micro secondes) mais elles suffisent à causer des bugs.
Légende : Zoom sur une anomalie dans un signal.
Pour remédier à ce problème, la solution qui semble la plus propre est d’utiliser des condensateurs. Plusieurs schémas sont donnés sur Internet, je vous propose celui-ci :
Monté avec l’arduino, ça peut donner ça :
Quatre résistances de 10Kohm (valeur nominale pour effectuer un pull up avec arduino http://arduino.cc/en/Tutorial/DigitalPins) et deux condensateurs de 0.1micro farad filtrent le signal.
Mais pourquoi ces valeurs ?
Un condensateur agit comme un réservoir qui va mettre un certain temps pour que la tension à ses bornes s’équilibre. Ce « certain temps » est en rapport avec la résistance qui l’alimente (le couple résistance-condensateur se nomme circuit RC) selon la formule :
Code : Tout sélectionner
Constante de temps = Résistance x Capacité du condensateur -> Secondes = ohm x farad
Nota : Sur le montage, les résistances qui chargent le condensateur sont en série (deux résistances de 10K?), leur valeur s’additionne.
Ce qui nous donne :
Temps = (10000 ohm x 2) x 0.0000001 farad = 0.002 seconde.
Cela signifie qu’avec deux résistance de 10K? (ou une de 20K?) et un condensateur de 0.1microfarad, il faut 0.002 seconde (2 millisecondes) pour que la tension aux bornes du condensateur soit à 3.15v (63% de 5v).
Dans notre cas, cela signifie surtout que les valeurs parasites de l’ordre de quelques micro secondes n’auront pas le temps d’envoyer un signal à l’arduino.
Maintenant que nous avons un signal « propre », il nous reste à écrire le programme.
Dans un premier temps, voyons le cas où l’encodeur est relié directement sur l’arduino, dans un second temps nous allons le faire fonctionner sur la ligne I2C.
Utilisation en direct sur l’arduino (UNO)
Le principe des interruptions est d'attacher une routine (un sous-programme / une procédure) à un port de l'arduino. Dit d'une autre façon, lorsque la tension d'une broche de l'arduino change, le programme exécute une fonction qu'on lui a désignée.
La commande est : attachInterrupt(int x,fonction,mode).
- numéro désigne le port de l'arduino ( sur la uno, se sont les broches 2(int 0) et 3(int 1), sur la léonardo, les broches 2(int 0), 3(int 1), 0(int 2), 1(int 3) et 7(int 4) ).
- fonction désigne la fonction à appeler.
- mode désigne la condition pour déclencher l'interruption :
- --LOW : la broche est en position basse
- --CHANGE : la broche change d'état
- --RISING : la broche passe de l'état bas vers l'état haut
- --FALLING : la broche passe de l'état haut vers l'état bas
La séquence dans le sens horaire est 11 00 00 10, dans le sens anti horaire 11 10 00 11. Mais le programme génère une interruption pour chaque changement d’état de chaque ligne et lorsque les deux lignes vont être à 0 en même temps, il verra d’abord la séquence 01 puis 00.
Vu que programme répète et stocke l’état précédent avec l’état actuel, la suite renvoyés par l’encodeur et lue par le programme sera :
- 1101 0100 0001 0111 lorsque l’encodeur est tourné dans le sens horaire.
- 1101 0100 0010 1011 lorsque l’encodeur est tourné dans le sens antihoraire.
Code : Tout sélectionner
byte portA = 2;
byte portB = 3;
int Compteur = 0;
byte etat = 0;
void setup() {
pinMode(portA, INPUT);
pinMode(portB, INPUT);
// digitalWrite(portA, HIGH); //Inutile
// digitalWrite(portB, HIGH); //Inutile
attachInterrupt(0, Rotation, CHANGE);
attachInterrupt(1, Rotation, CHANGE);
Serial.begin(57600);
}
void loop() {}
void Rotation() {
etat = etat << 1 | digitalRead(portA);
etat = etat << 1 | digitalRead(portB);
byte etat_test = (etat | B11110000) - B11110000;//test des 4 bits de poids faible
//Serial.println(etat_test,BIN);//débug : visualisation de la séquence
if ( etat_test == B0111 ){
Compteur++;
Serial.println(Compteur);
} else if ( etat_test == B1011 ){
Compteur--;
Serial.println(Compteur);
}
}
Légende : Fonctionnement « parfait » de l’encodeur, du filtre et du programme.
Légende : Test de l’encodeur. Remarquez l’analyseur logique qui nous permet d’avoir un visuel sur les signaux transmis. Celui-ci se branche en parallèle entre l’arduino et l’encodeur.
Utilisation via le bus I2C sur arduino (leonardo)
Reste maintenant à voir comment porter ce code sur l'arduino leonardo, et surtout l'utiliser par le bus IC2.
Pour ça, je vous propose d'utiliser le CI MCP23017, avec lesquels nous avons déjà travaillé.
Comme précédemment, on va tester l'état des deux broches de l'encodeur et les comparer avec les séquences qui correspondent à la rotation de l'encodeur.
Pour le circuit, après plusieurs tests j’ai très considérablement simplifié le schéma précédent : il s’avère qu’un condensateur de 0.1microfarad entre deux broches de l’encodeur donne un très bon résultat. Je ne comprends d’ailleurs pas trop pourquoi j’obtiens un si bon résultat en ne mettant qu’un seul condensateur (si quelqu’un peut me l’expliquer…)
Voilà le brochage :
Coté programmation, ça peut donner ça (utilisez la librairie MCP adafruit et les déclarations telles que nous les avons vues dans l'article « Ajout d'entrées/sorties binaires sur bus I2C ») :
En déclarations :
Code : Tout sélectionner
//encodeur rotatif
byte enrot_1_a = 0;
byte enrot_1_a_old = 0;
byte enrot_1_b = 0;
byte enrot_1_b_old = 0;
byte etat = 0;
Code : Tout sélectionner
//Partie encodeur rotatif
enrot_1_a = mcp[1].digitalRead(1);
enrot_1_b = mcp[1].digitalRead(2);
if ( (enrot_1_a != enrot_1_a_old) || (enrot_1_b != enrot_1_b_old) ){
etat = etat << 1 | enrot_1_a;
etat = etat << 1 | enrot_1_b;
byte etat_test = (etat | B11110000) - B11110000;
if (etat_test == B1100){
// sens horaire
clavier ('y',alt); //SimCycleRadioChannel UHF-Channel cycle.
} else if (etat_test == B1101){
// sens anti-horaire
clavier ('y',shift|alt); //SimDecRadioChannel UHF-Dec Radio Channal
}
}
enrot_1_a_old = enrot_1_a;
enrot_1_b_old = enrot_1_b;
Et ça fonctionne comme prévu.
On peut d'ailleurs l'utiliser pour des fonctions qui ne sont pas nécessairement liées à un encodeur numérique dans le cockpit. Par exemple, en lui donnant les raccourcis clavier de Audio COM1 volume dec et Audio COM1 volume inc, vous pourrez utiliser l'encodeur comme "potentiomètre" et ajuster le volume du COM1.
Vous pouvez aussi mettre plusieurs encodeurs, ou utiliser l'encodeur pour changer la couleur du HUD (je sais, les puristes vont me haïr).
Blague mise à part, ce qu’il faut surtout retenir dans cet article c’est le principe de base de l’encodeur, et n’oubliez pas que l’encodeur que vous allez acheter n’aura peut-être pas la même séquence que celui que j’ai utilisé (et acheté au hasard chez un commerçant chinois sur ebay.com). D’ailleurs, les deux encodeurs que j’ai acheté au même moment au même fournisseur n’ont pas exactement le même comportement.
L’idéal est d’avoir la notice technique de votre composant et/ou d’utiliser un analyseur logique (une dizaine d’euros sur ebay, ma solution préférée).
Sinon, à défaut, vous pourrez toujours trouver la séquence des sorties par le programme que nous avons utilisé précédemment, c’est d’ailleurs le plus simple (et cela permet d’avoir la séquence exacte vue par le programme).