LusoRobótica - Robótica em Português

Sistemas específicos => Arduino / AVR => Tópico iniciado por: joaobernardino em 06 de Outubro de 2012, 16:10

Título: Rotary Encoder Com arduino
Enviado por: joaobernardino em 06 de Outubro de 2012, 16:10
Viva alguém me sabe explicar porque é que neste código abaixo cada vez que rodo o rotary encoder, aumenta 4 valores e não 1? ou seja em vez de ser 1,2,3,4,.. é 0,4,8,12, ... ?
Código: [Seleccione]
//From bildr article: http://bildr.org/2012/08/rotary-encoder-arduino/

//these pins can not be changed 2/3 are special pins
int encoderPin1 = 2;
int encoderPin2 = 3;

volatile int lastEncoded = 0;
volatile long encoderValue = 0;

long lastencoderValue = 0;

int lastMSB = 0;
int lastLSB = 0;

void setup() {
  Serial.begin (9600);

  pinMode(encoderPin1, INPUT);
  pinMode(encoderPin2, INPUT);

  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on

  //call updateEncoder() when any high/low changed seen
  //on interrupt 0 (pin 2), or interrupt 1 (pin 3)
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);

}

void loop(){
  //Do stuff here

  Serial.println(encoderValue);
  delay(1000); //just here to slow down the output, and show it will work  even during a delay
}

void updateEncoder(){
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //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
}

Obrigado
Título: Re: Rotary Encoder Com arduino
Enviado por: rglove em 06 de Outubro de 2012, 17:24
Nunca usei nenhum rotary encoder, então não sei muito bem como funciona, mas penso que o encoder tem 4 fases e quando passa as 4 fases conta 1 step certo?
Se for isso acho que esse código está a ler o número de fases e não o número de steps, daí contar de 4 em 4... Se achares o valor inteiro da divisão do número de fases por 4 dá-te o número de steps.
Assim não precisas de delay. Se só dividires penso que o valor fica arrendondado, assim se por acaso estiver na 3ª fase de um step já vai enviar mais um step que os steps completos. Não sei se me fiz entender...
Penso que para obter o valor inteiro da divisão usa-se a barra de dividir ao contrário (\) ou "div", mas ainda só usei isto em vb.net, não sei se em C é igual.

Assim penso que já vais obter o número de steps correcto.
Experimenta e depois diz alguma coisa, estou curioso para saber  :)
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 06 de Outubro de 2012, 17:51
Era mesmo isso, bastou dividir o resultado por quatro.
Obrigado pela explicação.
Título: Re: Rotary Encoder Com arduino
Enviado por: rglove em 06 de Outubro de 2012, 18:07
Mas atenção que se dividires por 4 só não chega, porque imaginando que o contador tem 6 fases contadas... 6/4 = 1.5. Como o valor é inteiro vai ser arredondado para 2. Como está o código não há problema, visto que só usas o valor dos steps depois do step ser concluído, mas para outras aplicações tens de conseguir o valor inteiro da divisão. Eu estive a ver em arduino.cc e não encontrei lá o comando para obter o valor inteiro da divisão, apenas encontrei para descobrir o resto. Sendo assim, acho que tens que utilizar este pedaço de código para descobrir o valor inteiro:
Código: [Seleccione]
int resto = 0;
if (nrdefases >= 4) {
    resto = nrdefases % 4;
}
int nrdesteps = (nrdefases-resto) / 4;

Penso que é isto, não tenho a certeza, escrevi um bocado à pressa e não tenho aqui forma de testar.
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 06 de Outubro de 2012, 22:29
Obrigado, mas essa parte acho que não preciso.
Agora surgiu-me outro problema... :/
Se eu usar aquele delay(1000); no void loop(), tenho os valores todos certos como eu quero, se não usar o delay, ao rodar o rotary encoder salta números, porque?

Código: [Seleccione]
int encoderPin1 = 2;
int encoderPin2 = 3;

volatile int lastEncoded = 0;
volatile long encoderValue = 0;

long lastencoderValue = 0;

int lastMSB = 0;
int lastLSB = 0;

void setup() {
  Serial.begin (9600);

  pinMode(encoderPin1, INPUT);
  pinMode(encoderPin2, INPUT);

  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on

  //call updateEncoder() when any high/low changed seen
  //on interrupt 0 (pin 2), or interrupt 1 (pin 3)
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);

}

