Do You Need AnaQuad?

A project log for anaQuad!

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

eric-hertzEric Hertz 12/07/2021 at 19:3011 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. Suddenly, for maybe only one loop, 1/40,000th of a second, the Quadrature steps one more step than the system can process. 40,001 quadrature steps, in that second. One goes missed.

What happens?

Who Knows?

*BUT* /before/ that limit was surpassed, everything software was guaranteed to function properly. No missed steps. Couldn't happen. Deterministic outcome.

Then WHY even bother testing that limit when all it takes is a tiny rare occurrence to give an entirely different result than 999/1000 tests? That one, or many others I couldn't've predicted could cause an unknown outcome... And I'm not too fond of such, when it comes to linear rails that can smash my fingers, or send aluminum shrapnel all over the place, or destroy the mechanics of my system...

Why even tread into that territory, when, quite-simply, one can *guarantee* nothing like that can occur by simply looking into the specifications and not surpassing the Normal Operating Conditions? 

If 40,000 is the absolute-max, and, since I wrote it, I know my loop so well as to say 100% PWM could occur when requesting 30,000 loops/sec and suddenly the motor gets stalled by the tooling-bit's blade shattering, and its shaft hitting the workpiece, and so-forth... but, then the shaft (or the workpiece) snaps, and the motor's at 100% PWM, but even though it started accelerating it couldn't possibly accelerate to 40,000steps/sec before the PWM is decreased appropriately.... then 30,000 steps/sec can be guaranteed. And, if that's not enough of a guarantee, then an artificial limit can be set there, and a tiny bit of watchdog code could be added to make sure it fails safely (which, adding that code, may decrease my loopcount to 35,000, and I might choose to artificially limit it to 25,000, instead).

Just like a TTL chip can guarantee TTL-level output voltages while sourcing up to 3mA. Will it work at 4mA? Yes... *I* know its output will still be well within TTL-level input specifications. But I leave that margin hidden from the user. Will it work at 5mA? Probably... but maybe now its output is sagging a bit, *just* at the lower threshold for V-input-High for TTL-Specs. Now it starts to rely on the other device's input tolerances... Can't make any guarantees about other devices. Best I can say, at 5mA is that it won't hurt the chip. But, I /can/ say, with certainty, per the design, that exceeding 10mA might begin to damage the chip. Probably won't, but might... Especially if someone put a sticker atop it, which would trap in heat, and if it's 120 degrees that day... So, 10mA is the absolute-max, 3mA is the maximum recommended to the user... who might forget that resistors are only 5% tolerance, or forget that red LEDs drop 1.7V instead of 2.7 when they decide to change the UI...

And I might just do the same, despite having designed this system myself... it was several years ago, after all. But I designed it knowing I might forget some details like these, but I see that loopcount on the screen and know I shouldn't really approach that limit, unless I'm in a particularly experimentative mood.


