Driver and Arduino code for unipolar stepper motors

 Posted by:   Posted on:   Updated on:  2019-07-12T20:22:03Z

Learn to drive unipolar stepper motors with a homemade transistor driver and easy Arduino code.

A stepper motor is a brushless electric motor that rotates in small equal steps, as opposed to the continuous rotation of regular motors. It has the ability to rotate a predefined number of steps, which makes it useful for precision mechanical devices. To do this, it uses multiple coils that are energized in regular sequences by trains of digital pulses. The speed of these pulses determines the speed of the motor. The pulse sequence determines the rotation direction. Nowadays, you can find integrated drivers for all types of stepper motors. In this post you will learn to make your own driver and use the motor with a development board.

There are two main types of stepper motors. There are bipolar motors which contain two coils (4 wires). To drive this kind of motors, coils are energized one after another, then polarity is changed and the coils are energized again. Two H-bridge circuits are required in this case. Unipolar motors use two coils, each of them having a center tap. The center taps from both coils connect to a power line and the remaining four coil terminals are powered sequentially (5 wires). Unipolar mode simplifies a bit the driving circuit. Some manufacturers offer steppers that can be used either as unipolar or as bipolar. This kind of motors have the center taps not connected together (6 or 8 wires motor) and if the center taps are not used, the motor can be wired as bipolar.

Unipolar stepper driver built on perfboard

Unipolar stepper driver built on perfboard

This post will be about unipolar motors. Although there are libraries for steppers, generating the pulses is a very easy task for any microcontroller. But a microcontroller can't be connected directly to a high current inductive load. A driver is required. You can buy ICs designed for this task or you can make it using 4 power transistors. Note that some driver ICs contain more than the power transistors section and require different input signals than the coil pulses.

Transistor driver

To make the driver, I used some old N-channel FETs that I had around (2SK2382). Usually, N-FETs from power supplies are the best choice: they support high currents and they include protection diodes. Just make sure gate threshold is less than Arduino high levels (so that the transistors can be switched on). Bipolar transistors can be used as well.

Unipolar stepper driver schematic with transistors

Unipolar stepper driver schematic with transistors

