Shop OBEX P1 Docs P2 Docs Learn Events
Introducing "SyncroStepper" - Syncronization For Multi-Axis Machines — Parallax Forums

Introducing "SyncroStepper" - Syncronization For Multi-Axis Machines

idbruceidbruce Posts: 6,197
edited 2015-04-06 09:34 in Propeller 1
Hello Everyone

Here is some code that will allow you to get some good speed out of your stepper motors and keep the start of their movement syncronized. Interpolation is my next project, but for now, this may wet your appetite.

This really isn't what I wanted to do, because there is a lot of redundant code, but it works. I really wanted to create an object for it, but I was having trouble updating and checking my variables. If you want the existing code for the original plan for syncronized steppers, go to this post: http://forums.parallax.com/showthread.php/159900-Does-This-Method-Provide-The-Exact-Same-Return-As-SPINs-Square-Root-Operator?p=1313527&viewfull=1#post1313527

If you want more information pertaining to how the code works and settings for the constants and variables, go to this thread: http://forums.parallax.com/showthread.php/159853-New-Stepper-Driver-Object-A-Variant-Of-PulseTheStepPin

All I can say is.... "Oh yeah baby"

EDIT: The syncronization has not been checked with a scope. If the start of the axes are not truly syncronized, then they are pretty darn close. If more accuracy is needed, then perhaps a "WAITPEQ" would do the trick.

