Go Down

Topic: Piccolo ma fastidioso problema con encoder! (Read 1 time) previous topic - next topic

Elius94

Ciao!
Sto programmando un controller midi touch screen che oltre al controllo touchscreen, abbia anche un encoder rotativo in grado di cambiare un canale midi tra 0 e 99.
Il codice funziona, nel serial monitor si vede che ruotando l'encoder varia la variabile della sua posizione a sinistra o a destra e poi ricomincia a ciclo infinito se per esempio si va sotto lo zero o se si va sopra il 99.

il problema è che quando il codice è solo quello dell'encoder (all'interno del loop) funziona benissimo, anche girandolo ad alta velocità, ma quando inserisco nel codice tutto il restante listati (touchscreen ecc), nel serial monitor funziona, si leggono i valori del touch screen, quando lo tocco, si leggono i valori dei pulsanti, quando li premo, si leggono anche le posizioni dell'encoder quando lo giro, solo che se lo giro velocemente come prima, si mangia dei valori e non va all'indietro. se lo giro piano incrementa o decrementa bene. come posso fare?

Questo è il mio codice, mi potete dire se vedete qualcosa di sbagliato? Grazie :)

Code: [Select]

// by Elia Lazzari
//###########################################################################
//# Touch Screen Arduino Controller for Kaoss Pad External MIDI Controller  #
//###########################################################################
//
//  Il seguente firmware è privo di controllo sul led RGB e privo di midi out

// Dichiariamo i pin analogici usati dal touchscreen
int y1 = A0;
int x2 = A1;
int y2 = A2;
int x1 = A3;

//Dichiariamo le variabili di offset ecc.
int X;
int Y;
int xOFFSET = 180;
int yOFFSET = 100;
int xOFFSETSUP = 635;
int yOFFSETSUP = 835;
int xMIDI;
int yMIDI;

// Prossimamente da cambiare con le uscite PWM
const int bluePin = 41;
const int greenPin = 42;
const int redPin = 43;

// Costanti pin e stato di hold
const int holdPin = 45;
int holdStat = LOW;
boolean hold = false;

int val;
const int encoder0PinA = 36;
const int encoder0PinB = 40;
int encoder0Pos = 0;
int encoder0PinALast = LOW;
int n = LOW;

void setup() {
 
// Impostiamo che i pin dei led sono delle uscite
   pinMode (redPin,OUTPUT);
   pinMode (greenPin,OUTPUT);
   pinMode (bluePin,OUTPUT);

// E che il pin del pulsante hold è un ingresso   
   pinMode (holdPin,INPUT);

// Anche i due pin dell'encoder sono ingressi
   pinMode (encoder0PinA,INPUT);
   pinMode (encoder0PinB,INPUT);
   
  Serial.begin(9600);
}

// Impostiamo gli input e gli output fra i pin del touchscreen
int readX(){
  pinMode(y1, INPUT);
  pinMode(x2, OUTPUT);
  pinMode(y2, INPUT);
  pinMode(x1, OUTPUT);

  digitalWrite(x2, LOW); // Collegato a massa
  digitalWrite(x1, HIGH); // Collegato a +5V

  delay(5); //pause to allow lines to power up

  return analogRead(y1);
}

int readY(){

  pinMode(y1, OUTPUT);
  pinMode(x2, INPUT);
  pinMode(y2, OUTPUT);
  pinMode(x1, INPUT);

  digitalWrite(y1, LOW); // Collegato a massa
  digitalWrite(y2, HIGH); // Collegato a +5V

  delay(5); //pause to allow lines to power up

  return analogRead(x2);
}

