Rotary encoder in un intervallo 1-5

Ciao a tutti, mi vergogno un po' a chiedere perché la domanda sarà veramente banale per molti, ma io non ci arrivo.
La cosa è ancora teorica ma mi serve per capire, ho usato questo codice per che funziona bene ma volevo vincolarlo ad un intervallo 1-5 e 5-1 se vado in senso orario tutto bene, ma se vado in senso antiorario la scala parte da 255... come posso risolvere?
le righe commentate con "/MF" sono mie.
Grazie

/*******Interrupt-based Rotary Encoder Sketch*******
by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt, Steve Spence
*/

static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent

void setup() {
  pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
  attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
  Serial.begin(9600); // start the serial monitor link
}

void PinA(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
  if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    encoderPos --; //decrement the encoder's position count
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

void PinB(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    encoderPos ++; //increment the encoder's position count
  if(encoderPos >5) { //MF
    encoderPos =1;    //MF
    }                 //MF
  if(encoderPos <=0) { //MF
    encoderPos =1;    //MF
    }                 //MF
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

void loop(){
  if(oldEncPos != encoderPos) {
    Serial.println(encoderPos);
    oldEncPos = encoderPos;
  }
}

ciao

piccola premessa: se l'encoder ti serve per "scorrere" delle voci in un "menù" (ovvero non hai tantissimi impulsi al secondo) io trovo sprecato l'uso degli interrupts.Ma questa è solo una mia opinione.

Tornando alla tua richiesta: tu hai due funzioni chiamate PinA e PinB: PinA è quella preposta a diminuire il valore di encoderpos mentre PinB lo fa aumentare.

Hai semplicemente messo il controllo del valore minimo di encoderpos all'interno della funzione sbagliata.

ciao
pippo72

Grazie Pippo sei stato molto gentile, cosa suggerisci per un menù con pochi elementi.
Grazie 1000

ciao

Non hai però detto se quello che ti ho detto sull'errore del codice è giusto.

bimbo78:
... cosa suggerisci per un menù con pochi elementi...

Se la domanda è riferita all'utilizzo dell'encoder io non ho utilizzato interrupts ma due ingressi "normali": controlli un ingresso; quando varia controlli in se il secondo è alto o basso e da questo sai se giri in un senso o nell'altro.
Questo sistema può funzionare non tanto se hai poche "voci" da scorrere ma dipende da cosa altro deve fare il microcontrollore: se è "troppo" impegnato a fare vari calcoli può tranquillamente non vedere che stai girando l'encoder (oppure perdersi qualche "scatto").
Nel mio utilizzo dell'encoder per scorreve voci di un menù (e stamparle su LCD) ho creato un codice "bloccante" nel senso che in micro rimane in attesa appunto che l'encoder dia un segnale. Questo comportamento va bene a me, non è detto che vada bene anche a te.

Se invece la è riferita sul come fare un menù esiste in megatopic una libreria fatta apposta (che però io non ho mai usato).

ciao
pippo72

Grazie ancora Pippo, purtroppo sono così a ignorante che non saprei replicare quello che hai indicato tu con "due ingressi "normali", forse un giorno...

Il mio obbiettivo è fare un pid con lcd e solo un rotary encoder.

grazie
ciao

ciao

Per due ingressi normali intendo semplicemente 2 pin qualsiasi, non il 2 e 3 che sono quelli "collegati" agli interrupts.

bimbo78:
Il mio obbiettivo è fare un pid con lcd e solo un rotary encoder.

Se devi fare un PID direi che il mio modo di leggere un endcoder NON va assolutamente bene e devi utilizzare gli interrupts.

Comunque non hai ancora detto se sei riuscito a correggere lo sketch oppure no...

ciao
pippo72

bimbo78:
Ciao a tutti, mi vergogno un po' a chiedere perché la domanda sarà veramente banale per molti, ma io non ci arrivo.
La cosa è ancora teorica ma mi serve per capire, ho usato questo codice per che funziona bene ma volevo vincolarlo ad un intervallo 1-5 e 5-1 se vado in senso orario tutto bene, ma se vado in senso antiorario la scala parte da 255... come posso risolvere?
le righe commentate con "/MF" sono mie.
Grazie

/*******Interrupt-based Rotary Encoder Sketch*******

by Simon Merrett, based on insight from Oleg Mazurov, Nick Gammon, rt, Steve Spence
*/

static int pinA = 2; // Our first hardware interrupt pin is digital pin 2
static int pinB = 3; // Our second hardware interrupt pin is digital pin 3
volatile byte aFlag = 0; // let's us know when we're expecting a rising edge on pinA to signal that the encoder has arrived at a detent
volatile byte bFlag = 0; // let's us know when we're expecting a rising edge on pinB to signal that the encoder has arrived at a detent (opposite direction to when aFlag is set)
volatile byte encoderPos = 0; //this variable stores our current value of encoder position. Change to int or uin16_t instead of byte if you want to record a larger range than 0-255
volatile byte oldEncPos = 0; //stores the last encoder position value so we can compare to the current reading and see if it has changed (so we know when to print to the serial monitor)
volatile byte reading = 0; //somewhere to store the direct values we read from our interrupt pins before checking to see if we have moved a whole detent

void setup() {
  pinMode(pinA, INPUT_PULLUP); // set pinA as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  pinMode(pinB, INPUT_PULLUP); // set pinB as an input, pulled HIGH to the logic voltage (5V or 3.3V for most cases)
  attachInterrupt(0,PinA,RISING); // set an interrupt on PinA, looking for a rising edge signal and executing the "PinA" Interrupt Service Routine (below)
  attachInterrupt(1,PinB,RISING); // set an interrupt on PinB, looking for a rising edge signal and executing the "PinB" Interrupt Service Routine (below)
  Serial.begin(9600); // start the serial monitor link
}

void PinA(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; // read all eight pin values then strip away all but pinA and pinB's values
  if(reading == B00001100 && aFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    encoderPos --; //decrement the encoder's position count
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00000100) bFlag = 1; //signal that we're expecting pinB to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

void PinB(){
  cli(); //stop interrupts happening before we read pin values
  reading = PIND & 0xC; //read all eight pin values then strip away all but pinA and pinB's values
  if (reading == B00001100 && bFlag) { //check that we have both pins at detent (HIGH) and that we are expecting detent on this pin's rising edge
    encoderPos ++; //increment the encoder's position count
  if(encoderPos >5) { //MF
    encoderPos =1;    //MF
    }                //MF
  if(encoderPos <=0) { //MF
    encoderPos =1;    //MF
    }                //MF
    bFlag = 0; //reset flags for the next turn
    aFlag = 0; //reset flags for the next turn
  }
  else if (reading == B00001000) aFlag = 1; //signal that we're expecting pinA to signal the transition to detent from free rotation
  sei(); //restart interrupts
}

void loop(){
  if(oldEncPos != encoderPos) {
    Serial.println(encoderPos);
    oldEncPos = encoderPos;
  }
}

na roba del genere :confused:
if (encoder < 0) encoder =1;
if (encoder >5) encoder =5;

Francamente, io continuo a non capire perche' continuate ad usare DUE interrupt per leggere un'encoder azionato a mano, quando ne basta uno solo ...

Se colleghi un'interrupt ad esempio al pin A, quando si attiva perche' ruoti l'encoder, se ruota in un senso, il pin B sara' SEMPRE in uno stato (diciamo alto), mentre se ruota nell'altro sara' SEMPRE nell'altro stato (diciamo basso ... dipendera' da come li colleghi, ma il principio non cambia) ... e siccome e' un'encoder a mano, non potra' mai andare cosi veloce da far "fallire" la lettura dell'interrupt, come invece potrebbe essere per un'encoder da macchina utensile o roba simile ... non c'e' scopo ad usarne due ...

Per cui se ad esempio leggi un'interrupt in RISING sul pin A, nella ISR ti basta fare una cosa tipo (pseudocodice)

IF pinB ALTO & contature < 5
contatore + 1
ELSE IF pinB basso & contatore > 1
contatore - 1

Il che significa che incrementi la variabile contatore se gira in un senso, ma solo se non e' gia a 5, altrimenti non fai nulla, e decrementi girando nell'altro, ma solo se non e' gia 1, altrimenti non fai nulla ... :wink:

Etemenanki:
Francamente, io continuo a non capire perche' continuate ad usare DUE interrupt per leggere un'encoder azionato a mano, quando ne basta uno solo ...

per una cosa a "mano" proprio non c è ne vogliono di interrupt

Anche ... ma giusto per il caso che lo sketch sia strapieno di roba sempre in esecuzione, con un'interrupt sei sicuro di non perderti nessuna attivazione ... se invece lo sketch fa poco o nulla, si puo farne anche a meno ...

Inoltre se usi un'interrupt in condizione "rising" (o "falling", comunque attivato solo quando il segnale cambia in modo specifico, quindi non "change" e basta), non serve neppure settare delle flag e dei cicli if appositi per evitare che lo sketch "legga" la condizione piu volte di seguito ad ogni ciclo del loop :wink: