dimanche 21 septembre 2014

mardi 16 septembre 2014

Arduino : encodeur rotatif (KY040)

Je vous propose de voir une première utilisation simple d'un encodeur rotatif : le KY040 avec un arduino UNO.



Un exemple courant d'utilisation est le bouton d'un autoradio qui permet le réglage du volume mais aussi d'allumer et d'éteindre la radio. Il ressemble beaucoup à un potentiomètre mais contrairement à lui il n'est pas limité dans ça rotation et envoie un signal à chaque "crans". Il y a 30 crans par tour et l'on est capable de déterminer le sens de rotation de l'encodeur. Pour cela, l'encodeur nous envoie 2 signaux (on parlera du signal A et B) comme suit :


Source : Arduino UNO Tutorial 6 - Rotary Encoder
Pour faire simple, lorsque l'on est sur un cran, A et B ont le même niveau logique (0 ou 1). Si on tourne dans le sens horaire, on reçoit le changement d'état de A avant B et si on tourne dans le sens contraire on reçoit B avant A.

Mise en place du hardware

Pour "capturer" la rotation nous utiliserons les "interruptions" disponibles sur les entrées 2 et 3 de l'arduino. L'Uno ne disposant que de 2 entrées avec gestion des interruptions, le switch sera géré dans la fonction "loop".

Au niveau hardware, rien de compliqué :
  • DT   --> pin 2
  • CLK --> pin 3
  • SW  --> pin 4
Nous utiliserons l'écran LCD pour afficher le compteur ainsi que l'état des 3 entrées :
  • rs (LCD pin 4) --> Arduino pin 12
  • enable (LCD pin 6) --> Arduino pin 10
  • LCD D4 --> Arduino pin 8
  • LCD D5 --> Arduino pin 7
  • LCD D6 --> Arduino pin 6
  • LCD D7 --> Arduino pin 5

Le programme correspondant

On utiliser un "unsigned int" pour stocker un compteur que l'on incrémente ou décrémente suivant le sens de rotation de l'encodeur. Un appuis dessus remettra à 0 ce compteur.

De part l'utilisation d'un "unsigned int", ce compteur est forcement compris entre 0 et 65535.

Dernier point important, ce type de bouton génère des rebonds aux niveaux des entrées de l'arduino. Il faut donc un système d'anti-rebond. il existe plusieurs possibilités, soit hardware en utilisant par exemple des condensateurs (0.1µF devrait être bon), soit logique via un peu de code. C'est cette dernière solution que j'ai utilisé.

 

include <liquidcrystal.h>

LiquidCrystal lcd(12, 11, 10, 8, 7,6,5);

// définition des pin pour le KY040
enum PinAssignments {
  encoderPinA = 2,   // right (DT)
  encoderPinB = 3,   // left (CLK)
  clearButton = 4    // switch (SW)
};

volatile unsigned int encoderPos = 0;  // un compteur
unsigned int lastReportedPos = 1;   // gestion du changement
static boolean rotating=false;      // gestion de l'anti-rebonds

// variable pour les routines d'interruption
boolean A_set = false;             
boolean B_set = false;
boolean A_change = false;
boolean B_change= false;

void setup() {
  lcd.begin(20,4);
  lcd.clear();

  pinMode(encoderPinA, INPUT_PULLUP); // utilisation du pullup
  pinMode(encoderPinB, INPUT_PULLUP); // utilisation du pullup
  pinMode(clearButton, INPUT_PULLUP); // utilisation du pullup
  // activation de l'interruption 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);
  // activation de l'interruption 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);
}

void loop(){
  if (lastReportedPos != encoderPos) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print("Index: ");
    lcd.print(encoderPos, DEC);
    lastReportedPos = encoderPos;
    lcd.setCursor(0,1);
    lcd.print(digitalRead(encoderPinA));
    lcd.print(digitalRead(encoderPinB));
    lcd.print(digitalRead(clearButton));
  }
    lcd.setCursor(0,1);
    lcd.print(digitalRead(encoderPinA));
    lcd.print(digitalRead(encoderPinB));
    lcd.print(digitalRead(clearButton));
   
  if (digitalRead(clearButton) == LOW )  {
    encoderPos = 0;
    A_change = false;
    B_change= false;
    lcd.print(encoderPos);
  }

  delay (100);
}

// Interruption sur changement d'état de A
void doEncoderA(){
  // debounce
  if ( rotating ) delay (1);  // attendre un petit peut
  rotating = true; //activation de l'anti-rebond
  // Confirmation du changement
  if( digitalRead(encoderPinA) != A_set ) {
    A_set = !A_set;

    if (B_change) {
      encoderPos += 1;
      B_change = false;
    } else
      A_change = true;

    rotating = false;  //libération de l'anti-rebond
  }
}

// Interruption sur changement d'etat de B
void doEncoderB(){
  if ( rotating ) delay (1);
  rotating = true;
  if( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;

    if (A_change) {
      encoderPos -= 1;
      A_change = false;
    } else
      B_change = true;

    rotating = false;
  }
}


Source : ArduinoArduino UNO Tutorial 6 - Rotary EncoderUsing a Rotary Encoder with the Arduino