void loop()
{
  {
// Le prossime due righe rendono la variabile holdStat Toggle
   holdStat = digitalRead(holdPin);
   if (holdStat == HIGH) {
     hold = !hold;
     Serial.println (hold);
     delay(100);
   }
   
// Le prossime righe sono dedicate al codice che gestisce l'encoder e la sua variabile
// NON TOCCARE!
   n = digitalRead(encoder0PinA);
   if ((encoder0PinALast == LOW) && (n == HIGH)) {
     if (digitalRead(encoder0PinB) == LOW) {
       encoder0Pos--;
     } else {
       encoder0Pos++;
     }   
// In particolare, queste due righe impostano il range dell'encoder (0 : 99)
// Con il ritorno a ciclo infinito. L'intervallo da 0 a 99 è lo stesso del program
// Change del controllo MIDI del Kaoss Pad.
     if (encoder0Pos == -1) encoder0Pos = 99;
     else if (encoder0Pos == 100) encoder0Pos = 0;

// Per controllare via monitor seriale la posizione dell'encoder
     Serial.println (encoder0Pos);
   }
   encoder0PinALast = n;
}
// TOUCH SCREEN AREA!!!
{
  int x = readX();
  int y = readY();

// Impostiamo un limite inferiore, ovvero posizioniamo lo 0
  X = x - xOFFSET;
  Y = y - yOFFSET;
 
// Trucco per fare in modo che il touchscreen non mandi valori negativi
  if (X < 0) X = 0;
  if (Y < 0) Y = 0;
 
// Impostiamo un limite superiore per i due assi
  if (X > xOFFSETSUP) X = xOFFSETSUP;
  if (Y > yOFFSETSUP) Y = yOFFSETSUP; 
 
// Rimpappiamo il touchscreen sulle variabili xMIDI e yMIDI, con valori da 0 a 127, per essere d'accordo con il protocollo MIDI
  xMIDI = map(X,0,xOFFSETSUP,0,127);
  yMIDI = map(Y,0,yOFFSETSUP,0,127);

// Provvisorio: Serve per visualizzare i valori di xMIDI e yMIDI su serial monitor
  if(x < 1000 & y < 1000){
    Serial.print("X: ");
    Serial.print(xMIDI);
    Serial.print(" - Y: ");
    Serial.println(yMIDI);
  }
}
// delay(100); //just to slow this down so it is earier to read in the terminal - Remove if wanted

}

Elius94

Il problema mi sa che è nel void Loop, perchè se elimino la parte che non centra con l'encoder, funziona a meraviglia!

gingardu

prova a mettere il decoder sugli
interrupt  cosi ha sempre la precedenza
Le cose si possono considerare facili in due casi: quando le si conosce bene o quando non le si conosce affatto...

leo72


Il problema mi sa che è nel void Loop, perchè se elimino la parte che non centra con l'encoder, funziona a meraviglia!

Tutte le funzioni consumano un po' di tempo di calcolo (vedo anche dei delay) per cui, mentre vengono eseguite, ti fanno saltare delle letture dell'encoder.

Ti suggerisco anch'io la soluzione di gingardu.

Elius94

ok, grazie mille!
non conosco questa cosa... me la cavo con la programmazione e con l'elettronica ma questo non lo conosco. :P
mi potreste spiegare velocemente come imposto i pin dell'encoder nell'interrupt?  :)

leo72


ok, grazie mille!
non conosco questa cosa... me la cavo con la programmazione e con l'elettronica ma questo non lo conosco. :P
mi potreste spiegare velocemente come imposto i pin dell'encoder nell'interrupt?  :)

http://arduino.cc/en/Reference/AttachInterrupt

Intanto devi usare i pin 2 e 3 (INT0 e INT1), poi ti scrivi le funzioni che devono essere eseguite al cambio di stato dei pin a cui hai collegato l'encoder. Esse poi le attacchi agli interrupt e sei a posto. Quel codice te lo scordi e viene eseguito in automatico, tu ti ritrovi la posizione letta dall'encoder nel programma principale.

Elius94

ook. ci sono quasi! quindi in alto, scrivo:
Code: [Select]
attachInterrupt (1,function??, MODE?)

in pratica devo incollare in function tutta la parte che legge l'encoder, nel loop?

Grazie del supporto ! :)

Elius94

Ora ho fatto così, ma non credo che funzioni...

Code: [Select]

// by Elia Lazzari
//###########################################################################
//# Touch Screen Arduino Controller for Kaoss Pad External MIDI Controller  #
//###########################################################################
//
//  Il seguente firmware è privo di controllo sul led RGB e privo di midi out

// Dichiariamo i pin analogici usati dal touchscreen
int y1 = A0;
int x2 = A1;
int y2 = A2;
int x1 = A3;

