Improved Arduino Code for Unipolar Stepper Motors

 Author:   Posted on:   Updated on:  2017-12-09T08:57:06Z
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 (5 wires).

A simple transistor driver, motor connections and driving methods were discussed in the previous post: Unipolar Stepper Motors: Arduino Code and Driver. Some basic C functions were provided there. But, those functions are too basic for most usage scenarios. The motor can be driven only in 4-step increments 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 in 1-step increments and change the rotation direction.
As you learned from the previous post, there are three methods for driving unipolar 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.

Wave drive


The above video shows a simplified unipolar stepper motor driven in wave mode. Generating those pulses can be done by shifting a bit to the left or to the right for each next step. Refer to the function in the previous post. Here's what we write to the port and how it can be generated in different ways.
Unipolar Stepper Motor Wave Drive Maths
Basically we shift bit 1 to the left with the remainder of step number divided by 4. We must store the last remainder and make sure we start at it in case of a future call. Here is the function:
uint8_t remainderSteps = 0;

void waveDrive(unsigned int numSteps, unsigned int stepDelay = 5, boolean backwards = false) {
  numSteps += remainderSteps;
  for (unsigned int i = remainderSteps; i < numSteps; i++) {
    backwards ? PORTB = 8 >> (i & 3) : PORTB = 1 << (i & 3);
    delay(stepDelay);
  }
  remainderSteps = numSteps & 3;
}
One minor disadvantage of these functions is that the output pins are hardcoded. Writing directly to the port instead of using digitalWrite improves code efficiency and speed.

Half drive

This one was the hardest to implement in code. Let's see the video first.
This driving method doubles the number of steps per revolution. Therefore, to maintain consistency, the function below works in 2-step increments.
Unipolar Stepper Motor Half Drive Maths
The function doubles the given steps number. Then for the first half of a step it performs a simple shift like the wave function. For the other half of the step it adds two succesive steps like you will see in the full drive function.
uint8_t remainderSteps = 0;

void halfDrive(unsigned int numSteps, unsigned int stepDelay = 5, boolean backwards = false) {
  numSteps += numSteps + remainderSteps * 2;
  for (unsigned int i = (remainderSteps * 2); i < numSteps; i++) {
    unsigned int tmp = i / 2;
    if (i & 1) {
      backwards ? PORTB = 8 >> (tmp & 3) | 8 >> ((tmp + 1) & 3) : PORTB = 1 << (tmp & 3) | 1 << ((tmp + 1) & 3);
      delay(stepDelay);
    }
    else {
      backwards ? PORTB = 8 >> (tmp & 3) : PORTB = 1 << (tmp & 3);
      delay(stepDelay);
    }
  }
  remainderSteps = (numSteps / 2) & 3;
}
The variable tmp is the real step number in this function and it is used as a reference to compute shifts.

Full drive

Two coils must be energized at the same time.

The function performs a normal shift just like the wave drive one. Then it adds to it the next shift.
Unipolar Stepper Motor Full Drive Maths
Here is the function that generates these pulses:
uint8_t remainderSteps = 0;

void fullDrive(unsigned int numSteps, unsigned int stepDelay = 5, boolean backwards = false) {
  numSteps += remainderSteps;
  for (unsigned int i = remainderSteps; i < numSteps; i++) {
    backwards ? PORTB = 8 >> (i & 3) | 8 >> ((i + 1) & 3) : PORTB = 1 << (i & 3) | 1 << ((i + 1) & 3);
    delay(stepDelay);
  }
  remainderSteps = 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 unipolar 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), stepDelay is the delay between pulses in milliseconds and backwards changes rotation direction. For the same value of stepDelay, a motor driven in half mode will rotate twice as slow than in the other modes.

Data output to port is quite inflexible. If you can't afford to change other port pins (the port is 8 bit while you are writing a 4 bit value to it), read it first, then adapt the output value with what has been read. For example PORTB = (PORTB & 0xF0) stores 4 bits of the port. This values should be OR'ed with the motor bits and written to the port. Like this: PORTB = (PORTB & 0xF0) | (1 << (i & 3)).

Another inconvenience of these functions is the behavior when changing direction. The first step after a change of backwards parameter will be in the same position as the previous step made by the motor (no movement).

No comments :

Post a Comment

Please read the comments policy before posting.