Aller au contenu

Introduction à l’encodeur rotatif incrémental, de type mécanique (avec explication de fonctionnement, vue interne, schéma de câblage à un arduino, et exemples de code avec le KY-040)

Tuto encodeur rotatif arduino ky-040, hw-040, et EC11, introduction au rotary encoder incrémental de type mécanique, avec explication fonctionnement

Envie d’utiliser des encodeurs rotatifs « modernes », plutôt que de « vieux » potentiomètres analogiques là où c’est possible, dans vos prochains projets? Alors vous êtes au bon endroit 😉

Ici, je vous propose de découvrir les encodeurs rotatifs incrémentaux, de type mécanique (comme le « très connu » KY-040, facilement raccordable à un Arduino, ou à un Raspberry). Nous verrons ici comment ils fonctionnent, comment câbler un KY040 à un arduino, et surtout, comment interpréter les signaux CLK/A et DT/B d’un KY 040, pour en déduire le sens de rotation, et le nombre de crans parcourus. Ça vous dit ? Alors en avant !

Ce contenu vous plaît ? Alors abonnez-vous à la Newsletter pour ne rien louper !

Ce tuto n’est qu’une introduction aux encodeurs rotatifs, se limitant aux modèles incrémentaux, de type mécanique. Gardez donc bien à l’esprit que le monde des rotary encoder ne se limite pas à ce que je présente ici (bien au contraire !). Du reste, comme toujours, si vous relevez la moindre erreur ou coquille, n’hésitez pas à m’en faire part en zone commentaire, tout en bas de cette page. Encore merci à vous !

AperçuDescriptionLien achat
Encodeur rotatif incrémental EC11 à souder sur circuit imprimé PCB, fixation par soudure, modèle avec écrou de fixation autour de l'axe de rotationEncodeur rotatif incrémental EC-11 (modèle soudable sur carte PCB) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique
Module KY 040 avec headers soudés, carte électronique pour raccordement à l'aide de fils dupont, circuit imprimé équipé pour montages élecEncodeur rotatif incrémental KY-040 (modèle raccordable via fils dupont) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique

Qu’est-ce qu’un un encodeur rotatif, en comment ça fonctionne ?

Vous avez sûrement dû remarquer la présence de boutons rotatifs multifonctions au niveau de certains autoradios ou certaines chaînes hifi (pour régler le volume, ou naviguer dans les menus), ainsi qu’au niveau de certains écrans de contrôle d’imprimantes 3D ou CNC, par exemple. En fait, il s’agit d’encodeurs rotatifs. En apparence, ce sont des boutons que l’on peut tourner « à l’infini », dans un sens, comme dans l’autre. Et en terme de fonctionnalités, ils permettent le plus souvent d’abaisser ou augmenter une valeur donnée (comme le volume d’un autoradio), ou d’aller vers le haut ou vers le bas, dans un menu de navigation.

Exemple d'encodeurs rotatifs en pratique, sur consoles d'imprimante 3D, de CNC, d'autoradio pour réglage multifonction, tutoriel avec code

Par définition, un encodeur rotatif (ou « Rotary Encoder », en anglais) est un dispositif mécanique ou électronique, essentiellement constitué d’un bouton rotatif, qui lorsqu’on le tourne, actionne 2 contacts électriques déphasés. Et en suivant l’activité de ces contacts électriques, on peut en déduire le sens de rotation et l’angle de rotation « parcouru » (ou le nombre de crans parcourus, dans le cas d’un encodeur à X crans par tour).

De manière générale, les encodeurs rotatifs sont soit vendus « seuls », ou « montés sur carte PCB ». En voici quelques exemples, parmi tant d’autres :

ModèleEC11HW-040KY-040
AperçuEncodeur rotatif EC11 à souder sur circuit imprimé PCB, composant à 5 broches traversantes, contacts A CLK et B DT, avec bouton poussoir centralRotary encoder HW040 avec capuchon, incrémental à 30 crans par tour, composant mécanique pour réglages électroniques rapidesEncodeur incrémental KY040 avec bouton chapeau de réglage, filetage à la base de l'axe central, poussoir intégré SW, pour électronique
Type de produitNu (à souder « à la main »)Fixé sur PCBFixé sur PCB
Filetage axe ?OuiNonOui
Nbre de crans par tour203020
État des contacts lorsqu’on passe d’un cran à l’autreÉtat identique au précédentÉtat inverséÉtat identique au précédent
Bouton poussoir intégré ?OuiOuiOui
Lien (description/prix)Encodeur rotatif EC11 Lien externe produit ou composant électronique, pour projet ou montage à faire soi-même, external link passion élecEncodeur rotatif HW-040 Lien externe produit ou composant électronique, pour projet ou montage à faire soi-même, external link passion élecEncodeur rotatif KY-040 Lien externe produit ou composant électronique, pour projet ou montage à faire soi-même, external link passion élec

À noter qu’il existe de 2 familles de rotary encoder, qu’ils soient ou non montés sur PCB. En effet, il y a :

  • les encodeurs absolus, qui « donnent » une valeur comprise entre 0 et une valeur maximale (en fonction de l’angle de rotation effectué, sur le bouton rotatif)
  • et les encodeurs incrémentaux, qui, pour chaque cran parcouru, activent et désactivent des contacts électriques (de manière purement mécanique, magnétique, ou via faisceaux optiques ou laser)

Remarque : optionnellement, les rotary encoder peuvent également être munis d’un bouton poussoir central, « caché » sous le bouton rotatif ; cela leur permet d’avoir une fonction supplémentaire, sans prendre plus de place !

À présent, je vais uniquement vous parler des encodeurs incrémentaux de type mécanique.

Pour commencer, vous vous demandez sûrement comment fonctionne un encodeur rotatif incrémental de type mécanique ? En fait, c’est compliqué et simple à la fois, comme vous allez le voir !

Tout d’abord, il faut savoir que ce type d’encodeur rotatif est constitué d’un bouton rotatif central (axe), que l’on peut tourner dans un sens comme dans l’autre. À la base de cet axe est fixé un disque, aussi appelé « roue encodeuse » ; ce disque est conducteur par endroits, et isolant à d’autres. Sur ce disque viennent principalement reposer 2 contacts, qui sont disposés à des endroits particuliers. Et ces contacts sont « ouverts » ou « fermés » selon s’ils se trouvent dans une zone isolante ou conductrice du disque.

Tout ceci ne vous semble pas très clair ? Pas d’inquiétude, car je vais vous illustrer tout ça !

Pour ce faire, commençons tout simplement par jeter un coup d’œil à l’intérieur d’un encodeur rotatif (là il s’agit du modèle HW-040, comme présenté dans le tableau précédent, mais cette fois-ci « ouvert en deux » !). Ainsi, cela vous parlera certainement plus, que de longs discours 😉

Vue intérieur HW-040 contacts et roue encodeuse, avec sorties CLK DT et SW, alimentation PCB avec GND et +, détail mécanique interne à 30 crans par tour
Inside HW040 rotary encoder avec contacts DT et CLK détaillés et lamelles visibles, roue encodeuse crantée couplée à avec de bouton rotatif central

Comme vous pouvez le constater, on remarque la présence :

  • de lamelles de contacts conductrices (qui sont en fait reliées aux pins CLK, DT, et COMMUN, notées sur le PCB)
  • et d’une roue encodeuse (disque), partiellement conductrice d’un côté, et équipée de crans de l’autre

Maintenant, pour bien saisir comment fonctionne l’ensemble, laissez-moi vous représenter cela de manière schématique :

Contacts CLK et DT sur roue encodeuse d'un HW 040, disque à 30 crans par tour, détail interne de ce rotary encoder pour montages électroniques

Sur cette image, vous remarquerez tout d’abord que les contacts se font entre CLK et COMMUN, et DT et COMMUN, et ce, selon comment est tournée la roue encodeuse (les zones blanches de la roue étant conductrices, et les zones noires isolantes).

