An Electronic Leadscrew Controller using a Pi Pico

Ideally the Encoder, Bresenham and Stepper Motor pulse outputs would be handled by a couple of the Pico's built in PIO processors, leaving only the user interface to the microcontroller.
 
Last edited:
I looked around for the Teensy thread about an ELS, but it's a bit hard to search for the bare bones implementation from the long forum thread -- maybe I missed a repo.

I'm still going to try playing around with the pico a bit, unless I can find a slimmed down teensy or nano example that isn't a full package, but rather just encoder + motor as a platform / playground to start.

I found a PIO implementation of 4 synchronized stepper motor controls with the pico:


and I found a good video about interrupts and interfacing with quadrature motor encoders:


I'll try playing around with my absolute inexperience and the encoder example first, moving to attempting threading and communciation via global variables or something similar. Not sure if it's the wrong tree to bark up, but right now I just want spindle/encoder rpm readout and hopefully some testing of stepper control based on this reading next.
No public repo yet. I have a private one online. I wanted to make sure it actually worked, before it was made public for several reasons. A brave soul stepped up to the plate and I granted him access. His ELS, based on my hardware (custom PCB and enclosure) and software, is up and running. We both learned a bit along the way, and as a result the code is a bit more mature and generalized. There's nothing like having someone else debug your code to find bugs. ;) They find the bugs that you were blind to. I started my ELS May of last year. Things were running well by September, which was pretty good considering it was written from scratch, and I took advantage of existing Teensy libraries. I need to scrub my repo a bit, before making it public.

That aside, it's a good idea to play around with writing your own code and try to interface with hardware, even if they are "toy" motors and encoders. Experience is the best teacher. You don't get good at something without practice...
 
Ideally the Encoder, Bresenham and Stepper Motor pulse outputs would be handled by a couple of the Pico's built in PIO processors, leaving only the user interface to the microcontroller.
Thanks again for the encoder links.

I was able to get up and running with this micropython example: https://github.com/jamon/pi-pico-pio-quadrature-encoder/blob/main/python/quadrature.py#L1 . I did get told by someone else developing pico encoder functions that the library seems to have some issues at high speed, so maybe looking at one of the jump table examples would e a good idea. I also found that this was the official Pico encoder example with pio: https://github.com/raspberrypi/pico...pio/quadrature_encoder/quadrature_encoder.pio

Anyway, I'm up and running with A and B connected via a pullup resistor to 3v3, but with the odd caveat that my clone omron e6b2-cwz6c would not give any quadrature output or current draw until I supplied it with > 5.7V via the bench supply -- pico or not.

Anyhow, as a first hurdle, I finally have some encoder connectivity and I'm on to the actual problem of playing with things, which is fun.

I was thinking, re: implementing bresenham's algorithm in PIO or not, is it necessary? I can see how an interpolated best fit between two points would be a good solution to finding a fractional approximation of proportional steps to input encoder steps, but it seems like even the simple pseudopcode would bump up to the instruction memory limit of the PIO cores just trying to implement the arithmetic functions in the pared down assembly?

Currently, I'm wondering a bit about using threading and one of the main cores to read the encoder position asynchronously and to update and calculate a ratio-ized step output depending on input encoder speed, which another pio core can sit ready to take updates to proportional stepper output RPM. I guess I was also imagining that for an ELS there may be some leeway between perfect interpolation that's instantly updated in the pio cores without farming some arithmetic out to the main core. Not sure about this aspect, though.

anyhow, I'll hook up a stepper when I get some time and will experiment a bit more, thanks again for the encouragement y'all.
 
No public repo yet. I have a private one online. I wanted to make sure it actually worked, before it was made public for several reasons. A brave soul stepped up to the plate and I granted him access. His ELS, based on my hardware (custom PCB and enclosure) and software, is up and running. We both learned a bit along the way, and as a result the code is a bit more mature and generalized. There's nothing like having someone else debug your code to find bugs. ;) They find the bugs that you were blind to. I started my ELS May of last year. Things were running well by September, which was pretty good considering it was written from scratch, and I took advantage of existing Teensy libraries. I need to scrub my repo a bit, before making it public.

That aside, it's a good idea to play around with writing your own code and try to interface with hardware, even if they are "toy" motors and encoders. Experience is the best teacher. You don't get good at something without practice...
aha, thanks! I mostly write bugs, not code, so I'm sure the experience would be humbling :P

