Using the Allegro 5804 Stepping Motor Controller / Translator

Copyright, H. Paul Roach, Towanda Malone,Christine Samuels
Peter H. Anderson, Dept of Electrical Engineering
Morgan State University, Baltimore, MD 21014, Sept, 96

Background.

Volume 1 described the general theory of a unipolar stepping motor and a number of routines were presented to illustrate how to energize one winding, then two adjacent windings and then the second winding alone, etc. Thus, an array containing the following patterns was used. By outputting a pattern and advancing the index, the motor is turned one way. By decrementing the index, the motor is turned the other way. The time between outputting successive patterns determines the speed of the motor.

     Binary    Hex

     0001      0x01
     0011      0x03
     0010      0x02
     0110      0x06
     0100      0x04
     1100      0x0c
     1000      0x08
     1001      0x09  

Thus, four data bits were required. An Allegro 2803 inverting driver was used between the parallel port's TTL outputs and the motor windings.

Allegro 5804

The Allegro 5804 simplifies this considerably in that the actual patterns are generated by the 5804.

See Figure #1.

The motor's direction is controlled by output Data_1. The motor advances one step by outputting a pulse on Data_0. The motor actually advances on the falling edge of the pulse.

Various modes may be set using the Half Step and One Phase inputs as shown in the following table.

     Mode      HS   One Phase      Pattern

     0         0    0              W0W1 -> W1W2 -> W2W3, etc.
     1         0    1              W0 -> W1 -> W2 -> W3 -> W0, etc.
     2         1    0              W0 -> W0W1 -> W1 -> W1W2, etc
     3         1    1              No change

Note that mode 2 corresponds to the half step sequence which has been treated in other discussions. Mode 1 is simply energizing single coils in turn. The motor will run less smoothly but advance twice the direction per pulse. Mode 0 is similar in that there are only four states, but the use of two windings improves the torque.

In mode 3, it is important to note that the output pattern is retained. That is, the last state is present on the outputs and all pulses are ignored. Thus, the coils of the motor are energized and the motor is held in position.

The /Output Enable input is different than mode 3 in that when at logic one, all outputs are brought high and no coil is energized. The potential disadvantage in doing this is illustrated with a simple example. Assume a stepper is being used to lift and lower an object. If mode 3 is used for a "hold" or "pause" condition, the motor windings remain energized. However, if bringing /Output Enable high is used, the only thing holding the object is a slight residual magnetism in the motor. Chances are good that in de-energizing the motor windings by bringing /Output Enable high, the object will fall. Of course, this depends on the application.

Control of Multiple Stepping Motors.

Figure #2 illustrates an arrangement where up to eight stepping motors may be controlled using nothing more than the 8 bits on the parallel ports Data Port.

The mode control leads (Half Step and One Phase) and the Direction and Step leads (4 leads) are bussed to all 5804 circuits. A specific motor is selected by bringing its /Output Enable lead to logic zero

Addressing of the motor is controlled by three outputs from the Data Port using a 74LS138 one of eight decoder. The state of decoder inputs A2, A1 and A0 determines which of the decoder outputs goes to logic zero. For example, by outputting 101 on Data_6, Data_5 and Data_4, output Y5 goes low, which enables the 5804 associated with motor 5.

In the circuit, I used the Data_7 to enable the 74LS138. When low, the decoder is enabled and the output corresponding to the address on A2, A1 and A0 goes low. When at logic one, the decoder is disabled and all outputs are at logic one and thus, no motor is selected.

Thus, turning off all motors is simply;

     outportb(DATA, 0x80); /* Bring Data_7 to logic one */

Controlling one motor is a matter of logical oring the motor number (three bits), the mode (two bits) and the direction bit and then stepping by bringing the least significant bit high and then low.

     bits = (motor) << 4) | (mode <<2) | (direction<<1);

     outportb(DATA, bits | 0x01); /* pulse on ls bit */
     delay(50);
     outportb(DATA, bits);
     delay(50); 

Note that the use of Data_7 to turn off all motors may not be necessary in your application. The same thing could be accomplished by simply not stepping on the least significant bit. That is, one motor may be selected and the other seven are not, but as there are no steps on the least significant bit, the selected motor doesn't turn.

Another approach is to not assign one output of the 74LS138 to a motor, reserving this state for "all motors off".

In Figure #3, a one of 16 decoder is used to control up to 15 stepping motors with the motor_0 state not being assigned. Thus, motor is four bits (0001 - 1111), mode is two bits (00 - 11) and direction is one bit (0-1).

     outportb(DATA, 0x00); /* turn off all motors */

     bits = (motor) << 4) | (mode <<2) | (direction<<1);


     outportb(DATA, bits | 0x01); /* pulse on ls bit */
     delay(50);
     outportb(DATA, bits);
     delay(50); 

