Shop OBEX P1 Docs P2 Docs Learn Events
PulseTheStepPin revisited - Questions, answers, problems, solutions — Parallax Forums

PulseTheStepPin revisited - Questions, answers, problems, solutions

idbruceidbruce Posts: 6,197
edited 2011-12-19 08:36 in Propeller 1
Hello Everyone

As promised, I have finally managed to focus my attention to rewriting the PulseTheStepPin function. For those of you who are unfamiliar with this function, it is counter based PWM stepper driver software, that pulses the step pin on stepper driver hardware. More particularly, it pulses the step pin of stepper drivers that already have a step and direction translator in place.

Based upon a timed test using FullDuplexSerial, the original software driver made my motors run at approximately 13.34 revolutions per second with the aid of Gecko G251 stepper drivers. For your viewing pleasure, I have attached the original code below.

Out of necessity to make my code easier to read, more compact, and faster, I rewrote the function with a few more parameters. The new function is as follows:
PUB PulseTheStepPin(TotalSteps, StepPin, RampingRange, SpeedRange, CycleOffset) | RampingSteps, RunningSteps, Counter
  IF TotalSteps > RampingRange
 
    RampingSteps := SpeedRange
    RunningSteps := TotalSteps - RampingRange
 
  ELSE
    RampingSteps := TotalSteps / 2
    RunningSteps := TotalSteps // 2    
 
  CTRA[30..26] := %00100
  CTRA[5..0] := StepPin
  FRQA := 1
  DIRA[StepPin]~~
  Counter := CNT
 
  REPEAT RampingSteps
 
    PHSA := -HIGH_PULSE_WIDTH
    WAITCNT(Counter += CycleOffset--)
  REPEAT RunningSteps
 
    PHSA := -HIGH_PULSE_WIDTH
    WAITCNT(Counter += CycleOffset)
  REPEAT RampingSteps
 
    PHSA := -HIGH_PULSE_WIDTH
    WAITCNT(Counter += CycleOffset++)

