Lettura Encoder generando interrupt e tramite stati finiti

Salve, come da titolo ho un grattacapo riguardante la lettura di un Encoder in quadratura.
Utilizzo un Arduino Micro (originale) l'encoder
é collegato ai pin 2 e 3.
Utilizzo il PIN 2 per generare l'interrupt e poi leggo il valore sul PIN 3 per controllare la direzione, dopo di ché all'interno di una switch case aggiorno lo stato dell'encoder per ignorare i rimbalzi.
Più facile a vedersi che dirsi.

   // Input pins to connect to the encoder
const uint8_t CLK = 2;
const uint8_t DT = 3;

volatile int16_t        inputDelta = 0;       // Counts up or down depending which way the encoder is turned
volatile static bool    printFlag = false;    // Flag to indicate that the value of inputDelta should be printed
volatile static uint8_t state = 0;            // Variabile che indica lo stato dell'encoder
volatile bool           CLKstate;
volatile bool           DTstate;

void setup() {
    Serial.begin(115200);
    pinMode(CLK, INPUT_PULLUP);
    pinMode(DT, INPUT_PULLUP);
    attachInterrupt(digitalPinToInterrupt(CLK), readEncoder, FALLING);

}

void loop() {
    printDelta();
}

void readEncoder() {
    while(printFlag == false){
    CLKstate = digitalRead(CLK);
    DTstate = digitalRead(DT);
    switch (state) {
        case 0:                         // interrupt generato
                state = 1;              //salto al controllo direzione
            break;
        case 1:                        
            if (DT){                    // Turn clockwise and CLK goes low first
                state = 2;
            } else if (!DTstate) {      // Turn anticlockwise and DT goes low first
                state = 5;
            }
            break;
        // Clockwise rotation
        case 2:                     
            if (!DTstate) {             // Continue clockwise and DT will go low after CLK
                state = 3;
            } 
            break;
        case 3:
            if (CLKstate) {             // Turn further and CLK will go high first
                state = 4;
            }
            break;
        case 4:
            if (CLKstate && DTstate) {  // Both CLK and DT now high as the encoder completes one step clockwise
                state = 0;
                ++inputDelta;
                printFlag = true;
            }
            break;
        // Anticlockwise rotation
        case 5:                         // As for clockwise but with CLK and DT reversed
            if (DTstate) {
                state = 6;
            }
            break;
        case 6:
            if (CLKstate && DTstate) {
                state = 0;
                --inputDelta;
                printFlag = true;
            }
            break;
        }
     }
}

void printDelta() {
    if (printFlag) {
        printFlag = false;
        Serial.println(inputDelta);
    }
}

Probabilmente mi perdo in un bicchiere d'acqua.
Ma se compilo e carico il programma nella porta seriale vedo solo incrementi sia che giri in senso orario che antiorario

Buongiorno e benvenuto nella sezione Italiana del forum,

cortesemente, come prima cosa, leggi attentamente il REGOLAMENTO di detta sezione, (prestando molta attenzione al punto 15), dopo di che, come da suddetto regolamento, fai la tua presentazione NELL'APPOSITA DISCUSSIONE spiegando bene quali esperienze hai in elettronica e programmazione, affinché noi possiamo conoscere la tua esperienza ed esprimerci con termini adeguati.

Grazie,

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposita discussione, nel rispetto del succitato regolamento nessuno ti risponderà (eventuali risposte verrebbero temporaneamente nascoste), quindi ti consiglio di farla al più presto. :wink:

Questo non ha molto senso: DT è una costante numerica di valore 3, quindi if(DT) sarà sempre vero.

Ciao, Ale.

Non c'è un po' troppa roba in quella funzione di interrupt?... Addirittura ci sono una dozzina di digitalRead!
Per evitare i rimbalzi basta leggere la sequenza Gray che esce dall'encoder!

In un verso:
00
01
11
10
00

Nel verso opposto:
00
10
11
01
00

Se fa 00-01-11 è stato ruotato in un verso; se fa 00-10-11 è stato ruotato nell'altro.