Et si vous regardez de plus près encore, vous constaterez également que ces contacts sont légèrement décalés, si je puis dire, d’un des axes de symétrie de la roue. Du coup, selon comment est positionnée la roue encodeuse, vis à vis de ces contacts, on peut observer différents cas de figure. En effet, on peut avoir :

  • deux contacts, chacun situé sur une zone blanche
  • un contact situé sur une zone blanche, et l’autre sur une zone noire (comme c’est le cas, représenté à « un instant donné » ci-dessus)
  • ou deux contacts, chacun situé sur une zone noire

En fait, cette disposition particulière permet d’avoir des contacts qui « s’ouvrent » et se « ferment » de manière déphasée, lorsqu’on tourne le bouton d’un encodeur rotatif. Qui plus est, ces contacts ne s’ouvrent et ne se ferment pas « dans le même ordre », selon si on tourne dans un sens, ou dans l’autre.

Pour être plus parlant encore, voici un graphe qui présente l’ouverture et la fermeture des contacts du HW-040, selon si l’on tourne l’axe central dans le sens des aiguilles d’une montre (sens horaire), ou dans le sens inverse (sens antihoraire).

Chronogrammes des contacts CLK et DT d'un encodeur rotatif HW-040, signaux en avance ou retard de phase, suivant sens de rotation de l'axe

Au final, vous l’aurez compris : un encodeur rotatif incrémental est donc un bouton rotatif, qui ouvre et ferme ses contacts à des moments précis, et ce, différemment selon dans quel sens on tourne le bouton.

Remarque : vous verrez parfois noté « A » et « B » au lieu de « CLK » et « DT ». En fait, cela vient du fait que la « vraie » dénomination de l’encodeur intégré dans ces mini-plaquettes PCB est bien noté A et B, au niveau de ses contacts, et non CLK et DT. Mais les fabricants de ces cartes prêtes à l’emploi, avec « encodeur rotatif » monté dessus, ont préféré nommer « CLK » la ligne A, et « DT » la ligne B. Ne me demandez pas pourquoi 😉

Enfin, voici une façon schématique de représenter ce type d’encodeur rotatif (pris seul, et non monté sur PCB, j’entends), lorsqu’on saisit ses schémas électroniques :

Symbole représentation encodeur rotatif incrémental mécanique type EC11, ou dans KY-040, avec contacts A et B ainsi que poussoir SW

Nota : ici, je vous ai pris en exemple un rotary encoder modèle EC11 « nu », dont l’équivalent équipe les modules KY-040 (dont nous allons parler ensuite). Si vous souhaitez simplement représenter un module tout équipé, tel que le HW040 présenté précédemment, vous auriez simplement eu à représenter un bloc, avec ses 5 pins raccordées dessus (qui correspondent à GND, +, SW, DT, et CLK).

AperçuDescriptionLien achat
Encodeur rotatif incrémental EC11 à souder sur circuit imprimé PCB, fixation par soudure, modèle avec écrou de fixation autour de l'axe de rotationEncodeur rotatif incrémental EC-11 (modèle soudable sur carte PCB) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique
Module KY 040 avec headers soudés, carte électronique pour raccordement à l'aide de fils dupont, circuit imprimé équipé pour montages élecEncodeur rotatif incrémental KY-040 (modèle raccordable via fils dupont) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique

Encodeurs rotatifs VS potentiomètres (avantages et inconvénients)

En intro, je vous parlais de changer nos « vieux potentiomètres » par de « modernes encodeurs numériques », dans ses futurs projets. Avant tout, il faut bien comprendre que vous ne pourrez pas littéralement remplacer l’un par l’autre, sur des cartes déjà existantes, par exemple. Car non seulement le brochage n’est pas identique (ni même égal), mais qui plus est, ces pièces n’ont pas du tout les mêmes caractéristiques.

En effet :

  • un potentiomètre dispose d’une résistance donnée, avec un point milieu mobile (pouvant ainsi faire varier la valeur ohmique en point milieu, selon de combien on tourne l’axe du potentiomètre)
  • un encodeur rotatif dispose de contacts électriques, s’activant de manière séquentielle et déphasée, suivant de combien on tourne l’axe rotatif, et suivant le sens de rotation

Vous ne pourrez donc pas remplacer, de manière brute, un potentiomètre par un encodeur rotatif (sans bidouille aucune, j’entends). Par contre, vous pourrez « aisément » intégrer ces encodeurs rotatifs « modernes » à vos nouveaux projets, et par conséquent, vous passer de potentiomètres 😉

Du reste, voici grosso modo les avantages et inconvénients des encodeurs rotatifs, sur les potentiomètres analogiques :

AVANTAGES
  • sur un encodeur rotatif, il n’y a ni butée à gauche, ni butée à droite ; cela permet de pouvoir faire plusieurs tours pour avant d’atteindre une valeur mini ou maxi, et ainsi, les encodeurs rotatifs permettent de travailler plus « finement », d’un certain point de vue
  • une seule référence de rotary encoder (ou presque !), quelque soit ses besoins (contrairement aux potentiomètres analogiques, où il faut sélectionner une valeur ohmique particulière, pour chacun de ses besoins)
  • la présence de crans permet d’y aller « pas à pas », lorsqu’on tourne un bouton d’un encodeur rotatif (alors qu’un potentiomètre ne permet qu’un réglage « grossier », lorsqu’on « déplace » le curseur)
  • quasiment aucune dérive en fonction de la température et du temps sur un encodeur rotatif (alors qu’avec un potentiomètre, la résistance pourra varier avec le temps, et selon les conditions auxquelles elle est soumise … notamment de t°)
  • permet d’avoir un bouton multifonctions, très utile pour concevoir une navigation pour menus
INCONVÉNIENTS
  • un encodeur rotatif nécessite quelques composants alentours, avec idéalement un interfaçage avec microcontrôleur (ce qui est bien plus « sophistiqué » à mettre en œuvre, qu’un simple potentiomètre !)
  • certains encodeurs rotatifs ont des comportements différents, à brochage identique (nombre de pas différents, état des sorties identique ou inversé d’un cran à l’autre, …)

Présentation du module KY-040 (encodeur rotatif « idéal », pour débuter sur breadboard)

Maintenant que nous avons vu bon nombre de généralités, passons à des choses plus concrètes ! Pour cela, je vous propose la découverte du module KY-040, qui est en fait un encodeur rotatif monté sur PCB, et « prêt à l’emploi » grâce à ses petits composants/broches additionnels 😉

Physiquement, le KY 040 se présente de la manière suivante :

Brochage KY-040 module avec résistances pull-up intégrées, prêt à brancher sur Arduino ou Raspberry avec fils dupont via connecteur mâle

Au niveau du brochage, on retrouve :

  • une broche CLK et une broche DT, qui sont des liens vers nos « fameux » contacts, reliés à l’intérieur même de l’encodeur (comme nous l’avons vu en intro, au 1er §)
  • une broche SW, qui est un lien vers un des contacts du bouton poussoir placé sous l’axe rotatif de l’encodeur (pour rappel, il s’agit là d’un contact additionnel, et indépendant du reste)
  • une broche +, au travers de laquelle on alimentera certains composants de la platine PCB (les résistances pull-up, pour être plus précis) ; la tension à fournir est typiquement du +3,3 ou +5 VDC
  • et une broche GND, qui correspond à la masse, donc

Pour bien comprendre comment cette mini plaquette KY-040 est « câblée » en interne, voici son schéma électrique :

Schéma KY-040 du circuit imprimé pour Arduino Raspberry par exemple, détail rotary encoder schematic du PCB tout compris avec composants

Comme vous pouvez le constater sur ce schéma (en faisant abstraction de la photo du KY-040, que je vous ai mis en bas à gauche, pour bien vous représenter les choses), on trouve :

  • notre encodeur rotatif, au centre du schéma
  • nos contacts COM et SW1, qui sont reliés à la masse (GND)
  • nos contacts CLK, DT, et SW (sw2)
  • et 3 résistances pull-up (respectivement sur les lignes CLK, DT, et SW)

