0

I am new to Arduino and programming and I am muddling along with some tutorials, I have a little project I am working on. It's basically an LCD screen with 5 buttons, a simple menu system that will control some stepper motors.

Now I have been able to both program and run the LCD from the Arduino and program and run the stepper motors. This was pretty straightforward due to the abundance of information. Now due to the small number of inputs on the Uno I knew I would need to expand on these so I bought some MCP23017 I/O expander chips.

Setting up the LCD on the MCP23017 was easy enough again due to the lots of tutorials and libraries, but I just cannot figure out how to work a stepper motor via the MCP23017. I am using 28BYJ-48 stepper motors and ULN2003 stepper driver boards. Yes I know cheap and nasty but they are fine for my task.

So I am basically looking for a little help on how to run stepper motors through the MCP23017 chips. I have the wiring and connections set up, just need some help on the programming.

2

I couldn't understand what the problem is, since you already managed to connect an LCD to it... Anyway...

To run a stepper motor you have to first define the control technique. AFAIK there are a few, but the ones I know are

  • full step
  • rotated full step
  • half step
  • microstep

Your motor is a unipolar motor. You have to connect the common wire to +5V, the other ones to one transistor of the ULN2003, and each transistor should be directly controlled by one pin. These are called phases (and let's call these PH1, PH2, PH3 and PH4).

Now for the explanation I'll use some tables. In these tables, each row is a step, each column a pin, and if there is a X the transistor should be on during that step.

Now, the full step technique is the simplest one. It consists of four phases:

Step PH1 PH2 PH3 PH4
  1   X
  2       X
  3           X
  4               X

As you can see, you just have to power each phase through each step. Cycling the steps as 1-2-3-4-1-2-3... will rotate in one sense, 4-3-2-1-4-3-2... in the other.

The rotated full step technique powers two phases at each step:

Step PH1 PH2 PH3 PH4
  1   X   X
  2       X   X
  3           X   X
  4   X           X

This usually gives a higher current consumption, but also higher torque.

The half step is a mix of the two:

Step PH1 PH2 PH3 PH4
  1   X
  2   X   X
  3       X
  4       X   X
  5           X
  6           X   X
  7               X
  8   X           X

As you can see, the number of steps per cycle is twice as much. This means that you can increase the accuracy (since a 32 steps/rev motor will now require 64 steps to perform a complete revolution).

The microstepping technique requires a different hardware, since it better controls the current in the phases to further divide each step in different steps.

Anyway, a basic implementation using the MCP23017 and the full step technique can be this one. I used Adafruit's library, but you can change using whatever library you chose or the direct calls you used for the LCD.

#include <Wire.h>
#include "Adafruit_MCP23017.h"
Adafruit_MCP23017 mcp1;

const byte PH1 = 0, PH2 = 1, PH2 = 3, PH3 = 4;

const byte phasesTable[][] = {
    { HIGH, LOW,  LOW,  LOW  },
    { LOW,  HIGH, LOW,  LOW  },
    { LOW,  LOW,  HIGH, LOW  },
    { LOW,  LOW,  LOW,  HIGH }
    };

int currentPosition, targetPosition;
unsigned long previousMillis;
const int StepDelayMs = 100; // delay between steps

void setup()
{
    mcp1.begin(0);
    mcp1.pinMode(PH1, OUTPUT);
    mcp1.pinMode(PH2, OUTPUT);
    mcp1.pinMode(PH3, OUTPUT);
    mcp1.pinMode(PH4, OUTPUT);
    currentPosition = -1; // force first update
    targetPosition = 0;
}

void loop()
{
    void updatePosition(); // function prototype
    updatePosition(); // execute the update

    // ----------------------
    // Functional part
    // ----------------------
    // Edit this code according to your application
    // changing the targetPosition variable (DON'T CHANGE
    // the currentPosition variable

    if (currentPosition == targetPosition)
    {
        if (targetPosition == 50)
            targetPosition = 0;
        else
            targetPosition = 50;
    }
}

void updatePosition()
{
    if ((millis() - previousMillis) > StepDelayMs)
    {
        previousMillis += StepDelayMs;

        if (targetPosition != currentPosition)
        {
            if (targetPosition < currentPosition)
                currentPosition--;
            else
                currentPosition++;

            byte phIdx = currentPosition % (sizeof(phasesTable) / sizeof(phasesTable[0]));

            mcp1.digitalWrite(PH1, phasesTable[phIdx][0]);
            mcp1.digitalWrite(PH2, phasesTable[phIdx][1]);
            mcp1.digitalWrite(PH3, phasesTable[phIdx][2]);
            mcp1.digitalWrite(PH4, phasesTable[phIdx][3]);
        }
    }
}

NOTE: I did not test this code, but I'm pretty confident it should work. Maybe there are some bugs left, however, but you should be able to correct them yourself.

The program behaves this way: every 100ms (you can go a bit faster, but sometimes you can lose steps, so you have to test it) it checks if the motor has reached the final position he needs to reach (targetPosition). If it has, the program does nothing. Otherwise it moves one step towards the target. All this is managed in the updatePosition function.

You have to call this in the loop (and make a NON BLOCKING loop, otherwise also the motor will block). In the loop, modify the target position that you need to reach (the part I called Functional part). In the example, the program moves the motor 50 steps back and forth (so 50 steps in one direction, then back to the origin, then to 50 and so on).

The control technique used is the full step. If you want to use another, change the phasesTable constant variable.

If you need more functionalities, there are a lot of libraries for the steppers. Just use the proper commands to edit the pins on the MCP rather than the ones on the board.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.