//Dichiariamo le variabili di offset ecc.
int X;
int Y;
int xOFFSET = 180;
int yOFFSET = 100;
int xOFFSETSUP = 635;
int yOFFSETSUP = 835;
int xMIDI;
int yMIDI;

// Prossimamente da cambiare con le uscite PWM
const int bluePin = 41;
const int greenPin = 42;
const int redPin = 43;

// Costanti pin e stato di hold
const int holdPin = 45;
int holdStat = LOW;
boolean hold = false;

int val;
const int encoder0PinA = 2;
const int encoder0PinB = 3;
int encoder0Pos = 0;
int encoder0PinALast = LOW;
int n = LOW;

void setup() {
 
// Impostiamo che i pin dei led sono delle uscite
   pinMode (redPin,OUTPUT);
   pinMode (greenPin,OUTPUT);
   pinMode (bluePin,OUTPUT);

// E che il pin del pulsante hold è un ingresso   
   pinMode (holdPin,INPUT);

// Anche i due pin dell'encoder sono ingressi
   pinMode (encoder0PinA,INPUT);
   attachInterrupt (3, encoder, CHANGE);
   pinMode (encoder0PinB,INPUT);
   attachInterrupt (2, encoder, CHANGE);
   
  Serial.begin(9600);
}

// Impostiamo gli input e gli output fra i pin del touchscreen
int readX(){
  pinMode(y1, INPUT);
  pinMode(x2, OUTPUT);
  pinMode(y2, INPUT);
  pinMode(x1, OUTPUT);

  digitalWrite(x2, LOW); // Collegato a massa
  digitalWrite(x1, HIGH); // Collegato a +5V

  delay(5); //pause to allow lines to power up

  return analogRead(y1);
}

int readY(){

  pinMode(y1, OUTPUT);
  pinMode(x2, INPUT);
  pinMode(y2, OUTPUT);
  pinMode(x1, INPUT);

  digitalWrite(y1, LOW); // Collegato a massa
  digitalWrite(y2, HIGH); // Collegato a +5V

  delay(5); //pause to allow lines to power up

  return analogRead(x2);
}

void loop()
{
// Le prossime due righe rendono la variabile holdStat Toggle
   holdStat = digitalRead(holdPin);
   if (holdStat == HIGH) {
     hold = !hold;
     Serial.println (hold);
     delay(150);
   }
// TOUCH SCREEN AREA!!!
{
  int x = readX();
  int y = readY();

// Impostiamo un limite inferiore, ovvero posizioniamo lo 0
  X = x - xOFFSET;
  Y = y - yOFFSET;
 
// Trucco per fare in modo che il touchscreen non mandi valori negativi
  if (X < 0) X = 0;
  if (Y < 0) Y = 0;
 
// Impostiamo un limite superiore per i due assi
  if (X > xOFFSETSUP) X = xOFFSETSUP;
  if (Y > yOFFSETSUP) Y = yOFFSETSUP; 
 
// Rimpappiamo il touchscreen sulle variabili xMIDI e yMIDI, con valori da 0 a 127, per essere d'accordo con il protocollo MIDI
  xMIDI = map(X,0,xOFFSETSUP,0,127);
  yMIDI = map(Y,0,yOFFSETSUP,0,127);

// Provvisorio: Serve per visualizzare i valori di xMIDI e yMIDI su serial monitor
  if(x < 1000 & y < 1000){
    Serial.print("X: ");
    Serial.print(xMIDI);
    Serial.print(" - Y: ");
    Serial.println(yMIDI);
  }
}
// delay(100); //just to slow this down so it is earier to read in the terminal - Remove if wanted

}

void encoder()
{
// Le prossime righe sono dedicate al codice che gestisce l'encoder e la sua variabile
// NON TOCCARE!
   n = digitalRead(encoder0PinA);
   if ((encoder0PinALast == LOW) && (n == HIGH)) {
     if (digitalRead(encoder0PinB) == LOW) {
       encoder0Pos--;
     } else {
       encoder0Pos++;
     }   
// In particolare, queste due righe impostano il range dell'encoder (0 : 99)
// Con il ritorno a ciclo infinito. L'intervallo da 0 a 99 è lo stesso del program
// Change del controllo MIDI del Kaoss Pad.
     if (encoder0Pos == -1) encoder0Pos = 99;
     else if (encoder0Pos == 100) encoder0Pos = 0;

// Per controllare via monitor seriale la posizione dell'encoder
     Serial.println (encoder0Pos);
   }
   encoder0PinALast = n;
}

Elius94

dooh, non capisc come fare nel mio caso a usare questo metodo

gingardu

questo codice   l'ho testato su arduino uno e un encoder professionale 200 impulsi giro


int mela = 0;
int melaK = 0;
e il  il blink li ho messi per stampare sul serial monitor  una volta sola al secondo


Code: [Select]
int mela = 0;   
int melaK = 0;


int ledState = LOW;           
long previousMillis = 0;       

long interval = 10;   
#define encoder0PinA  2
#define encoder0PinB  4

volatile  long encoder0Pos = 0;

void setup() {


  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);       //  pullup
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH);       //pullup

  attachInterrupt(0, doEncoder, CHANGE); 
  Serial.begin (9600);
  Serial.println("start");   

}

void loop(){
  unsigned long currentMillis = millis();

  if(currentMillis - previousMillis > interval) {
     previousMillis = currentMillis; 
   if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;
  }

}

void doEncoder() {

  if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) {
    encoder0Pos++;
  } else {
    encoder0Pos--;
  }
if (ledState == LOW && mela == 0) Serial.println (encoder0Pos, DEC);
mela = 1;
if (ledState == HIGH )mela = 0;

}


void doEncoder_Expanded(){
  if (digitalRead(encoder0PinA) == HIGH) { 
    if (digitalRead(encoder0PinB) == LOW) {
                                           
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
    else {
      encoder0Pos = encoder0Pos + 1;         // CW
    }
  }
  else                                     
  {
    if (digitalRead(encoder0PinB) == LOW) {   
                                             
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }

  }
if (ledState == LOW && melaK == 0) Serial.println (encoder0Pos, DEC);
melaK = 1;
if (ledState == HIGH )melaK = 0;       
                                             

}

Le cose si possono considerare facili in due casi: quando le si conosce bene o quando non le si conosce affatto...

Elius94

OK Grazie mille, grazie a te, modificando un po il mio listato, ho finito, ora funziona bene tutta la lettura.
vi chiedo una cosa:
perchè se muovo l'encoder e mentre lo muovo premo il touchscreen, l'arduino si blocca?

cmq ecco il mio listato attuale:

Code: [Select]

// by Elia Lazzari
//###########################################################################
//# Touch Screen Arduino Controller for Kaoss Pad External MIDI Controller  #
//###########################################################################
//
//  Il seguente firmware è privo di controllo sul led RGB e privo di midi out

#define encoder0PinA  2
#define encoder0PinB  3

// Dichiariamo i pin analogici usati dal touchscreen
int x2 = A0;
int y1 = A1;
int x1 = A2;
int y2 = A3;

//Dichiariamo le variabili di offset ecc.
int X;
int Y;
int yOFFSET = 120;
int xOFFSET = 90;
int yOFFSETSUP = 765;
int xOFFSETSUP = 830;
int xMIDI;
int yMIDI;

// Prossimamente da cambiare con le uscite PWM
const int bluePin = 41;
const int greenPin = 42;
const int redPin = 43;

// Costanti pin e stato di hold
const int holdPin = 45;
int holdStat = LOW;
boolean hold = false;
int val;
int encoder0Pos = 0;
int encoder0PinALast = LOW;
int n = LOW;

void setup() {
 
// Impostiamo che i pin dei led sono delle uscite
   pinMode (redPin,OUTPUT);
   pinMode (greenPin,OUTPUT);
   pinMode (bluePin,OUTPUT);

// E che il pin del pulsante hold è un ingresso   
   pinMode (holdPin,INPUT);

// Anche i due pin dell'encoder sono ingressi
   pinMode (encoder0PinA,INPUT);
   pinMode (encoder0PinB,INPUT);
   attachInterrupt(0, doEncoder, CHANGE);
   
  Serial.begin(9600);
}

// Impostiamo gli input e gli output fra i pin del touchscreen
int readX(){
  pinMode(y1, INPUT);
  pinMode(x2, OUTPUT);
  pinMode(y2, INPUT);
  pinMode(x1, OUTPUT);

  digitalWrite(x2, LOW); // Collegato a massa
  digitalWrite(x1, HIGH); // Collegato a +5V

  delay(5); //pause to allow lines to power up

  return analogRead(y1);
}

int readY(){

  pinMode(y1, OUTPUT);
  pinMode(x2, INPUT);
  pinMode(y2, OUTPUT);
  pinMode(x1, INPUT);

  digitalWrite(y1, LOW); // Collegato a massa
  digitalWrite(y2, HIGH); // Collegato a +5V

  delay(5); //pause to allow lines to power up

  return analogRead(x2);
}

void loop()
{
 
// Le prossime due righe rendono la variabile holdStat Toggle
   holdStat = digitalRead(holdPin);
   if (holdStat == HIGH) {
     hold = !hold;
     Serial.println (hold);
     delay(100);
   }
   
// TOUCH SCREEN AREA!!!
{
  int x = readX();
  int y = readY();

// Impostiamo un limite inferiore, ovvero posizioniamo lo 0
  X = x - xOFFSET;
  Y = y - yOFFSET;
 
// Trucco per fare in modo che il touchscreen non mandi valori negativi
  if (X < 0) X = 0;
  if (Y < 0) Y = 0;
 
// Impostiamo un limite superiore per i due assi
  if (X > xOFFSETSUP) X = xOFFSETSUP;
  if (Y > yOFFSETSUP) Y = yOFFSETSUP; 
 
// Rimpappiamo il touchscreen sulle variabili xMIDI e yMIDI, con valori da 0 a 127, per essere d'accordo con il protocollo MIDI
  xMIDI = map(X,0,xOFFSETSUP,0,127);
  yMIDI = map(Y,0,yOFFSETSUP,0,127);

// Provvisorio: Serve per visualizzare i valori di xMIDI e yMIDI su serial monitor
  if(x < 1000 & y < 1000){
    Serial.print("X: ");
    Serial.print(xMIDI);
    Serial.print(" - Y: ");
    Serial.println(yMIDI);
  }
}
// delay(100); //just to slow this down so it is earier to read in the terminal - Remove if wanted

}

void doEncoder() {

  // Le prossime righe sono dedicate al codice che gestisce l'encoder e la sua variabile
// NON TOCCARE!
   n = digitalRead(encoder0PinA);
   if ((encoder0PinALast == LOW) && (n == HIGH)) {
     if (digitalRead(encoder0PinB) == LOW) {
       encoder0Pos--;
     } else {
       encoder0Pos++;
     }   
// In particolare, queste due righe impostano il range dell'encoder (0 : 99)
// Con il ritorno a ciclo infinito. L'intervallo da 0 a 99 è lo stesso del program
// Change del controllo MIDI del Kaoss Pad.
     if (encoder0Pos == -1) encoder0Pos = 99;
     else if (encoder0Pos == 100) encoder0Pos = 0;

// Per controllare via monitor seriale la posizione dell'encoder
     Serial.println (encoder0Pos);
   }
   encoder0PinALast = n;
}


Ciao

qsecofr

#11
Nov 22, 2012, 09:29 pm Last Edit: Nov 22, 2012, 09:39 pm by qsecofr Reason: 1

OK Grazie mille, grazie a te, modificando un po il mio listato, ho finito, ora funziona bene tutta la lettura.
vi chiedo una cosa:
perchè se muovo l'encoder e mentre lo muovo premo il touchscreen, l'arduino si blocca?



così su due piedi non ho la più pallida idea di perchè si pianti ma ho notato che hai messo un serial.print nella procedura di interrupt: non è una buona prassi... l'interrupt è per sua natura una procedura delicata da fare nella massima velocità... talvolta si mettono anche le istruzioni cli/sei proprio per evitare che gli interrupt si annidino uno dentro l'altro cambiandosi i valori delle variabili in corsa... quando il processore sente un interrupt lui va a eseguire la procedura di interrupt cascasse il mondo... ed un'operazione lenta come il serial print che potenzialmente potrebbe perdersi dietro ai protocolli ed hai tempi rs232... insomma non lo vedo molto bene...
io ho usato delle librerie già fatte... ce ne sono addirittura mezze fatte in assembler...
ho usato questa e legge perfettamente un encoder industriale da 4000 pulsazioni in quadratura al giro... veloce veloce...
http://www.pjrc.com/teensy/td_libs_Encoder.html

poi se ti interessa fare il giro a 99 ci sono metodi svariati: lo fai sul loop oppure appena prima di leggere potresti fare un modulo.... una cosa tipo
a = enco.read();
a = a % 99;
enco.write(a)

o anche proprio senza azzeramento del write tanto scrive su DW molto grandi... dubito che con una manopola potrai mai raggiungere valori di 2milioni di impulsi...

leo72


così su due piedi non ho la più pallida idea di perchè si pianti ma ho notato che hai messo un serial.print nella procedura di interrupt: non è una buona prassi... l'interrupt è per sua natura una procedura delicata da fare nella massima velocità... talvolta si mettono anche le istruzioni cli/sei proprio per evitare che gli interrupt si annidino uno dentro l'altro cambiandosi i valori delle variabili in corsa... quando il processore sente un interrupt lui va a eseguire la procedura di interrupt cascasse il mondo... ed un'operazione lenta come il serial print che potenzialmente potrebbe perdersi dietro ai protocolli ed hai tempi rs232... insomma non lo vedo molto bene...

Ciò che dici è tutto giusto però il compilatore Avr-gcc imposta gli interrupt atomici, quindi alla chiamata di una ISR vengono disattivati gli altri interrupt per cui un altro interrupt non può interrompere il codice.
Dall'IDE 1.0, poi, la gestione della seriale è stata portata tutta su interrupt per cui chiamare un serial.print ha minori ripercussioni di prima: i byte da spedire vengono posti nel buffer di trasmissione, e da qui, quando riabilitati gli interrupt a livello globale, un interrupt provvederà a spedire i dati.
Resta valido il consiglio di mettere nelle ISR meno cose possibili.

Elius94

Ok, grazie mille, vi voglio chiedere una cosa, inerente al progetto.. voi che di programmazone ne sapete più di me: ho fatto questa parte di codice, che gestisce l'evento della pressione del touchscreen, e che manda le coordinate via midi al canale 1.
so che non c'entra con l'encoder, ma vi chiedo questa cosa:
Code: [Select]

// Provvisorio: Serve per visualizzare i valori di xMIDI e yMIDI su serial monitor
  if(x < 1000 & y < 1000){
    MIDI.sendControlChange(92,127,1);
    MIDI.sendControlChange(12,xMIDI,1);
    MIDI.sendControlChange(13,yMIDI,1);
      }
  else {
    MIDI.sendControlChange(92,0,1);
    }   
}
// delay(100); //just to slow this down so it is earier to read in the terminal - Remove if wanted

}


così funziona, però vorrei fare in modo che nel primo "if", la funzione "MIDI.sendControlChange(92,127,1);" venga mandata solo al primo ciclo.
non so se avete capito cosa voglio dire...
In pratica

Code: [Select]

if(x < 1000 & y < 1000){
    MIDI.sendControlChange(92,127,1); //questa riga la voglio avere solo al primo ciclo dopo ogni volta che la condizione è vera
    MIDI.sendControlChange(12,xMIDI,1); //queste 2 invece, sempre
    MIDI.sendControlChange(13,yMIDI,1); // poi dal secondo ciclo in poi, mentre la condizione è verificata, voglio che vengano eseguite solo queste ultime due!
      }


So che non centra molto col topic, ma per favore, mi date una mano? :)

leo72

Devi usare un'altra variabile come flag.
Usi un boolean e lo imposti a false.
La condizione diventa:
Code: [Select]
if (x < 1000 & y < 1000) {
  if (!miaVarDiControllo) {
    miaVarDiControllo = true;
    MIDI.sendControlChange(92,127,1);
  }
  MIDI.sendControlChange(12,xMIDI,1);
  MIDI.sendControlChange(13,yMIDI,1);
}else {
  MIDI.sendControlChange(92,0,1);
  miaVarDiControllo = false; //per poter rispedire quanto sopra
}   

Go Up