A Worked Stepping Motor Example




A common stepper to find in a full-height IBM or Tandon 5.25 inch diskette drive is the type KP4M4 made by either Japan Servo Motors or Tandon; this is a permanent magnet motor with 3.6 degrees per step and 150 ohms per winding, with the center-taps of each winding tied to a common lead. Many half-height 5.25 inch diskette drives use very similar motors, although there is much more variety in newer drives, including some that use bipolar steppers.

Another stepper sometimes found in half-height drives is the `pancake format' motor from a 1/2 height 5.25 inch diskette drive; for example, a permanent magnet motor made by Copal Electronics, with 1.8 degrees per step and 96 ohms per winding, with center taps brought out to separate leads. The leads on these motors are brought out to multipin in-line connectors, laid out as follows:

Figure 6.1 

When the center-taps of these motors are connected to +12 and one end of either winding is grounded, the winding will draw from 170 mA to 250 mA, depending on the motor, so any of a number of motor drive circuits can be used. The original IBM full-height diskette drives used a pair of UDN3612N or UDN5713 chips; these are equivalent to chips in the SN7547X series (X in 1,2,3). The ULN2003 darlington arrays from Allegro Microsystems is probably the most widely available of the applicable chips, so it will be used in this example.

Consider the problem of controlling multiple steppers comparable to those described above from an IBM compatible DB25-based parallel output port. The pinout of this connector is given in Figure 6.2, as seen looking at the face of the female connector on the back of an IBM PC (or equivalently, as seen from the back of the male connector that mates with this):

Figure 6.2 

The IEEE 1284 standard gives the best available definition of the parallel port, but as an after-the-fact standard, nonconformance is common. Some documentation of this standard is available in the net. There is an extensive set of tutorial material available on the web discussing the IBM PC Parallel port. Another index of parallel port information is available from Ian Harries.

There is some confusion in the documentation of this connector about the labels on the SLCT and SLCTIN lines (pins 13 and 17); this is because these names date back to a Centronics printer design of the early 1970's, and the name SLCTIN refers to an input to the printer, which is to say, an output from the computer.

The names of some of these lines are relics of the original intended purpose of this I/O port, as a printer port. Depending on the level at which you wish to use the printer, some may be ignored. If the BIOS printer support routines of the IBM PC or the parallel port driver under various versions of UNIX sare to be used, however, it is important to pay attention to some of these signals:

The BIOS handles reinitializing the printer by producing a negative pulse on INIT (pin 16). We can use this as a reset pulse, but otherwise, it should be ignored! In the reset state, all motor windings should be off.

When no output activity is going on, the BIOS holds the output signal lines as follows:

  • STROBE (pin 1) high, data not valid.

  • AUTOFD (pin 14) high, do not feed paper.

  • INIT (pin 16) high, do not initialize.

  • SELCTIN (pin 17) low, printer selected.

To print a character, the BIOS waits for BUSY (pin 11) to go low, if it is not already low, and then outputs the new data (pins 2 through 9). Following this (with a delay of at least 0.5 microsecond), STROBE (pin 1) is pulsed low for at least 0.5 microsecond. The BIOS returns the inputs ACK, BUSY, PE and SLCT (pins 10 to 13) to the user program after printing each character.

The computer is entitled to wait for ACK (pin 10) to go low for at least 5 microseconds to acknowledge the output strobe, but apparently, IBM's BIOS does not do so; instead, it relies on device to set BUSY to prevent additional outputs, and it leaves the output data unmodified until the next print request. While neither MS/DOS nor the BIOS use the interrupt capability of the parallel port, OS/2 and various versions of UNIX use it. A rising edge on ACK (pin 10) will cause an interrupt request if interrupts are enabled. This interrupt design is only useful if, in normal operation, the trailing edge of the ACK pulse happens when BUSY falls, so that finding BUSY high upon interrupt signals an error condition.

Note that all input output to the parallel port pins is done by writing to various I/O registers; so as long as interrupts are disabled and the I/O registers are directly manipulated by application software, all 5 input pins and all 12 output pins may be used for any purpose. To maintain compatibility with existing drivers, however, we will limit our misuse of these pins.

If we only wanted to support a single motor, it turns out that the logic on the standard 5.25 inch diskette drive can, with an appropriate cable, be driven directly from the parallel port. Documentation on this approach to recycling motors from old diskette drives has been put on the web by Tomy Engdahl.

Since we are interested in supporting multiple motors, we will use DATA lines 4 to 7 to select the motor to control, while using the least significant bits to actually control the motor. In addition, because almost all stepping motor applications require limit switches, we will use the PE (12) bit to provide feedback from limit switches. The IEEE 1284 standard defines the PE, SLCT and ERR signals as user defined when in either Enhanced Parallel Port or Compatibility mode, so this is not a bad choice. Unfortunately, the BIOS occasionally checks this bit even when it is aware of no printer activity (for example, when the keyboard type-ahead buffer fills); thus, it is a good idea to disable the BIOS when the parallel port is used for motor control!

Note that fanout is not a problem on the IBM PC parallel port. The IEEE 1284 standard defines the parallel port outputs as being able to source and sink 14 milliamps, and older IBM PC parallel ports could sink about 24 milliamps. Given that a standard LS/TTL load sources only 0.4 milliamps and some devices (comparitors, for example) source 1.2 milliamps, an IEEE 1284 port should be able to handle up to 10 of motor interfaces in parallel.

A Minimal Design

As mentioned above, we will use the ULN2003 darlington array to drive the stepping motor. Since we want to drive multiple motors, the state of each motor must be stored in a register; while many chips on the market can store 4 bits, careful chip selection significantly reduces the parts count and allows for a single-sided printed circuit card!

With appropriate connections, both the 74LS194 and the 74LS298 can use a positive enable signal to gate a negative clock pulse; we will use the 74LS194 because it is less expensive and somewhat simpler to connect. Finally, we will use the 74LS85 chip to compare the 4 bit address field of the output with the address assigned to the motor being driven.

Figure 6.3 summarizes the resulting design:

Figure 6.3 

The 74LS194 was designed as a parallel-in, parallel-out shift-register with inputs to select one of 4 modes (do nothing, shift left, shift right, and load). By wiring the two mode inputs in parallel, we eliminate the shift modes, converting the mode input to an enable line. The unused right and left shift input pins on this chip can remain disconnected or can be grounded, tied to +5, or connected to any signal within the loading constraints.

Here, we show the 74LS194 being loaded only when bits 4 to 7 of the output data match a 4-bit address coded on a set of address switches. The comparison is done by a 74LS85, and the address switches are shown in Figure 6.3 as an 8-position DIP-switch. The cost of a DIP switch may be avoided in production applications by substituting jumpers.

One interesting aspect of this design is that the LS-TTL outputs driving the ULN2003 chip are used as current sources -- they pull up on the inputs to the darlington pairs. This is a borderline design, but careful reading of the LS-TTL spec sheets suggests that there is no reason it should not work, and the ULN2003 is obviously designed to be driven this way, with more than enough forward current gain to compensate for the tiny pull-up capacity of an LS-TTL output!

The Zener diode connected between pin 9 of the ULN2003 and the +12 supply increases the reverse voltage on the motor windings when they are turned off. Given the 50 volt maximum rating of the ULN2003, this may drop as much as 50-12 or 38 volts, but note that power dissipation may actually be an issue! At high frequency with unconventional control software, the power transfer to this diode can be quite efficient! With the stepping motors from old diskette drives, it may be possible to push a 12 volt zener to on the order of 1 watt. I used a 15 volt 1 watt zener, 1N3024.

If this motor is to be driven by software that directly accesses the low-level parallel port interface registers, directly loading data and then directly raising and lowering the strobe line, no additional hardware is needed. If it is to be driven through the BIOS or higher level system software, additional logic will be needed to manipulate ACK and BUSY.

Although the 74LS85 and the 74LS194 are no longer stocked by some mass-market chip dealers, they are still in production by Motorola, TI, Thompson SK and NTE. If over-the-counter availability of chips is your primary concern, adding a chipload of inverters or 2-input nand gates will allow just about any 4-bit latch to be used for the register, and address decoding can be done by a quad XOR chip and a 4-input nand gate.

If over-the-counter chips are of no concern, you can reduce the chip count even further! The Micrel MIC5800 4-bit parallel latch/driver can easily handle the loading of small unipolar steppers and combines the function of the ULN2003 and the 74LS194 used here! The resulting design is very clean.

Adding One Bit of Feedback

Surprisingly, no additional active components are needed to add one bit of feedback to this design! There are 3 spare darlington pairs on the ULN2003 driver chip, and, with a pull-up resistor for each, these can serve as open-collector inverters to translate one or two switch settings into inputs appropriate for the PC!

The ULN2003 includes pull-down resistors on each input, guaranteeing that the corresponding output will be turned off if left disconnected. Thus, connecting a ULN2003 input to a positive enable signal (pin 5 of the 74LS85 chip for example) will turn the output on only if it is both enabled and the switch is closed. It may be necessary to add a 1K pull-up to the LS-TTL output because, in addition to driving the ULN2003, it is also driving two normal LS-TTL inputs. Adding this pull-up will do no harm if it isn't needed (an LS-TTL output should be able to handle even a 300 ohm pull-up).

Since the ULN2003 is an open-collector inverter, the output needs a pull-up. We could rely on the PE input of the IBM PC parallel port to pull this line up (an open TTL input usually rises of its own accord), but it is better to provide a pull-up resistor somewhere. Here, we provide a 10K pull up on each stepping motor drive card; these pull-ups will be in parallel for all motors attached to a single parallel port, so if fewer than 10 motors are in use, proportionally smaller resistors may be substituted or the pull-ups may be omitted from some of the controller cards.

Figure 6.4 summarizes these additions to the design:

Figure 6.4 

Something You Can Build

Figure 6.5 shows a single-sided PC board layout for a 2.5 inch square board that incorporates all of the ideas given above. I have etched and tested the 7/8/1996 version of this artwork.

Figure 6.5  GIF of board  

(What's that about copyright notices? Well, put simply, if you're going to sell my design, please get in touch with me about it. You're free, however, to make a handful of boards from this design to control your own motors.)

This version of the board includes a jumper to ground the BUSY signal on the parallel port (pin 11) and it brings out the STROBE, ERROR, ACK and SELECT signals to allow for possible jumpering. These changes make it slightly more likely that this board can be hacked to work with native operating system drivers for the parallel port. As is, with no special jumpering other than the grounding of BUSY, it works under Linux.

To use the artwork in Figure 6.5 for etching a board, reproduce it at 100 pixels per inch . Both versions are positive images of the foil side of the board; depending on how you transfer the image to the resist for etching, you may need to flip it left-to-right and/or invert the black and white. Most GIF viewers allow for such transformations.

Figure 6.6 shows the component side of this board, with jumpers and parts in place.

Figure 6.6  GIF of layout

Note that this layout does not show mounting holes for the board, but that 4 corner pads are provided in appropriate locations. The layout also doesn't show a power connector, but the standard 4-pin Molex connectors used with 5.25" diskettes will fit the pads provided. The 26 pin header is wired so that it can be directly wired to a 25 pin DB-25 plug using ribbon cable and insulation displacement connectors. If multiple motors are to be used, a single ribbon cable can be made with multiple 26 pin connectors on it, one per motor.

Figure 6.6 shows 4 capacitors, 3 between +5 and ground, and 1 between +12 and ground. The two capacitors farthest from the power input connector can be .01 or .1 microfarad capacitors; it may be better to use larger capacitors at the power input pins, 1 microfarad or more.

The plug from the stepping motor goes on the the top header shown in Figure 6.6, with the center-tap lead(s) topmost. The board is arranged so that either a 5 or 6 pin header may be used here, compatable with the plugs on the motors shown in Figure 6.1. The limit switch goes on the bottom header. The latter is wired so that it can be used with either microswitch salvaged from a full-height Tandon 5.25 inch diskette drive.

The address may be hard-wired using 4 jumpers, or it may be set using a DIP-switch, as shown in Figure 6.6. Each bit of the address is set by two switches or jumper positions, the topmost pair of these sets the most significant of the 4 bits. To set a bit to 1, close the top switch and open the bottom switch in the corresponding pair (or put the jumper in the top position of the pair); to set a bit to 0, close the bottom switch and open the top one (or put the jumper in the bottom position of the pair). If it is at all likely that someone will change the address switches while power is applied to the board, use a 47 ohm resistor in place of the jumper directly above the switches! This will protect your power supply from the accidental short circuits that can result from improper switch settings.

Testing the Board

Under Linux

The standard Linux line printer driver attaches the device /dev/lp0 to the standard parallel port found on most PCs, and /dev/lp1 and /dev/lp2 to the optional additional parallel ports. The line printer driver has many operating modes that may be configured and tested with the tunelp command. The default mode works, and the following tunelp command will restore these defaults:

 tunelp /dev/lp0 -i 0 -w 0 -a off -o off -c off

This turns off interrupts with -i 0 so that the board need not deal with the acknowledge signal, and it uses a very brief strobe pulse with -w 0, sets the parallel port to ignore the error signal with -a off, ignores the status when the port is opened with -o off, and does not check the status with each byte output with -C off. The settings of the tunelp options -t and -c should not matter because this interface is always ready and thus polling loop iteration is never required.

Given a correctly configured printer port, the C routines allow output of motor control bytes to the port:

 /****
* ppaccess.c *
* package for Linux access to the parallel port *
****/
#include
#include
#include
#include
#include

#define paper_empty 0x20

static int pp; /* parallel port device descriptor */

void ppsetup()
/* setup access to the parallel port */
{
pp = open("/dev/lp0",O_WRONLY, 0);
if (pp < 0) {
printf( "can't open, %s\n",
_sys_errlist[errno] );
exit( 0 );
}
}

int ppput(int b)
/* put b to the parallel port, return the status */
{
char cbuf = (char) b;
int ibuf;
/* begin critical section */
if(write( pp, &cbuf, 1 ) != 1){
printf( "write error, %s\n",
_sys_errlist[errno] );
exit( 0 );
}
if(ioctl( pp, LPGETSTATUS, (char*)&ibuf ) < 0){
printf( "/dev/lp0 ioctl error, %s\n",
_sys_errlist[errno] );
exit( 0 );
}
/* end critical section */
return ibuf & paper_empty;
}

The comments above indicate a critical section! This is only a concern if the parallel port is shared by multiple processes, perhaps with one process in charge of each of a number of motors. In that case, a semaphore must be used to guard the critical section (see the UNIX semctl() kernel call), or a file lock using F_LOCK and F-ULOCK fcntl() commands, preferably through the lockf() function with a very large size. The latter approach (file locking using lockf()) is probably preferable.

Given these routines, the following main program should spin the motor at a few steps per second in a direction that depends on the sense switch setting:

 /****
* spin.c
* Linux program to spin a permanent magnet motor *
the sense switch gives forward/reverse control *
****/
#include

#define motor_number 15
#define microsec 1
#define millisec (1000*microsec)

int steptab[] = { 9, 10, 6, 5 };

main()
{
int step = 0;
ppsetup();
for (;;) {
if (ppput(steptab[step]|motor_number<<4)) {
step = (step + 1)&3;
} else {
step = (step - 1)&3;
}
usleep( 100 * millisec );
}
}

Under Quasic

To test your board, use the following little basic program. This was developed under Microsoft's Qbasic (C:DOS/QBASIC on a typical off-the-shelf PC) under bare MS/DOS (no version of windows running). This code has been tested with the prototype hardware for the design given above.

The first subroutine in this program outputs the data D to motor M attached to printer port P, and reads the status into S.

     100 OUT P, D + (16 * M)
OUT P + 2, &HD
OUT P + 2, &HC
S = INP(p + 1)
RETURN

The second subroutine updates D, the motor control output, to rotate the motor one step, then uses the first subroutine to output D.

     200 IF D = 9 THEN
D = 10
ELSEIF D = 5 THEN
D = 9
ELSEIF D = 6 THEN
D = 5
ELSEIF D = 10 THEN
D = 6
END IF
GOSUB 100
RETURN

The main program connects the second subroutine to the real-time clock and uses it to step the motor once per second, then repeatedly prints the status reported by the motor.

         P = &H378
M = 15
D = 10
ON TIMER(1) GOSUB 200
TIMER ON
305 LOCATE 5, 5
PRINT "STATUS: "; S; " "
GOTO 305

Unfortunately, QBASIC doesn't give you access to the high resolution of the hardware real-time clock, so this prototype code is only good for testing the hardware. While this code is running, the status of the microswitch (if connected) will be displayed on the screen, embedded in the rest of the I/O port status word.

If you want to report any meterial please