6. Midlevel Control of Stepping Motors

Part of Stepping Motors
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

WARNING: This material is new but fairly stable

Introduction

All of the low-level motor control interfaces described in the previous sections are quite similar, at an abstract level. Each interface has some number of logic inputs. Some of these inputs may be used to directly control which switches are open or closed, others may be encoded, while others may control subsystems such as the analog to digital converter in a microstepping interface.

The states of each of these logic inputs is referred to as the control vector for the motor, and a sequence of states used to rotate the motor is referred to as a control trajectory for the motor.

For example, the control vector for controlling a permanent magnet or hybrid stepping motor using any of the control circuits shown in Figures 3.4, 3.5, 3.13, 3.14, 3.15, 4.7, 4.8, 4.10, 4.11 or 4.12 will contain 4 bits, 2 bits to control each motor winding. In each case, the control vector can be expressed as <X1Y1X2Y2>, where X1 and Y1 control the current through motor winding 1 and X2 and Y2 control the current through motor winding 2. For any interface with this control vector, the following trajectory will step the motor through one full electrical cycle, using full stepping:

1010
1001
0101
0110
1010
Similarly, the following trajectory will half-step motor through the same electrical cycle:
1010
1000
1001
0001
0101
0100
0110
0010
1010
Other controllers have different control vectors. For example, the control vector for a permanent magnet or hybrid motor controlled by a pair of Allegro 3952 chips (see Figure 4.9) will be <B1E1P1M1B2E2P2M2>, where B, E, P and M are the ¬BRAKE, ¬ENABLE, PHASE and MODE control inputs for each chip. In this case, the following control trajectory will full-step the motor through 1 electrical cycle:
10001000
10001010
10101010
10101000
10001000
The following control trajectory will half-step the same motor through one electrical cycle, using dynamic braking to control resonance whenever a motor winding is turned off:
10001000
10000000
10001010
00001010
10101010
10100000
10101000
00001000
10001000
It is worth noting that, while dynamic braking on disconnected motor windings is an excellent way to control resonance during low speed operation, this will reduce the available torque at higher motor speeds.

The control vectors required for microstepped motors are more complex, but the basic idea remains the same. The problem we face here is to develop higher level control systems that will generate appropriate control trajectories, moving the motor one step, half-step or microstep each time the higher level control system requires a move.

Hardware Solutions

Early solutions to the problem of generating appropriate control trajectories for stepping motors were almost always based on direct synthesis of the control trajectory in hardware. Such solutions are still appropriate for some applications, but these days, when programmable interface controller chips are commonly used to replace random low-speed logic and when most stepping motor applications are ultimately controlled by computer systems of one kind or another, it is common to use software to generate the control trajectory.

All hardware solutions to generating the control trajectory are subsumed by the general model illustrated in Figure 6.1:

Figure 6.1 
                   ____________
        direction |            |   
      o---------->| next-state |/---
                  |____________|\-  |
                       | |        | |
        take step   ___\_/___     | |
      o------------|> state  |    | |
                   |_________|    | |
                       | |________| |
                       |  __________|
                    ___\_/___
                   | create  |
                   |  vector |
                   |_________|
                       | |
                       |  --------\ control
                        ----------/ vector
    
     A general model for mid-level control

The next-state and create-vector blocks in Figure 6.1 are combinational logic functions, while the state block is a register. Depending on how the state is encoded, the create-vector function may be trivial; for certain applications, the next-state function is also trivial, but in the general case, next-state becomes an up-down counter while create-vector becomes a ROM holding the sequence of state vectors needed to form the control trajectories.

It is worth noting that some stepping motor control systems include addtional inputs to the mid-level control system. Common additions include half-step and brake inputs. Braking control is meaningless in full-step mode but it is of some use in half-step mode. In the latter mode, shorting unused motor windings at low speed is an excellent way to limit resonance while at higher speeds, unused motor windings should be left open for efficiency. Finally, some motor control systems include a disengage input that removes power from all windings; in this case, if brake is asserted, it typically shorts all motor windings.

Practical Examples

Amature astronomers frequently need very slow motors to turn their telescopes, and in recent years, stepping motors have taken the place of synchronous motors and gear trains for many applications, particularly for barn-door or Scotch mounts, a class of extremely simple camera mounts used in astrophotography. For this application, circuits as simple as that shown in Figure 6.2 are common:

Figure 6.2 
              2a 2b          1a 1b
               o o            o o
       ________|_|__________  | |
      |  ____  | |    ____  | | |
      |_|    |_| |___|    |_|_| |
        |D  Q|   |   |D  Q|     |
        |   _|   |   |   _|     |
       _|C  Q|___|  _|C  Q|_____|
 take | |____|     | |____|
 step |            |
   o--o------------ 