Nota : la résistance R3 est « optionnelle », et mise ou non selon les fabricants. Si vous débutez, je vous recommande de partir sur un modèle KY-040 équipé de ces trois résistances (visibles au dos du PCB), afin que les tests soient facilités.

Compte-tenu de ce câblage, on s’aperçoit que :

  • les contacts CLK, DT, et SW sont à l’état haut au repos (car ramenés à +Vcc, via leurs résistances pull-up respectives)
  • les contacts CLK, DT, et SW sont à l’état bas lorsqu’ils sont activés (pour rappel, ils fonctionnent indépendamment les uns des autres)

Tout l’intérêt de ce genre de mini plaquette PCB est bien entendu de pouvoir facilement se brancher sur chacune des broches d’entrée/sortie, avec des fils dupont (pour faire ses essais sur breadboard, Arduino, Raspberry, ou autre !). C’est d’ailleurs ce que nous allons voir dès à présent, à commencer par un montage sur breadboard 😉

Montage de test « basique », pour bien comprendre son fonctionnement

Important : si vous souhaitez comprendre comment fonctionne un rotary encoder « une fois pour toute », je vous recommande vivement d’effectuer le montage suivant (qui est simple et rapide à faire, en plus). Car en tournant tout doucement le bouton rotatif de l’encodeur, dans un sens et dans l’autre, vous verrez exactement comment s’ouvrent et se ferment les contacts internes (CLK et DT) d’un encodeur rotatif. D’autant plus que cela vous permettrait de mettre en évidence le comportement différent de certains modèles, comme le HW-040 vis à vis du KY-040 (que nous allons prendre en exemple, à présent).

Voici une des manières les plus simples de câbler un module KY 040, pour bien comprendre son fonctionnement :

Schéma test encodeur rotatif KY040 sur breadboard, schéma avec leds et résistances pour visualisation des signaux CLK, DT, et SW facile

En fait, chaque LED sera « mise à la masse » (et s’éclairera donc) lorsque son contact correspondant sera fermé (actif, si vous préférez). Ainsi, vous pourrez visuellement comprendre comment tout s’active, en interne 😉

En image, voici ce que cela donne, une fois câblé sur breadboard (avec une alim externe +5V USB; « classique ») :

Montage de test manuel du rotary encoder KY 040, avec leds montées sur breadboard pour visualiser les états de DT CLK et SW facilement

Les composants utilisés ici sont :

Le montage est somme toute assez simple, comme vous avez pu le constater ! Et, au risque de trop insister, je vous recommande vivement de toujours faire ce montage de test, avec tout nouveau modèle d’encodeur rotatif (car, encore une fois, tous ne fonctionnent pas de la même manière, n’ont pas le même nombre de pas par tour, etc).

Ici, dans le cas du KY-040, vous constaterez que :

  • si vous tournez le bouton du rotary encoder vers la droite (sens horaire) : les leds reliées à CLK et DT s’illuminent puis s’éteignent, en passant d’un cran à l’autre
  • si vous tournez le bouton du rotary encoder vers la gauche (sens anti-horaire) : les leds reliées à CLK et DT s’illuminent puis s’éteignent, en passant d’un cran à l’autre

Quelle différence, me direz-vous ? Eh bien, dans un sens la led reliée à CLK s’allume avant celle de DT, et dans l’autre sens, c’est l’inverse. Pour le vérifier, tournez le bouton très doucement, pour voir « ce qu’il se passe », entre 2 crans.

On dira donc que le signal CLK est « en avance de phase » par rapport à DT, lorsqu’on tourne dans le sens horaire ; et en retard de phase, lorsqu’on tourne dans le sens antihoraire. Comme vous l’aurez compris, c’est justement en « regardant » quel contact s’active avant l’autre, que nous saurons dans quel sens le bouton a été tourné !

AperçuDescriptionLien achat
Encodeur rotatif incrémental EC11 à souder sur circuit imprimé PCB, fixation par soudure, modèle avec écrou de fixation autour de l'axe de rotationEncodeur rotatif incrémental EC-11 (modèle soudable sur carte PCB) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique
Module KY 040 avec headers soudés, carte électronique pour raccordement à l'aide de fils dupont, circuit imprimé équipé pour montages élecEncodeur rotatif incrémental KY-040 (modèle raccordable via fils dupont) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique

Comment raccorder un encodeur rotatif KY-040 à son arduino ?

Le branchement d’un encodeur rotatif KY-040 sur un Arduino est en fait plutôt simple. En effet, outre l’alimentation +/GND, un rotary encoder n’a besoin que de 3 fils de raccordement, pour assurer le retour de ses signaux (CLK, DT, et SW). Et encore, SW est optionnel, selon si vous souhaitez ou non utiliser le bouton poussoir intégré (logé sous l’axe rotatif du rotary encoder, pour rappel, lorsque l’encodeur rotatif en est équipé).

Cela étant dit, le choix des broches de raccordement peut quant à lui avoir une certaine importance. En effet, du fait qu’on fait généralement appel aux « interruptions externes » pour considérer les modifications de signaux CLK, DT, et/ou SW, on ne va pas se raccorder n’importe où sur l’Arduino.

Par exemple, si vous utilisez un Arduino Uno ou Nano, on aura tendance à sélectionner les lignes :

  • D2 de l’arduino, car cela correspond à l’interruption externe INT.0
  • et D3 de l’arduino, car cela correspond à l’interruption externe INT.1

Du reste, les branchements seront à adapter suivant le modèle de carte Arduino (ou de modèle Raspberry, ou de microcontrôleur) que vous utiliserez.

Nota : concernant les arduino, la page « officielle » de la fonction attachInterrupt() nous donne indirectement des infos sur les broches utilisables, pour les interruptions externes. Vous pouvez la consulter ici : https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/

Mais pour l’heure, faisons simple 😉

Voici un exemple de schéma de câblage d’un rotary encoder KY040 sur un Arduino Uno :

Schéma raccordement KY040 arduino uno, avec fils de liaison dupont d'alimentation et signaux de communication SW DT et CLK, pour débuter électronique

Rien de bien compliqué, en somme… car 5 fils suffisent, et rien d’autre, comme évoqué précédemment ! C’est d’ailleurs bien là l’avantage d’utiliser ces mini-plaquettes PCB, « toutes équipées »,et qui sont directement raccordables avec des fils dupont.

À présent, écrivons un bout de programme, pour faire interagir notre KY 040 avec l’arduino !

Code exemple Arduino #1 : programme lisant en permanence l’état des entrées CLK et DT

Voici le premier exemple que je vous propose de voir ensemble ! Ici :

  • du point de vue matériel, le KY-040 sera directement relié à un Arduino Nano, monté sur une breadboard (pour un câblage facilité et simplifié, comme visible ci-dessous)
  • et du point de vue logiciel, un programme arduino :
    • scrutera en permanence l’état des lignes CLK, DT, et SW du KY-040
    • interprétera ces signaux (incrémentera/décrémentera un compteur, lorsque le bouton est tourné)
    • et affichera sur le moniteur série de l’IDE Arduino le sens de rotation du bouton, le « nombre de pas » parcourus en tournant, et si le bouton SW a changé d’état entre temps (s’il vient d’être appuyé, ou relâché, donc)
Wiring schematic KY 040 rotary encoder on Arduino Nano, avec câblage de DT sur D4, CLK sur D3, et SW sur D2, pour test de l'encodeur rotatif

Nota : l’alimentation de l’arduino se fera par son câble de programmation USB ; et pour rappel, seulement 5 fils sont nécessaires ici, pour l’interconnexion de l’encodeur rotatif KY040 à l’Arduino (2 fils d’alim, et 3 fils reliés aux contacts du rotary encoder, en lui-même).

Côté programme, nous allons exploiter l’état des lignes CLK et DT des encodeurs rotatifs. Si vous avez bien lu tout ce qui précède, vous savez déjà que les signaux CLK et DT sont à quelque chose près « identiques », mais décalés dans le temps. Et suivant dans quel sens on tourne le bouton d’un rotary encoder, CLK se déclenchera en avance ou en retard, par rapport à DT. C’est d’ailleurs ce que nous allons exploiter ici.

