Reliably increase your quadrature-encoder's resolution using an ADC (or a bunch of comparators?)

Similar projects worth following
Some quadrature-encoders output analog waveforms, rather'n digital.

Typically, these analog waveforms are thresholded via a comparator (often built-in to the encoder circuitry) to create the typical digital-quadrature we expect from an optical encoder.

However, some encoders don't have that thresholding-circuitry built-in, in which case it's possible to use the analog waveforms to increase the positional resolution!

A good example of such an encoder is that from an old computer's ball-mouse, wherein typically (it seems) the *analog* output from the phototransistors is fed into a microcontroller with Schmitt-trigger *digital* inputs.

In my latest experiment, I have succeeded in *quadrupling* the positional resolution without dramatically increasing the processing time required. That's 1152 discernible steps from merely 72 "slots"!

New: Thoughts on anaQuad as a means for communication, similar to LVDS...

The basic idea is to increase the positional-resolution of an analog-output encoder by looking for *crossover* between various multiples of the input-signals... E.G. the first obvious crossover to detect is when channel A crosses over its inverse. In the thresholding-sense, this is equivalent to detecting when the sine-wave passes the halfway point. However, detecting *crossovers*, rather than thresholds, results in much better noise and calibration-immunity. Further, more crossovers can be detected than can be accomplished by merely thresholding each of the channels, by e.g. detecting for the crossover between channel A and channel B, or even channel A and -2 * channel B.

So far, anaQuad2x and anaQuad4x achieve 2x and 4x the resolution of a typical digital-output (or digital-thresholded) encoder's 4 steps per slot, giving 8 steps and 16 steps per slot, respectively. anaQuad6x and anaQuad8x are in the works to give 24 steps and 32 steps per slot.

Additionally, this same technique can be used for other locked-phase signals, such as two (of the three) 120-degree hall-effect sensors used on some BLDC motors.

An explanation of anaQuad's functionality follows the videos...


(New: 3/11/16: Thoughts on using anaQuad as a communication-method similar maybe to LVDS 4/1/17: More thoughts in the latest log!)

Here's a video of anaQuad running at 4x, with a bit of functional-explanation.

From a 72-slot encoder, 1152 discernible positions can be detected per revolution.

And now, a computer-mouse's encoder, 48 slots -> 768 measurable positions.

This guy's a great (unintentional) test of the system's functionality despite somewhat dramatic calibration-error... The software's configured for a sine wave from 0 to 2.5V, (no software change from the previous experiment) and the encoder's outputting 1V to 3V (whoops), yet seems to be working surprisingly well.

I'm half-tempted to draw up a huge gauge, attach a long needle, and see just how much accuracy/precision I'm actually getting out of this.... and Done:

Maybe at some point I'll hook up my DC motor-positioning software and have a really big dial on the wall indicating something to fractional-degree resolution... (and to test the accuracy of that positioning-software despite things like the "snappy" action of motors intended for speed rather'n positioning, etc.)

An excessively-complicated galvanometer ;)


Initial Thoughts:


Typically, the somewhat-sinusoidal outputs of the phototransistors are made square via comparators or Schmitt-Triggers.

Thresholding channels A and B

   _______         _ A-squared
  |       |       |
  | .-¯-. |       |
  |/     \|       |/  
 /|       |\     /|
A |       | '-_-' |
 _|       |_______|     
  :.      :       :
  : . 90-degree phase-shift
  :  .    :       :
  :   ._______    :    _ B-squared
  :   |   :   |   :   |
  :   | .-¯-. |   :   |
  :   |/  :  \|   :   |/
  :  /|   :   |\  :  /|
  : B |   :   | '-_-' |
  :  _|   :   |_______| 
  :   :   :   :   :   :
  :   :   :   :   :   :
      0   1   2   3   0
      One encoder-cycle.
      Four Discernible Positions.           
This method gives four discernible positions for each "slot" on an encoder's disk. So, e.g. if that old ball-mouse had 48 slots around the disk, then it probably measured 192 positions per revolution.

An easy way to double that resolution is to look at crossover of the analog waveforms between the channels...

        |    And Here
        v       |
    .-¯-.-¯-.   |   A   B
   /   /:\   \  v  /   /
 /:  /: : :\  :\ /:  /
 -_-' : : : '-_-'-_-'
  :   : : :   : : :
  0   1 : 2   3 : 0
       1.5     3.5
  One encoder-cycle.
  Six Discernible Positions.    
And to find the '0.5' and '2.5' positions, merely invert one channel and look for the

appropriate crossover.

Crossover-detection is actually quite easy, based on the fact that we're watching every transition; we only need to check for *two* crossovers, rather than all those possible.

 I reversed A and B in this drawing! 
 (and likely elsewhere)
(A is usually represented by sin(x),
 B by cos(x))

       ~B   A   B  
      .-¯-.-¯-.-¯-.   ~B  A   B
  B  /   /:\ /:\   \ /   /   /
 ~B  \ /. :/:\: :\ /:\ /:  /
  A _-'-_-' : '-_-'-_-'-_-'
      : . : : : : : : : :
      H A B C D E F G H A

 Say we're 'at' position A...
 So, checking for either H or B...
 B:  Channel ~B crosses Channel A
 H:  Channel B crosses Channel A
We don't have to do too much here... knowing which transition happened recently, we look for two other transitions;

from position A, it can either go to B or to H, depending on the direction.

From A -> B: channel A crosses the inverse of channel B: So test for A > ~B

From A -> H: channel A crosses channel B: So test for A < B

From B -> C: cB crosses c~B: B > ~B
From B -> A: cA crosses c~A: A < ~A

... I'll leave the rest to you.

