What I like most about .NET Micro Framework is the interfacing :) Today it will be blog post about interfacing and controlling of bi-polar stepper motor. I was about to do it some time ago, and when question on stepper was raised in the discussion group I’ve decided to pick the gauntlet. As you will see driving stepper motor is truly piece of cake.

How does stepper motor works

I don’t want to run into much details on stepper motor. There are better place to learn about stepper motors and it’s principles. One can start as usual on Wikipedia and go to more specialized places.

Most important information is that there are two types of stepper motors: Unipolar and Bipolar. The difference is in the construction as well as in the controlling sequence. Bi-polar motors has simpler construction and becoming more common and popular. Unlike the normal motors, steppers has stator with “independently” controlled coils. As those coils are energized in specific sequence the magnetic shaft (rotor) tends to align itself according to pole of the coils. This makes shaft to rotate.

Bi-polar motors are controlled by four steps sequence, where polarity of coils is inverted. Table bellow shows the sequence steps. Applying steps in reverse order will make motor to rotate opposite direction. Every motor has different resolution for one step, most common value is 1.8 degrees per step. This resolution has to be kept in mind while controlling the motor.

# A+ A- B+ B-
step 1 1 0 0 0
step 2 0 0 1 0
step 3 0 1 0 0
step 4 0 0 0 1

Interfacing the motor

Bi-polar motors are connected using four wires, two per each coil. To find out corresponding wires it’s good to measure resistivity between two wires. Two independent wires should return “zero” resistance. Once the corresponding wires for each coil is found, it can be connected to GPIO of the microcontroller. When I say connected I don’t mean directly connected. It’s obvious that GPIO output voltage can’t drive the stator coils and flowing current will destroy the CPU.

It’s necessary to use buffer, transistor or other solution to isolate control voltage from the power voltage. Very common circuit for this purpose is the L293D. I was blogging about it already in my robotics articles, so we can skip the details. L293D is the dual H-Bridge so it can drive four independent “high” current outputs. L293D has ability to drive output current of 600mA for each channel and maximum power voltage of +36V.

L293D and Stepper Motor 450x370 Figure 1. L293D and bipolar stepper motor

Controlling the motor

I believe you’ve already see, that to control stepper motor is not a big challenge for .NET Micro Framework. I’ve implemented simple BipolarStepper class, which takes two arguments. First is the array of pins where motor is connected and second one is resolution. Two public methods MoveAngle(float) and MoveSteps(int) rotates motor for appropriate distance.

MoveSteps method is “a bit more” complicated because it has to take care of direction change. To control the speed of the movement the Delay property is used. It’s nothing else than the delay between each steps. Usage of the class is pretty straight forward.

BipolarStepper motor = new BipolarStepper(new Cpu.Pin[] { 
                            Meridian.Pins.GPIO1,        // coil A+
                            Meridian.Pins.GPIO2,        // coil A-
                            Meridian.Pins.GPIO3,        // coil B+
                            Meridian.Pins.GPIO4},       // coil B-
                            1.8F);                      // 1.8 deg per step

motor.MoveAngle(-45);
motor.MoveAngle(45);
public class BipolarStepper : IDisposable
{
    /// <summary>
    /// Instance of BipolarStepper class
    /// </summary>
    /// <param name="portList">Array of Cpu.Pin ports: A+ A- B+ B-</param>
    /// <param name="resolution">Degrees per one step</param>
    public BipolarStepper(Cpu.Pin[] portList, float resolution)
    {
        // Argument validation
        if (portList == null || portList.Length < OUTPUT_COUNT || resolution <= 0)
            throw new ArgumentOutOfRangeException();

        // Create array of OutputPorts 
        this._portList = new OutputPort[OUTPUT_COUNT];
        for (int i = 0; i < OUTPUT_COUNT; i++)
        {
            this._portList[i] = new OutputPort(portList[i], false);
        }

        this.Resolution = resolution;            
        this.Delay = 3;
    }

    /// <summary>
    /// Move motor for given degrees
    /// </summary>
    /// <param name="degrees">Angle in degrees</param>
    public void MoveAngle(float degrees)
    {
        MoveSteps((int)(degrees / Resolution));
    }

    /// <summary>
    /// Move motor for given number of steps
    /// </summary>
    /// <param name="count">Number of steps</param>
    public void MoveSteps(int count)
    {
        int direction = (count < 0) ? -1 : 1;
        int stepCount = System.Math.Abs(count);

        if (direction != _lastDirection)
            AddStep(-2 * direction);

        for (int i = 0; i < stepCount; i++)
        {
            Output(_steps[_lastStep]);
            AddStep(direction);

            Thread.Sleep(this.Delay);
        }

        _lastDirection = direction;
    }

    /// <summary>
    /// Change lastStep value
    /// </summary>
    private void AddStep(int count)
    {
        _lastStep = _lastStep + count;

        if (_lastStep >= OUTPUT_COUNT)
            _lastStep = 0;
        else if (_lastStep < 0)
            _lastStep = OUTPUT_COUNT - 1;
    }

    /// <summary>
    /// Writes a value to the port output
    /// </summary>
    /// <param name="outputValue">Number to write out</param>
    private void Output(byte outputValue)
    {
        int portListLength = OUTPUT_COUNT;
        for (int i = 0; i < portListLength; i++)
        {
            this._portList[i].Write(((outputValue & 1) == 1));
            outputValue >>= 1;
        }
    }

    public void Dispose()
    {
        // Dispose array members        
        int portListLength = this._portList.Length;

        for (int i = 0; i < portListLength; i++)
        {
            this._portList[i].Dispose();
        }
    }

    /// <summary>
    /// Degrees per one step
    /// </summary>
    public float Resolution;

    /// <summary>
    /// Delay between steps
    /// </summary>
    public int Delay;

    private OutputPort[] _portList;
    private byte[] _steps = new byte[] { 1, 4, 2, 8 };
    int _lastStep, _lastDirection;
    const int OUTPUT_COUNT = 4;
}