Plus exactement, nous allons regarder quel est l’état de DT, à chaque fois que CLK passe au niveau bas (signalant qu’il devient actif). En effet, comme vous pourrez visuellement le constater sur les chronogrammes ci-dessous :

  • si DT est toujours au niveau haut, lorsque CLK passe au niveau bas, cela signifie que nous tournons le bouton du rotary encoder « vers la droite » (dans le sens des aiguilles d’une montre, ou sens horaire, si vous préférez)
  • si DT est déjà au niveau bas, quand CLK passe au niveau bas, cela signifie que nous tournons le bouton du rotary encoder « vers la gauche » (dans le sens inverse des aiguilles d’une montre, ou sens antihoraire, si vous préférez)
Chronogramme KY-040 rotary encoder suivant sens horaire ou antihoraire, évolution des signaux CLK et DT en fonction, suivi temporel

Côté code arduino, voici un exemple de programme qui va exploiter ces comportements de CLK et DT, pour en faire un affichage clair à l’écran (sur le moniteur série de l’IDE Arduino) :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prgArduino-1-TestKY040simple.ino
  
  Description :   Programme permettant de lire les états des lignes CLK, DT, et SW d'un encodeur rotatif KY-040,
                  d'effectuer un comptage du nombre de crans parcourus, de déterminer dans quel sens a été tourné
                  l'encodeur (sens horaire, ou anti-horaire), et d'afficher le tout sur le moniteur série de l'IDE Arduino

  Remarques :     - l'arduino utilisé ici sera un modèle Nano
                  - les signaux CLK et SW, émanant du KY-040, seront scrutés en permanence ; et le programme réagira
                  à chaque changement de l'un d'entre eux
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       12.05.2023

*/

// Constantes
#define pinArduinoRaccordementSignalSW  2       // La pin D2 de l'Arduino recevra la ligne SW du module KY-040
#define pinArduinoRaccordementSignalCLK 3       // La pin D3 de l'Arduino recevra la ligne CLK du module KY-040
#define pinArduinoRaccordementSignalDT  4       // La pin D4 de l'Arduino recevra la ligne DT du module KY-040

// Variables
int compteur = 0;                   // Cette variable nous permettra de savoir combien de crans nous avons parcourus sur l'encodeur
                                    // (sachant qu'on comptera dans le sens horaire, et décomptera dans le sens anti-horaire)

int etatPrecedentLigneSW;           // Cette variable nous permettra de stocker le dernier état de la ligne SW lu, afin de le comparer à l'actuel
int etatPrecedentLigneCLK;          // Cette variable nous permettra de stocker le dernier état de la ligne CLK lu, afin de le comparer à l'actuel

// ========================
// Initialisation programme
// ========================
void setup() {

    // Initialisation de la liaison série (arduino nano <-> PC)
    Serial.begin(9600);
    Serial.println(F("========================================================================="));
    Serial.println(F("Exemple 1 (programme de test Arduino Nano <-> module KY-040, scrutant en "));
    Serial.println(F("           permanence certains changement d'états de lignes de l'encodeur"));
    Serial.println(F("           KY-040, avec affichage des informations sur le moniteur série)"));
    Serial.println(F("========================================================================="));
    Serial.println("");

    // Configuration des pins de notre Arduino Nano en "entrées", car elles recevront les signaux du KY-040
    pinMode(pinArduinoRaccordementSignalSW, INPUT);         // à remplacer par : pinMode(pinArduinoRaccordementSignalSW, INPUT_PULLUP);
                                                            // si jamais votre module KY-040 n'est pas doté de résistance pull-up, au niveau de SW
    pinMode(pinArduinoRaccordementSignalDT, INPUT);
    pinMode(pinArduinoRaccordementSignalCLK, INPUT);

    // Mémorisation des valeurs initiales, au démarrage du programme
    etatPrecedentLigneSW  = digitalRead(pinArduinoRaccordementSignalSW);
    etatPrecedentLigneCLK = digitalRead(pinArduinoRaccordementSignalCLK);

    // Affichage de la valeur initiale du compteur, sur le moniteur série
    Serial.print(F("Valeur initiale du compteur = "));
    Serial.println(compteur);

    // Petite pause pour laisser se stabiliser les signaux, avant d'attaquer la boucle loop
    delay(200);

}


// =================
// Boucle principale
// =================
void loop() {

    // Lecture des signaux du KY-040 arrivant sur l'arduino
    int etatActuelDeLaLigneCLK = digitalRead(pinArduinoRaccordementSignalCLK);
    int etatActuelDeLaLigneSW  = digitalRead(pinArduinoRaccordementSignalSW);
    int etatActuelDeLaLigneDT  = digitalRead(pinArduinoRaccordementSignalDT);

    // *****************************************
    // On regarde si la ligne SW a changé d'état
    // *****************************************
    if(etatActuelDeLaLigneSW != etatPrecedentLigneSW) {

        // Si l'état de SW a changé, alors on mémorise son nouvel état
        etatPrecedentLigneSW = etatActuelDeLaLigneSW;

        // Puis on affiche le nouvel état de SW sur le moniteur série de l'IDE Arduino
        if(etatActuelDeLaLigneSW == LOW)
            Serial.println(F("Bouton SW appuyé"));
        else
            Serial.println(F("Bouton SW relâché"));

        // Petit délai de 10 ms, pour filtrer les éventuels rebonds sur SW
        delay(10);
    }

    // ******************************************
    // On regarde si la ligne CLK a changé d'état
    // ******************************************
    if(etatActuelDeLaLigneCLK != etatPrecedentLigneCLK) {

      // On mémorise cet état, pour éviter les doublons
      etatPrecedentLigneCLK = etatActuelDeLaLigneCLK;

      if(etatActuelDeLaLigneCLK == LOW) {
        
        // On compare le niveau de la ligne CLK avec celui de la ligne DT
        // --------------------------------------------------------------
        // Nota : - si CLK est différent de DT, alors cela veut dire que nous avons tourné l'encodeur dans le sens horaire
        //        - si CLK est égal à DT, alors cela veut dire que nous avons tourné l'encodeur dans le sens anti-horaire

        if(etatActuelDeLaLigneCLK != etatActuelDeLaLigneDT) {
            // CLK différent de DT => cela veut dire que nous comptons dans le sens horaire
            // Alors on incrémente le compteur
            compteur++;

            // Et on affiche ces infos sur le moniteur série
            Serial.print(F("Sens = horaire | Valeur du compteur = "));
            Serial.println(compteur);
        }
        else {
            // CLK est identique à DT => cela veut dire que nous comptons dans le sens antihoraire
            // Alors on décrémente le compteur
            compteur--;

            // Et on affiche ces infos sur le moniteur série
            Serial.print(F("Sens = antihoraire | Valeur du compteur = "));
            Serial.println(compteur);
        }

        // Petit délai de 1 ms, pour filtrer les éventuels rebonds sur CLK
        delay(1);
        
      }
    }

    // ********************************
    // Puis on reboucle … à l'infini !
    // ********************************
   
}

Une fois votre montage correctement câblé et l’arduino programmé, il ne vous restera plus qu’à ouvrir le moniteur série de votre IDE Arduino. Ainsi, vous devriez voir apparaître plusieurs lignes au démarrage, s’arrêtant avec le texte « Valeur initiale du compteur = 0 », si tout s’est correctement déroulé.

Ensuite, il vous suffira alors de tourner le bouton de l’encodeur rotatif à droite ou à gauche, pour observer les changements s’afficher sur le moniteur série. Voici d’ailleurs ce que j’ai obtenu, de mon côté :

Exemple lecture encodeur rotatif sur moniteur série de l'IDE Arduino, test de rotation en sens horaire et anti-horaire, ainsi que bouton poussoir

