[Arduino] Principe d'utilisation d'un encodeur rotatif

Fabrication de cockpit et divers ( palonnier , panels ..) c est ICI
Avatar de l’utilisateur

Topic author
Topper Harley.
Nouvelle Recrue
Nouvelle Recrue
Messages : 28
Inscription : 13 avril 2012

[Arduino] Principe d'utilisation d'un encodeur rotatif

#1

Message par Topper Harley. »

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.

Image
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.

Image
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.
Image
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,
Image
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 ».
Image
Légende : schéma logique de fonctionnement d’un encodeur.
Image
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.

Image
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.
Image
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.
Image
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 :
Image

Monté avec l’arduino, ça peut donner ça :
Image

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
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).
  • 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
Le programme va vérifier des changements d'état des deux lignes de l'encodeur. Dès qu’il note une différence de signal, il va les stocker dans un octet, ce qui va donner des séquences différentes selon si l’encodeur est tourné dans le sens horaire ou anti horaire.

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.
Il est (à mon humble avis) inutile et « buggogène » de filtrer sur la séquence entière, un test sur 4 octets suffit :

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); } }

Image
Légende : Fonctionnement « parfait » de l’encodeur, du filtre et du programme.

Image
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 :
Image
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;
Dans la boucle du programme :

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).
Avatar de l’utilisateur

jdaou
Elève Pilote
Elève Pilote
Messages : 652
Inscription : 10 mars 2003

Re: [Arduino] Principe d'utilisation d'un encodeur rotatif

#2

Message par jdaou »

Salut,

dommage qu'il soit perdu au fond du forum, car je cherche exactement ça depuis 1 bonne heure :cheer:

J'ai pour projet de me faire un petit boitier complémentaire ( donc vraiment pas un pit perso ) qui me facilitera la vie sur les appareils que je pilote sur DCS.
Pour le moment, j'ai besoin de quelques encodeurs, pour l'altimètre et le HSI, de quelques afficheurs 7 segments, et de quelques switch.

merci encore pour le tuto.
Avatar de l’utilisateur

Vico
Pilote Philanthrope
Pilote Philanthrope
Messages : 4122
Inscription : 16 avril 2009

Re: [Arduino] Principe d'utilisation d'un encodeur rotatif

#3

Message par Vico »

C'est vrai qu'il y a eu une série d'articles sur la programmation Arduino par Topper :notworthy qui méritent d'être épinglés pour les retrouver plus facilement.
Si un WM passe par là... :yes:
Image
Pilote indépendant de F16 block 50/52 sur Falcon BMS - HOTAS Cougar modé FCC1 - Saitek Pro Rudder Pedals - ED TRacker - 2x MFD Cougar - ICP "CatPlombe" - 1x Carte Pokeys - un cockpit F16 en chantier - CM MSI B650 Gaming ; AMD Ryzen 7800X3D 4,20GHz ; 32 Go DDR5 Corsair ; AMD Radeon RX7900XTX 24Go DDR6 ; Ecran Hisense 55" 4K
Avatar de l’utilisateur

Diabolo_101
Nouvelle Recrue
Nouvelle Recrue
Messages : 73
Inscription : 02 mai 2003

Re: [Arduino] Principe d'utilisation d'un encodeur rotatif

#4

Message par Diabolo_101 »

Je plussoie, ces articles sont une mine d'informations. Je les ai en favoris, mais les épingler seraient cool, ils sont très didactiques et pourraient intéresser pas mal de monde.
Répondre

Revenir à « Cockpit & mod perso »