Cool, thanks for the clarification about the repo.
 
aha, thanks! I mostly write bugs, not code, so I'm sure the experience would be humbling :p

Cool, thanks for the clarification about the repo.
That's how everyone starts out. I developed the code using the same encoder and used a small stepper motor to get familiar with things. With little motors, not much can go wrong!

I didn't bother interpolating Bresenham. Most of the time the numerator and denominator are relatively small if you reduce the fractions. I used all integer math for that. Haven't experienced a single thread that I've cut have any issues in practice or fit. My advice is to get all the basics working, then augment things to your liking. It's motivating to have parts working - it makes one want to continue to do more. You learn a lot with the incremental approach, the steps are a lot less daunting and it's often easier to implement. Good way to do it when you are starting out.
 
That's how everyone starts out. I developed the code using the same encoder and used a small stepper motor to get familiar with things. With little motors, not much can go wrong!

I didn't bother interpolating Bresenham. Most of the time the numerator and denominator are relatively small if you reduce the fractions. I used all integer math for that. Haven't experienced a single thread that I've cut have any issues in practice or fit. My advice is to get all the basics working, then augment things to your liking. It's motivating to have parts working - it makes one want to continue to do more. You learn a lot with the incremental approach, the steps are a lot less daunting and it's often easier to implement. Good way to do it when you are starting out.
That makes sense, I think. I was thinking that if it's polling the position incredibly fast anyhow, that I could calculate the decimal fraction for each time slice of encoder displacement, get a whole number steps to send for this poll to the motor, keep the decimal remainder in an added error tracker, then send the remainder of unsent steps in the next poll if the decimal portion of unsent steps adds up to a whole microstep threshold to add on to the next cycle of steps to send.

It seems like this could approximate more or less dividing the motion steps out equally, while the rate would vary in each poll of the encoder position. It doesn't seem perfect, but also it seems potentially that the main core could do the math fast enough to feed new step values to the PIO controlled motor to approximate matching the encoder rate at a given fraction. I'm sure it might be easier to dissuade myself from the efficacy after trying some experimenting out
 
That makes sense, I think. I was thinking that if it's polling the position incredibly fast anyhow, that I could calculate the decimal fraction for each time slice of encoder displacement, get a whole number steps to send for this poll to the motor, keep the decimal remainder in an added error tracker, then send the remainder of unsent steps in the next poll if the decimal portion of unsent steps adds up to a whole microstep threshold to add on to the next cycle of steps to send.

It seems like this could approximate more or less dividing the motion steps out equally, while the rate would vary in each poll of the encoder position. It doesn't seem perfect, but also it seems potentially that the main core could do the math fast enough to feed new step values to the PIO controlled motor to approximate matching the encoder rate at a given fraction. I'm sure it might be easier to dissuade myself from the efficacy after trying some experimenting out
The way I figure it is, to try something simple to see if it's good enough. If it is then you are done. Quite often we fall into the trap of making things perfect, when 99.998% of the time it's not required. Do you look at your lathe work at 1000x? I don't. I have looked at my threads at 10x and they seem ok. Don't make the problem anymore complex than it needs to be, unless required. JMHO.
 
The way I figure it is, to try something simple to see if it's good enough. If it is then you are done. Quite often we fall into the trap of making things perfect, when 99.998% of the time it's not required. Do you look at your lathe work at 1000x? I don't. I have looked at my threads at 10x and they seem ok. Don't make the problem anymore complex than it needs to be, unless required. JMHO.
For sure, for clarification the above last comment is my pretty naive, simple solution. The control seems pretty easy, so I'll try and see if it works.

Actually, I've never used a lathe as I just finally found one (small one) cheap enough to get, so I don't look at my lathe work with a microscope :D just entertaining myself with experimenting while cleaning things up and getting it going.
 
Starting out single pointing will have you looking at your thread quality. With or without an ELS. Lots of things to learn and mistakes to make! But my point really was, many times a simple solution is fine. If it doesn't work well enough, then improve the design or increase the complexity. Especially if you aren't experienced, making the project too complex makes the likelihood of completion low. I learned this the hard way, so I now try to have projects with simple small steps, most of which are easy to accomplish. It's surprising what you still can do, despite the approach.
 
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!
 
Back
Top