Newsgroups: comp.robotics
Path: brunix!sgiblab!swrinde!ihnp4.ucsd.edu!agate!howland.reston.ans.net!pipex!uunet!hobbes!earth.armory.com!rstevew
From: rstevew@armory.com (Richard Steven Walz)
Subject: Re: Driving Stepper Motors
Organization: The Armory
Date: Tue, 15 Mar 1994 08:52:06 GMT
Message-ID: <CMp7Aw.3ys@armory.com>
References: <94071.163022U54136@uicvm.uic.edu>
Sender: news@armory.com (Usenet News)
Nntp-Posting-Host: deeptht.armory.com
Lines: 247

In article <94071.163022U54136@uicvm.uic.edu>,  <U54136@uicvm.uic.edu> wrote:
>I have recently aquired a few 12 volt, 1.8 degree, bipolar step motors, but
>I am having trouble getting them to work. I have (1) built a simple computer
>interface to drive them, and (2) built a simple logic controller type of
>arrangement, but in both cases the stepper motor just kind of jumps around.
>
>How the heck do I get these things working? Does anyone know of any decent
>books on this subject.
>
>Thanks, Tim
------------------
Have you and others ween the tutorial steppers file to follow?
-Steve Walz   rstevew@armory.com
-----------------
                    CONTROL OF STEPPING MOTORS
                                By
                         Douglas W. Jones
                        University of Iowa
                     jones@herky.cs.uiowa.edu

                          COPYRIGHT 1990
                   All rights reserved.  Permision
                   is hereby given to make copies
                   for personal use and to distribute
                   copies of this material on the
                   USENET electronic network news
                   service.  All other copying of
                   this material is prohibited.

I see no advantage to using commercial stepping motor controllers.  I've
had an easy time with the direct software control of the 3 or 4 windings
in the typical stepping motor, using an 8 bit parallel output port to run
two motors.

Here's a simple plan for computer control of a stepping motor, the kind
where the DC resistance of the windings is all you need to limit the
current through the motor.  The control circuitry for other motors gets
more complex because of the need to limit the current through the
windings with a ballast resistor or better yet, a transistorized curent
limiter.

                           + logic power              + motor power
                           | ( 5 volts )              | ( 12 or 24 volts )
                           |                    ------|
                           |           diode   |      |
                           |          to kill  |    -----
                           |         inductive -   | one of the
                           |           surge   ^   | motor windings
                           |          1N4002   |    -----
                           | pull up           |      |
             -----------   R resistor           ------|
            |   Open    |  | 470 ohm                  | C
  Logic in  | Collector |  |                        -----
       -----|   Buffer  |--------------------------| power darlington
            | (SN7407)  |                        B | transistor (NPN)
            |           |                           -----  RCA SK 3180
             -----------                              | E
                                                      |
                                                      |
                                                      - system ground

Each winding requires one darlington transistor, one diode, one resistor,
and one sixth of the SN7407 chip.  The same circuit will work solenoids,
uncoupling magnets, and switch machines.  Because the transistors are
being used for switching, they don't dissapate much heat, and I just
bolted all of them to a single aluminum bar as a heatsink.  This is not
expensive!

If your motor has three windings, typically with 4 wires because one wire
is common to all three windings, it is probably a variable reluctance
stepping motor.  The common wire goes to the positive supply.  Assuming
positive logic, when the input is 1, the winding is on, and the following
outputs would rotate the motor:

  Winding 1 100100100100100100100100100
  Winding 2 010010010010010010010010010
  Winding 3 001001001001001001001001001
             time --->

If your motor has two center tapped windings, typically with 6 wires, it's
probably a permanent magnet stepping motor.  The center taps are connected
to the positive supply (possibly through a current limiter), and we'll
call the windings 1a, 1b, 2a, and 2b, where each of the two windings has
ends a and b.  Assuming positive logic on the 4 outputs needed to control
this motor:

  Winding 1a 1000100010001000  11000001110000011100000111
  Winding 1b 0010001000100010  00011100000111000001110000
  Winding 2a 0100010001000100  01110000011100000111000001
  Winding 2b 0001000100010001  00000111000001110000011100
              time --->

Note that the two halves of each winding are never energized at the same
time.  The left sequence shown above will rotate a permanent magnet one
step at a time, while the right sequence will rotate the motor one half
step at a time.  Half stepping works because if two windings are energized
at the same time, the motor will stop in a position midway between the
two.

Now, here's the code to make your motor run as if you had one of those
fancy stepper controllers.  I've used Pascal for no particular reason.
This code assumes only one motor, and it assumes it's attached to the
least significant bits of one parallel output port.

