Encodeur Rotatif avec Arduino UNO

J'ai besoin d'avoir une gestion d'un encodeur rotatif pour naviguer dans des menus ou avoir différentes actions sur un programme. L'interaction la plus simple est l'encodeur rotatif avec bouton poussoir qui va servir de validation. L'encodeur rotatif est composé de frotteurs mécanique, ce qui peut induire des faux-contacts. Si on ne prend pas de précautions, on va avoir, à cause des parasites induits par les faux-contacts, une grosse probabilité de pas supplémentaires dans la détection de rotation. On peut mettre des condensateurs pour intégrer les parasites. La solution que j'utilise est tout simplement une intégration logicielle, autant sur le codeur (A & B) que le bouton poussoir C qui peut lui aussi avoir des rebonds. Le programme ci-dessous utilise le timer 1 pour générer une fréquence de scrutation de 1kHz. la variable _seconde m'est utile mais pas pour l'encodeur et peut être supprimée. La led clignotante n'est pas utile non plus, ça permet juste de voir que la routine de scrutation fonctionne. Attention : le timer 1 utilisé ici l'est aussi par certaines librairies, dont servo.h. Faites donc vos recherches si vous avez des dysfonctionnement en les utilisant.
Le commun de l'encodeur et le bouton poussoir sont au GND.

#define LED   LED_BUILTIN
#define inA   2   // A ou B du codeur
#define inB   3   // A ou B du codeur
#define inC   4   // clic du codeur

#define LBAS  3
#define LHAUT 6
#define LHH   9
#define APPUI_LONG  300       // nb de ms pour un appui long
#define APPUI_TRES_LONG 2000  // nb de ms pour un appui très long

volatile unsigned _ms=0;
volatile unsigned _secondes=0;

void setup(){
  cli();
  // ---- Init TIMER 1 RTC ----
  TCNT1 = 65535-250;                 // Preset : 1ms@16Mhz
  TCCR1A  = 0x00;             
  TCCR1B  |= (1 << CS11) | (1 << CS10);      // prescaler timer1 à 64
  TIMSK1  = (1 << TOIE1);               // interruption sur overflow timer 1
  sei();
  pinMode(inA,INPUT_PULLUP);
  pinMode(inB,INPUT_PULLUP);
  pinMode(inC,INPUT_PULLUP);
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
}

volatile int  pos = 0;
volatile byte clic = 0, clic_long=0, clic_tres_long=0;

// --- Toutes les 1 ms ---

ISR(TIMER1_OVF_vect){
  static byte a=0, b=0;
  static byte int_sa=0, int_sb=0, int_sc=0;
  static byte _clic=0;
  static unsigned maintenu=0;
  byte voie_a, voie_b, voie_c;

  TCNT1 = 65535-250; 
  if (++_ms == 1000)           // 1 seconde écoulée
  { 
    digitalWrite(LED,digitalRead(LED) ^ 1 );
    _ms = 0;
    _secondes++;
  }

  voie_a = digitalRead(inA);
  voie_b = digitalRead(inB);
  voie_c = digitalRead(inC);
 
  //------------------------------------ 
  if (voie_a==0){           // voie A est active
    if (int_sa != 0)        // si integration en cours
      if (--int_sa <= LBAS)
        if (a!=0){
          a=0;
          if (b==0)
            pos++;
          else
            pos--;
        }
  }
  else
  {
    // voie_a == 1
    if (int_sa < LHH)        // voie A passe inactive
      if (++int_sa >= LHAUT)   // si integration en cours
        a=1;
  }
  // -------------------------------
  if (voie_b==0)
  {
    if (int_sb!=0)
       if (--int_sb <= LBAS)
          b=0;
  }
  else
  {
    // voie_b == 1
    if (int_sb < LHH)
      if (++int_sb >= LHAUT)
          b=1;
  }
  
  // traitement du clic
  if (voie_c==0)    // bouton cliqué
  {               
    if (int_sc != 0)
      if (--int_sc == 0)
        _clic=1;
  }
  else              // bouton relâché
  {
    // voie_c == 1
    if (int_sc < LHH)
    if (++int_sc > LHAUT)
    {
      if (_clic != 0)
      {
        _clic=0;
        if (maintenu < APPUI_LONG)
          clic = 1;
        else {
          if (maintenu < APPUI_TRES_LONG)
            clic_long = 1;
        }
        maintenu=0;
      }
    }
  }
  if (_clic != 0)
    if (++maintenu == APPUI_TRES_LONG)
      clic_tres_long = 1;
}

int pos_old = 0;

void loop(void)
{
  if (pos != pos_old)
  {
    Serial.println(pos);
    pos_old=pos;
  }
  
  if (clic != 0)
  {
    clic=0;
    Serial.println("clic !");
  }
  
  if (clic_long != 0)
  {
    clic_long=0;
    Serial.println("clic long !");
  }
  
  if (clic_tres_long != 0)
  {
    clic_tres_long=0;
    Serial.println("clic très long !");
  }
}

Le programme va modifier 4 variables :

  • pos qui donne la position de l'encodeur
  • clic qui se met à 1 au moment du clic relâché court
  • clic_long qui se met à 1 pour un clic relâché long (plus de 0,3s)
  • clic_tres_long qui se met à 1 pour un clic très long (plus de 2s)


les variables clicxxxxxxxx doivent être remises à 0 par le programme qui les utilise.
la variable pos peut être elle aussi remise à 0.