void loop(){
 
  if(encoderValue >396){
    encoderValue = 0;}
    if(encoderValue <0){
    encoderValue = 396;}
 
  Serial.print((encoderValue / 4)); //  midiCC(0xC0, 0, (encoderValue / 4));
  delay(1000); 
  }


void updateEncoder(){
 
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //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
}
void midiCC(char command, char value1, char value2){
  Serial.print(command, BYTE);
  Serial.print(value1, BYTE);
  Serial.print(value2, BYTE);
}
Título: Re: Rotary Encoder Com arduino
Enviado por: senso em 06 de Outubro de 2012, 22:31
Falta-te implementar deboucing do encoder, como qualquer botão mecânico, os encoders quando passam de uma posição para a outra, existe um periodo de tempo(2-15ms) em que o estado dos botões não é estavel, pois como são feitos de lamelas metálicas oscilção na transição.

Dá uma vista de olhos nisto:
http://arduino.cc/playground/Main/RotaryEncoders (http://arduino.cc/playground/Main/RotaryEncoders)
Título: Re: Rotary Encoder Com arduino
Enviado por: rglove em 06 de Outubro de 2012, 22:34
Para além disso não chega dividires por 4, tens que obter o quociente inteiro da divisão por 4... Assim os steps vão ser errados. Dá uma vista de olhos no código que meti em cima.
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 06 de Outubro de 2012, 22:57
Já dei uma vista de olhos em toda a página mas não encontro nada sobre "deboucing do encoder".
Podem ser mais especificos, eu sou mesmo novato nisto?

obrigado pela atenção
Título: Re: Rotary Encoder Com arduino
Enviado por: rglove em 06 de Outubro de 2012, 23:11
Vê aqui:
http://hifiduino.wordpress.com/2010/10/20/rotaryencoder-hw-sw-no-debounce/ (http://hifiduino.wordpress.com/2010/10/20/rotaryencoder-hw-sw-no-debounce/)

Podes fazer por hardware ou software. Por hardware é simples, resistências e condensadores.
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 07 de Outubro de 2012, 01:13
Agradeço as respostas, mas já estou farto de ler e não consigo construir o meu Software Debounce através daquele código :/
E também reparei que preciso para um botão que tenho.
Título: Re: Rotary Encoder Com arduino
Enviado por: rglove em 07 de Outubro de 2012, 01:48
Não sei se vai funcionar... Se não funcionar experimenta fazer o debouncing por hardware...

Código: [Seleccione]
int encoderPin1 = 2;
int encoderPin2 = 3;

volatile int lastEncoded = 0;
volatile long encoderValue = 0;

long lastencoderValue = 0;

int lastMSB = 0;
int lastLSB = 0;

void setup() {
  Serial.begin (9600);

  pinMode(encoderPin1, INPUT);
  pinMode(encoderPin2, INPUT);

  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on

  //call updateEncoder() when any high/low changed seen
  //on interrupt 0 (pin 2), or interrupt 1 (pin 3)
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);

}

void loop(){
 
  if(encoderValue >396){
    encoderValue = 0;}
    if(encoderValue <0){
    encoderValue = 396;}
    int resto = 0;
    if (encoderValue>4) {
        resto = encoderValue % 4;
    }
  Serial.print(((encoderValue-resto) / 4)); //  midiCC(0xC0, 0, (encoderValue / 4));
  }


void updateEncoder(){
 
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //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 --;
  if(sum == 0b1101 || sum == 0b0010 || sum == 0b1011 || sum == 0b1110 || sum == 0b0111 || sum == 0b0001) delay(2);

  lastEncoded = encoded; //store this value for next time
}
Título: Re: Rotary Encoder Com arduino
Enviado por: rglove em 07 de Outubro de 2012, 01:54
Já agora, que valores é que dá?
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 07 de Outubro de 2012, 09:58
O objectivo é fazer 0 a 99, superior a 99 volta para zero e inferior a zero volta para 99.
O que acontece é que se eu colcoar um delay grande (1000) no codigo, os valores dão todos certos e esse erro não acontece.
assim sá sempre esse erro, e além disso está sempre a enviar sinal e eu só quero enviar sinal quando o estado do rotary alterar.
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 07 de Outubro de 2012, 16:26
Hey finalmente ao fim de muito insistrir começo a ter progressos.
Já tenho o código para o botão com o debounce, e o do rotary encoder agora um bocadinho alterado também acho que está bom. Resta um problema, com a aleração que fiz o serial print está a ler-me as fases, ou seja, o que você tinha mencionado, cada passo do rotary encoder apresenta-me quatro valor os seja:
1ª rotação  1,2,3,4     2ª rotação 5,6,7,8  menos uma rotação 8, 7, 6,5....   por aí.

