RunDaStepperMotor
idbruce
Posts: 6,197
Hello Everyone
My previous stepper driver, PulseTheStepPin was quite difficult to understand, even for the guy who wrote it
So out of necessity for a specific requirement, and to create an easier to understand stepper driver, I have written a new driver for everyone to play with. It is full of documentation and comments to help assist in understanding the code and how it can be altered to obtain various ramping profiles.
I hope you enjoy it.
Bruce
My previous stepper driver, PulseTheStepPin was quite difficult to understand, even for the guy who wrote it
{
Copyright 2011. Bruce Drummond.
You may use and modify this software without any restriction.
Please note that the following settings are for a GeckoDrive
G251X microstepping driver in combination with an Applied
Motion HT23-400 high torque stepper motor. You may have to
alter these settings to suit your particular driver, motor,
and needs.
The main settings for affecting motor operation are:
START_STOP_SPEED
and
RAMP
By altering these two settings, a person can achieve many
different ramping profiles. So go ahead and experiment a
little and have some fun.
With a little creativitiy, this software can be modified to
run for a specified number of steps and with a specified
direction of rotation.
If you create a full blown stepper driver from this sample
code, it is recommended to set up constants for all your pins
and settings. Additionally, if you create some useful code
with this sample, please share it in the forum or OBEX.
}
CON
'Multiply the external frequency by 16.
_CLKMODE = XTAL1 + PLL16X
'External crystal of 5MHz.
_XINFREQ = 5_000_000
'Establish the clock frequency so that a couple constants may
'be declared in the CON block.
CLK_FREQ = ((_CLKMODE - XTAL1) >> 6) * _XINFREQ
'The GeckoDrive G251X microstepping driver requires a minimal
'pulse width of 1us, so establish this setting.
HIGH_PULSE_WIDTH = CLK_FREQ / 1_000_000
'The code needs a certain amount of time to operate, so set
'this. Additonally, this setting can be used to control the
'maximum speed of the motor. Just remember that the maximum
'execution speed takes priority over maximum speed of the
'motor, otherwise the code will falter.
MAX_EXE_MOTOR_SPEED = CLK_FREQ / 20_000
'Set the pin number for the pushbutton
PUSHBUTTON_PIN = 7
'Set the pin numbers for the step, direction, and disable pins
'for the stepper driver.
DIRECTION_PIN = 3
STEP_PIN = 4
DISABLE_PIN = 5
'This setting is the starting and stopping speed of the
'motor. Please note that the RAMP will be subtracted from
'this setting during the first loop. Higher settings decrease
'initial startup speed.
START_STOP_SPEED = 50_000
'This setting is the ramp incrementor/decrementor, and it will
'affect just how fast the motor ramps up to top speed and
'ramps down until the motor stops.
RAMP = 25
PUB Main
'Set the direction of the pushbutton pin as an input.
DIRA[PUSHBUTTON_PIN]~
'Set the directions of the direction, step, and disable pins
'for the stepper driver as outputs.
DIRA[STEP_PIN]~~
DIRA[DIRECTION_PIN]~~
DIRA[DISABLE_PIN]~~
'Enable the motor.
OUTA[DISABLE_PIN]~~
'Set the direction of rotation.
OUTA[DIRECTION_PIN]~
'Loop until there is input on the PUSHBUTTON_PIN and then stop the iteration.
REPEAT
IF INA[PUSHBUTTON_PIN]
QUIT
'Start motor operation.
RunDaStepperMotor(STEP_PIN, START_STOP_SPEED, MAX_EXE_MOTOR_SPEED, RAMP)
PUB RunDaStepperMotor(StepPin, CycleOffset, MaxExeMotorSpeed, RampIncDec) | Ramps, Counter
'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] := StepPin
'Set the value to be added to PHSA with every clock cycle.
FRQA := 1
'Set APIN as an output.
DIRA[StepPin]~~
'Get the current System Counter value.
Counter := CNT
'Ramp up motor speed
REPEAT WHILE CycleOffset => MaxExeMotorSpeed
'Send out a high pulse on the step pin for the desired duration.
PHSA := -HIGH_PULSE_WIDTH
'Wait for a specified period of time before send sending another
'high pulse to the step pin.
WAITCNT(Counter += CycleOffset -= RampIncDec)
Ramps++
'Please note that the pushbutton will be retained to start stepper
'operation, however, the pushbutton used in the following loop will
'be changed to a detent switch at a later point in time to permit a
'more automated process.
'Run the motor at full speed until we get contrary input and then
'stop this iteration.
REPEAT WHILE NOT INA[PUSHBUTTON_PIN]
'Send out a high pulse on the step pin for the desired duration.
PHSA := -HIGH_PULSE_WIDTH
'Wait for a specified period of time before send sending another
'high pulse to the step pin.
WAITCNT(Counter += CycleOffset)
'Ramp down the motor and come to a stop.
REPEAT Ramps
'Send out a high pulse on the step pin for the desired duration.
PHSA := -HIGH_PULSE_WIDTH
'Wait for a specified period of time before send sending another
'high pulse to the step pin.
WAITCNT(Counter += CycleOffset += RampIncDec)
'Return to the Main function and wait for a button press to start
'the process all over again.
Main
I hope you enjoy it.
Bruce


Comments
You are welcome, thanks for saying thank you. I am sure you will have to modify the settings a little to work with that driver, but the basic building blocks are in that code. If you have any problems, just let me know.
Bruce
Additionally, this version has just a few more notes added to clairfy some of the settings.
Bruce
It implements step/direction in software and could run multiple steppers in 1 COG with a proper motion controller manager.
To be able to achieve higher speeds with stepper motors, you must have some type of ramping system. Just a piece of advice.
Bruce
Here is a new version that adds a different twist to the program.
This program runs on my new machine, and on this machine, I always want the stepper motor to return to it's starting position. In my case, this is actually quite simple, since the motor's intended purpose is to repeatedly rotate a cam a 360 degrees. This version simply ensures that the motor stops on a completed revolution.
It is noteworthy to mention, that the added expressions and comparisons made it necessary to lengthen the execution speed of the program. However, this was a trait that I needed for my machine, so the speed of the machine must suffer. I am adding this version just in case someone else might have a similar application for this driver.
Bruce
Thanks for the GREAT work on this controller. I'm very new to spin, but I must say this was very easy to work with and understand. I'm developing an application that will let me control holiday props from DMX signals. In my application, I have a variable distance that I'd like to control with DMX. For some functions, I may do cyclical motions and others might simply move in one direction of variable length and speed. I'm using your driver as the basis. I'm essentially modifying the ramp rate as a factor of both speed and calculated distance to travel. This gives me a nice smooth acceleration profile that meets the other movement constraints.
While testing, I found that a fixed speed with a fixed ramp rate would yield a different speed as the movement distance changed. For example, if I allowed the motor to operate over a very wide range, I would get one speed. If I shortened the range of movement allowed, the speed would increase rather dramatically.
The code has three steps that control the ramping of the motor. The problem code is in the steady-state portion. Specifically the waitcnt does not account for the RampIncDec that is accounted for in both of the ramp up and ramp down phases of the motor speed profile. I have modified the waitcnt to read:
WAITCNT(Counter += (CycleOffset+RampIncDec))
This seems to provide a less noticeable jump to the steady-state condition as well as a steady speed profile across a wide range of travel distances.
Again, I'm VERY new to this as well as motor control, so if I've overlooked something or if I'm out of line, don't hesitate to let me know! Thank you so much for sharing this code. Once I get my object a little more refined, I'll post an update / object for DMX controlled stepper motors.
This stepper is an offshoot of another stepper driver named PulseTheStepPin. In it's current state, PulseTheStepPin is very hard to understand, and I was going to rewrite it to be more in line with this driver for easier comprehension. However, since I never got around to rewriting it, you may want to read this thread also.
http://forums.parallax.com/showthread.php?132373-PulseTheStepPin-revisted-Questions-answers-problems-solutions
Thanks for the post. It is nice to know that someone is putting it to good use.
Bruce
Your post came right after an update from me, so you may also want to look at Post #11.
Thank you as well for the thanks.
Bruce
Since you guys are messing around with stepper drivers, I put together a real world example of settings and calls to the PulseTheStepPin driver, as shown below and attached.
CON _CLKMODE = XTAL1 + PLL16X _XINFREQ = 5_000_000 'Machine Position Constants DEFAULT = -1 INCREMENT = 0 'Stepper Driver Constants 'MIN_SPEED, MAX_SPEED, and HIGH_PULSE_WIDTH are user adjustable settings. These settings are currently 'configured to correlate with the specifications of a 2,000 uStep G251 Gecko Drive. Please consult your 'drive specifications, and alter the following settings appropiately. '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 MIN_EXECUTION_TIME = CLK_FREQ / 21_500 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '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_RAMP_ADJUSTER = 25 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_RAMP_ADJUSTER = 25 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_RAMP_ADJUSTER = 25 ELEVATOR_TRAVEL_AMOUNT = 2_000 ELEVATOR_ADJ_TRAVEL_AMOUNT = 50 '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' PUB ExampleCalls 'Move the gantry left, then pause, then move it right OUTA[GANTRY_DIRECTION]~~ PulseTheStepPin(4_500, GANTRY_STEP, GANTRY_RAMPING_RANGE, GANTRY_SPEED_RANGE, GANTRY_CYCLE_OFFSET, GANTRY_RAMP_ADJUSTER) WAITCNT(100_000 + cnt) OUTA[GANTRY_DIRECTION]~ PulseTheStepPin(4_500, GANTRY_STEP, GANTRY_RAMPING_RANGE, GANTRY_SPEED_RANGE, GANTRY_CYCLE_OFFSET, GANTRY_RAMP_ADJUSTER) 'Raise wire cutter, then pause, then lower the cutter OUTA[CUTTER_DIRECTION]~ PulseTheStepPin(CUTTER_TRAVEL_AMOUNT, CUTTER_STEP, CUTTER_RAMPING_RANGE, CUTTER_SPEED_RANGE, CUTTER_CYCLE_OFFSET, CUTTER_RAMP_ADJUSTER) WAITCNT(100_000 + CNT) OUTA[CUTTER_DIRECTION]~~ PulseTheStepPin(CUTTER_TRAVEL_AMOUNT, CUTTER_STEP, CUTTER_RAMPING_RANGE, CUTTER_SPEED_RANGE, CUTTER_CYCLE_OFFSET, CUTTER_RAMP_ADJUSTER) 'Raise Elevator OUTA[ELEVATOR_DIRECTION]~~ PulseTheStepPin(ELEVATOR_TRAVEL_AMOUNT, ELEVATOR_STEP, ELEVATOR_RAMPING_RANGE, ELEVATOR_SPEED_RANGE, ELEVATOR_CYCLE_OFFSET, ELEVATOR_RAMP_ADJUSTER) WAITCNT(100_000 + cnt) OUTA[ELEVATOR_DIRECTION]~ PulseTheStepPin(ELEVATOR_TRAVEL_AMOUNT, ELEVATOR_STEP, ELEVATOR_RAMPING_RANGE, ELEVATOR_SPEED_RANGE, ELEVATOR_CYCLE_OFFSET, ELEVATOR_RAMP_ADJUSTER) 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)For the last several hours, I have been working on the culmination of my stepper drivers. PulseTheStepPin has been simplified to be more comprehensive and more in line with RunDaStepperMotor. The documention has been altered to provide a more complete understanding. Needless to say that there have been quite a few modifcations to the software itself as well as the documentation.
The most recent version of my work should be released approximately 12:00 PM, Feb. 28, 2012 within the Propeller forum, providing it is fully functional at that time. It will be entitled StepperDriveErz. You may want to keep an eye open for this posting to obtain the latest release.
Bruce