• Welcome to LusoRobótica - Robótica em Português.
 

Rotary Encoder Com arduino

Started by joaobernardino, 06 de October de 2012, 16:10

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

joaobernardino

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, ... ?
//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

rglove

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  :)

joaobernardino

Era mesmo isso, bastou dividir o resultado por quatro.
Obrigado pela explicação.

rglove

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:

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.

joaobernardino

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?


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);
}

senso

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
Avr fanboy

rglove

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.

joaobernardino

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

rglove

#8
Vê aqui:
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.

joaobernardino

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.

rglove

#10
Não sei se vai funcionar... Se não funcionar experimenta fazer o debouncing por hardware...


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
}

rglove

Já agora, que valores é que dá?

joaobernardino

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.

joaobernardino

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...

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

rglove

Experimenta assim:

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;
}
}