À noter qu’en l’état, si vous tournez le bouton plus ou moins vite, vous remarquerez l’émergence de « faux signaux » s’afficher à l’écran. En fait, cela est dû à la nature même des contacts du KY-040 (qui ne sont évidemment pas parfaits, en pratique). En effet, les contacts CLK, DT, et SW rebondissent plus ou moins lorsqu’ils sont actionnés en interne, entraînant ainsi de brèves ouvertures/fermetures supplémentaires, non souhaitées. Bien entendu, même si ces rebonds restent brefs, le programme arduino (« rapide » quant à lui) les considère comme de véritables manœuvres que nous aurions effectué (d’où ces « actions fantômes », apparaissant sur le moniteur série).

Nota : comme nous verrons plus loin, il existe plusieurs façons de filtrer ces rebonds, ne vous inquiétez pas !

À présent, voyons comment optimiser ce code, afin qu’il ne monopolise pas tout « le temps processeur » ! Car oui, jusqu’à présent, nous ne faisions que suivre l’état des entrées CLK, DT, et SW en permanence, dans la boucle loop (ce qui n’est pas l’idéal, pour l’intégration dans une application de plus grande envergure !).

AperçuDescriptionLien achat
Encodeur rotatif incrémental EC11 à souder sur circuit imprimé PCB, fixation par soudure, modèle avec écrou de fixation autour de l'axe de rotationEncodeur rotatif incrémental EC-11 (modèle soudable sur carte PCB) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique
Module KY 040 avec headers soudés, carte électronique pour raccordement à l'aide de fils dupont, circuit imprimé équipé pour montages élecEncodeur rotatif incrémental KY-040 (modèle raccordable via fils dupont) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique

Code exemple Arduino #2 : programme utilisant les interruptions arduino, pour libérer la charge du µC

Une chose a sûrement dû vous interpeller, dans le code précédent. En fait, dans l’exemple précédent, l’Arduino passe son temps à scruter les signaux de l’encodeur rotatif, sans jamais avoir réellement le temps de faire autre chose (sans quoi on risquerait de louper certains changements d’états, et donc, avoir une mauvaise interprétation des signaux). En effet, si on rajoutait des lignes de code intermédiaires dans le code précédent, celles-ci mettraient du temps à s’exécuter, et se faisant, nous feraient potentiellement louper des signaux émanant de l’encodeur rotatif.

La solution ? Arrêter « d’écouter » EN PERMANENCE les signaux de l’encodeur rotatif, et SEULEMENT les prendre en compte lorsqu’ils changent d’état. Et d’ailleurs, c’est quelque chose de plutôt facile à mettre en œuvre avec un Arduino, grâce aux « interruptions externes » ! Alors en avant 😉

Pour ce faire, nous allons reprendre le montage précédent (car les changements interviendront uniquement au niveau logiciel). Pour rappel, voici à quoi ressemble le montage précédent, une fois câblé sur breadboard :

Et concernant les modifications apportées au programme, les voici :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prgArduino-2-TestKY040avecInterruptions.ino
  
  Description :   Programme permettant de compter le nombre de crans tournés sur un encodeur KY-040,
                  de déterminer le sens de rotation, et d'afficher le tout sur le moniteur série, de
                  l'IDE Arduino.
  
  Remarques :     - l'arduino utilisé ici sera un modèle Nano
                  - le choix des broches D2 et D3 pour les interruptions n'est pas anodin ; cela correspond
                  aux entrées d'interruptions externes INT0 (D2) et INT1 (D3), natives sur arduino
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       13.05.2023

*/

// Constantes
#define pinArduinoRaccordementSignalSW  2       // La pin D2 de l'Arduino recevra la ligne SW du module KY-040
#define pinArduinoRaccordementSignalCLK 3       // La pin D3 de l'Arduino recevra la ligne CLK du module KY-040
#define pinArduinoRaccordementSignalDT  4       // La pin D4 de l'Arduino recevra la ligne DT du module KY-040

// Variables
int etatPrecedentLigneSW;           // Cette variable nous permettra de stocker le dernier état de la ligne SW, afin de le comparer à l'actuel

int compteur = 0;                   // Cette variable nous permettra de compter combien de crans ont été parcourus, sur l'encodeur
                                    // (sachant que nous compterons dans le sens horaire, et décompterons dans le sens antihoraire)

// ========================
// Initialisation programme
// ========================
void setup() {

    // Initialisation de la liaison série (arduino nano <-> PC)
    Serial.begin(9600);
    Serial.println(F("=========================================================================="));
    Serial.println(F("Exemple 2 (programme de test Arduino Nano <-> module KY-040, utilisant les"));
    Serial.println(F("           interruptions Arduino INT0 et INT1, avec affichage du nombre de"));
    Serial.println(F("           crans parcourus sur l'encodeur, ainsi que le sens de rotation)"));
    Serial.println(F("========================================================================="));
    Serial.println("");

    // Configuration de certaines pins de notre Arduino Nano en "entrées" (celles qui recevront les signaux du KY-040)
    pinMode(pinArduinoRaccordementSignalSW, INPUT);         // à remplacer par : pinMode(pinArduinoRaccordementSignalSW, INPUT_PULLUP);
                                                            // si jamais votre module KY-040 n'est pas doté de résistance pull-up, au niveau de SW
    pinMode(pinArduinoRaccordementSignalDT, INPUT);
    pinMode(pinArduinoRaccordementSignalCLK, INPUT);

    // Petite pause pour laisser le temps aux signaux de se stabiliser
    delay(200);

    // Mémorisation de la valeur initiale de la ligne SW, au démarrage du programme
    etatPrecedentLigneSW  = digitalRead(pinArduinoRaccordementSignalSW);

    // Affichage de la valeur initiale du compteur, sur le moniteur série
    Serial.print(F("Valeur initiale du compteur = "));
    Serial.println(compteur);

    // Activation d'interruptions sur les lignes CLK et SW
    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalCLK), changementDetecteSurLigneCLK, FALLING); // FALLING => détecte tout front descendant
    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalSW), changementDetecteSurLigneSW, CHANGE);    // CHANGE => détecte tout changement d'état

}


// =================
// Boucle principale
// =================
void loop() {

    // Boucle infinie ici, qui sera interrompue à chaque nouvelle interruption, et reprise juste après la fin d'exécution de la routine d'interruption

}


// =====================================================
// Routine d'interruption : changementDetecteSurLigneCLK
// =====================================================
void changementDetecteSurLigneCLK() {

    // Lecture de la ligne DT, issue du KY-040, et arrivant sur l'arduino
    int etatActuelDeLaLigneDT  = digitalRead(pinArduinoRaccordementSignalDT);
    int etatActuelDeLaLigneCLK = LOW;
        // Nota : ici, la ligne CLK est forcément au niveau bas (0V), du fait qu'on entre dans cette routine
        //        que sur front descendant de CLK (donc passage de 1 à 0)
        

    // On compare ensuite l'état des lignes CLK et DT
    // ----------------------------------------------
    // Nota : - si CLK est différent de DT, alors cela signifie que nous avons tourné l'encodeur dans le sens horaire
    //        - si CLK est égal à DT, alors cela signifie que nous avons tourné l'encodeur dans le sens antihoraire

    if(etatActuelDeLaLigneCLK != etatActuelDeLaLigneDT) {
        // CLK différent de DT => cela veut dire que nous tournons dans le sens horaire
        // Alors on incrémente le compteur
        compteur++;

        // Et on affiche ces infos sur le moniteur série
        Serial.print(F("Sens = horaire | Valeur du compteur = "));
        Serial.println(compteur);
    }
    else {
        // CLK est identique à DT => cela veut dire que nous tournons dans le sens antihoraire
        // Alors on décrémente le compteur
        compteur--;

        // Et on affiche ces infos sur le moniteur série
        Serial.print(F("Sens = antihoraire | Valeur du compteur = "));
        Serial.println(compteur);
    }
    
}