The circuit shown in Figure 6.2 only operates in one direction, generating the signals needed to turn a permanent magnet or hybrid motor one full step for each pulse on the take-step input. In terms of the general model in Figure 6.1, the next-state and create-vector functions are trivial and require no logic to generate so long as each flipflop in the state register has outputs available in both straight and complemented form.

Modifying this circuit for bidirectional operation is straightforward, as is illustrated in Figure 6.3:

Figure 6.3 
                        2a 2b                1a 1b
                         o o                  o o
           ______________|_|________________  | |
          |  ___   ____  | |    ___   ____  | | |
           -|   | |    |_| o---|   | |    |_|_| |
            |XOR|-|D  Q|   |   |XOR|-|D  Q|     |
           -|___| |   _|   |  -|___| |   _|     |
          |      _|C  Q|___| |     __|C  Q|_____|
   take   |     | |____|     |    |  |____|
   step   |     |            |    |
     o----|-----o------------|---- 
direction |                  |
     o----o------------------

The circuit shown in Figure 6.3 uses two exclusive or gates to compute two different versions of the next-state function, depending on the value of the direction input. Each flip-flop presents both inverted and non-inverted outputs to the world; this allows an equivalent circuit to be made by substituting a double-pole double-throw switch for the exclusive-or gates, and another equivalent circuit can be derived from this by substituting a pair of 2-input 1-output multiplexors for the switch.

A number of manufacturers of stepping motor control circuitry make integrated circuits that include slightly more complex logic allowing both full and half stepping modes. For example, the Motorola (and others) MC3479 chip includes a 16 volt 350mA H-bridge and control logic with step, direction, and mode inputs to control half-stepping.

The SGS-Thompson (and others) L297 includes just the mid-level control logic for full or half-step control of a pair of H-bridges used to control a permanent magnet or variable reluctance motor. This chip is specifically designed to control the L298 dual 2 Amp H-bridge, and in addition to the mid-level control logic, it includes the one-shots and comparators required to use this H-bridge as a current limited chopper.

Software Solutions

If a microprocessor, programmable interface controller or other computer system is used to control a stepping motor, the computer can directly generate the control vector in software and present it to the motor interface through a parallel output port, as shown in Figure 6.4:

Figure 6.4 
                   |
      Software     |     Hardware
               ____|____
              |parallel |
  step(dir)   | output  |--------\ control
    function  |  port   |--------/ vector
              |_________|
                   |
                   |
                   |

   A general model for mid-level control

In this software-centered model, the basic problem of mid-level control is reduced to how the step function is implemented. Each call to step(d) causes the control vector to advance along the control trajectory in the direction specified by dir. The call step(+1) causes the motor to step forward, while the call step(-1) causes it to take one step back.

The control trajectory for any motor can be described as a circular array of control vectors. The simple full-stepped trajectory given in the introduction can be represented as:

t, a 4 element array where
t[0] = 10
t[1] = 9
t[2] = 5
t[3] = 6
Similarly, the corresponding half-stepped trajectory for the same motor interface would be:
t, an 8 element array where
t[0] = 10
t[1] = 8
t[2] = 9
t[3] = 1
t[4] = 5
t[5] = 4
t[6] = 6
t[7] = 2
Similar definitions, differing only in the size of the trajectory array and the values of each entry, will suffice to describe one complete cycle of the control trajectory for any stepping motor interface!

The step routine itself does not depend in any way on the control vector. For all of the above examples, the step routine is:

p, a statically allocated integer variable, initially zero.

step( d )
-- a function of one argument d
-- where d must be either +1 or -1
-- no value is returned

p = p + d ( mod size( t ) )
output( t[p] )
Here, we assume that the output function sends one control vector to the motor. The details of this function will depend on the computer, the interface used, and the operating system, if any. The above pseudocode also assumes a size function that returns the size of the array holding a cycle of the the trajectory; how this is done will definitely vary from one programming language to another.

Note that, when translating the above code into real programming languages, the simple use of the mod operator above can rarely be preserved. Mathematicians generally agree that

y > x ( mod y ) > 0
but the designers of programming languages frequently depart from this definition when x is negative. As a result, in languages such as C, C++, Java and Pascal, care must be taken to avoid negative values on the righthand side of the mod (or %) operator!

Simple Practical Examples

The following C code will control a single 3-phase variable reluctance motor taking its control vector from the three least significant bits of the parallel port of a Unix or Linux system, assuming that the parallel port has been opened in the correct mode using the file descriptor pp:

    #define STEPS 3
    int t[STEPS] = { 1, 2, 4 };

    int p = 0;

    void step( int d )
    {
        char c;
        p = (p + STEPS + d) % STEPS;

	/* output t[p] using low-level Unix I/O */
        c = t[p];
        write( pp, &c, 1 );
    }