Questo è il blocchetto di codice che uso abitualmente:

int E; // Risultato della routine encoder(): 1, -1, 0.
byte S; // Lettura dei due valori dell'encoder.
byte So;// Lettura precedente dell'encoder.
int X; // Usato in encoder() per evitare letture multiple.

void encoder()
{
//              PD 76543210
// S=3-PIND      &B00000011; Gli I/O 0 e 1 sono PD0 e PD1, perciò non devo scorrere a destra.
// S=3-(PIND>>3) &B00000011; Serviva per l'encoder su PD3 e PD4.
// S=3-((PIND>>4)&B00000011); // Gli I/O 4 e 5 erano PD4 e PD5, perciò dovevo scorrere a destra di 4 bit.
S=3-((PIND>>2)   &B00000011); // Uso PD2 e PD3, quindi scorro a destra di 2 bit per farli diventare 0 e 1. 
// Il valore binario di S rappresenta A e B. Il centrale dell'encoder è a massa, quindi faccio complemento a 3 (11).  
S^=S>>1; // Faccio XOR (^) fra S (gray) e il suo bit 1, facendolo scorrere a Dx: AB XOR A,
         // ottenendo un binario che per ogni scatto fa 0-1-2-3-0 oppure 0-3-2-1-0.
E=0;
if (S!=So && S==0) X=0;
if (X==0)
  {
  if (So==1&&S==2)
    {E=1; X=1;
    // Bip();
    }
  if (So==3&&S==2)
    {E=-1; X=1;
    // Bip();
    }
  if (S==0)
    {E=0; X=0;}
  So=S;  
  }
}

Sì, si potrebbe ottimizzare un po'...

int E; // Risultato della routine encoder(): 1, -1, 0.
byte S; // Lettura dei due valori dell'encoder.
byte So;// Lettura precedente dell'encoder.
int X; // Usato in encoder() per evitare letture multiple.

void encoder()
{
//              PD 76543210
// S=3-PIND      &B00000011; Gli I/O 0 e 1 sono PD0 e PD1, perciò non devo scorrere a destra.
// S=3-(PIND>>3) &B00000011; Serviva per l'encoder su PD3 e PD4.
// S=3-((PIND>>4)&B00000011); // Gli I/O 4 e 5 erano PD4 e PD5, perciò dovevo scorrere a destra di 4 bit.
S=3-((PIND>>2)   &B00000011); // Uso PD2 e PD3, quindi scorro a destra di 2 bit per farli diventare 0 e 1. 
// Il valore binario di S rappresenta A e B. Il centrale dell'encoder è a massa, quindi faccio complemento a 3 (11).  
S^=S>>1; // Faccio XOR (^) fra S (gray) e il suo bit 1, facendolo scorrere a Dx: AB XOR A,
         // ottenendo un binario che per ogni scatto fa 0-1-2-3-0 oppure 0-3-2-1-0.
E=0;
if (S!=So && S==0)
  {
  X=0;
  if (S==2)
    {     
    X=1;
    if (So==1)
      {
      E=1;
      // Bip();
      }
    else if (So==3)
      {
      E=-1;
      // Bip();
      }
    }
  else if (S==0)
    {
    E=0;
    X=0;
    }
  So=S;  
  }
}

Solitamente non uso l'interrupt, perché il loop è abbastanza veloce oppure faccio le impostazioni in un while apposito, che attende la pressione del pulsante.

Oh' ecco, come dicevo mi sono perso in un bicchier d'acqua, svista da principiante.
A quante pare sono serviti altri occhi per vedere la disattenzione.

Scusate ma ormai era un sacco di tempo che riguardavo il codice e non ne venivo a capo, ormai avevo viziato la vista.

Grazie.

Mi scuso se ho mancato di rispetto al regolamento del forum e grazie per avermi riportato all'attenzione.

Ho provveduto a tutto quanto indicato.

Lapo.

Molte grazie per la dritta, un altra occasione per "imparare"

Lapo.