Interfacer un codeur rotatif avec un Yocto-Knob

Interfacer un codeur rotatif avec un Yocto-Knob

Nous avons déjà utilisé le Yocto-Knob pour interfacer des potentiomètres, des boutons, des photodiodes, mais jamais de codeur rotatif. Cette semaine, nous allons voir comment interfacer un codeur rotatif avec un Yocto-Knob pour réaliser une interface homme-machine plus évoluée.




Un codeur rotatif permet de détecter la rotation d'une mollette, mais contrairement à un potentiomètre, un codeur rotatif est capable de mesurer un nombre illimité de tours. Il existe plusieurs types de codeurs rotatifs: Les codeurs rotatifs absolus et les codeurs rotatifs incrémentaux. Les codeurs rotatifs absolus permettent de déterminer l'angle du bouton indépendamment du contexte, en mesurant les différents signaux du codeur. La girouette que nous avons fabriqué dans cet article est en fait un codeur rotatif absolu.

Un codeur rotatif et son bouton
Un codeur rotatif et son bouton



Un codeur rotatif incrémental ne permet pas de déterminer l'angle du bouton, mais détecte une rotation d'un angle de la molette et le sens de la rotation. Par exemple il permet de détecter l’événement "rotation de 1/30 ème de tour vers la droite". Mais il n'est pas possible de calculer la position absolue de la molette depuis les signaux de ce codeur.

La roulette d'une souris utilise un codeur rotatif incremental
La roulette d'une souris utilise un codeur rotatif incremental



L'avantage d'un codeur rotatif incrémental est qu'il est plus simple à fabriquer. Il suffit de deux signaux A et B pour détecter une rotation d'une fraction d'un tour (par exemple 1/16 de tour). Un encodeur rotatif est très pratique pour réaliser une interface homme-machine, particulièrement pour naviguer dans des menus ou des listes. Vous êtes probablement en train d'utiliser un codeur rotatif incrémental: la roulette de votre souris.

Les signaux A et B sont inversé tout les 1/X tours, mais pour l'on puisse détecter le sens de rotation les deux signaux sont décalés. De cette manière, il est possible de déterminer le sens de la rotation en regardant si la dernière transition est celle du signal A ou du signal B.

La logique d'un codeur rotatif incrémental
La logique d'un codeur rotatif incrémental



Pour déterminer de manière fiable le sens de rotation et le nombre de rotations, il est crucial de ne pas manquer une transition et de mémoriser la dernière transition de chaque signal. Détecter une série de transition avec un mini PC ARM (Raspberry Pi ou autre) peut paraître simple, mais ce n'est pas le cas: Il faut vérifier l'état de l'entrée A et B sous interruption pour être sur de ne pas manquer une transition et supprimer l'effet de rebond de chaque transition. Heureusement, le Yocto-Knob est capable de prendre en charge ces deux tâches.

Pour interpréter l’état de l'encodeur, nous allons utiliser le compteur de transition du Yocto-Knob. Le Yocto-Knob possède un compteur de transition pour chaque entrée. En plus de ces compteurs le Yocto-Knob stocke "l'heure" de la dernière transition "pressé" et la dernière transition "relâché" de chaque entrée. Grâce à ces informations il est possible de déterminer le nombre de rotations et la direction.

Notre encodeur est un modèle cranté et chaque cran correspond à 1/30 ème de tour. Quand cet encodeur est sur un cran, les deux entrée sont dans la même position (cf diagramme plus haut). Nous allons présenter deux implémentations: Une en polling et une en callback.

Utilisation par polling


Pour commencer, il faut mettre à zéro les compteurs de transition des deux entrées A et B ainsi que notre variable global old_counter.


encoderA = YAnButton.FindAnButton("encoderA")
encoderB = YAnButton.FindAnButton("encoderB")
encoderA.set_pulseCounter(0)
encoderB.set_pulseCounter(0)
old_counter = 0
 



