PulseTheStepPin revisited - Questions, answers, problems, solutions
idbruce
Posts: 6,197
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:
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:
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:
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
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:
And he also 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.
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: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.
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
Bruce
First off, add this as one of your constants
And then try this function as previously described
Two more changes
First
MIN_EXECUTION_TIME = CLK_FREQ / 20_000 'CON definition
Second
WAITCNT(Counter += CycleOffset -= 20) 'alter in PulseTheStepPin
MIN_EXECUTION_TIME = CLK_FREQ / 21_500
EDIT: On second thought, hold off with this setting, for now.
Two things worth mentioning:
- The sweet spot for the minimum execution time is MIN_EXECUTION_TIME = CLK_FREQ / 21_500
- 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
I wish someone would rewrite this so that it made more sense.
Bruce