An Electronic Lead Screw controller using a Teensy 4.1

I uderstand both the motor and driver are not very good. They are only for me to get my feet wet. Don't have much outlay into it at this point. I was looking at the 4Nm NEMA 24 stepper at steppersonline. That driver seems familiar as well. I didn't want to sink a lot of cash into something that I couldn't get going. This minimizes my cost exposure.

I'm still not quite sure I will get everything to work. Been a steep learning curve so far. Once I get the basic framework figured out, I will get a bit more serious about real hardware, and even user interfaces. I don't even have the basic system figured out in my head yet. Once I figure that out, it will become much easier for me.
Sounds like a sensible approach to me. Once you get a token/test system figured out, it will be easier to figure out what hardware is actually needed. Upgrade the user interface, then the mechanicals, then decide if you want to do simple circuit board fab for reliability/robustness.
 
Got the AccelStepper library to talk to the stepper. Had a logic inversion issue. Thought I was enabling the drive, nope. Flipped the polarity and the stepper started to move at least. Stepper is a bit talkative for a little thing, meaning it is noisier than I thought it would be. Definitely feel the clunks of the steps. Since I need to get experience with microstepping, I will set it up tomorrow. Maybe the baby stepper will get a little less vocal.

Have a total rats nest of wires. Some sort of hardwired board and effective wire management are needed. But at least there was some motion! It is a start.
 
@greenail in the reference you give by Didge, what does he mean by encoder resolution? Is the total number of edges per revolution, or the number of A pulses per revolution? My encoder has 1024 A and 1024 B pulses per revolution. But obviously 4096 edges per revolution. It is my understanding that the ratio Rp needs to be < 1, where

Rp = ( pitchdistance/turn * steps/rev * microsteps/step ) / (leadscrewdistance/turn * encoder_resolution) = (Pd * S)/(Pl * E)

So if I were to thread 8 TPI, Rp >1 (2.34375) if encoder_resolution = 1024, but Rp < 1 (0.585) if encoder_resolution = 4096. So knowing the right value of E (encoder_resolution) is important.

What do you do if (Pd * S), or (Pl * E) are not integers? Round them and calculate the error?

Also, for your stepper motor controller, is the microstep (number of microsteps per step) software programmable? On my junk stepper driver, the number of microsteps is set by a dip switch, which would not be good for a real application.
 
That would be determined by what the quadrature decoder register counts. It could be edges or pulses or something in between. My algorithm counts edges. Since you have the Teensy working you should be able to measure and verify what it is doing here.

Convert float ratios to integer fractions. Use accuracy requirements to determine the denominator.

If Rp > 1 you need more encoder resolution or fewer microsteps or to change gears.
 
Also, for your stepper motor controller, is the microstep (number of microsteps per step) software programmable? On my junk stepper driver, the number of microsteps is set by a dip switch, which would not be good for a real application.
Don't know of any stepper drivers that are software programmable for the number of microsteps. Potentially servo drivers are.
 
That would be determined by what the quadrature decoder register counts. It could be edges or pulses or something in between. My algorithm counts edges. Since you have the Teensy working you should be able to measure and verify what it is doing here.

Convert float ratios to integer fractions. Use accuracy requirements to determine the denominator.

If Rp > 1 you need more encoder resolution or fewer microsteps or to change gears.
My quadrature encoder detects all the edges so it would be 4096. That's good.

What do you mean by: Use accuracy requirements to determine the denominator? For the example I had above, the denominator was 8669.86666. But I calculated it by converting to pitch. I suppose I could convert to ratios of TPI for imperial threading.

Then the numerator is 19200, and the denominator 32768. That works well. I can see getting metric threads to come out will be a pain.
 
As you make the denominator larger the precision of the integral fraction generally increases (in terms of representing an irrational value).

Metric just adds a 25.4mm per inch factor. Not that bad.
 
Last edited:
Ok, for a 12 TPI leadscrew and wanting to make a 0.5mm thread, I get a ratio of 0.0922736220472441. I found a python function that returns the integers whose ratio that matches the input ratio. If you use it correctly, ie, input the number as string, I get 46137/ 500000 for the exact ratio and 923/1000 if I round the ratio to 0.0923. The latter is a more reasonable sort of ratio. I suppose I can use some sort of lookup table for this for all the threads. Of course a lot will change with the final mechanical configuration. But at least there is a way to do this.
Python:
#!/usr/bin/env python3
from numpy import around
from decimal import *