Since each of my motors run under different circumstances and a different load, I decided to provide constant values for each motor that could easily be changed to affect only that individual motor. To give you an example of some of the different settings, here is another portion of code for you that supplies many of the parameters used by this function:
  'Clock Frequency Equation
  CLK_FREQ                      = ((_CLKMODE - XTAL1) >> 6) * _XINFREQ
  'This is the setting for the minimal step pulse width.
  HIGH_PULSE_WIDTH              = CLK_FREQ / 1_000_000 'CLK_FREQ / 500_000
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  'Wire Dispenser Stepper Motor
  DISPENSER_STEP                = 2                     'I/O Pin 'Step Pin
 
  DISPENSER_CYCLE_OFFSET        = 8_000
  DISPENSER_MIN_SPEED           = 8_000
  DISPENSER_MAX_SPEED           = 11_000
  DISPENSER_SPEED_RANGE         = DISPENSER_MAX_SPEED - DISPENSER_MIN_SPEED
  DISPENSER_RAMPING_RANGE       = DISPENSER_SPEED_RANGE * 2
  DISPENSER_FEED_AMOUNT         = 250
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  'Wire Feeder Stepper Motor
  FEEDER_STEP                   = 3                     'I/O Pin 'Step Pin
  FEEDER_DISABLE                = 4                     'I/O Pin 'Disable Pin
 
  FEEDER_CYCLE_OFFSET           = 15_000
  FEEDER_MIN_SPEED              = 8_000 '15_000
  FEEDER_MAX_SPEED              = 11_000 '20_000
  FEEDER_SPEED_RANGE            = FEEDER_MAX_SPEED - FEEDER_MIN_SPEED
  FEEDER_RAMPING_RANGE          = FEEDER_SPEED_RANGE * 2
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  'Wire Bender Stepper Motor
  BENDER_STEP                   = 8                     'I/O Pin 'Step Pin
  BENDER_DIRECTION              = 9                     'I/O Pin 'Direction Pin
 
  BENDER_CYCLE_OFFSET           = 8_000
  BENDER_MIN_SPEED              = 8_000
  BENDER_MAX_SPEED              = 11_000
  BENDER_SPEED_RANGE            = BENDER_MAX_SPEED - BENDER_MIN_SPEED
  BENDER_RAMPING_RANGE          = BENDER_SPEED_RANGE * 2
  FIRST_BENDING_AMOUNT          = 1_180
  SECOND_BENDING_AMOUNT         = 1_025
  NINETY_DEGREE_BEND            = 820
  BENDER_DIRECTION_CHANGE       = 1_000
  'CCW_PHOTO_SENSOR_ADJ          = 3
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  'Gantry Stepper Motor
  GANTRY_STEP                 = 10                    'I/O Pin 'Step Pin
  GANTRY_DIRECTION            = 11                    'I/O Pin 'Direction Pin
 
  GANTRY_CYCLE_OFFSET         = 8_000
  GANTRY_MIN_SPEED            = 8_000
  GANTRY_MAX_SPEED            = 11_000
  GANTRY_SPEED_RANGE          = GANTRY_MAX_SPEED - GANTRY_MIN_SPEED
  GANTRY_RAMPING_RANGE        = GANTRY_SPEED_RANGE * 2
  GANTRY_TRAVEL_AMOUNT        = 1371
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  'Wire Cutter Stepper Motor
  CUTTER_STEP                   = 12                    'I/O Pin 'Step Pin
  CUTTER_DIRECTION              = 13                    'I/O Pin 'Direction Pin
 
  CUTTER_CYCLE_OFFSET           = 8_000
  CUTTER_MIN_SPEED              = 8_000
  CUTTER_MAX_SPEED              = 11_000
  CUTTER_SPEED_RANGE            = CUTTER_MAX_SPEED - CUTTER_MIN_SPEED
  CUTTER_RAMPING_RANGE          = CUTTER_SPEED_RANGE * 2
  CUTTER_TRAVEL_AMOUNT          = 3_500
  CUTTER_ADJ_TRAVEL_AMOUNT      = 100
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''  
  'Elevator Stepper Motor
  ELEVATOR_STEP                 = 14                    'I/O Pin 'Step Pin
  ELEVATOR_DIRECTION            = 15                    'I/O Pin 'Direction Pin
 
  ELEVATOR_CYCLE_OFFSET         = 8_000
  ELEVATOR_MIN_SPEED            = 8_000
  ELEVATOR_MAX_SPEED            = 11_000
  ELEVATOR_SPEED_RANGE          = ELEVATOR_MAX_SPEED - ELEVATOR_MIN_SPEED
  ELEVATOR_RAMPING_RANGE        = ELEVATOR_SPEED_RANGE * 2
  ELEVATOR_TRAVEL_AMOUNT        = 2_000
  ELEVATOR_ADJ_TRAVEL_AMOUNT    = 50
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''  
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  'Spring Remover Stepper Motor
  REMOVER_STEP                  = 16                    'I/O Pin 'Step Pin
  REMOVER_DIRECTION             = 17                    'I/O Pin 'Direction Pin
 
  REMOVER_CYCLE_OFFSET          = 16_000
  REMOVER_MIN_SPEED             = 8_000
  REMOVER_MAX_SPEED             = 11_000
  REMOVER_SPEED_RANGE           = REMOVER_MAX_SPEED - REMOVER_MIN_SPEED
  REMOVER_RAMPING_RANGE         = REMOVER_SPEED_RANGE * 2
  REMOVER_TRAVEL_EXTENT         = 10_400
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''    
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  'Packaging Table Stepper Motor
  TABLE_STEP                    = 18                    'I/O Pin 'Step Pin
  TABLE_DIRECTION               = 19                    'I/O Pin 'Direction Pin
 
  TABLE_CYCLE_OFFSET            = 200_000
  TABLE_MIN_SPEED               = 8_000
  TABLE_MAX_SPEED               = 11_000
  TABLE_SPEED_RANGE             = TABLE_MAX_SPEED - TABLE_MIN_SPEED
  TABLE_RAMPING_RANGE           = TABLE_SPEED_RANGE * 2
  TABLE_TRAVEL_AMOUNT           = 1_000
  ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