A Slightly Different and much Better Approach.

In the above discussion, it was noted that there is a difference between using mode 3 (both Half Step and One Phase at logic one) and the /Output Enable feature to hold a motor in a "pause" or "stop" state. In mode 3, the coils remain energized, and, going back to my example, the object doesn't fall like a stone when the motor is not selected.

Note that the circuitry in Figure #3 uses the /Output Disable feature to address the motor which will is to be turned. Unfortunately, all of the other motors are totally off. You can almost hear the objects falling on the floor! (smile).

Another approach is presented in Figure #4. Note that the Half Step inputs are hardwired to logic one and the selection of the motor is performed by bringing the One Phase lead on the selected motor to a logic zero. Thus, when a motor is selected, it is operated in mode 2 (the traditional half step mode) and when it is not selected, it is in mode 3.

In Figure #4, the "no motor selected" feature is implemented by not assigning Motor 0.

In addition, there is an "all coils off" function which may or not be desirable. Note that Data_7 must be at logic zero to enable the /Output Enable on all of the 5804s. If it is at one, as would probably be the case when the PC is turned off, all of the /Output Enables are at logic one, causing all windings to be de-energized.

Thus," all coils off" is implemented as;

     outportb(DATA, 0x80); /* /Output Enable brought high */

In operating a selected motor, note there is no longer a choice of modes.

     bits = (motor << 4) | (direction << 1);
     outportb(DATA, bits);
     
     /* now turn it as required */
     outportb(DATA, bits | 0x01); /* pulse on ls bit */
     delay(50);
     outportb(DATA, bits);
     delay(50); 

Pull-Up Resistors.

The 5804 data sheet indicates that the input voltage logic one may be as low as 3.5 Volts, which is a bit high for a totem-pole output TTL device where the output can easily sag to 2.8 Volts. Thus, pull up resistors are required on all TTL outputs driving the 5804. We offer this observation from first hand experience!

Thermal Characteristics.

Although Allegro indicates a drive current of up to 1.5A, be careful.

The maximum temperature of the junction, T_j_max is rated at 150 degrees C and the thermal resistivity of the junction to ambient, R_theata_ja = 43 degrees C / W.

Thus a general expression for P_max;

P_max = (T_j_max - T_ambient) / R_theta_ja

If T_ambient is 30 degrees C, P_max is calculated as 2.79 Watts.

When in saturation, V_ce = 1.5 Volts. Thus,

I_max_total = P_max / V_ce = 2.79 / 1.5 = 1.86 Amps

If you are using a half step mode on a continuous basis, the average number of windings which are energized is 1.5. Therefore;

I_winding = I_max_total / num_windings = 1.86 / 1.5 = 1.24 A

However, if you are running in a half step mode and plan on halting the motor for a period of time, two windings may well then be energized;

I_winding = I_max_total / num_windings = 1.86 / 2 = 0.93 A

You should be able to use the above analysis to determine whether you can drive a stepping motor you may have based on your maximum expected ambient temperature and your particular operating conditions.

Note that the 5804 does have a thermal shut down mechanism which protects the output transistors when the junction temperature exceeds 165 degrees C.

Availability.

The 5804 is available from Newark Electronics at a cost of $3.50 in single unit quantities. I have included a separate discussion on obtaining Allegro components elsewhere in this manual.

Programs.

Program 5804_1.C.

Program 5804_1.C illustrates a straight forward technique for controlling a single stepping motor as shown in Figure #1. Note that the stepping mode may be set to 0, 1, or 2 and is in bit positions 3 and 2. The direction is in bit position 1. The pause is implemented using a simple delay.

/*
** Program 5804_1.C
**
** Turns motor in one direction for 5 seconds, pauses for 5 seconds,
** turns in other direction for 5 seconds.
**
** See Figure #1.
**
** Towanda Malone, Morgan State University, August 10, '96, Sept 27, '96
*/

#include <stdio.h>
#include <dos.h>

#define DATA 0x03bc

int data = 0x00; /* note global */

