Drive stepper motors with Arduino (code)

 Author:   Posted on:   Updated on:  2019-07-13T19:11:35Z

Arduino compatible C code for driving unipolar stepper motors. Functions for wave, half and full drive.

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, being made of multiple coils that are energized in regular sequences by trains of digital pulses. 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 (they have at least 5 wires). On the other hand, bipolar stepper motors have two coils that are powered one after another then voltage polarity is reversed and coils are powered again (this type of motors have only 4 wires).

A simple transistor driver and connections (only for unipolar steppers) and driving methods were discussed in the previous post: Driver and Arduino code for unipolar stepper motors. Some basic Arduino functions were provided there. But those functions are too basic for most usage scenarios. The motor can be driven only in with multiples of 4 or 8 steps and you can't change rotation direction. In this post, I will explain further the driving methods and I will generate driving pulses programmatically, with the ability to move one step at a time and change the rotation direction.

Drive stepper motors with Arduino (code)

As you learned from the previous post, there are three methods for driving stepper motors: wave drive, half drive and full drive. In wave drive only one coil is energized at a time while in full drive two coils are energized at any time. Half drive is something between wave and full drive.

As opposed to unipolar motors, bipolar ones have only two (electrical) coils. A and A# are wires of the same coil; same for B and B#. However, all the driving modes can be applied for them too. That's because a coil can be non-energized, energized with a polarity and energized with opposite polarity. At specific moments, when both A and A# (or both B and B#) pulses are low, it means the coil is not energized. When both pulses for A and A# are high, it would mean the same (zero volts across coil), but that never happens. We'll see more about bipolar motors in a future post.

Before writing functions for each driving mode, let's define pulse sequences and implement a function that will write the 4-bit pulse data to the port. I went for direct port writing in the previous post and I'm still using this method. This means the pins on which pulses are generated are not easy to change. With an ATmega328 Arduino board, you may use PORTB and PORTD. I chose bits 3:0 of port B, which translate to digital pins 8-11. Changing the pins requires modification of the next function and of the data direction register setting for the port.

const uint8_t wavePulse[4] = { B00000001, B00000010, B00000100, B00001000 };
const uint8_t halfPulse[8] = { B00000001, B00000011, B00000010, B00000110,
                               B00000100, B00001100, B00001000, B00001001
                             };
const uint8_t fullPulse[4] = { B00000011, B00000110, B00001100, B00001001 };

uint8_t stepIndex = 0;

void pulseWrite(const uint8_t currentPulse) {
  uint8_t port = PORTB;
  port &= 0xF0; // clear bits 3:0, keep bits 7:4
  port |= currentPulse;
  PORTB = port;
}

There is also a step index variable that is needed by next functions. If we stop the motor after a number of steps that's not multiple of 4, we need to know where we stopped when we'll generate pulses again.

Wave drive

Only one coil is energized at any time when the motor is driven in wave mode.

Wave drive pulses (animation)

Wave drive pulses

A function that generates wave drive pulses could look like this:

void waveDrive(uint16_t numSteps, uint16_t stepDelayMs = 5, bool backwards = false) {
  uint8_t sequence = 0;
  numSteps += stepIndex;
  for (uint16_t i = stepIndex; i < numSteps; i++) {
    backwards ? sequence = wavePulse[3 - (i & 3)] : sequence = wavePulse[i & 3];
    pulseWrite(sequence);
    delay(stepDelayMs);
  }
  stepIndex = numSteps & 3;
}

Half drive

Half of the time, one coil is energized. The other half, two coils are energized.

Half drive pulses (animation)

Half drive pulses

A function that generates half drive pulses could look like this:

void halfDrive(uint16_t numSteps, uint16_t stepDelayMs = 5, bool backwards = false) {
  uint8_t sequence = 0;
  numSteps += numSteps + stepIndex * 2;
  for (uint16_t i = (stepIndex * 2); i < numSteps; i++) {
    backwards ? sequence = halfPulse[7 - (i & 7)] : sequence = halfPulse[i & 7];
    pulseWrite(sequence);
    delay(stepDelayMs);
  }
  stepIndex = (numSteps / 2) & 3;
}

Full drive

At any time, two coils are energized in full drive mode.

Full drive pulses (animation)

Full drive pulses

A function that generates full drive pulses could look like this:

void fullDrive(uint16_t numSteps, uint16_t stepDelayMs = 5, bool backwards = false) {
  uint8_t sequence = 0;
  numSteps += stepIndex;
  for (uint16_t i = stepIndex; i < numSteps; i++) {
    backwards ? sequence = fullPulse[3 - (i & 3)] : sequence = fullPulse[i & 3];
    pulseWrite(sequence);
    delay(stepDelayMs);
  }
  stepIndex = numSteps & 3;
}

Conclusion

I don't think a library is needed for driving stepper motors from Arduino. After all, it's not that hard. The three functions presented above can drive both unipolar and bipolar motors, step by step and can change rotation direction. Parameter numSteps represents the number of steps (i.e. for a 1.8 degree/step motor, set numSteps to 200 to make a full revolution), stepDelayMs is the delay between pulses in milliseconds and backwards changes rotation direction. For the same value of stepDelayMs, a motor driven in half mode will rotate twice as slow than in the other modes. The entire sketch with the above functions can be on GitHub.

No comments :

Post a Comment

Please read the comments policy before publishing your comment.