And here is a portion of code showing its use:
PUB MakeSprings
  OUTA[REMOVER_DIRECTION]~~
  PulseTheStepPin(SolenoidPlacementPosition, REMOVER_STEP, REMOVER_RAMPING_RANGE, REMOVER_SPEED_RANGE, REMOVER_CYCLE_OFFSET)
  OUTA[REMOVER_DIRECTION]~
  'Reposition Bender Head For CCW Bend
  OUTA[BENDER_DIRECTION]~~
  AlignBender(CCW_BEND_PHOTO_SENSOR)
  REPEAT ProductionQuantity
    'Feed Wire
    PulseTheStepPin(FirstWireFeedAmount, FEEDER_STEP, FEEDER_RAMPING_RANGE, FEEDER_SPEED_RANGE, FEEDER_CYCLE_OFFSET)
 
    'Raise Elevator
    OUTA[ELEVATOR_DIRECTION]~~ 
    PulseTheStepPin(ELEVATOR_TRAVEL_AMOUNT, ELEVATOR_STEP, ELEVATOR_RAMPING_RANGE, ELEVATOR_SPEED_RANGE, ELEVATOR_CYCLE_OFFSET)
 
    'Bend Wire CCW - First Bend
    OUTA[BENDER_DIRECTION]~ 
    PulseTheStepPin(FIRST_BENDING_AMOUNT, BENDER_STEP, BENDER_RAMPING_RANGE, BENDER_SPEED_RANGE, BENDER_CYCLE_OFFSET)
    WAITCNT(100_000 + cnt)
 
    'Reposition Bender Head For CCW Bend
    OUTA[BENDER_DIRECTION]~~
    AlignBender(CCW_BEND_PHOTO_SENSOR)
 
    'Feed Wire
    PulseTheStepPin(SecondWireFeedAmount, FEEDER_STEP, FEEDER_RAMPING_RANGE, FEEDER_SPEED_RANGE, FEEDER_CYCLE_OFFSET)
    WAITCNT(100_000 + cnt)
 
    'Bend Wire CCW - Second Bend
    OUTA[BENDER_DIRECTION]~
    PulseTheStepPin(SECOND_BENDING_AMOUNT, BENDER_STEP, BENDER_RAMPING_RANGE, BENDER_SPEED_RANGE, BENDER_CYCLE_OFFSET)
    WAITCNT(100_000 + cnt)
 
    'Reposition Bender Head For CCW Bend
    OUTA[BENDER_DIRECTION]~~
    AlignBender(CCW_BEND_PHOTO_SENSOR) 
    'Feed Wire
    PulseTheStepPin(ThirdWireFeedAmount, FEEDER_STEP, FEEDER_RAMPING_RANGE, FEEDER_SPEED_RANGE, FEEDER_CYCLE_OFFSET)
    'LowerElevator
    OUTA[ELEVATOR_DIRECTION]~ 
    PulseTheStepPin(ELEVATOR_TRAVEL_AMOUNT, ELEVATOR_STEP, ELEVATOR_RAMPING_RANGE, ELEVATOR_SPEED_RANGE, ELEVATOR_CYCLE_OFFSET)
 
    WAITCNT(100_000 + cnt)
 
    'Raise Elevator
    OUTA[ELEVATOR_DIRECTION]~~ 
    PulseTheStepPin(ELEVATOR_TRAVEL_AMOUNT, ELEVATOR_STEP, ELEVATOR_RAMPING_RANGE, ELEVATOR_SPEED_RANGE, ELEVATOR_CYCLE_OFFSET)
 
    'Bend Wire CCW - Third Bend
    OUTA[BENDER_DIRECTION]~
    PulseTheStepPin(NINETY_DEGREE_BEND, BENDER_STEP, BENDER_RAMPING_RANGE, BENDER_SPEED_RANGE, BENDER_CYCLE_OFFSET)
    WAITCNT(100_000 + cnt)
 
    'Reposition Bender Head For CCW Bend
    OUTA[BENDER_DIRECTION]~~
    AlignBender(CCW_BEND_PHOTO_SENSOR)

Please notice that the output of the direction pin which is sent to the stepper driver is set prior to the use of the function. Some of you may want to alter this and add it as a parameter of the function, I did not.