Now, since I long-ago decided it wise to start "backtracking" in that regard (favoring lock-stepped/polled multitasking over event-handling, for *many* of my projects' needs), I revised my previously interrupt-driven digital-quadrature code for the fastest /polled/ implementation I could come up with, such that despite the fact it's called *every* loop (which means it usually does nothing), ideally it takes the same amount of minimal processing time regardless of what happens with the actual encoders. Thus, the loops/second are as fast as possible, and ideally consistent. Quadrature_Update() was born, plugged into main. Badda-bing badda-boom.

Now, when I came up with anaQuad, it just fit... It's *way* bigger, in total code, than the digital quadrature code... Mostly because it handles 16 states instead of four. BUT, each state is actually only slightly slower to process than for digital quadrature. Thus executing a single anaQuad_update() takes nearly the same time as executing a Quadrature_update().

And, then, of course, it's designed to be on-par with digital quadrature's discrete-stepped nature and the noise-immunity that comes with that...


So... if I've got a quadrature encoder with analog output, then why not use it?


THAT is pretty much the original intended use-case... If you've got a system that ideally works with discrete steps, then anaquad can throw an analog sensor in its place; gaining some benefit of improved resolution, without introducing the analogness into a system designed to be digital. And *barely* increasing the CPU load, to boot!


Now, I *definitely* appreciate its usefulness in other applications, but the fact is that there are probably options better-suited to most of those other applications.

This, anaQuad, is my own invention, 100% born of my own ideas/experience/goals/needs.

 Something like it may exist elsewhere, some college professor may have his name attached to the same concept, I don't know.

It may, in fact, be a really bad idea to try to use this concept in another circumstance, I don't know.

It may run /slower/ than arctan and floating-point on a 32-bit processor due to branch-prediction and cache misses. I don't know.

On an 8bit RISC with C's Optimization set for Speed, I can't imagine it being implementable much faster. As I intended, it probably barely introduces much extra overhead being written in C rather than assembly. It bucks many CS concepts of "good practice," such as avoiding gotos (I used a switch statement, but that's somehow acceptable?), avoiding global/static variables (it's designed to be inlined, so their *real* scope, as opposed to their scope within C's perspective) is the lock-stepped main loop). These were design tradeoffs made by a person with decades of experience. But, again, I don't know.


Frankly, I'm pretty proud of what I came up with, here. It fills a niche, maybe none other than my own. 

I thought other folk may benefit from it, be inspired to throw a ball-mouse's pitiful 48-slot encoder-disk on the back of a toy motor and be amazed at the accuracy half-a-degree resolution it provides.

Instead, quite frankly, like most the things I provide freely, folk generally tend to complain that it's not as good as something else. Fine.

The fact is, it's far better than that, for the right use-cases. The fact is, I made this well-aware of those other methods, for a reason.


the *arctan function* is accurate to 0.02deg, NOT the system. The SYSTEM is only as accurate as the data fed into the arctan function... It's like saying you've got a 4K TV, then expecting 4K when you watch a VHS on it.

anaQuad, OTOH, is more like turning 16 color VGA into 256 colors by recognizing that pixels aren't a factor of the CRT's grid, but a factor of the pixel clock on the video card; bump that pixel clock to twice the speed, keeping the hsync/vsync/porch timings the same, and send two 16 color "pixels" for every one intended pixel, and now you've got 256 colors at every intended pixel. Bump that clock up to three pixel clocks per pixel and get thousands of colors... See? Now, if you have a good nough GPU to give 16million colors, don't bother with this. But this could be a useful tool to have if you're trying to squeeze more out of an otherwise limited system, with barely any extra overhead besides codespace. Yahknow? It wasn't easy to make this reliable, when the analog realm is fraught with things like noise and slew rates and dc offsets and inter-channel gain differences, calibration error, and external light leaking into encoder enclosures... ALL of which also contribute to "the arctan function's 0.02deg" being rather meaningless, nevermind its being a huge CPU resource hog. Further, anaQuad *could* be implemented with actual comparator chips!

years latee...

I can't help but feel this same aggravation every time I think of this project (or several others of my greatest undertakings!). Imagine puting months of effort into designing a Grandfather Clock, a friggin heirloom to be passed down for generations, then making the design open to the public, with extensive documentation as to the whys of the decisions made, then the only recognition it gets is someone who seems competant and willing to do peer review ultimately complaining that he wasted his time building it because it's harder to read than a five dollar digital bedside clock. Heck, I didn't even need validation at all. I know what went into this project, and so many others. I know their benefits. I was proud of it. Still am, even if no one else ever grasps it... if I could just get others' godforsaken stupid "digital clocks are better" mentalities out of my head. 

You want 4K with that?


AlasterJames wrote 03/14/2023 at 13:19 point

thats a great thing to be considered . I appreciacte that.

  Are you sure? yes | no


[this comment has been deleted]

Eric Hertz wrote 01/06/2024 at 03:11 point

What on earth? I ain't clicking that. Explain.

  Are you sure? yes | no

Dr. Cockroach wrote 01/06/2024 at 11:02 point

Yeah, WTH...

  Are you sure? yes | no

Eric Hertz wrote 10/25/2022 at 01:11 point

Aside from the last paragraphs, which maybe I'll remove in a better mood someday, this still seems a decent-enough "latest log", so I guess I'll throw TODOs here...

This is clever:

Add two sensors, Replace the lever with a knob (effectively removing the 90degree stop), and add anaQuad.

Or use arctan. Whatever you prefer.

  Are you sure? yes | no

Eric Hertz wrote 08/15/2023 at 17:47 point

Been thinking rather fruitlessly off n on about cheap/easy analog encoder wheel options... Today it hit me. Use two polarizer sheets!

  Are you sure? yes | no

Eric Hertz wrote 01/06/2024 at 03:10 point

360 pots, with two wipers 90 deg out of phase:

  Are you sure? yes | no

Eric Hertz wrote 10/16/2022 at 08:13 point

Givrn the mood of the past few days, i guess it only suits that I came across this again.

  Are you sure? yes | no

Daren Schwenke wrote 10/16/2022 at 14:42 point

Well you have the subliminal messaging down at least.  The word likely in your main photo is cropped to produce reading 'like'.

  Are you sure? yes | no

Eric Hertz wrote 10/25/2022 at 01:04 point

Heh, looks like the AI overlord of the HaD overlords did that... but, mine's cropped at "lik" so, apparently it's trying to tell me something different... Targetted-Cropping?

BTW, it didn't email me about your response, you didn't happen to comment elsewhere I also missed, did you?

  Are you sure? yes | no

Daren Schwenke wrote 10/25/2022 at 23:29 point

I occasionally just take a traipse wherever the project log chain takes me. Notifications should tell you though.

  Are you sure? yes | no

Eric Hertz wrote 10/26/2022 at 00:12 point

Holy Crud. How long has that been there?! I'd been thinking about writing a script to curl all my pages and diff once a week!

  Are you sure? yes | no