Assume these declarations and values for a three winding variable
reluctance motor:

     const maxstep = 2;
           steps = 3;
     var   steptab: array [0..maxstep] of integer;
           step: integer;
           motor: file of integer; { this is the I/O port for the motor }
     begin
           step := 0;
           steptab[0] = 1; { binary 001 }
           steptab[1] = 2; { binary 010 }
           steptab[2] = 4; { binary 100 }
           write( motor, steptab[step] );

Assume these declarations and values for a permanent magnet motor:

     const maxstep = 3;
           steps = 4;
     var   steptab: array [0..maxstep] of integer;
           step: integer;
           motor: file of integer; { this is the I/O port for the motor }
     begin
           step := 0;
           steptab[0] = 1; { binary 0001 }
           steptab[1] = 4; { binary 0100 }
           steptab[2] = 2; { binary 0010 }
           steptab[3] = 8; { binary 1000 }
           write( motor, steptab[step] );

Assume these declarations and values for half-step control of a permanent
magnet motor:

     const maxstep = 7;
           steps = 8;
     var   steptab: array [0..maxstep] of integer;
           step: integer;
           motor: file of integer; { this is the I/O port for the motor }
     begin
           step := 0;
           steptab[0] = 1;  { binary 0001 }
           steptab[1] = 5;  { binary 0101 }
           steptab[2] = 4;  { binary 0100 }
           steptab[3] = 6;  { binary 0110 }
           steptab[4] = 2;  { binary 0010 }
           steptab[5] = 10; { binary 1010 }
           steptab[6] = 8;  { binary 1000 }
           steptab[7] = 9;  { binary 1001 }
           write( motor, steptab[step] );

The remainder of the code is the same and doesn't depend on the motor.
The following procedure will advance the motor one step in either
direction, where the direction parater must be either +1 or -1 to
indicate the direction.

     procedure onestep( direction: integer );
     begin
         step := step + direction;
         if step > maxstep then step := 0
         else if step < 0 then step := maxstep;
         write( motor, steptab[step] );
     end;

Software control of a stepping motor is a real-time task, and you need
at least a bit of feedback.  One bit is enough; typically, this will be
a bit indicating that a cam on the turntable (or whatever the motor is
driving) is interrupting a light beam or closing a microswitch.  To avoid
hysteresis problems in reading the position from this cam, you should
only read zero to one transitions as indicating the home position when
the turntable is spinning in one direction.  Especially with switches
or where gear trains are involved between the motor and the turntable,
the one to zero transition in the other direction won't usually occur
at exactly the same position.

Given that you can read the sense bit and that you have a programmable
interval timer interrupt on your system, it is easy to make the timer
interrupt service routine operate the motor as follows:

     const maxpos = 11111; { maxpos + 1 is calls to onestep per rev }
     var position: integer; { current position of motor }
         destination: integer; { desired position of motor }
         direction: integer; { direction motor should rotate }
         last: integer; { previous value from position sensor }
         sensor: file of integer; { parallel input port }
     begin
         read( sensor, last );
         position := 1;
         setdest( 0, 1 ); { force turntable to spin on power-up until
                            it finds it's home position }

     procedure timer; { interval timer interrupt service routine }
     var sense: integer;
     begin
         read( sensor, sense );
         if (direction = 1) and (last = 0) and (sense = 1)
           then position = 0;
         last := sense;

         if position <> destination then begin
             onestep( direction );
             position := position + direction;
             if position > maxpos then position := 0
             else if position < 0 then position := maxpos;
         end;

         if position <> destination
           then settimer( interval_until_next_step );
     end;

The following procedure is the only procedure that user code should call.
This procedure sets the destination position of the turntable and sets
the direction of rotation, then sets the interval timer to force an
immediate interrupt and lets the timer routine finish rotating the
turntable while the applications program does whatever else it wants.

     procedure setdest( dst,dir: integer );
     begin
         destination := dst;
         direction := dir;
         if position <> destination
           then settimer( min_interval ); { force a timer interrupt }
     end;

If you want to control multiple stepping motors, it is easiest if you have
one interval timers and one parallel port per motor.  If you hardware has
only one timer, then you can use it to simulate multiple interval timers,
but this is most of the way to the job of writing a real-time executive.

A final note:  If you try to step a motor too fast, it will slip and your
software will lose track of the motor position.  Motors typically come
with a rating that indicates a maximum number of steps per second, but
you may not be able to accelerate the motor to that number of steps per
second from a dead start without running it at a lower speed first.  This
is especially true if the inertia of the load is fairly large, and in
fact, with appropriate acceleration sequences, you can usually excede the
maximum rated speed.

In the above code, interval_until_next_step is shown as a constant.  If
you are dealing with high-inertia loads or very short intervals, you'll
have to make this a variable, using longer intervals during starting and
stopping to take care of accelerating and decelerating the motor.

------------------------------------------------------------------------