So where do we go from here. To be perfectly honest, I still have the need for more speed, which is one of the reasons that this rewrite is still a work in progress. In my attempts to gain more speed this is what I have learned. By altering a portion of this code, I have found that the motors start gaining speed much quicker, which of course results in faster excution. Here is the portion showing the altered code:
REPEAT RampingSteps
 
    PHSA := -HIGH_PULSE_WIDTH
    WAITCNT(Counter += CycleOffset -= 4)
    'WAITCNT(Counter += CycleOffset--)
  REPEAT RunningSteps
 
    PHSA := -HIGH_PULSE_WIDTH
    WAITCNT(Counter += CycleOffset)
  REPEAT RampingSteps
 
    PHSA := -HIGH_PULSE_WIDTH
    WAITCNT(Counter += CycleOffset += 4)
    'WAITCNT(Counter += CycleOffset++)

With this alteration, 7 out of 8 of my motors ran flawlessly with more speed, the 8TH motor failed miserably. I would attribute this to something that Dave Hein tried to point out to me, and he said:
Bruce, what is the value of MIN_SPEED? You initialize CounterOffset to MIN_SPEED, but you decrement CounterOffset every time it loops. If it gets too small the target cycle count for waitcnt will be less than the value of CNT, which means that waitcnt will wait for 4 billion cycles instead of just a few hundred or thousand cycles. You should limit CounterOffset to a minimum value that will prevent this.

EDIT: I ran your loop under SpinSim, and it takes about 2500 cycles per loop. So CounterOffset should not be allowed to get below this. A minimum value of 3000 should be safe to use.
And he also said:
Bruce, you are initializing CycleOffset to MIN_SPEED, and then decrementing CycleOffset, which is fine. If you start at 8000 you will reach 2,500 after 5,500 loops. That should take about one-third second at 80MHz. If the input bit changes in less than one-third second it will work fine. If it takes longer than one-third second there will be a problem. As I said, you can resolve that issue if you limit the minimum value of CycleOffset to 3000, or maybe 3,500 to account for the additional instructions in the loop.
Okay so now there is some food for thought. I want to gain more speed by adding another parameter to this function, but I also want to limit the possibility of failure. So I am looking for guidance and some rules to follow when setting the value of this parameter. Here is what the proposed new function will look like:
PUB PulseTheStepPin(TotalSteps, StepPin, RampingRange, SpeedRange, CycleOffset, IncrementorDecrementor) | RampingSteps, RunningSteps, Counter
  IF TotalSteps > RampingRange
 
    RampingSteps := SpeedRange
    RunningSteps := TotalSteps - RampingRange
 
  ELSE
    RampingSteps := TotalSteps / 2
    RunningSteps := TotalSteps // 2    
 
  CTRA[30..26] := %00100
  CTRA[5..0] := StepPin
  FRQA := 1
  DIRA[StepPin]~~
  Counter := CNT
 
  REPEAT RampingSteps
 
    PHSA := -HIGH_PULSE_WIDTH
    WAITCNT(Counter += CycleOffset -= IncrementorDecrementor)
  REPEAT RunningSteps
 
    PHSA := -HIGH_PULSE_WIDTH
    WAITCNT(Counter += CycleOffset)
  REPEAT RampingSteps
 
    PHSA := -HIGH_PULSE_WIDTH
    WAITCNT(Counter += CycleOffset += IncrementorDecrementor)
Alright there you have it, new function, example of usage, and some proposed changes.

Bruce

