Thursday, January 5, 2012

HDD rotary encoder with interrupts!

I ripped apart a few of  the many old IDE HDD siting around for the BLDC motors that they contain to create a rotary encoder device.

something like:

The circuit building turned out pretty well. The HDD had a BLDC with 3 pins(whye windings) so i used the following scheme, but only the first two Op-Amps. One of the windings(pin) is a reference to the other two windings and used to determine direction. Signal 1 and 2 go to digital ports 2 and 3 on the Arduino for a 3 pin motor.
VCC(5v) and GND also need to be connected.
The coding side was, of course, an adventure; I first attempted it all on my own with interrupts(i understand them finally :)) and it worked OK, but could be improved a little. These are the waveforms of the two pins that the Arduino relies on to determine the direction the motor is turning.

Code that someone posted that is more efficient, but i don't understand: Reading Encoder Gray code 2 bit

Pics:
Encoder and needed LM324N IC. Also the joystick breakout cable is there.



My Code:

#define EncPinA 2
#define EncPinB 3
volatile unsigned int EncPos = 0;
void setup()
{
  pinMode(EncPinA, INPUT);
  digitalWrite(EncPinA, HIGH);

  pinMode(EncPinB, INPUT);
  digitalWrite(EncPinB, HIGH);

  attachInterrupt(0, EncA, CHANGE);  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(1, EncB, CHANGE);  // encoder pin on interrupt 1 (pin 3)
  Serial.begin (115200);
}
void loop()
{
  //FREE for a MIDI interface(turntables?) or something!
}
void EncA(){
  // look for a low-to-high on pin A
  if (digitalRead(EncPinA) == HIGH) {
    // check pin B to see which way encoder is turning
    if (digitalRead(EncPinB) == LOW) {
      EncPos = EncPos - 1;         // CW
    }
    else {
      EncPos = EncPos + 1;         // CCW
    }
  }
  else   // must be a high-to-low edge on pin A                                    
  {
    // check pin B to see which way encoder is turning
    if (digitalRead(EncPinB) == HIGH) {
      EncPos = EncPos - 1;          // CW
    }
    else {
      EncPos = EncPos + 1;          // CCW
    }
  }
  Serial.println (EncPos, DEC);        
  // use for debugging - remember to comment out
}
void EncB(){
  // look for a low-to-high on pin B
  if (digitalRead(EncPinB) == HIGH) {
    // check pin A to see which way encoder is turning
    if (digitalRead(EncPinA) == HIGH) {
      EncPos = EncPos - 1;         // CW
    }
    else {
      EncPos = EncPos + 1;         // CCW
    }
  }
  // Look for a high-to-low on pin B
  else {
    // check pin B to see which way encoder is turning
    if (digitalRead(EncPinA) == LOW) {
      EncPos = EncPos - 1;          // CW
    }
    else {
      EncPos = EncPos + 1;          // CCW
    }
  }
}


4 comments:

  1. It would be faster to read directly from the registers. I did like this in a similar setup:

    void leftencoder()
    {
    if((PIND & B00100100) == 0 || (PIND & B00100100) == 36)//pin 2 == pin 5
    leftTachPos--;
    else
    leftTachPos++;
    }
    void rigthencoder()
    {
    if((PIND & B01001000) == 0 || (PIND & B01001000) == 72)//pin 3 == pin 6
    rightTachPos--;
    else
    rightTachPos++;
    }

    ReplyDelete
  2. it seemed to work ok with regular digital read, but it would probably make a big difference when im using multiple encoders on a racing wheel/pedals im testing. thanks

    "(PIND & B00100100) == 0"
    ok since PORTD maps to Arduino digital pins 0 to 7, you are testing digital pin 2 and 5 to see if they are LOW, correct?

    (PIND & B00100100) == 36
    What is this; why 36?

    http://www.arduino.cc/en/Reference/PortManipulation doesnt say anything about reading ports...
    this is great though: http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=37871 ill read up

    ReplyDelete
  3. Hi, nice tutorial.
    For someone, like me, who doesn't have an oscilloscope, how can I decode which pin is which? Also, why do you connect the fourth pin to the opamp if you don't need it?

    Thanks.

    ReplyDelete
    Replies
    1. you can see in the image of my setup with the two opamps on the breadboard, that i didn't use that last wire. the three are there in the scheme to show what is needed for more precision/etc.

      if the output is going the opposite way that you would like, just swap the two enocder wire.

      Delete