Rewriting the first few lines of the above code allows us to convert it for use with a permanent magnet or variable reluctance motor operated in half-step mode from the 4 least significant bits of the parallel port:
    #define STEPS 8
    int t[STEPS] = { 10, 8, 9, 1, 5, 4, 6, 2 };

An Object Oriented Design

Things are more complex if multiple motors are in use! Although ad-hoc solutions can are common, a systematic approach is appropriate, and even in languages with no support for object oriented methodology, the easiest way to describe such solutions is in object oriented terms.

After initialization, which may depend on many low level details, the high level software isn't interested in how the motor works. Therefore, for each motor object, the visible interface needs only the step function. The class of motor objects is polymorphic because the details of how step operates for one motor may differ considerably from the details of how it works for another.

The following code is given in Java; translation to C++, Simula 67 or other object oriented languages should be straightforward:

    abstract class StepMotor
    {
        public abstract void step(int d);
            // step the motor in direction d (+1 or -1)
    }
This is an abstract class; that is, objects of this class cannot be instantiated because we have yet to specify any of the details of how the motor or interface works. Each useful stepping motor interface must be supported by an extension or subclass of this abstract class! The class below illustrates this, incorporating the code discussed in the sections above:
    class UniversalStepMotor extends StepMotor
    {
        private int s;         // the size of the trajectory
        private int[] t;       // the trajectory for this motor
        private int p;         // motor's position in trajectory
        private OutputStream o;// the output port to use

        public void step(int d)
            // step the motor in direction d (+1 or -1)
        {
            p = (p + s + d) % s;
            o.write( t[p] );
        }

        public UniversalStepMotor( int[] table, OutputStream out )
	    // initializer
        {
	    s = table.length;
            t = table;
            p = 0;
            o = out;

            o.write( t[p] );
        }
    }
The above code assumes that the interfaces to motors are accessible through output streams of type java.io.OutputStream, and the initializer not only builds the data structure but sends an initial control vector to the motor.

Given that MotorPort is an object of type java.io.OutputStream, the following Java code will create a UniversalStepMotor object m for a 3-phase variable reluctance motor:

    StepMotor m = UniversalStepMotor(
        new int[] {4,2,1}, // step table for the motor
        MotorPort          // OutputStream for the motor
    );
With this declaration and initialization in place, the call m.step(1) will turn the motor one step.

For many applications, the class StepMotor defined above will need to be extended with a reset procedure, used to reset both the motor object and the motor interface. This procedure should probably be incorporated directly into the definition of the StepMotor class, as opposed to adding it by class extension. This procedure would typically be called as the first step in recovering from an error detected at higher levels in the control hierarchy.

When Objects Won't Do

For programmers who can't use object oriented design, for any reason, the following example illustrates an appropriate mid-level design that avoids the use of such features. Using Pascal, the following types can be used to describe each motor:

    type
        direction = -1 .. +1;

        trajectory = array [0 .. MaxTrajectorySize] of int;

        StepMotor = record
            s: integer;    { size of this motor's trajectory }
            t: trajectory;
            p: integer;    { this motor's position in trajectory }
            o: port;       { the output port to use for this motor }
        end { record };
The StepMotor record type given here corresponds exactly to an object of the class UniversalStepMotor defined in Java above. The array type trajectory is explicitly named so that a trajectory may be passed, as a formal parameter, to an initializer procedure for records of this type. The subrange type direction allows Pascal's subrange restriction mechanisms to check the correctness of parameters to the step function, a desirable feature that is missing in languages descended from C.

Given the above definitions, we can now construct a general purpose step procedure:

    procedure step(var m: StepMotor, d: direction);
        { step motor m in direction d }
    begin
        with m do begin
            p = (p + s + d) mod s;
            output( o, t[p] );
        }
    end { step };
In all of the above, we have assumed that variables of the type port are used to describe output ports to which motors may be attached, and that the procedure output outputs one vector to the indicated port.

In effect, in abandoning programming languages with direct support for object orientation, we have had to chang our notation from:

    motor.step( dir );
to:
    step( motor, dir );
This is not a major sacrifice in a system where there is only one type of motor interface, but when there are multiple motor types, support for the object oriented model will be useful. If this must be done in a language like Pascal, the declaration for the type StepMotor must be modified to allow for all types of motor interfaces, for example, by declaring it to be a variant record, and the step procedure must be modified to check the motor type and act appropriately.