// ====================================================
// Routine d'interruption : changementDetecteSurLigneSW
// ====================================================
void changementDetecteSurLigneSW() {

    // On lit le nouvel état de la ligne SW
    int etatActuelDeLaLigneSW = digitalRead(pinArduinoRaccordementSignalSW);

    // On mémorise le nouvel état de la ligne SW, puisqu'il vient de changer (sans quoi nous ne serions pas dans cette routine d'interruption)
    etatPrecedentLigneSW = etatActuelDeLaLigneSW;

    // Puis on affiche le nouvel état de SW sur le moniteur série de l'IDE Arduino
    if(etatActuelDeLaLigneSW == LOW)
        Serial.println(F("Bouton SW appuyé"));
    else
        Serial.println(F("Bouton SW relâché"));

}

// Nota : en pratique, sans "filtre anti-rebond", vous noterez qu'il y a parfois pas mal de comptes/décomptes non souhaités, en l'état

Comme vous avez sûrement dû le remarquer, la boucle « loop » de ce programme ne contient plus aucun code (vous pourrez donc y loger d’autres lignes de code, indépendamment du reste). En fait, tout se passe dans les fonctions « changementDetecteSurLigneCLK » et « changementDetecteSurLigneSW » ; en sachant que ces fonctions là ne sont exécutées qu’en cas de changement respectivement constaté sur les lignes CLK et SW.

En clair : le programme principal arduino (dans la boucle « loop ») est libre de faire ce qu’il veut ; et il sera interrompu lorsqu’une interruption externe sera détectée (ici, en cas de changement d’état de CLK ou SW, ou plus exactement, de front descendant sur CLK ou de changement d’état sur SW).

Tout ceci est permis grâce aux directives « attachInterrupt », présentes dans la partie « setup » du programme arduino, avec :

  • « FALLING » sur CLK, qui permet de lancer le code « changementDetecteSurLigneCLK » si un front descendant est détecté sur CLK (branché sur la pin D3, pour rappel)
  • « CHANGE » sur SW, qui permet de lancer le code « changementDetecteSurLigneSW » si un changement d’état (passe de LOW à HIGH, ou de HIGH à LOW) est détecté sur SW (branché sur la pin D2, pour rappel)

À noter que les broches D2 et D3 de cet arduino n’ont pas été choisies par hasard. En effet, celles-ci correspondent aux « 2 seules véritables » entrées disponibles sur le Nano, pour l’attachement de routines individuelles d’interruptions externes (en fait, il y aurait d’autres moyens de faire la même chose avec d’autres broches, mais pas de manière aussi simple et efficace … histoire d’être plus précis).

En pratique, si vous réalisez ce montage, uploadez ce nouveau code, et tournez votre encodeur rotatif dans un sens puis dans l’autre, vous devriez obtenir quelque chose du style, sur votre moniteur série :

Exemple rotary encoder interrupt sur arduino, aperçu du moniteur série IDE avec affichage d'un compteur et indication sens de rotation

Bien entendu, comme pour l’exemple précédent, vous verrez sans doute apparaître des « lignes inattendues » sur votre moniteur série, selon comment et à quelle vitesse vous activerez les contacts du KY-040. Cela est encore une fois dû à la présence indésirable de rebonds mécaniques, sur les contacts du rotary encoder. C’est d’ailleurs quelque chose que nous allons essayer de filtrer, à présent 😉

Ajout d’un système anti-rebond matériel, pour filtrer les signaux non désirables

Première façon de filtrer les signaux non souhaités, dus aux rebonds à même les contacts internes des encodeurs rotatifs, tel que le KY-040 : l’ajout de « filtres RC » (Resistance/Condensateur), sur ces lignes. Ainsi, chaque condensateur aura tendance à « lisser » les changements d’états rapides, et donc, filtrer certains signaux électriques non désirés.

D’un point de vue pratique, voici un exemple d’insertion de systèmes anti-rebond matériels, à base de filtres RC, sur la base des exemples précédents :

Anti-rebond matériel pour KY040 branché Arduino, système avec résistance et condensateur, pour filtrage des signaux rebonds non désirés

Alors … histoire de bien comprendre comment tout cela fonctionne, il faut avant tout se remémorer que :

  • les lignes CLK, DT, et SW sont au niveau haut au repos, maintenues par leurs résistances pull-up internes (de 10 Kohm, soudées au dos du PCB de l’encodeur rotatif, donc non visibles ici)
  • les lignes CLK, DT, et SW sont au niveau bas lorsqu’actives, car ramenées à la masse (GND) à ce moment-là

Du coup :

  • chaque condensateur va se charger au travers d’une pull-up de 10 kohm + sa résistance série de 1 kohm (lorsqu’un contact est au repos, j’entends)
  • chaque condensateur va se décharger au travers de sa résistance série de 1 kohm uniquement, lorsque « son » contact va être activé

Ainsi, les condensateurs auront tendance à « lisser » tous les changements d’états rapides, au niveau de ces lignes (leurs constantes de temps étant fonction des résistances qui limitent leur courant, en chemin). Ceci n’est évidemment pas parfait, loin de là même, mais d’un point de vue pratique, ça marche plutôt pas mal 😉

Au passage, voici ce que ça donne, une fois câblé sur breadboard :

Mais comme nous allons le voir à présent, il y a plus simple et efficace à mettre en œuvre, et qui plus est, sans avoir à rajouter tous ces composants !

AperçuDescriptionLien achat
Encodeur rotatif incrémental EC11 à souder sur circuit imprimé PCB, fixation par soudure, modèle avec écrou de fixation autour de l'axe de rotationEncodeur rotatif incrémental EC-11 (modèle soudable sur carte PCB) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique
Module KY 040 avec headers soudés, carte électronique pour raccordement à l'aide de fils dupont, circuit imprimé équipé pour montages élecEncodeur rotatif incrémental KY-040 (modèle raccordable via fils dupont) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique

Ajout d’un système anti-rebond logiciel, pour plus de performances encore

Dans les exemples de code #1 et #2, nous jetions seulement un coup d’œil au niveau de la ligne DT, chaque fois que la ligne CLK tombait au niveau bas. Pour rappel :

  • si DT était toujours au niveau haut pendant que CLK passait au niveau haut, alors cela signifiait que nous avions tourné le bouton dans le sens horaire (« vers la droite », si vous préférez)
  • si DT était déjà au niveau bas pendant que CLK passait au niveau haut, alors cela signifiait que nous avions tourné le bouton dans le sens anti-horaire (« vers la gauche », si vous préférez)

Avec, pour rappel également, le schéma électrique de test suivant :

Schéma de branchement KY 040 rotary encoder sur Arduino Nano, avec raccordements de SW CLK et DT sur D2 D3 et D3, via fils dupont intermédiaires

Ici, afin de faire un système plus efficace de filtrage de signaux indésirables (dus aux rebonds sur les contacts des rotary encoder), nous n’allons plus nous limiter à lire l’état de DT, lorsque CLK passe au niveau bas. En effet, nous allons également vérifier l’état de DT, lorsque CLK repasse au niveau haut. Ainsi, nous observerons un « cycle complet » CLK/DT, que nous pourrons comparer avec les 2 séquences de fonctionnement théoriques possibles, ici :

Évolution temporelle signaux CLK et DT d'un encodeur rotatif KY040, suivant sens de rotation, avec détail phases niveaux haut et bas des lignes

En clair, à chaque changement d’état de CLK, et avec 0=niveau bas / 1=niveau haut :

  • si on a une succession CLK=0 et DT=1, puis CLK=1 et DT=0, c’est alors que l’axe de l’encodeur rotatif a été tourné dans le sens horaire
  • si on a une succession CLK=0 et DT=0, puis CLK=1 et DT=1, c’est alors que l’axe de l’encodeur rotatif a été tourné dans le sens antihoraire

Et c’est exactement ce que va faire le programme qui suit :