Then we have a switch() statement in an update() function:

    //Advancing Right to B
    if( adcVal_A > INVERT_CHANNEL(adcVal_B) )
       lastPhaseState = PHASESTATE_B;
    //Advancing Left to H
    else if( adcVal_A < adcVal_B )
       lastPhaseState = PHASESTATE_H;
    //else, wait here...
 //From B -> C: cB crosses c~B:  B > ~B
 //From B -> A: cA crosses c~A:  A < ~A
    //Advancing Right to C
    if( adcVal_B > INVERT_CHANNEL(adcVal_B) )
       lastPhaseState = PHASESTATE_C;
    //Advancing Left to A
    else if( adcVal_A < INVERT_CHANNEL(adcVal_A) )
       lastPhaseState = PHASESTATE_A;
    //else, wait here...

This system is tested-functional! Woot!

Resolution can be quadrupled by doing a tiny bit of math (this is now functional!).

Click "Read More" for quite a bit more!

Read more »

Here we go! This is anaQuad 4x, which can resolve an analog-output optical-encoder's output to 16 steps per slot.

Zip Archive - 27.84 kB - 03/07/2016 at 11:23


  • Do You Need AnaQuad?

    Eric Hertz12/07/2021 at 19:30 11 comments

    Yet another draft not submitted... It's now May, this was written in December......


    AnaQuad's design is for some very specific use-cases... I should put more effort into highlighting that. 

    Not long ago, using a [digital!] quadrature encoder at all was such a CPU-intensive process that pretty much the only reasonable way to do-so in any high-speed/high-resolution application (CNC) required a chip dedicated to counting quadrature... (e.g. HCTL-2000-series). This was pretty much the /only/ fast-enough solution to assure no steps went missed.

    Today, even a lowly 8bit RISC microcontroller can be put to that task without hiccuping... BUT, doing-so with /any/ processor running code, it's *extremely* important that the quadrature-decoding code be executed fast-enough and often-enough that no steps can possibly be missed. 

    This is where things get tricky. It's not only a matter of making sure the quadrature-handling code is fast and called often, but also a matter of making *certain* any other code running on the same processor can never delay it too much, e.g. during a rare case like an interrupt occurring at exactly the wrong time. 

    Thus, one obvious technique is to put the quadrature-handler *in* a high-priority edge-triggered interrupt... this also has the benefit of sparing CPU cycles by not executing the quadrature-handler when there is no motion. 

    But that too brings about its own set of problems... E.G. now the opposite is true, when it moves fast, quadrature decoding uses a lot more CPU cycles than when it moves slow. 

    So, now, imagine your CPU is trying to calculate a motion-control feedback loop... Slow motions work great! But as the speed increases, suddenly the feedback loop is being starved of CPU time, just when it needs more of it!


    Thus, to try to get a more realistic idea of what such a system's limits are (from the start of such a project, rather than building the project and later testing its limits), I've actually stepped *back* in the de-facto progression of such development. E.G. Removing the quadrature code from its [seemingly ideal] interrupt, and instead lockstepping it with the motion-control code. In this way, neither can starve the other, because both are *always* running at the maximum rate possible *for both.*

    Note in my vids, my display shows a "loop counter" that, if I do things right, to my intent, should remain constant regardless of motion or lack thereof. This tells me *exactly* the limits of the system, regardless of whether I'm pushing those limits, or just turned it on for the first time with nothing attached. A loop-count of 40,000/sec tells me the motor cannot exceed 40,000 quadrature-steps/sec. And, in fact, the motion-control code, lockstepped with the quadrature code, guarantees it can't unless something is wrong (shorted motor driver-chip, etc.). Then, I can be certain not to surpass those limits in my usage of the system. 

    Think of 40,000 steps/sec, here, like the "Absolute Maximum" specification in a chip's datasheet. Request this value and it will likely work just fine... certainly won't break... OTOH, who knows what'll happen? TESTING that limit is *very* implementation/situation-specific... 999 times out of 1000 it may work without a hitch.

    Say the motor requires 70% PWM duty-cycle to move your CNC axis at 40,000 quardrature steps/sec. Hey, works great! No limits being pushed. Well within expectations! Let's use it!

    OK, now say the carriage encounters an aluminum shaving on the leadscrew. The PWM increases to 75% to maintain the requested 40,000 steps/sec, due to the extra drag caused by that shaving sliding along with the carriage.

    OK, now that shaving falls off the leadscrew.

    What happens?

    Well, the system detects it doesn't require as much power to maintain 40,000steps/sec, so decreases the PWM back down to 70%... right?


    Because when that shaving falls off at 75% PWM, the carriage speed will accelerate....

    Read more »

  • Inspired experiments in visualization

    Eric Hertz12/07/2021 at 08:41 0 comments

    This crossover-detection scheme is really hard to explain in words... I've tried countless times but what I really need is a great graphic.

    Here are some attempts:

    (And DAG NAB wouldn'tchaknow, switching tabs cleared all my work, leaving me with only a handful of screenshots)

    Above is an example of what can result from using arctan when the measurements are off by +-10% (5%?)... This may seem like extremes that are unlikely to be encountered, but bear with me, anyhow.

    The overall error at any of the measured positions is, frankly, not even near the step-size of anaQuad. So, at first glance it still seems like arctan is a great choice for higher resolution. And, indeed, depending on your application, it may be.

    But consider this: With enough (only 5%?) measurement-error, noise, miscalibration, low battery, etc., arctan can report positions *decreasing* when in fact the motions are increasing. That can be a pretty significant problem if those position measurement/calculations are being fed into a feedback control loop.

    Also, as described before, the area near arctan's discontinuities is of great concern, now multiplied. Notice how now the arctan lines overlap, jumping back and forth between roughly -90 and +90 degrees. Such a case would require special attention to avoid potential disaster in a motion-control situation.

    Here's another example:

    The stair-steps are anaQuad's output. Again, it has hysteresis...

    Thus It's rather difficult to trigger the misdetection of a reversal where there was none.

    The example above is at an extreme 20% error on each measurement, which one might hope never to run into...

    On the other hand, we tend to push things beyond their specifications, in these parts... maybe completely unintentionally. And that's easy to do in analog.

    For example, in one of my older videos, my analog optical encoder circuit had a strange effect where the outputs' amplitudes decreased somewhat dramatically with motor speed (WHAT?!). Maybe something to do with slew-rates? Rise/fall times? Internal capacitance?

    But, say the amplitude, was merely 10% in error... no big deal, eh? But, consider this: The encoder is powered by a single supply voltage... its output is not centered at 0V. And the DC offset /also/ varied somewhat dramatically. So now our measurements with respect to ground, as the ADC reads them, are 20% in error! And to add to it, that was the error on only one of the two WAY-off channels.

    Here's the extreme, where anaQuad may start failing:

    40% error on both channels(!!!) causes weird cases where the hysteresis oscillates back and forth between two measured positions. As shown by the gaps and dashes in the stairsteps.

    TBH, I'd never really contemplated what would happen in such a case... and I've never looked at it like this, graphically...

    And it's past friggin midnight, brain-mush is allowed, no?

    I *think* the hysteresis still applies, so e.g. the yellow line would *not* occur, falling through the crack to the next step, but then coming right back to the previous through the same crack... hmmm...

    This is a very weird and enlightening visualization for me! And it can be animated, too.

    ...and I've been extremely busy with other things like destroying my progress on #Vintage Z80 palmtop compy hackery (TI-86)  which could really help with a much more pressing project than even that, so what am I doing /here?!/

    Yeah, guess I'm pretty proud of this one.

  • AnaQuad was Peer Reviewed!

    Eric Hertz12/06/2021 at 19:41 2 comments

    The other day I stumbled on a couple videos from by @Jesse Schoch , wherein he actually implemented anaQuad in a system, gave a pretty thorough overview/discussion, and even expanded on it both in software and by using it in a unique way!

    My work was Peer Reviewed!

    like... the highest honor for a scientist... and somehow I didn't find out, except by accident, two years later. Believe me, Jesse, I'd've definitely been in contact, had I known.


    Jesse doesn't have much of a profile here at HaD, instead he has a wealth of YouTube videos of such projects and concepts.

    Back in 2019 (I almost wrote 1999, jeeze I'm old), Jesse dedicated some of his time to implementing anaQuad, and made these vids.

    (Apologies; I'd planned to be more thorough in analyzing the vids, themselves, but these days I'm on flakeynet most of the time... I cross my fingers every time I click "Save". Heh!)

    Part 1:

    Jesse discusses a use-case I'd never considered, a pair of analog hall-effect sensors used to detect the 2mm-spaced alternating poles of a magnetic strip (refrigerator magnet) passing by. AnaQuad is used here to determine the linear motion/position.

    I'm not sure I understand his sensor, it has an X and a Z axis, and somehow their readings are at or near 90deg out of phase. However it works, it's a great observation, and a perfect use for anaQuad!

    It reminds me of a linear version of 5.25in floppy spindle motors, which have a similar magnet with many alternating poles wrapped around the edge of the (for lack of a better word) flywheel. The hall-effect sensors, there, are three-phase, so offset by 60deg each. I did once modify anaQuad[120] for two of those sensors. Though, the poles were so far apart that resolution was quite low despite the 10+ inches of magnet spinning by.

    One observation about this setup is that, if I understand hall-sensors, the waveform output by the sensors would vary in amplitude (and DC-offset?) if these magnets aren't perfectly-aligned with the sensor throughout the entirety of their motion. E.G. if the sensor's path relative to the linear strip isn't /perfectly/ parallel. Also, who knows about the magnetic poles themselves? Surely a fridge-magnet isn't made to tight tolerances. Here, I'm not so much talking about the spacing, that's probably pretty consistent due to the machine that made it. Instead I'm talking about the /strength/ of each pole being consistent across the entire strip. E.G. I would imagine some poles may weaken with various outside factors; being exposed to other magnets on the fridge, etc. Or, also, plausibly, if this is used on an axis of a metal-cutting CNC, the measured flux may vary slightly based on the amount of nearby metal stock.


    The whole point of its crossover-detection is to reduce the effects of such things on the position-sensing. Imagine one weak pole surrounded by many normal ones, for which the system was calibrated. There, the sine-wave would have a reduced amplitude. And, the surrounding poles might even cause it to have a slight offset from "zero". I won't go into all the details, they've been harped-on in other logs, sufficed to say anaQuad is by-design highly tolerant to such variances.


    In this first vid, Jesse touches on the concept of merging anaQuad with "sin-cos" postioning. This, honestly, is something I never really considered, as I'd kinda figured the two to be somewhat competitors. Heh!

    Part 2:

    In the second video, Jesse goes into a bit more detail about how anaQuad works, then shows how to combine it with trigonometry to gain even higher positional-resolution.

    The use of trigonometry is something I was /actively/ trying to avoid when developing anaQuad. So, it's difficult for me not to go into all the details of why, and instead see the benefits. I'll try to be open-minded ;)

    First-off, having seen his graphical visuals, then inspired to run my own calculations, I'm actually quite surprised...

    Read more »

  • PRML

    Eric Hertz09/23/2018 at 03:09 1 comment


    So, hard disks now use ADCs and DSPs to detect ones and zeros...

    This looks like a job for ANAQUAD!

    Is it too much to ask that two heads on opposite sides of the platter (or maybe two on the same?) stay perfectly-aligned over time and temperatures? 'Cause a rough estimate using anaQuad would easily double, plausibly even quadruple, the capacity despite using twice the physical space...

    Thanks @Starhawk for sending me down that path, looking up ZIP drives' encoding-schemes!

  • motor as encoder

    Eric Hertz09/14/2018 at 03:41 1 comment


    #Motor as Encoder 

    Use a BLDC motor as an encoder. Almost *Exactly* as I'd imagined. Combine this with anaQuad, and yer all set!

    Note that anaQuad *also* works with two sources that are 120 degrees out of phase, so only two windings of the three in a typical BLDC should be necessary. Also, his technique could probably also be applied to stepper motors.

    I dunno how i never seem to come up with this simple concept, myself...

    "how can I measure capacitance? Drive with an AC signal, measure phase/magnitude..."

    Had to learn this one by example... Simple capacitive touch switches charge the cap, then measure the time it takes to discharge...

    Friggin' simple method to apply multiple pushbuttons on a single pin is exactly the same, tie them to different resistor values, charge a cap, measure the discharge time, simple ADC. Bam. I even have a well-developed project based on that (anaButtons, as I recall).

    No phase/mag necessary. So, why, then, couldn't I come up with the same for inductors? Duh.

    Nice goin' @besenyeim!

    I'mma linking this one here so's I can come back to it when the time is right-er.

  • QPSK

    Eric Hertz11/21/2017 at 21:13 0 comments

    stumbled upon QPSK (quadrature phase-shift keying) which apparently is the/a method used to transmit data in WiFi...

    This looks, actually, quite similar to my earlier rambling thoughts on using anaQuad as a data-transmission method.

    Some key (hah) points: 

    QPSK, since it's tied to RF signals, relies on a phase-shift that's maintained for *numerous* cycles (the carrier wave), whereas anaQuad uses two separate wires, and no carrier frequency, so phase-shifts can be easily detected several times *within* a cycle.

    While this may make the two seem quite different, I think both techniques are quite similar in concept, owing most of their differences to their medium.

    Also QPSK allows for *two* bits to be transmitted simultaneously... one for each phase in the quadrature signal. Whereas my earlier thoughts for anaQuad only transmits one.

    (QPSK, Wikipedia)

    There's good reason. QPSK can allow for signal changes with discrete jumps, each bit-encoding is independent of the previous. The same could be done with anaQuad, plausibly allowing up to 16 or more values (4 bits) to be encoded in each ... thing. (Phase-shift, maybe. Or key?)

    My earlier ramblings on anaQuad used as data-transmission explicitly removed the ability for sudden jumps in the signal. Thus, one bit is encoded into each "key" dependent on whether there's a positive phase-change or negative. (Plausibly trinary, considering no-change). Thus, each bit-value's representation (as viewed as a waveform) is entirely dependent on the previous.

    Now, I'm not certain I read this correctly, but I think I saw mention that QPSK, or some derivation, is sometimes used similarly.

    My reasoning: transmission-lines and slew rates. Sudden changes have slew rates to take into account... transmission lines can *smooth* a sharp edge by the time it reaches a receiver. Also, the receiver itself has slew-rates, the transistors don't switch *immediately*... and... anaQuad pushes those switching-speeds by working with smaller/slower changes (and linear regions).

    But... if that somewhat arbitrary limitation was removed, and if the technique was transmitted over a carrier-wave, instead of two wires (and a separate clock? Probably only necessary in the trinary case)... and if it were cut down to only four phases (actually, it seems there are QPSK-derivatives that have more), it would seem the ramblings on using anaQuad for data transmission are... darn-near exactly the same as QPSK, of which I had no prior knowledge.


    A particularly interesting-to-me learning from QPSK is how the quadrature signals are combined into one... at any instant, except during a phase-change, the signal appears to be nothing but the carrier frequency. So detecting the phase change must require the receiver to have a duplicate of the unshifted carrier-wave running somewhere to compare to... Not dissimilar to how an LVDS receiver uses a PLL to recreate the bit-clock, or how a floppy drive synchronizes an internal clock to clock-pulses on the diskette which are *not* alongside *every* bit. 

    Not sure how/if to use that with anaQuad... in the two-wire, single-bit, method, clocking is inherent with the bit-phase-transitions. OTOH, it requires two wires.

    Mathematically, knowing the two "wire" phases are always quadrature, is it possible to use superposition to combine them on a single wire, still extract the two signals to gain the benefits of "crossover-detection" (noise/source-calibration immunity, mainly) and have inherent-clocking? Sounds like a tall order. But... hmmm....

  • more graphing...

    Eric Hertz04/14/2017 at 10:36 0 comments

    UPDATE: Ramblings on LVMDS feasibility/usefulness... why do I do this? See the bottom.


    A little more on LVMDS (anaQuad serial-transmission)

    explained below...


    Thought I'd experiment with different graphing-methods... my old go-to online-grapher seems to be down, so here's another:

    The idea was to show only the crossovers, for a half-period of the quadrature-signal... one half sine-wave, which is the passing of one "slot" in the encoder-disk over the photo-sensor (the other half-sine-wave would be passing of the light-blocking material, which I can't figure out the name for, at the moment... maybe "tooth"?). The other half-sine-wave would be basically the same.

    So we have two piecewise-equations:

    y1(t) = sin(t)       * (t<pi/8)   * (t>-1)  
          + sin(t)       * (t>pi/8)   * (t<3*pi/8)
          + sin(t+pi/2)  * (t>3*pi/8) * (t<5*pi/8)
          + sin(t)       * (t>5*pi/8) * (t<7*pi/8)
    y2(t) = -sin(t)      * (t<pi/8)   * (t>-1)
          + sin(t+pi/2)  * (t>pi/8)   * (t<3*pi/8)
          + -sin(t+pi/2) * (t>3*pi/8) * (t<5*pi/8)
          + -sin(t+pi/2) * (t>5*pi/8) * (t<7*pi/8)

    As it stands, I think it's a bit too funky to visualize, the other graphs were more intuitive for me.

    But the ultimate goal, here, (which I'd forgotten until I started writing this, having developed these graphs before the ordeal at home weeks ago) is to make it easier to visualize an encoder (or LVMDS) where the speed/direction changes...

    E.G. For an encoder-disk, we'd be talking about say a motor slowing to a stop at a specific position. With a poorly-tuned algorithm, it might oscillate around that point due to overshoot. Then, graphing that using the method shown here would allow for a more ideal simulation where speed/position isn't affected by other factors such as the motor's windings or friction. That'd help for explanation-purposes.

    E.G.2. For LVMDS, we'd be talking about transmission of data-bits, wherein a "1" would be represented by a clockwise-rotation of one "step" and a "0" would be represented by a counter-clockwise-rotation of a "step."

    The latter-case would be much easier to visualize with this sort of graphing technique... E.G. show the transmission of a data-byte in this anaQuad method. So, maybe I'll get there.

    As it stands, this graphing program only allows for two plots... so the color-coordination doesn't really make sense... it doesn't correspond to anything in particular, you have to look at the piecewise functions to see what's happening.

    Also, this only demonstrates the simplest implementation of anaQuad, which only doubles the resolution of a typical digital-output optical-encoder.

    (If this were compared to two LVDS signals, this method doubles the number of bits that can be transferred within one "eye" or one bit-clock, which would be the blue line crossing over the horizontal axis at 0 and PI).

    So, maybe I need to write a program to do the graphing... or at least generate the necessary equations.

    We'll see.

    #Iterative Motion also could benefit from a GUI, so maybe it makes sense to try to refresh myself on OpenGL...

    Oh, I have a relatively simple idea for LVMDS... brb.

    So, here we can see the binary pattern 11011000 transmitted via anaQuad aka LVMDS... not quite as visually-intuitive as I'd prefer, but a start, anyhow.

    Also, doesn't show the hysteresis method explained in a previous log, wherein a switch in direction/bit-value would maybe take *two* crossovers to detect, rather than one, just to assure stability, should the electrical-values end-up *near* a crossover, rather than halfway-between.

    So, you might be able to see, if there were *two* parallel LVDS signals, their "eyes" would align on the half-sine-wave roughly-outlined in blue. Two data-bits could be transferred simultaneously between 0 and 3.14, one bit on each "signal."

    With LVMDS, using two electrically-similar signals as those in LVDS, when those two signals are in quadrature, *four* data-bits can be transferred in the same time, without increasing slew-rates, etc. Something to ponder, anyhow...


    So, some pondering, maybe... between...

    Read more »

  • LVMDS revisited...

    Eric Hertz04/01/2017 at 14:01 3 comments

    It's been some time... I don't recall what LVMDS stands for anymore...

    LVDS = Low-Voltage Differential-Signalling...

    And the M...? Multi?

    Some theorizing was presented in previous logs (long ago), here's my attempt at trying to remind myself what I was thinking.


    Anyways, here's anaQuad revisited:

    There are two sine-waves, with a 90-degree phase-shift between 'em. (the two blue lines). These are, e.g. the outputs of the two Quadrature signals of an optical-encoder.

    anaQuad, again, increases the resolution of the encoders by looking at crossovers between various multiples of the two input signals. By *inverting* the two input-signals (red), we can detect 8 positions (8 cross-overs) per full-cycle (the blue dots). (This resolution is twice as high as could be detected with digital quadrature signals).

    By multiplying the input-signals by ~2, we can double the resolution to 16 positions (the green dots). The resolution can be further-increased by adding more [and more complicated] multiples of the two input-signals, but let's ignore all these for a second and just look at the red and blue waves.

    The red and blue waveforms give 8 positions per full cycle. That's 4 positions per half-cycle, using two quadrature signals.

    That's for increasing the resolution of a quadrature [optical] encoder with analog output.


    Imagine, now, if these crossovers represented data-bits in a serial-data-stream...


    First some background...

    Here's an "eye diagram" for a regular ol' serial-data-stream (e.g. RS-232)


    The waveforms forming the "eye" are numerous bits of a data-stream overlayed atop each other.


    When the bit in the center is high, and the two surrounding it are low (#7, above), you'll see a full-sine-wave, starting low on the left, rising to the top of the "eye," and ending low on the right. (If the baud-rate is *really slow* compared to the rise/fall times, it'll look more like a square-wave... as one might expect of a digital signal. For a high-speed serial data-stream, the rise-times and fall-times are almost as long as the high-level and low-level bits, making the waveform more of a sine-wave.)

    Similarly, when the bit in the center is low, and the two surrounding it are high (#5), you'll see the same half-sine-wave flipped upside-down.

    When three bits are high (#4), you get the top line straight across. Three low, straight across the bottom (#2)... And several other combinations (two bits high, one bit low (#8), and so-forth).

    So the eye-diagram shows many bit-patterns overlapping. One might say the center of the "eye" is sampled by the input of the receiving shift-register.


    Now, for a *differential* serial data signal, we'd see a similar diagram; for every high-bit on one wire, the other wire sends a simultaneous low bit, and vice-versa. It's symmetrical across the horizontal line bisecting the eye (wee!).

    Generally, the receiver might be e.g. a comparator connected to those two opposite-valued signals. When one signal is higher than the other, the output of the comparator is 1, when the other signal is higher, the output is 0.

    The bit-value is, essentially, determined at the *time of crossover* between the two input-signals. (Though, technically, most LVDS receivers *sample* the bit-value in the middle of the "eye").


    Now let's go back to anaQuad...

    anaQuad works by looking at the *crossover* of two signals, much like differential-signals (LVDS). But, does-so between not only the input-signal (blue) and its inverse (red), but also a second input-signal and its inverse (the second blue and red pair, respectively)... (as well as multiples thereof (green), which I'm ignoring for now).

    By transmitting two signals, in quadrature (the two blue waveforms), at the same frequency as, say, an LVDS signal, we can determine *four* different crossover values (the blue dots) in a single "eye" (half-sine-wave) (ignoring, again, the...

    Read more »

  • The Idea...

    Eric Hertz03/30/2017 at 08:48 0 comments

    The idea of anaQuad is to use an analog-quadrature source (such as the encoder disks used in an old "ball-mouse") to achieve significantly higher resolution than could be achieved by treating that quadrature-signal digitally. While being relatively immune to analog noise, calibration-error, etc.

    It does-so, reliably, by *not* looking at analog *thresholds*, but instead looking at analog *crossovers*. This is well-explained in these pages.

    There are several potential sources... Some quadrature-encoders output an analog signal rather than digital (again, e.g. looking at the output of the photo-transistors in a computer's ball-mouse). Some other sources include the hall-effect sensors used in BLDC motors (these are usually 120 degrees out of phase, rather than 90, as in quadrature, but anaQuad120 can handle that).

    To *use* this system... Currently it's a software-only approach:

    Two analog-to-digital converters are necessary for each encoder.

    (Though, theoretically this could be implemented in hardware with a few comparators and op-amps, or maybe even voltage-dividers!)

    anaQuad (the software) implements a non-blocking function-call to test the current state of your ADCs and determine the current state (and therefore update the "position")...

    In all, it executes only a handful of instructions to detect a change in position, or lack thereof. So as long as it's called often-enough (faster than changes can occur), it puts little strain on your system. The same could be accomplished with a non-blocking digital quadrature-encoder routine in only a few fewer instruction-cycles. This could be called in a timer interrupt or from your main loop (if everything therein is non-blocking, and it loops fast enough).

    Interestingly, anaQuad appears capable of "resynchronizing" if a few "steps" go undetected. So, e.g. if your main loop is slowed for some reason during one cycle, and a few anaQuad "steps" are missed, as long as the next few main-loops are faster, the system won't lose any steps! (Again, probably smarter to use a timer interrupt if you're not certain).

  • Home-made anaQuad discs!

    Eric Hertz04/30/2016 at 09:29 5 comments

    @Logan linked an interesting and easy-to-build encoder-disk method in a comment over at @Norbert Heinz's #Self replicating CNC for 194 (or more) countries project (which has a bunch of ideas for various positioning systems)...

    Check out this guy:

    I'll let the image speak for itself:

    So, the current implementation of anaQuad(4x) would give 16 positions with that disk, per revolution. But, that could easily be bumped to 32 via software, and there's no reason the disk has to have only two "poles".

    I think something like this would be easier to build than the ol' "slotted" style disks, since slots would require a tiny (or masked) sensing-area, etc.

View all 19 project logs

Enjoy this project?



Jesse Schoch wrote 11/14/2019 at 18:37 point

I was toying around with your code and I think I have it working with an ALS31313 to measure linear position using.... a refrigerator magnet!  The sensor's "common mode" seems to have a nice 90 degree phase between it's X and Z measurements which I pushed into your anaQuad code.  The pole pitch of the fridge magnets is ~2mm which should give ~.1mm resoultion.  It would make a fairly cheap digital tape measure or DRO.  I'm going to mount it with a stepper and ballscrew to see how accurate the measurements are.  Have you thought any more about upping the resolution?

  Are you sure? yes | no

Jesse Schoch wrote 11/15/2019 at 15:31 point

To clarify, i'm measuring linear motion, not rotary.

it looks like I can get around .25mm resolution for the fridge magnets and .125 for the 1mm pole pitch magnet.  I read a number of papers about sin/cos encoding schemes and I don't believe the sensor i'm using is properly setup for this application.   The sensor has inner and outer hall elements for X and Y however it only reads them in single pairs or addative/differential pairs.   The Z signal is offset simply because it is on the right place in the chip.  Ideally you have seperate channels for all 4 x elements.  I did notice that if the direction of movement is moved 45 degrees you get an offset in the X and Y signals which could also be fed into anaquad.  The math is a bit over my head but my intuition says more resolution is available.  

All that said I think this sensor paired with a rack and pinion would deliver higher resolution for linear encoding.  I have a bunch of fridge magnet material I bought for a nother poject i'd love to find a use for though!

  Are you sure? yes | no

Eric Hertz wrote 12/01/2021 at 04:40 point

Sheesh! I /try/ to keep up with all my messages through here... it's not like I get that many. How could I have missed these?!

Check this out, I found these messages from you today by googling this project, not even finding this project's page, nor my own youtube vids about it in the first *numerous* pages of results, via friggin google, instead finally stumbling across a message you sent to @Ted Yapo, via his github, wherein you linked your vid... which... actually implemented this project! Holy moly, what a poor choice of project-name, apparently... 

But holy moly, what a cool thing to find out someone tried it out! Two vids, no less!

Then I *finally* reached the end of the results and it said many results were hidden because they seemed irrelevant... Click here to see them... and bam. This page nears the top of the list, with your comments in the snippet. Sheesh.

I'm beat, but I've got many thoughts on your vids... coming soon.

  Are you sure? yes | no

Eric Hertz wrote 05/24/2019 at 18:18 point

Howdy Jamey-N! Long time no see!

Congrats on last year's Prize, I've actually been meaning to congratulate you for some time.

I have, indeed, looked into your guys' encoder system, which you linked. It is impressive.

However, it's quite a bit different than anaQuad, in many ways.

The key concept of anaQuad is that it can be run on a lowly 8-bit microcontroller lacking floating-point. That means no use of sine/cosine/tangent which, even when using lookup tables and integers, are very computationally-intensive (thus your use of an FPGA).

Note that anaQuad's realtime calculations consist of little more than a bit-shift, negation, comparison, and a switch-statement. In all, it executes less than a dozen or so CPU instructions. And, with knowledge of the previously-detected position, reordering of those operations reduces the instruction-count even further.

In the end, anaQuad is almost on-par with simple digital quadrature decoding, in terms of computational overhead, while increasing the resolution dramatically.

Sure, nowhere near your "millions" of points, but you get the idea.

Note also anaQuad's relative lack of need for recalibration. Rather than computing trigonometry from datapoints that may or may not actually be measured accurately (electrical noise, light pollution, dirty sensors, which may change over time), and may or may not actually lie within the expected coordinates of a circle, anaQuad bases its calculations on cross-over detection, similar to many well-established methods for, e.g. dimming 120VAC lightbulbs. 

Yes, this allows for some similar "play," with those same sorts of noise that might affect your system, but never should there be a case where the measurements might result in a datapoint that can't resolve. (E.G. what would sin/cos/tan do with the measured coordinate (1,1) on a circle with radius 1? Or, similarly, what happens if one of the LEDs is slightly dimmer than the other, resulting in an oval rather than a circle, and all the additional computation necessary to handle that!) And, in fact, those same sources of "noise" also effect digital quadrature systems, but generally the effects go unnoticed because the "play" is dramatically smaller than the step-size.

Further-still, anaQuad bases its result on the previous position. So even if a measurement comes through that's way out of range, the resulting calculated-position will be only one "step" away from the last. This acts as a sort of "filter" for such described previously. But also comes with a pretty groovy feature, in that measurements don't have to be performed with any particular timing, and anaQuad can in-fact "catch-up" with steps which were "missed" (or erroneous).

E.G., say an interrupt prevents anaQuad from detecting three steps in real-time. The next time anaQuad is called, it will detect a single positive step, the second time it's called it'll detect another, and the third time it's called, it'll detect another. Assuming, normally, when there's not an interrupt, anaQuad is called faster than steps can physically come through (due to the limits of the motor-speed), and that it hasn't been "out of the loop" for too many steps, there will be a point where anaQuad will indicate no-change, and it has "caught-up."

Also, then, if the designer is conscientious to the above, anaQuad can be run from a "main-loop" rather than from e.g. a timer-interrupt.

And, with awareness of the system's physical limitations, step-detections resulting in step-commands can, also, occur irregularly, and be "filtered" by the physical system. (Similar to how electrical-noise on a digital encoder causes unnoticeable "play"). 

...presuming anaQuad (and resulting the step-commands) are lock-stepped and called often-enough... which isn't hard to do, as anaQuad is very light on CPU-instruction-time.

  Are you sure? yes | no

James Newton wrote 05/24/2019 at 20:53 point

yep, didnt mean that as a comparison in the "this is equal" sense, just pointing out another means of using analog data from an encoder. In fact, looking at the zero crossings is something we are starting to use to help find our position on startup as we dont have home switches. We are adding "index" slots which are narrower and purposely "squish" the eye on sin then cos in a recognizable pattern. Starting the detection of those from the zero crossing gives us a very accurate position, vs trying to fined a max or min.

  Are you sure? yes | no

Eric Hertz wrote 05/24/2019 at 22:41 point

clever idea with an index; using the same slots/sensors!

  Are you sure? yes | no

David H Haffner Sr wrote 11/21/2017 at 23:45 point

Hey esot.eric, I'm using a 2-bit quad encoder (Bourns 64 PPR) for my motor controlled turret, and I'm using the mega 2560's internal ADC but there still seems to be a "lag" (the counting is fine, it's the motor is never catches up fast enough.) no matter what. It still works but since its used for controlling a rotating diffraction grating system it's movements are extremely small, if you get some time, here is the program code;

UR experiments here are very interesting and I'd like to be able to control this thing with more precision. Thanks man and I hope and UR furry friend are doing well :)

  Are you sure? yes | no

Eric Hertz wrote 12/10/2017 at 23:19 point

Howdy Haffner. Apologies for the delay, guess I didn't get an email for this one.
Not sure I understand what you're going for, here... that code seems to be for regular digital quadrature... and stepper motors... no? You're trying to implement a system where a human turns a knob and the steppers rotate accordingly?

  Are you sure? yes | no

David H Haffner Sr wrote 12/11/2017 at 09:50 point

Thanks for replying, yeah, the problem is, the new encoder works a lot different than the old KY-40, the Mega 2560 doesn't seem to be able to ever catch up with the quad encoder no matter how I set the timing?

Do you think I should just use the Atmel 1284 chip instead?

  Are you sure? yes | no

Eric Hertz wrote 12/13/2017 at 05:13 point

i think you might reconsider updating the lcd between every step... that could slow stepping dramatically. but I'm still not really grasping the scenario... i don't see adc's being used in your code... Are you trying to use anaQuad?

  Are you sure? yes | no

David H Haffner Sr wrote 12/13/2017 at 21:07 point

I used the simplest method, using the MCU to interrupt when ever the state of the two pins changes. This worked fine for the KY-040 encoder but not for the Bourns 2-Bit Quadrature code encoder.

The whole idea here is the stepper motor just turns three holographic diffraction gratings for a spectrometer, like a turret, the thing is the steps need to be very precise. The turret never rotates continually so speed is not an issue, but misses are.

What I was thinking was, sampling the encoder pins every 1Khz (1ms,) this should cover any bounce that might occur without having to use pullup's, this new encoder has two 47k resistors built in to pin A and B. 

This is part of an interrupt code I was working on for the Atmel 1284 chip?

 void encoder_isr() { static int8_t lookup_table[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; static uint8_t enc_val = 0; enc_val = enc_val << 2; enc_val = enc_val | ((PIND & 0b1100) >> 2)

  enc_count = enc_count + lookup_table[enc_val & 0b1111];

Ok, also the math formula has to remain, and that is eating up much memory?

  Are you sure? yes | no

Eric Hertz wrote 12/13/2017 at 23:01 point

i have some digital quadrature-encoder code available in one of my githib projects, surely... I use switch statements for each case. not as efficient in code-space, but compiles such that each change takes roughly the same processing time. But, here's a thought, rather than using interrupts, do polling at a rate far faster than you expect actual position changes, but also not too fast to eat up cpu during bounce. Allow bounce, though. It doesn't matter, as the motor can't move that fast anyhow... and the end-result will be the proper position, as similarly, the encoder can't be rotated so fast that bounce would still be in play by the next change. Now, YES, your step-handler function will go crazy trying to work with every bounce... but since you'd be polling, (within the same loop), the extra processing-time necessitated by the bounces will also, essentially, *be* your debouncer. You'll maybe catch three bounces rather than hundreds, and, again have the same end-result. Now: don't (necessarily) do *Everything* each time a change is detected... e.g. don't try to step the motor and update the lcd. Those are both very slow, as is floating point math. And none of that really benefits from immediate action, can't advance a physical step millions of times a second, can't read updates on the screen faster than maybe 10 times a second... so those things can be updated periodically. ... and will be *Right* by the next update after the bouncing stops... or, you could just lockstep *EVERYTHING* in a single loop (no interrupts), and consider that part of the bounce-inhibition, described earlier, but you need to make sure *everything* can be handled, say, five times faster than you'll ever be able to generate position changes by the encoder. One way to do-so would be to *not* contain your screen updates, math, etc. within if statements.... run them in *every* loop, regardless of whether there's a change. Now, figure out a way to visualize how fast the loop runs... maybe toggle an led and look at it with a scope.

the above techniques, obviously, aren't in any way power-efficient, and rely on the idea that this microcontroller will only ever be handling this task...

  Are you sure? yes | no

Eric Hertz wrote 12/16/2017 at 03:19 point

ok, i've looked into the two encoders. First note: both are digital... it's *Possible* the bourns outputs the raw reading from the phototransistors, but most likely its output signal is square/digital. So... anaquad won't work with either... or if it does, will take *Numerous* updates to "catch up". Maybe that's what you're referring to...? In the case where you use an interrupt on a pin-change, then only used *that* to call anaQuad_update(), it would *never* catch up, except maybe by luck. anaquad is really meant to be polled, and *really* meant to be used with quadrature encoders with sinusoidal outputs... which are rare except in really old optical encoders and really cheap ones... though it mightn't be hard to hack a nice one like the bourns, by basically removing all its internal circuitry except the phototransistors.

Ok. Nowm let's say you're not using anaquad (as in your example code); note that *both* your encoders are quadrature... it just happens that the KY travels two "ticks" during each detent, guaranteeing both outputs to be high or low at the same time once a detent is reached.., whereas the bourns (likely) does not. The (digital, not anaquad) code for handling the bornes should also work for the ky. The ky would increment twice for each detent. but, I wouldn't make the same statement going the other way... (some) code written for the ky *may not* work for the bourns, because the bourns may wind-up in a location where the two outputs differ in value. that may be your difficulty. 

I'd have to take a closer look at your code with this new insight.

Note also, the ky has 40 detents, that'd be 80 quadrature state changes per revolution, whereas I think the 64PPR of the bourns is refering directly to quadrature state changes... fewer PPR than the ky.

Finally, the output of the bourns (being an optical encoder) probably doesn't bounce....

Oh, and note that in all this you might need to consider external noise (e.g. long coiled up unshielded cords, nearby motors, etc.) which can have dramatic effect on quadrature signals... maybe more-so than bounce.

  Are you sure? yes | no

Eric Hertz wrote 12/16/2017 at 03:27 point

the lookup table method looks clever, but am in no mental-state these days to parse it... sorry!

  Are you sure? yes | no

David H Haffner Sr wrote 12/16/2017 at 07:12 point

"because the bourns may wind-up in a location where the two outputs differ in value. that may be your difficulty."

Hey my man, you just hit one of the nails on the head and cleared up some things, you were taking about the KY moving 2 "ticks" each detent, and the Bourns doesn't, that was twisting my noodle! I also didn't consider the optical nature of the Bourns encoder and having it mounted on such a small PCB close to the MCU.

Perhaps a re-design and an external ADC for the Bourns to relieve the burden off the MCU so it can concentrate it's efforts on the stepper motor and math?

Man, this project has spiralled out of control :)

  Are you sure? yes | no

Eric Hertz wrote 12/16/2017 at 19:02 point

what?! no! No ADC! Your bourns is DIGITAL.

  Are you sure? yes | no

David H Haffner Sr wrote 12/16/2017 at 19:19 point

Yeah I know but I have the Arduino mega 2560 maxing out because of the floating point math function. That's why I was wondering if the Atmel 1284P chip would do better?

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates