[Risolto] Pinout interrupt hardware Arduino Mega

Buonasera e Buon Anno.

Sto cercando di utilizzare 2 encoder rotativi sullo stesso arduino.
Dopo aver provato diverse librerie che promettevano di fare ciò con facilità, le ho abbandonate, perchè non sono abbastanza veloci e perdono parecchie letture anche con un solo encoder soprattutto se utilizzate in codice corposo con una durata del loop non trasurabile.

Finora per avere letture veloci ed accurate utilizzavo questo codice:

/***********************************************************
  File name: _16_RotaryEncoderModule.ino
  Description: The information of rotary encoder module has been
             detected by UNO R3,and displayed in the serial monitor
             When the rotary encoder turns clockwise, the angular
             displacement is increased;when it turns counterclockwise,
             it’s decreased.If you press the switch on the rotary
             encoder, related readings will return to zero
  Website: www.adeept.com
  E-mail: support@adeept.com
  Author: Tom
  Date: 2016/06/15
***********************************************************/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);

#define APin 2 //Set the digital 2 to A pin
#define BPin 3 //Set the digital 3 to B pin
#define Prova 14
int lastEncoded = 0;
float encoderValue = 8700;
long lastencoderValue = 0;
float frequency;

int lastMSB = 0;
int lastLSB = 0;

void setup()
{
  lcd.init();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("hello");
  pinMode(APin, INPUT_PULLUP);//initialize the A pin as input
  pinMode(BPin, INPUT_PULLUP);//initialize the B pin as input
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);
  pinMode(Prova, INPUT_PULLUP);

  Serial.begin(9600); //opens serial port, sets data rate to 9600 bps

}

void loop()
{

  Serial.println(encoderValue);


  frequency = encoderValue / 100;

  if (frequency < 87.00) frequency = 87.00;
  if (frequency > 108.00) frequency = 108.00;

  Serial.println(frequency);
  lcd.setCursor(0, 1);
  lcd.print(encoderValue);
  lcd.setCursor(0, 2);
  lcd.print(frequency);
  if (digitalRead (Prova) == LOW) {
    lcd.setCursor(0, 3);
    lcd.print("0");
  }
  else if (digitalRead (Prova) == HIGH)
  {
    lcd.setCursor(0, 3);
    lcd.print("1");
  }
}

void updateEncoder() {
  int MSB = digitalRead(APin); //MSB = most significant bit
  int LSB = digitalRead(BPin); //LSB = least significant bit
  int encoded = (MSB << 1) | LSB; //converting the 2 pin value to single number
  int sum  = (lastEncoded << 2) | encoded; //adding it to the previous encoded value
  if (sum == 0b1101 || sum == 0b0100 || sum == 0b0010 || sum == 0b1011) encoderValue ++;
  if (sum == 0b1110 || sum == 0b0111 || sum == 0b0001 || sum == 0b1000) encoderValue --;
  lastEncoded = encoded; //store this value for next time
}

A questo punto ho collegato un secondo encoder ai pin 18 e 19 ed ho copiato il codice necessario modificandone le variabili per non duplicarle ed aggiungendo i necessari riferimenti per i pin e gli interrupt.
A tal proposito, consultando il pinout del MEGA, ho notato che gli interrupt sui pin 2 e 3 sono indicati come 4 e 5 e quindi, negli attachInterrupt corrispondenti, li ho sostituiti ma, così facendo, l’encoder corrispondente non funziona; viceversa rimettendo 0 e 1 il valore varia correttamente.

A ricerca di conferme, ho aperto il file pins.h del MEGA, e stranamente, nella definizione degli interrupt, sembra che effettivamente sui pin 2 e 3 vi siano gli interrupt 0 e 1: ora, visto che anche il pinout dell’AtMega 2560 li riporta come 4 e 5, mi piacerebbe capire se c’è un errore da qualche parte o, se chi ha scritto il file, li ha deliberatamente assegnati così magari per compatibilità con l’UNO.

P.S.

Nei giorni scorsi si era già parlato di encoder che perdevano passi: se l’autore del topic non ha risolto o volesse provare senza librerie, può ripulire il codice riportato, utilizzando come valore utile encoderValue (il resto sono mie aggiunte per utilizzarlo nella sintonia dei moduli radioFM)

Scusa, di quali encoder parli, quelli manuali a scatti o quelli per motori ? ... perche' nel codice e' menzionato il pulsante dell'encoder, che c'e' solo in quelli manuali a scatti ...

Se e' cosi non ti serve a nulla usare due interrupt per singolo encoder ed incasinarti la vita ... basta che per ogni encoder usi un solo interrupt su uno dei pin settato su "rising" (se gli interruttori chiudono a VCC) oppure su "falling" (se chiudono a massa), e poi nella corrispondente ISR controlli solo l'altro pin ... siccome quelli a scatti fanno sempre le stesse due commutazioni per ogni scatto, nel momeno in cui uno dei pin cambia, lo stato dell'altro ti dice in che senso stai girandolo ...

Ovviamente nelle ISR devi fare il meno possibile, per cui solo impostare una variabile o sommare/sottrarre, ma questo e' valido sempre e per qualsiasi ISR :wink:

Per quanto riguarda il tipo di encoder uso sia quelli a scatti che quelli per motori ed anche manopole/encoder per macchine utensili ed il codice è solo un test per verificare tutte le funzionalità ed un adattamento per le mie esigenze (per il codice pulito vedere i riferimenti contenuti).

Ma la domanda era un'altra; come mai vi è incongruenza tra il pinout degli interrupt da adottare e quello che effettivamente funziona?

Per il fatto di usare un solo interrupt , non ho verificato, ma penso che le varie librerie disponibili usino già un tale approccio e perdono comunque dei passi; potresti spiegarti meglio magari con un esempio, perchè alla fine vorrei utilizzare 2 encoder sull'AtMega 644/1284 che ha solamente 3 interrupt e probabilmente le sopracitate librerie non funzionano perchè puntano ai pin dell'UNO o del MEGA (ci sono già passato, tentando di capire come mai il codice non funzionava passandoci ore e ponendo domande qui, prima che qualcuno mi facesse notare che i pin sono diversi)

Ma la domanda era un'altra; come mai vi è incongruenza tra il pinout degli interrupt da adottare e quello che effettivamente funziona?

Se leggi la documentazione nell'ultimo paragrafo spiega come assegnare correttamente l'interrupt giusto per ogni pin.

Ciao, Ale.

Perfetto, ti ringrazio; adesso mi è chiaro perchè ci fosse questa incongruenza però rimane il fatto che, basandosi solo sui pinout che si trovano in rete si, viene tratti in inganno e non tutti arrivano a consultare la documentazione ufficiale.

Io, per esempio, mi sono affidato a vari libri cartacei anche relativamente avanzati, ma in nessuno vengono spiegate le differenze tra le varie board e come scrivere codice portabile e gran parte della documentazione che si trova è in inglese(che mastico molto poco)

Saluti , Valter.

Non so come funzionino le librerie, il sistema che dico io funziona correttamente solo con gli encoder a scatti (e solo se non ci si ferma a meta' di uno scatto tornando indietro, ma questo mi sembra ovvio da non fare ... se ci sono gli scatti meccanici, per un motivo li avranno messi :D)

Diciamo come esempio che hai un'encoder a scatti che chiude verso massa (se e' il contrario si inverte la logica, ma il principio e' identico) ... ora, un'encoder a scatti, per ogni scatto completo fa per forza 4 operazioni in sequenza, sempre le stesse e sempre con la stessa sequenza, per cui se giri verso destra avrai, ad esempio

chiude1->chiude2->apre1->apre2 ... per ogni singolo scatto, e viceversa se giri verso sinistra avrai

chiude2->chiude1->apre2->apre1

quindi vedi che se, ad esempio, metti un'interrupt "falling" su 1, e nella ISR controlli solo 2, puoi avere, per un'uso corretto dell'encoder, solo 2 risultati ... se quando 1 va basso 2 e' alto, stai girando verso destra, se e' basso stai girando verso sinistra ... quindi basta che nella ISR fai

if 2 == high incrementa variabile else decrementa variabile (o l'opposto se incrementi nel senso opposto)

nella ISR va fatto il meno possibile, perche' finche' e' li dentro non vengono eseguite altre istruzioni, se nella ISR c'e' troppa roba rischia di perdere altri comandi ...

Ringrazio anche te; adesso mi è chiaro perchè con gli encoder a scatti il conteggio avanzi di 4 unità ad ogni scatto; devo recuperare un encoder per motori senza scatti per vedere come si comporta.
Per il resto ci dedicherò del tempo; ne avrei giusto bisogno per un controllo di volume.

Saluti , Valter

Etemenanki:
quindi vedi che se, ad esempio, metti un'interrupt "falling" su 1, e nella ISR controlli solo 2, puoi avere, per un'uso corretto dell'encoder, solo 2 risultati ... se quando 1 va basso 2 e' alto, stai girando verso destra, se e' basso stai girando verso sinistra

Questo è il sistema più semplice e comodo, che però è sensibile a piccole rotazioni involontarie del perno quando ad esempio si vuole fare solo un click premendolo. Il modo completo è seguire l'intera sequenza dei quattro fronti da una posizione stabile all'altra.

nella ISR va fatto il meno possibile, perche' finche' e' li dentro non vengono eseguite altre istruzioni, se nella ISR c'e' troppa roba rischia di perdere altri comandi

Vero, però anche il loop deve poi riuscire a stare dietro ai valori prodotti dalla ISR, quindi tutto quanto deve essere ben progettato.

Ovvero, usare 2 pin con interrupt ed il codice che già uso o equivalente, oppure si può fare con un interrupt solo?
L'argomento mi interessa; nel mio caso non avrei grossi problemi con il metodo segnalato perchè non utilizzerei il pulsante e per un controllo di volume la precisione non è fondamentale, però mi potrebbe essere utile in futuro.

Saluti , Valter

Usando gli interrupt ne servirebbero due.
Un paio di esempi ci sono in questo thread recente.

Li sto già seguendo ed ho visto una soluzione che uso con profitto e volevo fare alcune prove pratiche, prima di dire la mia su quel thread.

Saluti , Valter