Queria só agora que cada rotação aumentasse só um valor, 1,2,3,4,5,....

Não consegui fazer com o que me foi dado por vocês anteriormente...

Código: [Seleccione]
int encoderPin1 = 2;
int encoderPin2 = 3;

volatile int lastEncoded = 0;
volatile long encoderValue = 0;

long lastencoderValue = 0;

int lastMSB = 0;
int lastLSB = 0;

long previousMillis = 0;
long interval = 100;    // update a cada "X" ms para não ocorrer erros de leitura   

void setup() {
  Serial.begin (9600);

  pinMode(encoderPin1, INPUT);
  pinMode(encoderPin2, INPUT);

  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on

  //call updateEncoder() when any high/low changed seen
  //on interrupt 0 (pin 2), or interrupt 1 (pin 3)
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);

}

void loop(){
}

void updateEncoder(){
 
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //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 --;

  if(lastEncoded != encoded){
  Serial.println(encoderValue);
   lastEncoded = encoded;
}
}

obrigado
Título: Re: Rotary Encoder Com arduino
Enviado por: rglove em 07 de Outubro de 2012, 16:56
Experimenta assim:

Código: [Seleccione]
int encoderPin1 = 2;
int encoderPin2 = 3;

int oldsteps = 0;

volatile int lastEncoded = 0;
volatile long encoderValue = 0;

long lastencoderValue = 0;

int lastMSB = 0;
int lastLSB = 0;

long previousMillis = 0;
long interval = 100;    // update a cada "X" ms para não ocorrer erros de leitura   

void setup() {
  Serial.begin (9600);

  pinMode(encoderPin1, INPUT);
  pinMode(encoderPin2, INPUT);

  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on

  //call updateEncoder() when any high/low changed seen
  //on interrupt 0 (pin 2), or interrupt 1 (pin 3)
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);

}

void loop(){
    int resto = 0;
    int steps = 0;
    if (encoderValue >= 4) {
        resto = encoderValue % 4;
        steps = (encoderValue-resto)/4;       
    }
    if (steps != oldsteps) {
        Serial.println(steps);
    }
    oldsteps = steps;
}

void updateEncoder(){
 
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //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 --;

  if(lastEncoded != encoded){
   lastEncoded = encoded;
}
}
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 07 de Outubro de 2012, 22:29
Está perfeito faltam apenas duas coisas, tem de começar em zero, mas eu já mudei a variavel
Código: [Seleccione]
int oldsteps=-5, logo quando inicia é difirente dos steps e envia o valor 0.
Não sei se recorda mas tinha falado que queria o estilo de um circulo, chegava a 99 voltava a 0, e abaixo de 0 volta para 99. Fui capaz de passar de 99 para 0 sem qualquer problema atraves de
Código: [Seleccione]
if(steps>99){
    encoderValue=0;
  }
Mas o contrário de 0 para 99 dá-me o mesmo erro que sempre falei desdo o inicio de saltar do 0 para 98 sem passar pelo 99, eu usei o seguinte.
Código: [Seleccione]
if(encoderValue<0){
    encoderValue=396;    //  encoderValue=396   ( steps= 99)
  }

Agradeço o seu esforço, muito obrigado a sério.
Título: Re: Rotary Encoder Com arduino
Enviado por: rglove em 07 de Outubro de 2012, 22:57
Está perfeito faltam apenas duas coisas, tem de começar em zero, mas eu já mudei a variavel
Código: [Seleccione]
int oldsteps=-5, logo quando inicia é difirente dos steps e envia o valor 0.
Não sei se recorda mas tinha falado que queria o estilo de um circulo, chegava a 99 voltava a 0, e abaixo de 0 volta para 99. Fui capaz de passar de 99 para 0 sem qualquer problema atraves de
Código: [Seleccione]
if(steps>99){
    encoderValue=0;
  }