void main (void)
{
   int n;
   int mode = 1;
   /* set mode to 0, 1 or 2;
   ** Mode 0 - W0W1 -> W1W2 -> W2W3 -> W3W0, etc
   ** Mode 1 - W0 -> W1 -> W2 -> W3 -> W0, etc
   ** Mode 2 - W0 -> W0W1 -> W1 -> W1W2, etc
   */
   data = 0x00 | (mode << 2) | 0x02;  /* mode and dir set */
   outportb(DATA, data);
   for (n=0; n<100; n++)
   /* turn in one direction for 5 seconds */
   {
      data = data | 0x01;  /* step high */
      outportb(DATA, data);
      delay(25);
      data = data & (~0x01);  /* step low */
      outportb(DATA, data);
      delay(25);
   }
   delay(5000); /* pause for 5 secs */

   data = data & (~0x02);  /* direction set to logic 0 */
   outportb(DATA, data);
   for (n=0; n<100; n++)
   /* turn in other direction for 5 seconds */
   {
      data = data | 0x01;  /* step high */
      outportb(DATA, data);
      delay(25);
      data = data & (~0x01);  /* step low */
      outportb(DATA, data);
      delay(25);
   }
}

Program 5804_2.C.

Program 5804_2.C is simply an implementation of 5804_1.C using functions. It uses the ftime function to continually perform a task while continually fetching the time off the system clock and halting the task when the specified time has elapsed.

Note that in the pause_motor function, mode 3 was used. In fact, this is not really necessary as no step pulses are being output. I decided to illustrate it in case you decide to control the step function using an external clock, for example a free running 555.

/* Program 5804_2.C
**
** Uses functions to turn motor using specified mode in specified
** direction at defined speed for specified duration, and to stop 
** motor for specified time.
**
** See Figure #1
**
** Towanda Malone, Peter H. Anderson, Sept 27, '96
*/

#include <stdio.h>
#include <dos.h>
#include <sys\timeb.h>

#define DATA 0x03bc

#define CW 1
#define CCW 0

int data = 0x00;

void turn_motor (int mode, int dir, int time, int duration);
void pause_motor (int duration);
void set_mode_and_dir (int mode, int dir);
void step (int time);

void main (void)
{
   int mode;
   /* set mode to 0, 1 or 2;
   ** Mode 0 - W0W1 -> W1W2 -> W2W3 -> W3W0, etc
   ** Mode 1 - W0 -> W1 -> W2 -> W3 -> W0, etc
   ** Mode 2 - W0 -> W0W1 -> W1 -> W1W2, etc
   */
   mode = 2;
   turn_motor(mode, CW, 50, 5000);  
               /* turn CW slow for 5 secs - Mode 2 */
   turn_motor(mode, CW, 25, 5000);  /* faster for 5 more secs */
   pause_motor(5000);         /* pause for 5 secs */
   mode=1;
   turn_motor(mode, CCW, 50, 5000); /* turn other way - Mode 1 */
   turn_motor(mode, CCW, 25, 5000);
}

void turn_motor(int mode, int dir, int time, int duration)
{
   struct timeb t_curr, t_start;
   int t_diff;

   ftime(&t_start);
   set_mode_and_dir(mode, dir);
   do
   {
      step(time);
      ftime(&t_curr);
      t_diff = (int) (1000.0 *(t_curr.time - t_start.time)
             + (t_curr.millitm - t_start.millitm));
   }
   while (t_diff < duration);
}

void pause_motor (int duration)
{
   struct timeb t_curr, t_start;
   int t_diff;

   ftime(&t_start);
   outportb (DATA, (3 << 2)); /* place in mode 3 */
   do
   {
     ftime(&t_curr);
     t_diff = (int) (1000.0 * (t_curr.time - t_start.time)
         + (t_curr.millitm - t_start.millitm));
   }
   while (t_diff < duration);
}

void set_mode_and_dir(int mode, int dir)
{
   data = (mode << 2) | (dir << 1);
   outportb(DATA, data);
}

void step (int time)
{
   data = data | 0x01;
   outportb(DATA, data);
   delay (time/2);
   data = data ^ 0x01;
   outportb(DATA, data);
   delay (time/2);
}

Program 5804_3.C.

Program 5804_3.C extends 5804_2.C to the control of multiple stepping motors as illustrated in Figure #2.

The pause_motors function is simply a wait without outputting pulses.

/* Program 5804_3.C
**
** Uses functions to turn specified motor in specified mode in specified
** direction at defined speed for specified duration.
**
** See Figure #2. (Mutiple Stepping Motors).
**
** Towanda Malone, Peter H. Anderson, Sept 27, '96
*/

#include <stdio.h>
#include <dos.h>
#include <sys\timeb.h>

#define DATA 0x03bc

#define CW 1
#define CCW 0

int data = 0x00;

void turn_motor (int motor, int mode, int dir, int time, int duration);
void set_motor_and_mode_and_dir (int motor, int mode, int dir);
void step (int time);
void pause_motors(int time);