Pour avoir un résultat fiable, nous calculons la rotation uniquement si les signaux A et B ont la même valeur (car il sont sur un cran). On calcule ensuite le nombre de transition depuis le dernier appel en en soustrayant old_counter à la valeur du compteur de l'entré A. Après avoir déterminé le nombre de transitions, il faut déterminer le sens de la rotation: Si la dernière transition est le signal B c'est que l'on une rotation dans le sens des aiguilles d'une montre. Si au contraire la dernière transition est le signal A, c'est dans le sens contraire des aiguilles d'une montre.

La fonction est relativement simple et utilise les méthodes suivantes de la classe YAnButton:

  • get_isPressed: Retourne vrais si l'entrée est "pressé"
  • get_pulseCounter:Retourne la valeur du compteur d'impulsions.
  • get_lastTimePressed: retourne le nombre de millisecondes entre la mise sous tension du module et la dernière transition "pressé".




def handleRotatePolling():
    if encoderA.get_isPressed() != encoderB.get_isPressed():
        return
    counter = encoderA.get_pulseCounter()
    global old_counter
    nb_transitions = counter - old_counter
    if nb_transitions != 0:
        b_get_last_time_pressed = encoderB.get_lastTimePressed()
        a_get_last_time_pressed = encoderA.get_lastTimePressed()
        if (b_get_last_time_pressed > a_get_last_time_pressed):
            print("rotate clockwise of %d Detents " % nb_transitions)
        else:
            print("rotate anti-clockwise of %d Detents" % nb_transitions)
        old_counter = counter
 



Il ne reste plus qu'à appeler handleRotatePolling() régulièrement pour détecter les rotations.


while True:
    handleRotatePolling()
    ...
    do_some_stuff()
    ...
 



Attention à ne pas trop attendre entre chaque appel, par exemple si l'on appelle notre fonction une fois par seconde, vous risquez d'avoir des erreurs d’interprétation. Durant une seconde, l'utilisateur à la possibilité de tourner d'un quart de tour à gauche et d'un quart de tour à droite (pour revenir à la même position). Dans ce cas, au lieu de détecter deux rotations inverses, le programme va détecter une rotation de 180° à droite.

Utilisation par callback


Si vous avez besoin d'une réactivité maximum, vous pouvez travailler en callback. Dans ce cas, on ne va plus utiliser le compteur de transition du Yocto-Knob mais on va enregistrer un callback sur chaque entrée. Le callback est appelé à chaque changement de valeur des entrées du Yocto-Knob, le nombre de rotations est donc toujours de 1. Pour déterminer le sens cette rotation, on regarde si c'est le signal A ou B qui est monté ou descendu.


encoderA = YAnButton.FindAnButton("encoderA")
encoderB = YAnButton.FindAnButton("encoderB")
last_ev_pressed = encoderA.get_isPressed()
encoderA.registerValueCallback(handleRotate)
encoderB.registerValueCallback(handleRotate)

...

def handleRotate(button, value):
    """
    handle rotation of encoder

    @type button: YAnButton
    @type value: str
    """

    global last_ev_pressed
    pressed = int(value) < 500
    if pressed != button.get_userData():
        if last_ev_pressed != pressed:
            if (button.get_logicalName() == "encoderA"):
                print("rotate clockwise of 1 Detents")
            else:
                print("rotate anti clockwise of 1 Detents")
            last_ev_pressed = pressed
        button.set_userData(pressed)
 



Le fichier source est disponible sur GitHub.

Conclusion


  
Exemple d'interface homme-machine qui utilise un codeur rotatif avec un Yocto-Knob



Les encodeurs rotatifs sont un moyen astucieux et plus élaboré de réaliser des interfaces homme-machine. D'autant plus que certains encodeurs intègrent un bouton qui permet de détecter une pression sur la molette. Dans ce cas, avec une seule molette, il est possible d'utiliser trois fonctions: rotation, contre-rotation, et clic. C'est particulièrement utile pour naviguer dans une liste ou des menus. Attention tout de même à ne pas en abuser :-)

Commenter aucun commentaire
Retour au blog












Yoctopuce, get your stuff connected.