Friday, February 26, 2010

Linear position tracking with a quadrature encoder

I recently searched the internet for information regarding linear position tracking with quadrature encoders. Surprisingly, there wasn't much available. I didn't find any development kits, blog postings, or other all-in-one information sources. This is strange since position tracking is needed for many applications, and can be done accurately and inexpensively. All inkjet printers, old ball-type computer mice and flatbed scanners use quadrature encoders to track motion (either linear or rotary).

I eventually found this:

However, $30 for each encoder, and $15 for each plastic linear strip seems a little excessive. The plastic strips are printed with a series of lines that provide an optical pattern for the sensor to track. These particular strips look like an alignment nightmare since the width of the printed area is so small. They don't seem to offer any kits either.

I also found that a company called Avago manufactures encoders, but not the plastic linear strips ("codestrips") that are required to actually use the encoder. I didn't see any development kits.

Mouser sells the Avago encoders:

I chose the AEDS-9641. The suffix determines the coder's resolution limit. I chose AEDS-9641-P10 for 150 lines-per-inch.

After extensive internet searching, I found some 150 lines-per-inch (lpi) plastic strips here:

These strips almost certainly were designed for inkjet printers. They are called linear code strips, codestrips, encoder strips, etc. They are quite wide, and have the pattern printed nearly across their whole width.
I connected the encoder's phases to my oscilloscope and pulled the plastic strip through the encoder.

OK, that looks great. Now I need to decode and count the quadrature sensor output. Fortunately, Avago also sells decoder chips that are built to work with their sensors, and Mouser carries some of them. I bought the HCTL-2022 and HCTL-2032.

Here I have the HCTL-2022 hooked up to a National Instruments USB-6501. The 6501 reads eight LSBs from the 2022 and displays the count on a graph ranging from 0 to 255.

There are some sharp transitions in this graph because the counter was wrapping around the possible data range as I moved the plastic strip through the encoder. The 2022 chip is a so-called "4x counter", so it registers a spatial resolution that is 4 times higher than the number of lines-per-inch on the encoder strip. It does this by tracking the rising and falling edges for both the "A" and "B" phases on the encoder. So, at 150lpi*4 = 600 counts/inch. The 8-bit range of 255 can cover about 255/600 = .425". For my application, this will be enough.

The system is very stable. I was able to feed the codestrip through the encoder by hand (with unavoidable wiggling and twisting), and the system maintained perfect tracking. The encoder has some plastic bumps that keep the strip in alignment, and this seems to work superbly. Avago says the encoder can count up to 60,000 lines per second, so at 150lpi, this is 400 inches/sec -- pretty fast.