"""
Equations from https://github.com/prototypicall/Didge/blob/master/doc/How.md
"""
if __name__ == '__main__':

  leadscrewTPI = 12           # my lathe TPI
  encoder_res = 4 * 1024      # edges per revolution
  microstep = 8               # setting on controller
  basestep = 200              # number of normal steps/rev for stepper
  S = basestep * microstep
  n = 4                       # how many decimal places
 
  print("leadscrew TPI = ", leadscrewTPI)
  imperial = False
  if imperial:
    myTPI = 40
    latheTPI = leadscrewTPI
    N = leadscrewTPI * S
    D = myTPI * encoder_res
    print("desired thread TPI = ", myTPI)
    print("N = ", N)
    print("D = ", D)
  else:
    mypitch = 0.5             # in mm
    leadscrewpitch = 25.4/leadscrewTPI  # in mm
    N = mypitch * S
    D = leadscrewpitch * encoder_res
    print("desired thread pitch = ", mypitch, " mm")
    print("N = ", N)
    print("D = ", D)
   
  ratio = Decimal(format(N/D,'f'))
  ration= Decimal(format(around(N/D,n),'f'))
  print("true ratio   = ", N/D)
  print("approx ratio = ", around(N/D,n))
  print("ratio as integer ratio = ", ratio.as_integer_ratio())
  b = Decimal(format(ration,'f'))
  print("approx ratio as integer ratio = ", b.as_integer_ratio())
  if (ratio > ration):
    error = (1 - around(N/D,n)/(N/D))*100
  else:
    error = (1 - (N/D)/around(N/D,n))*100
  print("Error in percent due to approximation = ", around(error, 3))
  print("Encoder ppr   = ", encoder_res)
  print("Steps per rev = ", S)
Gee that was a lot easier banging out than futzing in C.

edit: output
Code:
In [75]: run didge.py                                                         
leadscrew TPI =  12
desired thread pitch =  0.5  mm
N =  800.0
D =  8669.866666666667
true ratio   =  0.0922736220472441
approx ratio =  0.0923
ratio as integer ratio =  (46137, 500000)
approx ratio as integer ratio =  (923, 10000)
Error in percent due to approximation =  0.029
Encoder ppr =  4096
Steps per rev =  1600
 
Python is quite nice. :)

One technique is to precalculate all the configurations using Python and make a config file for the C code to read at startup, it can build structures to hold the settings. Alternately calculating fractions isn't all that difficult to set up in C if desired. My lathe has a 15 speed gearbox so I'll want to calculate all the ratios for all the gear settings, giving the operator a choice of what gear settings to use, and showing the max RPM for that configuration, which can be limited by the spindle or by the stepper motor. So it could be a large table. This solves the problem of microsteps as the gearbox effectively changes that. I have seen drivers that communicate with the micro for things like current and microsteps, they are common in 3D printers. But the industrial types don't usually have that feature.

One thing that I've considered for the feeds is to use a constant denominator, which makes it much easier to change feed settings while in motion. Making the denominator large enough makes it plenty accurate, especially for feeding. For threading the fractions need to be more accurate, but we don't need to change threading rates while in motion, so I'll require the spindle to be stopped before allowing threads to be changed in the UI.
 
Don't know how well this will post, but here is a table I came up with. If the error was small enough (error in mm for 10 pitches of the thread) I used only 3 decimal points for the approximate ratio, if the error was high enough, I used 4 decimal places for the approximate ratio.
Code:
leadscrew TPI =  12
Steps per rev =  1600
Encoder ppr   =  4096

Thread    Approx Ratio    int N, int D    Error in 10 pitches [mm]
0.2       0.037           (37, 1000)      -0.005
0.25      0.046           (23, 500)        0.007
0.3       0.0554          (277, 5000)     -0.002
0.35      0.0646          (323, 5000)     -0.0
0.4       0.0738          (369, 5000)      0.001
0.45      0.083           (83, 1000)       0.003
0.5       0.0923          (923, 10000)    -0.001
0.6       0.1107          (1107, 10000)    0.002
0.7       0.1292          (323, 2500)     -0.001
0.75      0.1384          (173, 1250)      0.001
0.8       0.1476          (369, 2500)      0.002
0.9       0.166           (83, 500)        0.005
1.0       0.1845          (369, 2000)      0.003
1.25      0.2307          (2307, 10000)   -0.001
1.5       0.2768          (173, 625)       0.001
1.75      0.323           (323, 1000)     -0.002
2.0       0.369           (369, 1000)      0.005
2.5       0.4614          (2307, 5000)    -0.002
Have no idea how to edit this so the table will be readable. Overall have kept the 10 thread error under 0.007 mm, so I think that is ok. A bit concerned about these large denominators, 10000 is pretty big. Have to do a similar thing for imperial threads.
 
Last edited:
Back
Top