/*
   ______               _                  _///_ _           _                   _
  /   _  \             (_)                |  ___| |         | |                 (_)
  |  [_|  |__  ___  ___ _  ___  _ __      | |__ | | ___  ___| |_ _ __ ___  _ __  _  ___  _   _  ___
  |   ___/ _ \| __|| __| |/ _ \| '_ \_____|  __|| |/ _ \/  _|  _| '__/   \| '_ \| |/   \| | | |/ _ \
  |  |  | ( ) |__ ||__ | | ( ) | | | |____| |__ | |  __/| (_| |_| | | (_) | | | | | (_) | |_| |  __/
  \__|   \__,_|___||___|_|\___/|_| [_|    \____/|_|\___|\____\__\_|  \___/|_| |_|_|\__  |\__,_|\___|
                                                                                      | |
                                                                                      \_|
  Fichier :       prgArduino-4-TestKY040avecAntiRebond.ino
  
  Description :   Programme permettant de compter le nombre de crans tournés sur un encodeur KY-040, de
                  déterminer le sens de rotation, et d'afficher le tout sur le moniteur série, de l'IDE
                  Arduino. Inclus : système d'antirebond logiciel, pour le couple de signaux CLK/DT.
  
  Remarques :     - l'arduino utilisé ici sera un modèle Nano
                  - un système "d'anti-rebond logiciel" sera mis en oeuvre ici, pour filtrer au maximum
                    les signaux indésirables
                                    
  Auteur :        Jérôme TOMSKI (https://passionelectronique.fr/)
  Créé le :       13.05.2023

*/

// Constantes
#define pinArduinoRaccordementSignalSW  2       // La pin D2 de l'Arduino recevra la ligne SW du module KY-040
#define pinArduinoRaccordementSignalCLK 3       // La pin D3 de l'Arduino recevra la ligne CLK du module KY-040
#define pinArduinoRaccordementSignalDT  4       // La pin D4 de l'Arduino recevra la ligne DT du module KY-040

// Variables
int etatPrecedentLigneSW;           // Cette variable nous permettra de stocker le dernier état de la ligne SW, afin de le comparer à l'actuel
int etatPrecedentLigneCLK;          // Cette variable nous permettra de stocker le dernier état de la ligne CLK, afin de le comparer à l'actuel
int etatPrecedentLigneDT;           // Cette variable nous permettra de stocker le dernier état de la ligne DT, afin de le comparer à l'actuel

int compteur = 0;                   // Cette variable nous permettra de compter combien de crans ont été parcourus, sur l'encodeur
                                    // (sachant que nous compterons dans le sens horaire, et décompterons dans le sens antihoraire)

// ========================
// Initialisation programme
// ========================
void setup() {

    // Initialisation de la liaison série (arduino nano <-> PC)
    Serial.begin(9600);
    Serial.println(F("=========================================================================="));
    Serial.println(F("Exemple 3  (programme de test Arduino Nano <-> module KY-040, utilisant les"));
    Serial.println(F("           interruptions matérielles arduino, avec système antirebond logiciel"));
    Serial.println(F("           permettant de filtrer au maximum les signaux non désirés, puis"));
    Serial.println(F("           affichage du nombre de crans parcourus et le sens de rotation)"));
    Serial.println(F("========================================================================="));
    Serial.println("");

    // Configuration de certaines pins de notre Arduino Nano en "entrées" (celles qui recevront les signaux du KY-040)
    pinMode(pinArduinoRaccordementSignalSW, INPUT);
    pinMode(pinArduinoRaccordementSignalCLK, INPUT);
    pinMode(pinArduinoRaccordementSignalDT, INPUT);

    // Petite pause pour laisser le temps aux signaux de se stabiliser
    delay(200);

    // Mémorisation des valeurs initiales des lignes SW/CLK/DT, au démarrage du programme
    etatPrecedentLigneSW  = digitalRead(pinArduinoRaccordementSignalSW);
    etatPrecedentLigneCLK = digitalRead(pinArduinoRaccordementSignalCLK);
    etatPrecedentLigneDT  = digitalRead(pinArduinoRaccordementSignalDT);

    // Affichage de la valeur initiale du compteur, sur le moniteur série
    Serial.print(F("Valeur initiale du compteur = "));
    Serial.println(compteur);

    // Activation d'interruptions sur les lignes CLK et DT
    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalCLK), changementSurLigneCLK, CHANGE);   // CHANGE => détecte tout changement d'état
    attachInterrupt(digitalPinToInterrupt(pinArduinoRaccordementSignalSW), changementSurLigneSW, CHANGE);     // CHANGE => détecte tout changement d'état

}


// =================
// Boucle principale
// =================
void loop() {

    // Boucle infinie ici, qui sera interrompue à chaque nouvelle interruption, et reprise juste après la fin d'exécution de la routine d'interruption

}


// ==============================================
// Routine d'interruption : changementSurLigneCLK
// ==============================================
void changementSurLigneCLK() {

    // Lecture des lignes CLK et DT, issue du KY-040, arrivant sur l'arduino
    int etatActuelDeLaLigneCLK = digitalRead(pinArduinoRaccordementSignalCLK);
    int etatActuelDeLaLigneDT  = digitalRead(pinArduinoRaccordementSignalDT);

    // ************************
    // * CAS : Incrémentation *
    // ************************
    // Si CLK = 1 et DT = 0, et que l'ancienCLK = 0 et ancienDT = 1, alors le bouton a été tourné d'un cran vers la droite (sens horaire, donc incrémentation)
    if((etatActuelDeLaLigneCLK == HIGH) && (etatActuelDeLaLigneDT == LOW) && (etatPrecedentLigneCLK == LOW) && (etatPrecedentLigneDT == HIGH)) {

        // Alors on incrémente le compteur
        compteur++;

        // Et on affiche ces infos sur le moniteur série
        Serial.print(F("Sens = horaire | Valeur du compteur = "));
        Serial.println(compteur);
        
    }

    // ************************
    // * CAS : Décrémentation *
    // ************************
    // Si CLK = 1 et DT = 1, et que l'ancienCLK = 0 et ancienDT = 0, alors le bouton a été tourné d'un cran vers la gauche (sens antihoraire, donc décrémentation)
    if((etatActuelDeLaLigneCLK == HIGH) && (etatActuelDeLaLigneDT == HIGH) && (etatPrecedentLigneCLK == LOW) && (etatPrecedentLigneDT == LOW)) {

        // Alors on décrémente le compteur
        compteur--;

        // Et on affiche ces infos sur le moniteur série
        Serial.print(F("Sens = antihoraire | Valeur du compteur = "));
        Serial.println(compteur);
        
    }

    // Et on mémorise ces états actuels comme étant "les nouveaux anciens", pour le "tour suivant" !
    etatPrecedentLigneCLK = etatActuelDeLaLigneCLK;
    etatPrecedentLigneDT = etatActuelDeLaLigneDT;
    
}


// =============================================
// Routine d'interruption : changementSurLigneSW
// =============================================
void changementSurLigneSW() {
  
    // On lit le nouvel état de la ligne SW
    int etatActuelDeLaLigneSW = digitalRead(pinArduinoRaccordementSignalSW);

    // On affiche le nouvel état de SW sur le moniteur série de l'IDE Arduino
    if(etatActuelDeLaLigneSW == LOW)
        Serial.println(F("Bouton SW appuyé"));
    else
        Serial.println(F("Bouton SW relâché"));

    // Puis on mémorise le nouvel état de la ligne SW
    etatPrecedentLigneSW = etatActuelDeLaLigneSW;

}

Ainsi, en ne considérant que les 2 enchaînements possibles (CLK/DT de 0/1 vers 1/0, ou de 0/0 vers 1/1), on filtre automatiquement « tous » les signaux/rebonds non souhaités (ou presque !). C’est en tout cas un moyen simple et super efficace, pour filtrer au maximum les rebonds non désirés, émanant des contacts internes aux encodeurs rotatifs ! D’ailleurs faites l’essai, et vous verrez 😉

De mon côté, voici ce que j’ai obtenu sur le moniteur série (sans « fausse information » relevée, en pratique) :

Affichage moniteur série avec montage anti-rebond arduino sur rotary encoder KY-040, détail en fonction du sens de rotation et comptage

Avouez que c’est quand même bien plus sympa de filtrer les rebonds via le code arduino, plutôt que d’avoir à rajouter des condensateurs et résistances, sur les lignes de communication. Cela étant dit, ce filtrage logiciel n’est pas parfait non plus, même s’il est bien meilleur, selon moi !

KY-040, HW-040, EC11 : quelles différences ?

Juste une rapide parenthèse, au niveau des différents modèles d’encodeur rotatif, qu’on retrouve couramment, pour câblage sur Arduino ou Raspberry : celui qu’on rencontre le plus souvent est le KY-040. Il se présente sous la forme d’un rotary encoder monté sur PCB, avec des pins disponibles pour y brancher des fils dupont.

Cela étant dit, on retrouve des modèles très similaires dans le commerce, tel que le HW-040. Ce dernier est identique au KY-040 en apparence, bien qu’il ait un nombre de crans par tour et de fonctionnement interne parfois différent (sans parler aussi que l’un dispose d’un filetage à la base de son axe rotatif, pour maintien par écrou, et l’autre non)..

Par ailleurs, vous pourriez être intéressé par l’emploi d’un « encodeur rotatif nu », c’est à dire « non monté sur plaquette PCB » (cela permettant de pouvoir l’intégrer à même vos circuits imprimés, en le soudant directement et fermement dessus).

Du coup, en synthèse, voici une petite image vous résumant ce qui distingue tous ces « encodeurs rotatifs populaires » :

Comparatif KY-040 vs HW-040 visuel, avec filetage ou non de la base de l'axe de rotation, comparaison KY040 et HW040 avec le rotary encoder EC11

Et en bonus, un petit aperçu de l’intérieur d’un rotary encoder EC11, afin de bien vous montrer la conception interne, pour bien en comprendre le fonctionnement :

Vue intérieure encodeur rotatif EC11, aperçu des contacts CLK et DT avec lamelles conductrices, 20 crans par tour et bouton central poussoir intégré

Nota : gardez bien à l’esprit qu’il ne s’agit là que de 3 références « très » populaires, parmi tant d’autres. Ne croyez donc pas que l’univers des encodeurs rotatifs incrémentaux se limite au KY-040, HW-040, et EC11 !

AperçuDescriptionLien achat
Encodeur rotatif incrémental EC11 à souder sur circuit imprimé PCB, fixation par soudure, modèle avec écrou de fixation autour de l'axe de rotationEncodeur rotatif incrémental EC-11 (modèle soudable sur carte PCB) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique
Module KY 040 avec headers soudés, carte électronique pour raccordement à l'aide de fils dupont, circuit imprimé équipé pour montages élecEncodeur rotatif incrémental KY-040 (modèle raccordable via fils dupont) Caddie plein 24x24, icone passion électronique fr, achat de matériels d'élec, idéal débutant et amateurs d'électronique

Encodeurs rotatifs incrémentaux : conclusion !

Nous voici au terme de cette introduction aux encodeurs rotatifs incrémentaux ! J’espère que tout ce contenu sera suffisamment clair pour vous, afin que vous puissiez intégrer ces rotary encoder dans vos futurs montages !

Liens GitHub des programmes figurant dans cet article :

Sur ce, je vous dis à très bientôt, et bon courage à vous 😉
Jérôme.

À découvrir aussi : les photorésistances (avec exemples de code arduino)

Ce contenu vous plaît ? Alors abonnez-vous à la Newsletter pour ne rien louper !

(*) Mis à jour le 16/01/2024

16 commentaires sur “Introduction à l’encodeur rotatif incrémental, de type mécanique (avec explication de fonctionnement, vue interne, schéma de câblage à un arduino, et exemples de code avec le KY-040)”

  1. Site passionelectronique.fr

    Merci Jérôme pour ce tuto très complet, et en français ce qui ne gâche rien !

    De quoi passer quelques heures à découvrir et faire fonctionner « en vrai » ces boutons « sans fin ».

    Merci encore et au plaisir de découvrir d’autres composants ou matériels à venir dans tes partages de haute qualité !

  2. Site passionelectronique.fr

    Bonjour Jérôme, merci pour ce tuto de très haute qualité (ce qui est un pléonasme au regard des précédentes publications que je suis avec beaucoup d’intérêts).

    Patrick

  3. Site passionelectronique.fr

    Bonjour Jérôme,

    Merci pour ce tuto !

    J’ai une petite question : comment utiliser ce capteur pour connaitre la position angulaire du bouton ? Faut-il ajouter des capteurs périphériques pour « l’étalonnage » de la position de référence ? Y-a-t-il d’autres solutions ? Ou un autre type d’encodeur permet-il de faire ceci ?

    Merci 🙂

    1. Site passionelectronique.fr

      Salut !

      Hum… tu peux connaître la position angulaire d’un « encodeur rotatif incrémental », en comptant le nombre de crans passés en avant ou en arrière, depuis la mise sous tension (ou depuis un instant « t » donné). Mais clairement, ce n’est pas l’idéal.

      Le mieux est plutôt d’utiliser un « encodeur rotatif absolu », par exemple, qui est bien plus adapté. Car à chaque position angulaire correspond un état de sortie unique (par contre, je n’ai pas de référence en tête à te donner, désolé).

      Voilà ! Bon courage à toi 😉
      Jérôme.

  4. Site passionelectronique.fr

    Bonjour

    J’ai appliqué ce code pour un projet de rotonde en modélisme ferroviaire et c’est tout simplement génial.
    Les pages du menu associé au bouton ne présente aucun saut. Merci pour ce tutoriel fort intéressant.

  5. Site passionelectronique.fr

    Bonjour,

    Merci pour votre article très clair!

    Dans le cas d’une utilisation d’un encodeur nu pour monter sur un PCB, peut on utiliser les résistances internes de l’arduino en les configurant avec INPUT_PULLUP ? Dans ce cas, le VCC est il obligatoire ? Si on utilise un encodeur sans switch, on ne brancherait que 3 fils : CLK, DT ET GND

    Merci beaucoup

    1. Site passionelectronique.fr

      Salut Julien !

      Oui, tu peux parfaitement utiliser les résistances pull-up internes de l’Arduino (qui font environ 50 kohms), pour se substituer à ces pull-up « externes ». Et dans ce cas précis tu as raison : on pourrait également se passer du Vcc, si celui-ci se limitait à alimenter ces pull-up (car elles ne seraient plus présentes).

      Voilà ! Bon courage à toi 😉
      Jérôme.

  6. Site passionelectronique.fr

    Bonjour Jérôme,

    Merci pour tous ces articles très détaillés, en français et de surcroît sans fautes d’orthographe, c’est si rare !
    Comme autre exemple d’encodeur rotatif, tu aurais pu citer aussi la molette de la souris qui fonctionne sur le même principe.

    Ne t’excuse pas pour le manque de temps, on sait ce que c’est. Je dis toujours que les journées devraient faire 48 heures !

    A bientôt,
    Pierre.
    PS – Et pendant que j’y suis, meilleurs vœux à toutes et tous pour la nouvelle année !

    1. Site passionelectronique.fr

      Salut Claude !

      Alors, sur un encodeur EC11, les contacts sont nommés A et B. Normalement, A correspond à CLK, et B à DT.

      Par contre, comme ce n’est pas quelque chose de normalisé, les fabricants peuvent inverser ces contacts. Si cela arrive, il suffit simplement d’inverser également ces contacts au niveau logiciel, pour neutraliser ce problème. Ainsi, pas de risque de reculer en avançant, ou de décrémenter en incrémentant 😉

      En espérant avoir bien répondu à ta question !

      Excellente journée à toi,
      Jérôme.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Symbole danger point d'exclamation site PSE, triangle jaune avec contour noir, texte noir alerte au milieuAfin de filtrer au maximum les messages de type "spam" ou "inappropriés", chaque commentaire est soumis à modération, et validé manuellement. Du coup, il se peut que certains commentaires ne soient pas publiés, ou sinon, avec un peu de retard. Par ailleurs, j'ai malheureusement plus de messages à traiter que de temps pour y répondre ; c'est pourquoi je ne pourrais pas répondre à tout le monde. Désolé …