You can power steppers from constant voltage supplies. But a constant current supply is a better choice especially when you want good performance and you don't know motor's voltage ratings. I got my motor from some old hardware and all I can find out about it is the coil resistance (it's printed on it). So, I decided to supply a maximum of 500 mA to the motor using a simple LM317 circuit. If you, like me, don't know the motor current or voltage, try with gradually increasing currents. The motor can get warm but it shouldn't be hot enough so you can't keep your hand on it. If this happens, lower the current.

The biggest challenge you'll face when trying to drive motors without documentation is to get the right pinout. First of all you need to identify the number of leads (wires) and the number of individual coils (with a multimeter/ohmmeter). Then, by trial and error you can find the right sequence to make the motor rotate. If you have a 4-wire motor, this is bipolar only and cannot be used with above driver.

Stepper motor connections to unipolar driver

Stepper motor connections to unipolar driver

Sometimes, you can get information about the coil connections by opening the motor case. In the photo below you can easily see the center taps of a NEMA17 6 wire stepper. Then with a multimeter I was able to confirm to which coil the remaining wires belong (A and A# belong to left center tap, while B and B# belong to right center tap).

6-Wire Stepper motor wire connections

6-Wire Stepper motor wire connections

The driver schematic above is complete. For most motors, the diodes can be fast rectifier types rated at a higher current than motor current. For the above application, with 0.5 A current, UF4004 or BA157 can be used. If you're using N-FETs with included protection diodes, there is no need for external diodes. Also, with FET transistors, the gate can be connected directly to digital Arduino pin, without 1k resistors. Those resistors are required if you use bipolar transistors instead of FETs. You can use NPN transistors. Darlington transistors may be required depending on motor current. Make sure the transistors you use can handle load current (drain-source current, collector-emitter current) and can be fully switched on by Arduino (gate threshold voltage, sufficient DC gain).

The resistor connected between OUT and ADJ pins of LM317 sets the constant current. For 2.4 ohms, the current is 1.25/2.4 = 0.5A. Use a suitable wattage resistor (2-3W). Replace the resistor with a proportional value for the motor rated current. The voltage at LM317 input cannot exceed 35V. Note that a big heatsink may be required for LM317. For testing purposes, you can build this driver on the breadboard.

Pulse sequences

The coils must be energized in a specific order: a half of one coil (A), then a half of the other (B). Pulses should then energize the remaining coil halves (A# half of the first coil, then B# half of the other coil). The order determines direction. Just remember: do not energize in a sequence one half of a coil, then the other half of the same coil (A followed by A#). The motor will make one step in a direction and one step backwards. It doesn't harm the motor though, but it will not rotate. A-B-A#-B# and A-B#-A#-B are valid sequences, but A-A#-B-B# is not (with regard to pin naming seen above). Reversed sequences preserve validity (B#-A#-B-A and B-A#-B#-A are good).

Because you can power 4 coils, there are actually 3 ways of doing it. You can use wave drive. Each of the 4 coils is powered sequentially so that only one coil is powered an any time. This results in less power consumption and less torque. Below is the pulse train.

Unipolar stepper wave drive

Unipolar stepper wave drive

This is a simple mode. If you increase the pulse width and keep it high for the first half of the other, you get the second driving mode, half drive. Half of the time, two adjacent coils are energized, resulting in higher torque. When both adjacent coils are on, the axis turns one half of the step, resulting in a smoother rotation and an apparent doubling of the number of steps per revolution.

Unipolar stepper half drive

Unipolar stepper half drive

But each two physically adjacent coils (such as A and B) can be kept on all the time. This is full drive mode, with the highest available torque.

Unipolar stepper full drive

Unipolar stepper full drive

Stepper library

The Stepper library is included by default in Arduino IDE. I decided to give it a try. I declared a Stepper object like this:

Stepper motor(STEPSPERREV, 8, 9, 10, 11);

where STEPSPERREV is 200 (for my motor). I expected to get one of the above pulse sequences. But the results were not quite the same.

Stepper library output

Stepper library output

Indeed, this is full drive mode, but the pulse pins are in a different order. First pair of pins (8 and 9) must go to the same coil (A and A#) and the second (10 and 11) goes to the other coil (B and B#). Keep this in mind when declaring pins.

Easy code

Arduino comes with libraries for almost everything. In fact, driving a stepper motor is not that hard without a library. And, as you can see the library only supports full drive mode.

I decided to write directly to the port. This has some advantages like high speed (not needed here) and simultaneous setting of multiple pins. But the code is harder to debug in case of errors and the motor driving pins are hardwired. ATmega328 on Arduino platform has PORTD available and wired to digital pins D0 to D7 and PORTB wired to D8 to D13. By simply assigning an unsigned byte value to PORTB or PORTD, the bits of that byte are present as voltage levels on specific pins. The problem is that D0 and D1 are serial port pins and you are not allowed to change their data direction and state. The same for PORTB highest two bits which are crystal pins.

First of all, let's set 4 bits of the port (pinMode()) in a single line of code.

DDRB |= 0x0F;

Data direction register for PORT B is read and OR'ed with 0x0F. This ensures the lower 4 bits are set as output, which translates to Arduino pins D8-D11. Let's generate the pulse train for wave drive.

const int wavePulse[4] = { B0001, B0010, B0100, B1000 };

void waveDrive() {
  byte port = 0;

  for (int i = 0; i < 4; i++) {
    port = PORTB & 0xF0; // keep bits 4-7
    port |= wavePulse[i];
    PORTB = port;
    delay(5);
  }
}

The above function causes the motor to make four steps, with a delay of 5 milliseconds between each step. Half drive is a bit different. The number of steps gets doubled.

const int halfPulse[8] = { B0001, B0011, B0010, B0110, B0100, B1100, B1000, B1001 };

void halfDrive() {
  byte port = 0;

  for (int i = 0; i < 8; i++) {
    port = PORTB & 0xF0; // keep bits 4-7
    port |= halfPulse[i];
    PORTB = port;
    delay(5);
  }
}

Although the above function causes the motor to make 8 smaller steps, the degrees per 8 half drive steps are the same as per 4 wave/full drive steps. At last, full drive resembles wave drive.

const int fullPulse[4] = { B0011, B0110, B1100, B1001 };

void fullDrive() {
  byte port = 0;

  for (int i = 0; i < 4; i++) {
    port = PORTB & 0xF0; // keep bits 4-7
    port |= fullPulse[i];
    PORTB = port;
    delay(5);
  }
}

Each of the above functions read and store port first. Only the lower 4 bits, which correspond to motor wiring pins, are changed. The functions are straightforward and easy to implement. However, they only work in 4 steps multiples and as they are do not allow changing rotation direction and speed. I will attempt to improve them in a future post.

No comments :

Post a Comment

Please read the comments policy before publishing your comment.