void main (void)
{
   int mode;
   /* set mode to 0, 1 or 2;
   ** Mode 0 - W0W1 -> W1W2 -> W2W3 -> W3W0, etc
   ** Mode 1 - W0 -> W1 -> W2 -> W3 -> W0, etc
   ** Mode 2 - W0 -> W0W1 -> W1 -> W1W2, etc
   */
   mode = 2;
   turn_motor(1, mode, CW, 50, 5000);
   /* turn motor 1, mode 2, CW slow for 5 secs */

   turn_motor(2, mode, CW, 25, 5000);
   /* turn motor 2, mode 2, CW, fast for 5 secs */

   pause_motors(5000);         /* pause for 5 secs */

   mode=1;
   turn_motor(7, mode, CCW, 50, 5000);
   /* turn motor 7, mode 1, CCW slow for 5 secs */

   turn_motor(7, mode, CCW, 25, 5000);
   /* turn motor 7, mode 1, CCW faster for 5 secs */
}

void turn_motor(int motor, int mode, int dir, int time, int duration)
{
   struct timeb t_curr, t_start;
   int t_diff;

   ftime(&t_start);
   set_motor_and_mode_and_dir(motor, mode, dir);
   do
   {
      step(time);
      ftime(&t_curr);
      t_diff = (int) (1000.0 *(t_curr.time - t_start.time)
             + (t_curr.millitm - t_start.millitm));
   }
   while (t_diff < duration);
}

void pause_motors (int duration)
{
   struct timeb t_curr, t_start;
   int t_diff;

   ftime(&t_start);
   do
   {
      ftime(&t_curr);
     t_diff = (int) (1000.0 * (t_curr.time - t_start.time)
             + (t_curr.millitm - t_start.millitm));
   }
   while (t_diff < duration);
}

void set_motor_and_mode_and_dir(int motor, int mode, int dir)
{
   data = (motor << 4) | (mode << 2) | (dir << 1);
   outportb(DATA, data);
}

void step (int time)
{
   data = data | 0x01;
   outportb(DATA, data);
   delay (time/2);
   data = data ^ 0x01;
   outportb(DATA, data);
   delay (time/2);
}

Program 5804_4.C.

Program 5804_4.C illustrates the control of multiple stepping motors using the arrangement shown in Figure #4. Note that there is no mode control. Rather, a motor is either turning using mode 2 (half step) or it is halted, but unlike the arrangement in Figure #2, the windings remain energized.

Note that in function pause_motors, the 74LS138 is disabled by addressing motor 0 which was not assigned. Again, this is shown for illustration only. In fact, no motor turns as no pulses are applied to any motor.

/* Program 5804_4.C
**
** Uses functions to turn specified motor in specified direction at
** defined speed for specified duration.
**
** Note that mode is hardwired as mode 2 (half step).
**
** See Figure #4.
**
** Towanda Malone, Peter H. Anderson, Sept 27, '96
*/

#include <stdio.h>
#include <dos.h>
#include <sys\timeb.h>

#define DATA 0x03bc

#define CW 1
#define CCW 0

int data = 0x00;

void turn_motor (int motor, int dir, int time, int duration);
void set_motor_and_dir (int motor, int dir);
void step (int time);
void pause_motors(int time);

void main (void)
{

   turn_motor(1, CW, 50, 5000);
   /* turn motor 1, CW slow for 5 secs */

   turn_motor(2, CW, 25, 5000);
   /* turn motor 2, CW, fast for 5 secs */

   pause_motors(5000);         /* pause for 5 secs */

   turn_motor(7, CCW, 50, 5000);
   /* turn motor 7, CCW slow for 5 secs */
   turn_motor(7, CCW, 25, 5000);
   /* turn motor 7, CCW faster for 5 secs */
}

void turn_motor(int motor, int dir, int time, int duration)
{
   struct timeb t_curr, t_start;
   int t_diff;

   ftime(&t_start);
   set_motor_and_dir(motor, dir);
   do
   {
      step(time);
      ftime(&t_curr);
      t_diff = (int) (1000.0 *(t_curr.time - t_start.time)
             + (t_curr.millitm - t_start.millitm));
   }
   while (t_diff < duration);
}

void pause_motors (int duration)
{
   struct timeb t_curr, t_start;
   int t_diff;

   outportb(DATA, 0x00); /* address unassigned motor 0 */
   ftime(&t_start);
   do
   {
      ftime(&t_curr);
      t_diff = (int) (1000.0 * (t_curr.time - t_start.time)
             + (t_curr.millitm - t_start.millitm));
   }
   while (t_diff < duration);
}

void set_motor_and_dir(int motor, int dir)
{
   data = (motor << 4) | (dir << 1);
   outportb(DATA, data);
}

void step (int time)
{
   data = data | 0x01;
   outportb(DATA, data);
   delay (time/2);
   data = data ^ 0x01;
   outportb(DATA, data);
   delay (time/2);
}