Comments

  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-01-31 23:14
    idbruce wrote:
    ... this may wet your appetite.
    'Sorry to be a grammar nazi, but the word is "whet" (meaning sharpen), not "wet" (meaning dampen). Anyway, it looks like an interesting project, Bruce. Please keep us informed!

    -Phil
  • idbruceidbruce Posts: 6,197
    edited 2015-02-01 03:37
    Phil

    No need to apologize for corrections and please feel free to correct me at anytime, because if I am ignorant, I certainly do not want to remain that way :)

    As far as being an interesting project, I would have to ask which part, the project as a whole or just this section of code? However I will say this, there is something very satisfying about seeing two homemade linear actuators operate as smooth as silk and in unison. I am impatiently waiting for the day when I can finally test my extruder, but I am quite a ways from that point.
  • idbruceidbruce Posts: 6,197
    edited 2015-04-03 22:18
    The following source code represents a test that I performed on the attached archive above, just to see if the axes were truly in sync. I cannot actually say they are in sync any longer, but they are not to far off, and the test does provide some interesting data and results. Additionally, I am just guessing, but I would assume that it would probably be close enoungh for a CNC machine.

    The test represents X and Y both moving 12 inches in one direction and then 12 inches in the other, under a repeat loop of 10 times, so it represents 20 linear feet of movement for each axis. Additionally, there are several waitcnt(s) in the test code, so these movements would actually be a lot faster.
    CON
    
      _CLKMODE            = XTAL1 + PLL16X
      _XINFREQ            = 5_000_000
    
      'Clock Frequency Equation
      CLK_FREQ            = ((_CLKMODE - XTAL1) >> 6) * _XINFREQ 
    
      'X Axis  
      X_DIRECTION         = 22
      X_STEP              = 23
      X_DISABLE           = 24
    
      X_PULSE_WIDTH       = CLK_FREQ / 1_000_000 '= 80
      X_MIN_SPEED         = CLK_FREQ / 5_714 '= 14_000 Rounded
      X_MAX_SPEED         = CLK_FREQ / 16_000 '= 5_000
      X_RAMP_INC_DEC      = CLK_FREQ / 26_666_666 '= 3 Rounded
      X_INC_DIR           = 0 '= Counter-clockwise rotation
      X_DEC_DIR           = 1 '= Clockwise rotation
      X_STEPS_PER_REV     = 2_000 '= 200 Full steps * 10 microsteps
      X_STEPS_PER_METER   = 55_555
      X_RAMPING_STEPS     = (X_MIN_SPEED - X_MAX_SPEED) / X_RAMP_INC_DEC
      X_TOTAL_RAMPING_STEPS = X_RAMPING_STEPS * 2
      X_MAX_STEPS         = 16_933 '12 inches of linear travel
      
      'Y Axis
      Y_DISABLE           = 25
      Y_STEP              = 26  
      Y_DIRECTION         = 27
    
      Y_PULSE_WIDTH       = CLK_FREQ / 1_000_000 '= 80
      Y_MIN_SPEED         = CLK_FREQ / 5_714 '= 14_000 Rounded
      Y_MAX_SPEED         = CLK_FREQ / 16_000 '= 5_000
      Y_RAMP_INC_DEC      = CLK_FREQ / 26_666_666 '= 3 Rounded
      Y_INC_DIR           = 1 '= Counter-clockwise rotation
      Y_DEC_DIR           = 0 '= Clockwise rotation
      Y_STEPS_PER_REV     = 2_000 '= 200 Full steps * 10 microsteps
      Y_STEPS_PER_METER   = 55_555  
      Y_RAMPING_STEPS     = (Y_MIN_SPEED - Y_MAX_SPEED) / Y_RAMP_INC_DEC
      Y_TOTAL_RAMPING_STEPS = Y_RAMPING_STEPS * 2
      Y_MAX_STEPS         = 16_933 '12 inches of linear travel
    
    VAR
    
      LONG lMoveNow
    
      BYTE bX_Direction
      LONG lX_MoveComplete
      LONG lX_RampingSteps
      LONG lX_RunningSteps
      LONG lX_Stack[100]
      
      BYTE bY_Direction
      LONG lY_MoveComplete  
      LONG lY_RampingSteps
      LONG lY_RunningSteps
      LONG lY_Stack[100]
    
      LONG xStartCount
      LONG xEndCount
    
      LONG yStartCount
      LONG yEndCount
    
      LONG StartDifference
      LONG EndDifference
    
    OBJ
    
      Debug : "FullDuplexSerial"
    
    PUB Main
    
      Debug.start(31, 30, 0, 115200)
      WAITCNT(CLKFREQ + CNT)
    
      COGNEW(X_Stage, @lX_Stack)
      COGNEW(Y_Stage, @lY_Stack)
      
      WAITCNT(CNT + (3 * CLKFREQ))
    
      REPEAT 10
    
        TestStageSyncronization
    
        Debug.str(STRING("xStartCount = "))
        Debug.dec(xStartCount)
        DEBUG.tx(13)
        Debug.str(STRING("xEndCount = "))
        Debug.dec(xEndCount)
        DEBUG.tx(13)
        Debug.str(STRING("yStartCount = "))
        Debug.dec(yStartCount)
        DEBUG.tx(13)
        Debug.str(STRING("yEndCount = "))
        Debug.dec(yEndCount)
        DEBUG.tx(13)
    
        IF xStartCount > yStartCount
    
          StartDifference := xStartCount - yStartCount
    
        ELSE
    
          StartDifference := yStartCount - xStartCount
    
        IF xEndCount > yEndCount
    
          EndDifference := xEndCount - yEndCount
    
        ELSE
    
          EndDifference := yEndCount - xEndCount
    
        Debug.str(STRING("StartDifference = "))
        Debug.dec(StartDifference)
        Debug.str(STRING(" Clock Cycles"))
        DEBUG.tx(13)
        Debug.str(STRING("EndDifference = "))
        Debug.dec(EndDifference)
        Debug.str(STRING(" Clock Cycles"))
        DEBUG.tx(13)
        DEBUG.tx(13)
    
    PUB TestStageSyncronization
    
      lX_MoveComplete := TRUE
      lY_MoveComplete := TRUE
    
      'Prepare X stage for positive movement
      UpdateX_RampingAndRunningSteps(X_MAX_STEPS)
      bX_Direction := X_INC_DIR
    
      'Prepare Y stage for positive movement  
      UpdateY_RampingAndRunningSteps(Y_MAX_STEPS)
      bY_Direction := Y_INC_DIR
    
      'Now make the moves
      lMoveNow := TRUE
    
      'Loop until the moves are complete and then reinitlize the variables
      REPEAT
      WHILE lX_MoveComplete == FALSE AND lY_MoveComplete == FALSE
    
      lX_MoveComplete := FALSE
      lY_MoveComplete := FALSE
    
      'Wait a bit for test purposes
      WAITCNT(CNT + (5 * CLKFREQ))
    
      'Now get ready to go in the opposite direction
    
      'Prepare X stage for negative movement
      UpdateX_RampingAndRunningSteps(X_MAX_STEPS)
      bX_Direction := X_DEC_DIR
    
      'Prepare Y stage for negative movement  
      UpdateY_RampingAndRunningSteps(Y_MAX_STEPS)
      bY_Direction := Y_DEC_DIR
    
      'Now make the moves
      lMoveNow := TRUE
    
    PUB UpdateX_RampingAndRunningSteps(TotalSteps)
    
      'If maximum speed can be obtained, determine the actual number of
      'ramping and running steps.
      IF TotalSteps > X_TOTAL_RAMPING_STEPS
    
        'Copy the global amount of ramping steps
        lX_RampingSteps := X_RAMPING_STEPS
    
        'Determine the number of full speed steps.  
        lX_RunningSteps := TotalSteps - X_TOTAL_RAMPING_STEPS    
    
       'Else ramp upto the half-way point and then ramp down.    
      ELSE
      
        lX_RampingSteps := TotalSteps / 2
        lX_RunningSteps := TotalSteps // 2
    
    PUB UpdateY_RampingAndRunningSteps(TotalSteps)
    
      'If maximum speed can be obtained, determine the actual number of
      'ramping and running steps.
      IF TotalSteps > Y_TOTAL_RAMPING_STEPS
    
        'Copy the global amount of ramping steps
        lY_RampingSteps := Y_RAMPING_STEPS
    
        'Determine the number of full speed steps.  
        lY_RunningSteps := TotalSteps - Y_TOTAL_RAMPING_STEPS    
    
       'Else ramp upto the half-way point and then ramp down.    
      ELSE
      
        lY_RampingSteps := TotalSteps / 2
        lY_RunningSteps := TotalSteps // 2
    
    PUB X_Stage | Counter, StartStopSpeed
    
      xStartCount := CNT
    
      DIRA[X_DIRECTION] := 1
    
      REPEAT
    
        IF lMoveNow == TRUE
    
          lMoveNow := FALSE
    
          StartStopSpeed := X_MIN_SPEED
    
          'Set up the CTRMODE of Counter A for NCO/PWM single-ended.
          CTRA[30..26] := %00100
    
          'Set the output pin for Counter A.
          CTRA[5..0] := X_STEP
    
          'Set the value to be added to PHSA with every clock cycle.
          FRQA := 1
    
          'Set APIN as an output.
          DIRA[X_STEP] := 1
    
          'Set the OUTA register to match the desired direction of rotation.
          OUTA[X_DIRECTION] := bX_Direction
    
          'Get the current System Counter value.
          Counter := CNT
    
          'Ramp up the stepper motor to maximum speed.
          REPEAT lX_RampingSteps
      
            'Send out a high pulse on the step pin for the desired duration.
            PHSA := -X_PULSE_WIDTH
    
            'Wait for a specified period of time before sending another
            'high pulse to the step pin.
            WAITCNT(Counter += StartStopSpeed -= X_RAMP_INC_DEC)
    
          'Maintain maximum speed    
          REPEAT lX_RunningSteps
       
            'Send out a high pulse on the step pin for the desired duration.
            PHSA := -X_PULSE_WIDTH
    
            'Wait for a specified period of time before sending another
            'high pulse to the step pin.
            WAITCNT(Counter += StartStopSpeed)
    
          'Ramp down the stepper motor and come to a stop. 
          REPEAT lX_RampingSteps
      
            'Send out a high pulse on the step pin for the desired duration.
            PHSA := -X_PULSE_WIDTH
    
            'Wait for a specified period of time before sending another
            'high pulse to the step pin.
            WAITCNT(Counter += StartStopSpeed += X_RAMP_INC_DEC)
            
          xEndCount := CNT
    
          lX_MoveComplete := TRUE
    
    PUB Y_Stage | Counter, StartStopSpeed
    
      yStartCount := CNT
    
      DIRA[Y_DIRECTION] := 1
    
      REPEAT
    
        IF lMoveNow == TRUE
    
          lMoveNow := FALSE
    
          StartStopSpeed := Y_MIN_SPEED
    
          'Set up the CTRMODE of Counter A for NCO/PWM single-ended.
          CTRA[30..26] := %00100
    
          'Set the output pin for Counter A.
          CTRA[5..0] := Y_STEP
    
          'Set the value to be added to PHSA with every clock cycle.
          FRQA := 1
    
          'Set APIN as an output.
          DIRA[Y_STEP] := 1
    
          'Set the OUTA register to match the desired direction of rotation.
          OUTA[Y_DIRECTION] := bY_Direction
    
          'Get the current System Counter value.
          Counter := CNT
    
          'Ramp up the stepper motor to maximum speed.
          REPEAT lY_RampingSteps
      
            'Send out a high pulse on the step pin for the desired duration.
            PHSA := -Y_PULSE_WIDTH
    
            'Wait for a specified period of time before sending another
            'high pulse to the step pin.
            WAITCNT(Counter += StartStopSpeed -= Y_RAMP_INC_DEC)
    
          'Maintain maximum speed    
          REPEAT lY_RunningSteps
       
            'Send out a high pulse on the step pin for the desired duration.
            PHSA := -Y_PULSE_WIDTH
    
            'Wait for a specified period of time before sending another
            'high pulse to the step pin.
            WAITCNT(Counter += StartStopSpeed)
    
          'Ramp down the stepper motor and come to a stop. 
          REPEAT lY_RampingSteps
      
            'Send out a high pulse on the step pin for the desired duration.
            PHSA := -Y_PULSE_WIDTH
    
            'Wait for a specified period of time before sending another
            'high pulse to the step pin.
            WAITCNT(Counter += StartStopSpeed += Y_RAMP_INC_DEC)
    
          yEndCount := CNT
    
          lY_MoveComplete := TRUE  
    

    attachment.php?attachmentid=113765&d=1428124512
    282 x 685 - 68K
  • StefanL38StefanL38 Posts: 2,292
    edited 2015-04-04 12:38
    ManAtWork wrote code to synchronize step/dir signals without jitter.
    It needs a cog per steppermotor but it eliminates timedifferences between steppulses. The bresenham algorithm creates this jitter if the "slave"-axle runs at certain speedratios that are not a fraction represented by small even numbers
    Best regards
    Stefan
  • Paul Sr.Paul Sr. Posts: 435
    edited 2015-04-04 12:55
    Stefan - this sounds very interesting! Is this code in the OBEX or can you point to where one my look at/retrieve it?

    StefanL38 wrote: »
    ManAtWork wrote code to synchronize step/dir signals without jitter.
    It needs a cog per steppermotor but it eliminates timedifferences between steppulses. The bresenham algorithm creates this jitter if the "slave"-axle runs at certain speedratios that are not a fraction represented by small even numbers
    Best regards
    Stefan
  • idbruceidbruce Posts: 6,197
    edited 2015-04-04 13:50
    Stefan
    It needs a cog per steppermotor but it eliminates timedifferences between steppulses.

    The time differences are an absolute necessity to achieve motor ramping. Without ramping, a stepper motor will never be able to achieve it's full potential. So in actuality, time differences are a good thing.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-04-04 13:57
    Bruce, he was referring to the sporadic differences that lead to jitter, not the intervals needed for ramping.

    -Phil
  • idbruceidbruce Posts: 6,197
    edited 2015-04-04 15:16
    @Phil
    Bruce, he was referring to the sporadic differences that lead to jitter, not the intervals needed for ramping.

    Oh, I see... Thanks for clafying.

    @Stefan

    My apologies for the misinterpretation
  • davidsaundersdavidsaunders Posts: 1,559
    edited 2015-04-04 15:23
    idbruce wrote: »
    Stefan



    The time differences are an absolute necessity to achieve motor ramping. Without ramping, a stepper motor will never be able to achieve it's full potential. So in actuality, time differences are a good thing.
    Thank you for pointing out what I forgot in my firmware. I need to ramp the steppers up, oops. I know I had it in the original PASM version, and I likely would not have caught it until I got as far as testing the new firmware.

    Thank you.
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2015-04-04 15:53
    In addition to ramping, you also have to be careful to avoid the steppers' resonance regions. As long as a motor doesn't spend much time there, i.e. if you're just accelerating through a resonance zone, you're okay. But sometimes during linear or circular interpolation, the slower motor may be caught for a period of time in resonance and can lose steps. The only software remedy is to alter the speed of the faster motor to get the slower one out of resonance. The hardware solution is some sort of viscous damping in the mechanical linkage.

    -Phil
  • davidsaundersdavidsaunders Posts: 1,559
    edited 2015-04-04 16:03
    In addition to ramping, you also have to be careful to avoid the steppers' resonance regions. As long as a motor doesn't spend much time there, i.e. if you're just accelerating through a resonance zone, you're okay. But sometimes during linear or circular interpolation, the slower motor may be caught for a period of time in resonance and can lose steps. The only software remedy is to alter the speed of the faster motor to get the slower one out of resonance. The hardware solution is some sort of viscous damping in the mechanical linkage.

    -Phil
    Thank you for that information. I had not given it much thought. My 3D-Printer is working great as is, though I will be careful in the rewrite.
  • Mark_TMark_T Posts: 1,981
    edited 2015-04-05 03:33
    Bruce, he was referring to the sporadic differences that lead to jitter, not the intervals needed for ramping.

    -Phil

    By the way I posit jitter is neither here-nor-there if you are using a reasonable amount of micro-stepping,
    partly because the effect is so small (slight distortions of the sine-waves) and partly because you are generating
    steps at a rate where the chopper circuit period is significant, so synchronising with that (not normally possible)
    would be needed to avoid jitter.

    Or more simply the jitter in microseconds is a much smaller value. You do have to generate steps faster of course.
  • Mark_TMark_T Posts: 1,981
    edited 2015-04-05 03:36
    StefanL38 wrote: »
    ManAtWork wrote code to synchronize step/dir signals without jitter.
    It needs a cog per steppermotor but it eliminates timedifferences between steppulses. The bresenham algorithm creates this jitter if the "slave"-axle runs at certain speedratios that are not a fraction represented by small even numbers
    Best regards
    Stefan
    Do you have a link to that?
  • StefanL38StefanL38 Posts: 2,292
    edited 2015-04-06 09:34
    I'm on eastern holiday typing on this mice-cinema called smartphone.

    http://forums.parallax.com/showthread.php/142705-Step-Dir-signal-generator-for-CNC?highlight=manatwork

    brs
Sign In or Register to comment.