Comments

  • idbruceidbruce Posts: 6,197
    edited 2011-06-18 04:40
    On a side note, I am also trying to port an algorithim which is in C code for the PIC18F252. This algorithim is supposed to provide a nice smooth ramping transition for stepper motors while retaining available torque. If you are interested visit EE Times Design at http://www.eetimes.com/design/embedded/4006438/Generate-stepper-motor-speed-profiles-in-real-time. I will have to see if this driver will outperform the previously presented function.

    Bruce
  • idbruceidbruce Posts: 6,197
    edited 2011-06-18 06:26
    This altered version is definitely faster, but I am still tweeking it.

    First off, add this as one of your constants
    MIN_EXECUTION_TIME   = CLK_FREQ / 16_000 'Look at previous code for obtaining CLK_FREQ
    

    And then try this function as previously described
    PUB PulseTheStepPin(TotalSteps, StepPin, RampingRange, SpeedRange, CycleOffset) | RampingSteps, RunningSteps, CompensationSteps, Counter
      IF TotalSteps > RampingRange
      
        RampingSteps := SpeedRange
        RunningSteps := TotalSteps - RampingRange
        
      ELSE
        RampingSteps := TotalSteps / 2
        RunningSteps := TotalSteps // 2    
      
      CTRA[30..26] := %00100
      CTRA[5..0] := StepPin
      FRQA := 1
      DIRA[StepPin]~~
      Counter := CNT
      CompensationSteps := 0
      
      REPEAT RampingSteps
      
        PHSA := -HIGH_PULSE_WIDTH
        IF CycleOffset => MIN_EXECUTION_TIME
        
          WAITCNT(Counter += CycleOffset -= 4)
        ELSE
          WAITCNT(Counter += CycleOffset)
          CompensationSteps++
      RunningSteps += CompensationSteps
        
      REPEAT RunningSteps
       
        PHSA := -HIGH_PULSE_WIDTH
        WAITCNT(Counter += CycleOffset)
      RampingSteps -= CompensationSteps
      REPEAT RampingSteps
      
        PHSA := -HIGH_PULSE_WIDTH
        WAITCNT(Counter += CycleOffset += 4)
    
  • idbruceidbruce Posts: 6,197
    edited 2011-06-18 07:06
    Alright, we are really starting to move now!

    Two more changes

    First
    MIN_EXECUTION_TIME = CLK_FREQ / 20_000 'CON definition

    Second
    WAITCNT(Counter += CycleOffset -= 20) 'alter in PulseTheStepPin
  • idbruceidbruce Posts: 6,197
    edited 2011-06-18 07:55
    Okay so here is the MAGIC constant value that makes certain the code executes with a 80Mhz crystal.

    MIN_EXECUTION_TIME = CLK_FREQ / 21_500

    EDIT: On second thought, hold off with this setting, for now.
  • idbruceidbruce Posts: 6,197
    edited 2011-06-18 09:24
    So here is the final function. I am sure that someone can rework the math and parameters to accomplish more work in less, but for me, I am currently happy with it. These simple changes drove up my production capabilities by nearly 10,000 units per day. That is over 25,400 units per day total :) WOW!!!


    Two things worth mentioning:
    1. The sweet spot for the minimum execution time is MIN_EXECUTION_TIME = CLK_FREQ / 21_500
    2. The RampSpeedAdjuster parameter should not be a setting of higher than 25, at least for my drivers. So anything between 0 and 25 should be fine.
    I have not performed a speed test on the new version, but I can state unequivocally that it is much faster than the original function. With the information provided in this post, you should be able to run your stepper drivers at a very decent speed. Also with all the different possible settings, you should be able to fine tune each stepper motor. I hope you enjoy it.

    Bruce
    PUB PulseTheStepPin(TotalSteps, StepPin, RampingRange, SpeedRange, CycleOffset, RampSpeedAdjuster) | RampingSteps, RunningSteps, CompensationSteps, Counter
      IF TotalSteps > RampingRange
     
        RampingSteps := SpeedRange
        RunningSteps := TotalSteps - RampingRange
     
      ELSE
        RampingSteps := TotalSteps / 2
        RunningSteps := TotalSteps // 2    
     
      CTRA[30..26] := %00100
      CTRA[5..0] := StepPin
      FRQA := 1
      DIRA[StepPin]~~
      Counter := CNT
      CompensationSteps := 0
     
      REPEAT RampingSteps
     
        PHSA := -HIGH_PULSE_WIDTH
        IF CycleOffset => MIN_EXECUTION_TIME
     
          WAITCNT(Counter += CycleOffset -= RampSpeedAdjuster)
        ELSE
          WAITCNT(Counter += CycleOffset)
          CompensationSteps++  
     
      REPEAT RunningSteps += CompensationSteps
     
        PHSA := -HIGH_PULSE_WIDTH
        WAITCNT(Counter += CycleOffset)  
      REPEAT RampingSteps -= CompensationSteps
     
        PHSA := -HIGH_PULSE_WIDTH
        WAITCNT(Counter += CycleOffset += RampSpeedAdjuster)
    
  • idbruceidbruce Posts: 6,197
    edited 2011-12-19 08:36
    I have not seriously looked at this code in quite a while and I must admit that this code is very hard to follow and understand.

    I wish someone would rewrite this so that it made more sense. :)

    Bruce
Sign In or Register to comment.