Mas o contrário de 0 para 99 dá-me o mesmo erro que sempre falei desdo o inicio de saltar do 0 para 98 sem passar pelo 99, eu usei o seguinte.
Código: [Seleccione]
if(encoderValue<0){
    encoderValue=396;    //  encoderValue=396   ( steps= 99)
  }

Agradeço o seu esforço, muito obrigado a sério.

Antes de mais trata-me por tu, de certeza que sou mais novo que tu (eu trato toda a gente por tu, não quero estar a faltar ao respeito)  :)

Antes de mais coloca essas mudanças do encoderValue dentro da interrupção, porque se por acaso passarem 2 valores abaixo de 0 (-2) ele vai passar para 396 e vão-se perder fases, depois os steps já não vão estar correctos!

Acho que o problema está em passares de -1 para 396. Como disseste, 396 = 99 steps. O que está a acontecer é que quando passas do step 0 para -1 estás a subtrair 4 ao encoderValue, ficando com -4.
Como sempre que o valor é negativo passa a 396, quando encoderValue = -1, este muda para 396. Ainda falta subtrair 3, logo no final vais ficar com 393. O valor inteiro da divisão de 393 por 4 é igual a 98. Logo ficas com 98 steps e não 99. Para além disto ainda perdes uma fase, visto que deverias ter passado de -1 para 395 e não 396. Logo tens de alterar o 396 para 399. Assim quando o encoderValue estiver em -1, vai passar a 399. Ainda falta subtrair 3 para completares o step. Logo 399-3 = 396. 396/4 = 99 steps.

Espero ter ajudado!
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 07 de Outubro de 2012, 23:19
Qual é interrupção?
Título: Re: Rotary Encoder Com arduino
Enviado por: rglove em 07 de Outubro de 2012, 23:41
O void updateEncoder  :)

Mete no final do void, acho que antes tinhas no void loop...
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 14 de Outubro de 2012, 19:01
O código final que me ajudaram a construir encontra-se aqui abaixo, funciona na perfeição se trabalhar sozinho, ao juntar com outros codigos para funcionar em conjunto com um botao e um touchscreen apenas funciona o rotary encoder e nada do resto da sinal :/ porque?
Código: [Seleccione]
int encoderPin1 = 3;
int encoderPin2 = 2;

int oldsteps = -5;

volatile int lastEncoded = 0;
volatile long encoderValue = 0;

long lastencoderValue = 0;

int lastMSB = 0;
int lastLSB = 0;

 

void setup() {
  Serial.begin (31250);

  pinMode(encoderPin1, INPUT);
  pinMode(encoderPin2, INPUT);

  digitalWrite(encoderPin1, HIGH); //turn pullup resistor on
  digitalWrite(encoderPin2, HIGH); //turn pullup resistor on

  //call updateEncoder() when any high/low changed seen
  //on interrupt 0 (pin 2), or interrupt 1 (pin 3)
  attachInterrupt(0, updateEncoder, CHANGE);
  attachInterrupt(1, updateEncoder, CHANGE);

}

void loop(){
 
  int resto = 0;
    int steps = 0;
    if (encoderValue >= 4) {
        resto = encoderValue % 4;
        steps = (encoderValue-resto)/4;       
    }
    if (steps != oldsteps) {
        Serial.println(steps);
        midiCC(0xC0, 0, steps);
    }
    oldsteps = steps;
    if(steps>99){
    encoderValue=0;
  }
   if(encoderValue<0){
    encoderValue=399;    //  encoderValue=396   ( steps= 99)
  }
   
   
}

void updateEncoder(){
 
 
  int MSB = digitalRead(encoderPin1); //MSB = most significant bit
  int LSB = digitalRead(encoderPin2); //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 --;

  if(lastEncoded != encoded){
   lastEncoded = encoded;
}
}

void midiCC(char command, char value1, char value2){
  Serial.print(command, BYTE);
  Serial.print(value1, BYTE);
  Serial.print(value2, BYTE);
}
Título: Re: Rotary Encoder Com arduino
Enviado por: senso em 14 de Outubro de 2012, 23:28
Tens de mostrar como é que estás a juntar o código..
Título: Re: Rotary Encoder Com arduino
Enviado por: joaobernardino em 15 de Outubro de 2012, 00:18
Já está resolvido, como ainda não percebo muito disto, meti o void updateEncoder() dentro do void loop ().

obrigado na mesma.