Thanks, lots to think about! I haven't had time to try some new code out, will soon, mostly have been finding potential simple mental pseudocode to try out should one avenue prove unsuccessful.I've looked at a lot of ELS examples, and the simple approach seems to be quite adequate. Folks start to theorize about rates and various feedback loops and complicated numerical algorithms but I think that's the wrong approach. If you set up your encoder steps small enough, and your stepper steps small enough you can do the simple integer algorithm and be well within the precision the machine is physically capable of and avoid problems with floating point precision and stability of feedback loops. Essentially we are implementing a digital gear ratio with an adjustable ratio.
In terms of putting it all into the PIO, that would be optimal, but it may not be practical and it certainly isn't necessary. A good fast encoder algorithm in the PIO probably isn't even required. It's been awhile since I did the math but you can dedicate one Pico core to reading the encoder and doing the gearing algorithm. It's read 2 bits, or in the previous position 2 bits, index a 16 lookup table, the result is +1, -1 or zero which gets added to the position. A couple of compares to check for over/underflow, add/subtract if necessary, and put out a pulse or not. I haven't timed this on a Pico in C but it should be able to do this at a rate of many megahertz in a dedicated loop, no interrupts or fancy non-determinstic distractions on that core. The steppers can't keep up with the pulses at the full rate this could operate, so the limiting factor is not the Pico. The other core can handle everything else. The only tricky thing is changing the ratio. Generally this is not done while moving and that makes it simple. If you want to allow changing the gearing ratio while the system is in motion some care would be required. For threads that is probably never needed. For feed rates it might be nice to adjust the feed rate without stopping the spindle, but it can be constrained in ways that make it easier to adjust while running, or some careful code can change the ratio at the moment a motor pulse is being generated, and do some smoothing to account for acceleration/deceleration. But to start with I'd just require the spindle to be stopped, which is pretty much how the mechanical gearboxes work anyway.
Using MicroPython would be easier but will hit limits sooner. If a PIO encoder state machine is used it may still be more than adequate. One question is what max spindle RPM do you end up keeping up with. This depends on the encoder you choose and the gearing between the encoder and the spindle as well as the rate the software can perform. The software should be working at the single encoder count cycle rate, so it only ever sees 1 count at a time for best results. So it boils down to see a count or not, output a pulse or not. Never more than one encoder count or one motor pulse in one cycle. This is simple, tight control. The motor pulse output must be slower than the encoder counts, so the encoder counts per rotation should be high enough that you never have to output more than one motor pulse for one encoder pulse for the simple integer approach. It would be excellent to run MicroPython in one core and a C control algorithm in the other core. But I haven't seen support for that and it is probably beyond what most folks are willing to dig in and do. If you want C level performance in the control core then it is easiest to use C for the other core as well, the way the development environments are generally set up.
If you have a gearbox in your lathe you can use that to help setup optimal pulse ratios. Also the motor microstepping can be optimized, but most ELS and CNC systems use 8-10x microstepping for good overall performance without getting the pulse rates too high. Do some math on your lathe's gearing. The place where there can be trouble is cutting really coarse threads. This requires a lot of motor pulses per spindle encoder pulse.
Start out simple and go from there!
Yes, I too was thinking about the max RPM I'd need to accommodate. Given 4000 state canges/rev, maybe 3000 RPM max (aka 50hz If I repower my spindle) = 200,000 state changes/sec the pico needs to deal with. 133,000,000hz pico clock/200,000 = 665 clock cycles (assuming the PIO) per potential max tick rate for the spindle. The PIO core seems it should be more than OK to keep up, though your mentioning of gearing is useful re: gearbox or encoder. I do have a quick change gearbox, this is an old 9x20 harbor freight import lathe.
Anyhow, I can see the appeal of tight coupling, e.g., assuming 1/4 ratio as an example: 1 encoder pulse = 3 x-- decrements to waste cycles before finally generating one motor step pulse on the 4th cycle of the core controlling the motor. I did also imagine the steppers would have a finite time to "keep up" and actuate in the real world before needing to be fed more pulses.
In any case, I'll start out playing with micropython and the PIO encoder + stepper output, despite the potential limitations on slowness. I wasn't sure exactly how fast I could imagine the micropython side of things running, but I imagined that it wouldn't be difficult to send multiple steps and eschew perfectly tight coupling in favor of a fairly fast (but still as slow as micropython would limit us) loop that takes PIO derived encoder displacement (more real-time) and sends steps to be taken. I didn't take into account jerkiness or acceleration. It's also a bit unclear as to whether to think about things as velocity, or steps, or I guess steps in time = velocity. I digress.
I guess my three main approach thoughts are now:
-micropython core #1, looping and getting PIO core #1 (encoder) displacement as fast as uPython can, acting as a proportional step pump for steps at desired ratio for PIO core #2 (motor). Arithmetic dead simple to implement. micropython core #2 can supply variables that core #1 uses to derive desired encoder step fraction, or enable/disable actuation. Unknowns: not tightly coupled, may be too slow, unknown jerkiness
- pio core #1 capturing each pulse, pio core #2 immediately acting on pulse to generate a step or wait X # of cycles before pulse to give the proper ratio. Micropython initializing and or refreshing ratio to pio core #2 and starting/stopping actuation.
- playing with a C solution, or mixing as you said.
Last edited: