Shop OBEX P1 Docs P2 Docs Learn Events
RunDaStepperMotor — Parallax Forums

RunDaStepperMotor

idbruceidbruce Posts: 6,197
edited 2012-02-28 00:55 in Propeller 1
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.
{
  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

  • idbruceidbruce Posts: 6,197
    edited 2011-12-21 09:23
    I was having problems editing the previous post, so I will add a side note here:
    The amount of voltage going through the driver and motor will definitely affect permissible settings. This code is highly functional. If you cannot get it to work, just post your code and hardware setup, and I will take a peek.
    Bruce
  • Martin_HMartin_H Posts: 4,051
    edited 2011-12-21 10:55
    Bruce, I bought that Pololu stepper driver you linked to and was just about to try it with a bipolar stepper. So thanks for posting this as it should save me some time.
  • idbruceidbruce Posts: 6,197
    edited 2011-12-21 11:25
    Martin_H

    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
  • idbruceidbruce Posts: 6,197
    edited 2011-12-21 11:51
    This is an updated version that eliminates two hard-code constants. Programmatically, it is equivalent to the first version, but this version eliminates the potential for error due to a crystal or clock frequency change.

    Additionally, this version has just a few more notes added to clairfy some of the settings.

    Bruce
  • pedwardpedward Posts: 1,642
    edited 2011-12-21 16:02
    I wrote a bipolar half stepping driver a while back, have a look here: http://obex.parallax.com/objects/795/

    It implements step/direction in software and could run multiple steppers in 1 COG with a proper motion controller manager.
  • idbruceidbruce Posts: 6,197
    edited 2011-12-21 17:14
    @pedward

    To be able to achieve higher speeds with stepper motors, you must have some type of ramping system. Just a piece of advice.

    Bruce
  • pedwardpedward Posts: 1,642
    edited 2011-12-21 17:17
    Yup, the code I wrote only implements the drive component, not the motion controller. The motion controller is responsible for generating the proper accel/decel rates for the driver.
  • idbruceidbruce Posts: 6,197
    edited 2011-12-22 08:33
    Hello Everyone

    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
  • sielbearsielbear Posts: 3
    edited 2012-02-27 13:25
    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.
  • idbruceidbruce Posts: 6,197
    edited 2012-02-27 13:47
    sielbear

    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
  • photomankcphotomankc Posts: 943
    edited 2012-02-27 13:47
    Bruce, very timely for me as well. I was not getting very far in adding accel in a way that was useful. This may well end up being just what I needed to try to the propeller to work driving my bot. Thanks for sharing it.
  • idbruceidbruce Posts: 6,197
    edited 2012-02-27 13:50
    photomankc

    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
  • idbruceidbruce Posts: 6,197
    edited 2012-02-27 14:25
    @sielbear and photomankc

    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)    
    
  • idbruceidbruce Posts: 6,197
    edited 2012-02-28 00:55
    To Whom It May Concern

    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
Sign In or Register to comment.