I coupled the HCTL-2022 to an Analog Devices AD558 to provide a nice 0-10V analog output signal that can be sampled at high speed by a National Instruments DAQ device.


  1. you should try those magnetic strips they use for digital calipers

    this 40" 0.0005" accurate one is about 200$
    ouch.. maybe there are cheaper ones per feet

  2. thanks for sharing this, i learn more something new on linear position tracking. I also plan to set up mine. Good thing i found this short tutorial. Thanks Ben!

  3. I've been looking for a source of those stupid strips for a year. Thanks!

  4. Thank you so much for this post. You have no idea. I'm with Zack. Why is it so hard to find stuff like this? Somebody get on that.

  5. Hey Ben, what did you use for a clock on the decoder chip? It looks like you have a little yellow wire going off to the left from the clock pin. I'm trying to figure out how to read the HCTL-2022 from my Arduino. The data sheet for the HCTL-2022 says that data is unstable during certain points of the clock cycle so I wonder if I need to keep the HCTL-2022 synchronized with the Arduino clock.

  6. Dave, I used a 558 quad timer for prototyping (shown on the breadboard), but replaced it with a CMOS 555 chip for lower power consumption and smaller footprint in the final circuit. It's true that the HCTL-2022's clock is asynchronous with respect to the AD558, National Instruments device (or whatever will be reading the 8 bits from the 2022). You're right, the data sheet goes so far to say that the output data are only valid for 2ns after a rising clock edge on the 2022. This might be important when clocking at 33 MHz, but at slow speeds, you are almost guaranteed to read the data correctly at any given time. Smart manufacturers make their data sheets overly conservative to avoid potential problems, but it is common that chips will be easier to work with than stated -- especially at low speeds. I did not record any blips or incorrect data reads with the National Instruments device. I suppose it's possible that the National Instruments device could start reading data at the exact instant the 2022 is changing the state of the output bytes, and the NI device could read some bits in the old state and other bits in the new state, but this is apparently so unlikely (when clocking at 100 KHz, or so), that it essentially never happens. Good luck, let me know how you project goes.

  7. Hey Ben, I hope it's ok that I keep bugging you! You've been so helpful and I get such a kick out of your blog that I've been telling my engineer friends about it. Thanks for the wisdom about conservative data sheets and realistic expectation for asynchronous clocks. That all makes a lot of sense.

    I ran this problem by another friend and he brought up the good point that as long as I'm taking the trouble to program a micro controller I may as well see if I can write my own counter. It would save a bunch of ICs and multiplexers from the final version since I intend to have 3 rails arranged in an H for 2 degrees of freedom.

    I'm having a hard time though reading the signal from the AEDS-9641 optical encoder. The data sheet mentions a resistor for the emitter side but nothing for the detector side, so at first I just had my Arduino's 5v supply going to the Vcc pin, the GND pin going straight to ground, and the dual channels going to input pins on my Arduino. The detector side got so hot that it melted a mylar strip. I put a 300 ohm resistor after GND for the detector side and it cooled down. Now I'm measuring one of the output channels (on a new encoder in case I damaged the old one) and it only changes between 4.15 and 4.20 volts for when the LED is on or off respectively. I'm looking at your picture of your oscilloscope but I can't tell what the voltage difference is between high and low. Was the voltage delta that small for you too, or did you have some trick to make it more pronounced? Do I need to use a difference amplifier if I go down this road? I ultimitely will need something like a 0v to 5v signal that my Arduino can read.

    I've done my fair share of programming, but not electronics, so that may be a noob question. I forgot to rate limit an ADS-9641 detector earlier and it blew out in a flash of orange. Good thing I order extas for all these parts I'm destroying, heh. Anyway, thanks again, you've been super helpful.

  8. oooooook, I needed to take data sheets 101. The block diagram listed the pins in a different order than the pin out diagram. I just had to switch the leads around and now I'm off to the races.

  9. Dave, I seem to remember making a similar mistake, and I burned up one of my quadrature encoders too. The encoders take 0 and 5V, and the outputs are open-collector, meaning they need a pull-up resistor to get a 0 to 5V signal. You can use a pretty stiff resister, eg 1K or 5K. Lower values should give you faster edge times for high speed tracking. If your device is slow-speed and/or you need to conserve power, I'm sure the encoder would work with 100K resistors too (such as the Arduino internal pull-up).

    If you end up using the HCTL-2032 or 2022 chip, be sure to pull the "index" input line to ground if you are not using indices. I spent many hours trying to figure out why the counter was not incrementing, while the chip was outputting the raw counts from the encoder. This is definitely an omission on the datasheet, and the fact that the line should be pulled to ground and not +Vcc doesn't make sense given that most encoders are open-collector and mode code strips have a black line for index. Oh well.

    Good luck, let me know how your project goes. -Ben

  10. Hi Guys-

    I realize this is a pretty old post, but I've come across it several times while trying to solve my issues implementing a 2022. I'm using an arduino mega to drive the 2022 and can't get any data from the IC. My circuit diagram is here:

    and my arduino sketch here:

    Given that this appears to be one of the only articles on the 2022 on the web, I was hoping this might be the last stop on my insanity train. If you can be of any help I'd owe you big time!

    Thanks ahead of time!


  11. baltus, I looked at your schematic, but I don't have time to check the code. Some suggestions:

    Your life will be considerably easier if you have access to an oscilloscope. Consider getting one if you are interested in electronics like this.

    1. Check the raw quadrature phases at the 2022's input pins. You should see something like the picture in this blog post. Make sure you have clean +5 square waves with a 90* phase difference.

    2. Monitor the U/D pin on the 2022 while moving your encoder back and forth. If there is no signal, the problem is with the 2022 clock, power supply or encoder. If there is a signal there, the problem is with the counter/output circuit inside the 2022. Make sure the index line is pulled to ground (alternatively, try pulling it high, as it might be different between the 2032 and 2022). Instead of using the arduino, use a DMM or oscilloscope to monitor D0 while making changes. As soon as you see the signal present on D0, you can move on to adding the arduino.

    3. Add .1uF capacitors between the Vcc and gnd leads of the 2022 and your encoder.

    Let me know how it turns out

  12. ben-

    thanks for the quick response. I've gone through the steps that you suggested and have more or less conclusively decided that my clock signal must be messed up somehow. I have power at the chip, a good quad signal and tried pulling the index high and low- to no avail.

    I'm not too familiar with pulling an external clock from an atmega chip so I more or less grabbed a couple of lines from a gentlemen on the arduino forums though I'm not sure if it's working properly. I realize your implementation does not use an arduino but I'll post the few lines of code anyway just in case:

    pinMode(clockPin, OUTPUT); // select Pin as ch-A
    TCCR1A = B01000011; // Fast PWM change at OCR1A
    TCCR1B = B11001; // System clock
    OCR1A = 0; // 8 MHz

    I added the decoupling capacitor that you suggested so at this point I have to assume I goofed something in the above code. I really can't think of anything else...

    thanks for your help!

  13. If you are unsure of your clock signal, try building a simple 555 oscillator (100KHz has been a good choice for many of my projects), and use that instead of the arduino code. Don't worry about the 2022 being asynch with the arduino -- it will not matter for many applications.

    If you do not have access to an oscilloscope to examine the clock signal coming out of the arduino, I would not recommend spending any time trying to debug it. You need to isolate each part of your system and verify each part works before trying to integrate everything and test it all at once.

  14. Hi Ben:

    Sorry to bother you. I am using an HCTL-2032 to decode an optical enconder (HEDS-5540) and I need to read at least 16 bits from the HCTL-2032. I have a knob attached to my encoder in order to test it. Also, I'm using LabVIEW in order to read the counter's output. The problem is this:

    If I roll the knob fast, the count goes OK. But if I roll the knob slow enough to see how the count goes up and down, if I get near 255, the counter resets to 0 and begins to count again from 1,2, etc.. Also, if I am rolling the knob backwards, if I get near 255, the counter resets again to 0. This only happens if I roll the knob slowly.

    Some guy in AVR freaks suggested to read the four bytes in the order the datasheet suggests. I did that and I'm still having the same problem.

    Do you have an idea of what may be causing this strange behavior?

    I have the Index input of the HCTL-2032 connected to GND and I am controlling the OE, SEL1, SEL2 and RSTX inputs from my DAQ.

    Thank you very much,

    Jorge Cifuentes

  15. Hi Ben!

    I kind of solved the problem. I connected the RSTX input to VCC (so the counter is never reseted) and noticed that the issue is gone this way. Then it must be some problem with my LabVIEW code that for a very short amount of time my DAQ sends a logical '0' instead of a '1' to the RSTX input, therefore my counter resets unexpectedly.

    Thanks for having this great blog. :-)

    Have a nice day!

    Jorge Cifuentes

  16. thanks for the Goldmine encoder link. i spent a lot of time trying to find some of these. the encoders are easy enough to buy but you are supposed to be using thousands of them and have your codescales custom made. you saved me a bunch of time i would have spent looking and looking.

  17. Hi Ben,

    Thanks for putting this info up. I need some help with finding the right linear encoder. My application is as follows: Two approximately matchbox size boxes. One slightly larger than other. Smaller one needs to slide into the other and I want to measure its location very precisely. Another important aspect is the mechanical movement itself. This needs to be robust, repeatable and with little variation over time and minimal friction. If I could find a line encoder that already has the linear mechanical movement integrated, that would be ideal. In terms of resolution looking for something in the .001 inches range. Any suggestions would be greatly appreciated.


  18. Timbob, you may consider hacking a DRO scale, digital caliper, or digital depth gauge:

    I don't know of any other combined slide mechanisms with integrated encoders. Your best bet is to hack existing equipment like a flatbed scanner, inkjet printer, machinist tools, etc. Good luck

  19. I will give that a go. Thank you.

  20. Hi Ben,
    I am trying to implement a counter for my DC motor encoder using HCTL-2022 and am running into a lot of problems.
    First, there was no clock on the counter so I built a 555 circuit with freq ~ 1.5KHZ and supplied the signal to the 2022 as clock.
    Still I am not able to get anything on my D0-D7
    I checked my U/D pin and it looks like that is also messed up. It gives me a very random high-low , I suppose U/D should only change state when direction of rotation changes.
    I have pulled my index low and am using pull-ups for output pins. I am giving a high to OEN(Pin 5) and Sel1 and Sel2 are High and Low respectively to get the first byte.
    Its super frustrating.

  21. Saurabh, monitor the CNTDEC line while moving your encoder wheel/strip. This should give you a pulse for each transition regardless of direction. If you are not getting clean, meaningful pulses on the CNTDEC line, this indicates the quadrature signals themselves have a problem (eg are not 90* phase separated), or that your clock is not fast enough to catch the transitions. 1.5KHz is fairly slow for typical applications. I generally use 100KHz for encoder strips with 150 lines per inch, moving at rates caused by normal hand movements.

    If your chip doesn't have a CNTDEC line, double check your input quadrature signals to be sure they have a nice 90* phase separation. Ideally, you could view one or both signals on a 'scope while also viewing the HCTL clock. There should be many (10 or more) clock cycles per quadrature input cycle. The U/D line is only valid during a CNTDEC pulse, so the signal on U/D may appear unusual because its state is unusable outside a CNTDEC pulse. If your encoder is being moved slowly, U/D will fluctuate even though the direction of the encoder is not being changed.

    Make sure your reset line is apprpriately set, then play with the index pin again. Try it high and low and cycle it a few times. I've had lots of problems with the index, and I suspect the documentation is omitting some details about how it is implemented.

    Good luck

  22. Hi Ben,
    Thanks for the prompt reply. My chip doesn't have a CNTDEC line, and I have checked my quadrature signals they are coming out great. I tried speeding up the timer circuit and now I am at 68KHZ. I discovered my U/D only fluctuates in one direction, in the other it is zero. My Reset, OE and Index pins are open, will try your suggestions. Someone told me that the counters are latched and if I just try to scope the output with sel pins tied I wouldn't get nything. Will try everything in the lab tomorrow and will keep you posted. Thanx again!

  23. > My Reset, OE and Index pins are open

    OE should be pulled low. Reset should be pulled high. Index should be pulled either high or low -- I forget which. Unless a datasheet specifically says that you can leave logic pins open, you should always tie them to gnd or Vcc.

  24. Thanks for the help Ben, got it working, Reset high was the critical part I was missing.

  25. Hi there. Might be a daft question but the pin out diagram on the data sheet appears different to the functional pin descriptions further down. Is it just a case of the writing (HCTL-2022) on the pin out diagram being upside down



  26. Ha! I pulled the linear encoder and plastic strip out of a dead ink jet printer tonight and wired it up to my scope and a speaker (velocity slide whistle). Googling for data sheets brought up your blog post. Small world.

  27. Thanks Ben,

    Goldmine is sold out (I wonder if it's due to the "The Krasnow Bump"... :) )
    What would be needed to interface this to one of the DRO displays sold on eBay?
    This seems like. Poor mans version of the very expensive glass linear encoder scales.


  28. Hi Ben!
    I'm using the HCTL 2022 as well and my problem is that sometimes I have troubles with the count and get a negative number that is not supposed to appear. Is only when I'm counting down and getting closer to zero. So I wanted to try to solve the problem via code by giving a pulse to the reset signal each time it gets negative to make the count start from zero again. I don't know if doing that would actually change anything so I wanted to now if you could help me with this!

  29. BUMP - another comment on an old post (though this is one of those reasons the long tail is so great!)

    If you buy some laser-printable transparency paper, you can draw your 150-300 lpi using CAD or another drawing program that delivers adequate precision & print them out on a regular laser printer. I've only personally done this with 'strips' for rotary encoders, but there's no reason it wouldn't work for linear as well. Another option (which I've done more than once!) is to just rip the strips out of old printers. :)

  30. Hi - this was one of the prominent pages I found while trying to get my Arduino to read a DRO glass scale's quadrature output. And here, I believe I found out about the HCTL family of chips, which all appear to require a clock. I ended up using the LSI-7166, which is asynchronous, however a tad more tricky to set up. Full write is googleable via arduino lsi-7166 bench

    Anyhow, thanks for the great post which was the gateway to my solution. :) -Chris

  31. I want to add HCTL-2032 with ATmega32.m pleased to help in programming.Thanks

  32. Hi, I have a HTCL-2021 need to interface with my linear motion platform and DAQ device (NI PCI-6221). I need at least 16 bits output. Right now I only successful to generate 8 bit output (RESET pin set to high, SEL set to high). According to the datasheet, for proper operation of the inhibit logic during a two-byte
    read, OE and SEL must be synchronous with CLK due to the falling edge sampling of OE and SEL. The internal inhibit logic on the HCTL-2021 inhibits the transfer of data from the counter to the position data latch during the time that the latch outputs are being read. The inhibit logic allows the microprocessor / microcontroller to first read the high order 8 bits from the latch and then read the low order 8 bits from the latch.
    The problem is I don't have microprocessor / microcontroller to connect with decoder IC. Instead the decoder IC need to interface with DAQ device (NI PCI-6221). I would like to ask if it is possible to LabView to control the SEL and OE inputs from my DAQ device? If so, how to achieve it? Appreicate if anybody can help.

  33. I am using an AEDR-8300-k2: It is a reflective 75LPI encoder.

    My solution is to draw the codestrip in photoshop, and then laser print it onto silver gift wrapping paper. It work very well, but I am still trying to figure out how to waterproof the paper, as its needed for my application. Laminating the strip makes the black lines too specular reflective, and sanding the laminate makes the silver lines too diffuse reflective!

    If anyone has ideas how to do it, let me know!

  34. Anonymous, maybe try laser-printing the black bars onto a plastic transparency, and then paint the backside of the transparency white or silver? You could also try laser-cutting the bars out of aluminum foil. Maybe use a vinyl cuter to mask off areas of aluminum foil. If you can use a transmissive codestrip, that would avoid a lot of these challenges.

  35. I've been trying to read quadrature encoders with HCTL2022. Following is my configuration:

    1) +5v 20MHz clock signal
    2) Quadrature signals on pin 9 (A) and 10 (B) of HTCL 2022.
    3) Index on the index signal pin
    4) OE_bar and Reset_bar are connected to controller.
    5) SEL1 and SEL2 are also connected to controller

    No matter how I toggle OE_BAR, RESET_BAR SEL1 and SEL2 I couldn't get the data to change on the data pins. The U/D pin did pulse continuously on rotating in one side (and glitched sometimes when rotated in the other) but still no data.

    Reading this blog I realized I haven't messed with INDEX till yet. How could the manufacturer not mention such a crucial part if it is (as I will shortly find out) responsible for the data problem on data pins?
    Currently my index is always high and pulses to ground before returning to high again. Should I invert this and try it again?

    Please provide suggestions. thnx

  36. prince, yes I was frustrated about the lack of level info for INDEX and A+/A- channels. I remember the index pin being quite a problem. After getting the chip initialized, try toggling INDEX a couple times, then test leaving it in different states to see which one is inactive. It's been a while since I have used the chip, and I would be more inclined to use a faster microcontroller, and have it interrupt on the quadrature signals if they were slow enough.

  37. Hi,
    I am facing the same problem as Jorge Cifuentes.HCTL2032 counts resets to 0 as count reaches 200(200 ppr encoder) when I rotate encoder slowly manually with hand.Out of four bytes only LSB is varying.
    But when I rotate the encoder faster say 1 revolution in 2 sec I can see variations in next higher byte.I monitored the reset signals,they remain high through out the operations.
    Please help...!

  38. Hi, What is the best way to monitor (and then eventually debug) hardware (or see pre-existing hardware) signals. Is it with something like USB-6501? What are the benefits and limitations? is there any other affordable device/technology you would recommend (again - benefits and limitations).. Thanks!

  39. Rachel, the Bus Pirate ( and the Salae Logic are both good choices for debugging hardware logic signals. Good luck!

  40. Hello Ben,

    Thank you very much for this site; best thing I could find so far on HCTL 2022. So, we hooked up an AEDR 8500 encoder with an HCTL and using a beagle bone as our microcontroller. We get good signals out of the encoder and we used a test code to read signals out of the data pins, which were fairly clean. However, when we ran our actual position tracking script, we kept reading 2^32-1 for the position (all bits are 1). We are not sure how we could be reading this value, even without using the strip

  41. To elaborate on the post above with an example, when we attempt to read the position by moving the strip over the encoder, we can see the D2 (and other data pins) provide an appropriate pattern into the microcontroller. However, the microcontroller is outputting a 1 for every bit every time we take a position reading, resulting in a 2^32-1 position

  42. EngineerInTraining, it sounds like a software bug. You could use a logic analyzer or a short bit of test code to read the value of the pins directly. If the data is good, but your variable is not being set properly, there must be a software bug.

  43. Hello Ben,

    Thank you for the quick response.

    We inspected the datasheet to see if we had the logic right. The datasheet states that OE needs to be low (not OE high) in order to capture a position and high (not OE low) to end the cycle. When we do this, we got 2^32-1. When we reversed the logic, we got a better stream of numbers (not 2^32-1) though still not completely satisfactory. We were wondering if the datasheet is incorrect about the OE pin or if we are still doing something wrong.

  44. EngineerInTraining, I don't remember specific problems with OE, but I do remember the index input being a problem. It must be tied low (or high -- I can't remember) for the chip to work properly if your sensor doesn't have an index output. Try both. Make sure you have the correct clock edge polarity when signaling the SEL and OE lines. Check the timing diagram very carefully.

  45. We have an index output on our sensor, so we tied it to high with a pull up resistor as shown in the example in the datasheet.

  46. One other question concerning the encoder inputs into the decoder. Are the A and B inputs supposed to be default high?

  47. Sorry to keep bothering with questions, but I just noticed another comment:

    "OE should be pulled low. Reset should be pulled high. Index should be pulled either high or low -- I forget which. Unless a datasheet specifically says that you can leave logic pins open, you should always tie them to gnd or Vcc."

    Do you mean not OE should be pulled low and not Reset should be pulled high? Because if reset is high, wouldn't the latch keep getting set to 0 and if OE were set low, the inhibit would be on. Also, why would I need to tie the pins to Vcc or gnd if we use a microcontroller to keep not Reset at high throughout the run?

  48. EngineerInTraining, yes, I've never said or written "not OE", but would probably just say "OE", and it's understood that it's an active-low signal as denoted by the bar above the signal name. I meant that unused pins should be tied low or high. If they are connected to the microcontroller, then of course, you don't need to connect to gnd or vcc. The encoders are usually built to be open-collector, so when the codestrip is blocking the light, the pullup resistor on the A, B, and index inputs raises the voltage. When the codestrip is clear, light falling on the sensor causes it to conduct, and pull the voltage down to ground.

  49. Good info on encoder strips (although the source you mentioned seems to be out; but it is now 11 years later). I see them available new for $25 and up, but I think a dead inkjet printer might be cheaper to source.