Reading a 10bit Absolute Optical Rotary Encoder

TLDR: Can't read 10bits fast enough.

So I'm working on my first big project-- in short, it's a precision wire feed mechanism. A stepper motor will be driving a wire/cable through a tube and I need to measure the position of the wire's end over ~1.5metres. The wire will run over a roller attached to a rotary encoder and with the right dimension measurements I should be able to equate the angular position of the roller to the length of wire fed.

What I'm running into now is being able to keep counting after each incremental revolution, or over 1023. The situation would be easily solved if I could eliminate skipping any positions... But that seems to be a tall order. The code I have running now works as long as I don't rotate the encoder too fast, but any sudden moves across the 0/360degree point and my turn increment goes nowhere. I'm wondering if there's a standard process for reading these encoders, because I haven't come across any write-ups on the web as of yet on how to handle absolute encoders. It would be fine to skip bits while the wire is in motion as when it stops it would read it's exact position (the beauty of this project's needs), but I just need to catch the transition between turns so that I'm reading 800mm instead of 600mm.

The output of the encoder EP50S8-1024-2F-N-5 (segment of the datasheet: http://smartbuy.img.paran.com/industry/Encoder/EP50S8%201024%202F%20N%205%20d2.jpg) is either ~.63v or 0v, so I used 3 LM339 quad Comparators to shift the .63 signals to 5v so they can be read by my Arduino Micro.

Any questions-- please ask!

Absolute encoder optical disk example: http://www.parkermotion.com/dmxreadyv2/blogmanager/app_engine/assets/images/encoder-rot-large.jpg

//October 15, 2013
//arduino encoder interface

int k,j,sensed,prevSensed,absoluteSense;
int bits[]={0,1,2,3,4,5,6,7,8,9};
int pin[]={3,4,5,6,7,8,9,10,11,12};
int power[]={1,2,4,8,16,32,64,128,256,512};
int turn[]={0,1024,2048,3072,4096,5120,6144,7168,8192};

void setup(){
  Serial.begin(115200); //i figure faster is better, right?
  sensed,prevSensed=0;
  j=0;
  
  //initiates the read every time bit0 changes (1024times/rev)
  attachInterrupt(0, encode, CHANGE);
  
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(7, INPUT);
  pinMode(8, INPUT);
  pinMode(9, INPUT);
  pinMode(10, INPUT);
  pinMode(11, INPUT);
  pinMode(12, INPUT);
}

void loop(){

}

void encode(){
  sensed=0;
  Serial.print("binary: ");
  //reads all 10 bits of the encoder
  for(k=9; k>=0; k--){
    bits[k]=digitalRead(pin[k]);
    Serial.print(bits[k]);
    sensed=bits[k]*power[k]+sensed; //"binary addition" by bit
  }
  
  //detects an increasing rollover
  if(sensed==0){
    if(prevSensed==1023){
      j=j++;
    }
  }
  //detects a decreasing rollover
  if(sensed==1023){
    if(prevSensed==0){
      if(j>0){
        j=j--;
      }
      else{         //keeps j positive
        j=0;
      }
    }
  }
  prevSensed=sensed; //saves for comparing next read
  //displays the position
  //(accounting for additional turns if they were sensed)
  absoluteSense=sensed+turn[j];
  Serial.print("     Position: ");
  Serial.println(absoluteSense);
}

Don't do serial prints inside an ISR.

http://www.gammon.com.au/interrupts

http://www.gammon.com.au/forum/?id=12153#trap4

Doing a multiply for every bit seems slow to me. Also you can read 8 bits using a direct port access. In particular pins D0 to D7 are all on PIND.

eg.

byte foo = PIND;  // read D0 to D7

Can't you just read the position periodically, calculate the distance traveled since the last reading (since you don't know the direction of travel yet this will be ambiguous; you will have two answers - one representing forward travel and one backward). Then assume that whichever has the smallest absolute value represents the true direction of travel.

For example, if the reported position changes from 1022 to 1023 then this could be a movement of +1 or -1023. Obviously, it's +1. Similarly if it goes from 1023 to 0 it could be a movement of +1 or -1023; obviously it's +1. Now that you know which direction it's moving, counting the number of revolutions is simple.

With this approach you don't have to detect every single position change, as long as you detect at least one out of every 512 changes. It would IMO make sense to trigger this logic off an interrupt triggered by the encoder lsb, but you could do it on a timed basis if the interrupt frequency turns out to be excessive.