controll up to 12 stepper motors

hi guys,

I developed a hardware and a software for arduino which is capable of controlling the movement of up to 12 stepper motors (or even more if one doesn't care about speed).

I would like to share it with the community. How would I do that?

regards b00tsy

Put the pictures, details and code here. Us ethe buttons above when entering text

inserts code

The 3rd button along inserts an image, etc.

the idea behind it is the following:

have one stepper-driver-board, where 4 (bipolar) steppers can be attached. it can be connected to the arduino board and it is possible to have several of those stepper-driver-boards attached serially.

Therefore I used for each board 4 L293D and 2 74HCT595:

I wrote as well a SW-library for it:

header file:

 /*
  MasterVerwalter.h - Hardware Stepper Library
  Copyright (c) 2008 Jan Winter jwinter@gmx.net.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/
#define MAX_NUMBER_OF_PANELS 12
#define MAX_NUMBER_OF_MOTORS 12

//movement types of the motor:
#define INVERSE 0
#define WAIT      1
#define ONCE      2
#define ENDLESS      3
#define POSITION 4

#define RECHTS 1
#define LINKS -1

#ifndef PanelArray_h
#define PanelArray_h

#include "WConstants.h"

class Motor
{
public:
      Motor(void);                                    //constructor
      int step(int);                                    //gets the next step position of this motor
      int release(void);                              //releases the motor
      void SetStepsToMove(int, int, int, int);            //
      int getSpeed(void);
      int getStepsFinished(void);
private:
      /**
            @brief the actual index of the movement vector
            */
      int _StepIndex;                  
      /**
            @brief number of steps to step
            */
      int _NumberOfStepsToMove;      
      /**
            @brief number of stepped steps
       */
      int _NumberOfStepsMoved;                  
      /**
            @brief whether the movement is inverted or not after reaching the step limit
       */
      int _Inversion;                                    
      /**
            @brief Movement mode
            @param INVERSE, WAIT, ONCE, ENDLESS
       */
      int _MovementMode;                              
      /**
            @brief Speed
            @param 1 = max, 2 = half, 3 = third
       */
      int _Speed;                                          
      /**
            @brief Direction
            @param left, right
       */
      int _Direction;                                    
};

class MasterVerwalter
{
public:
      MasterVerwalter(unsigned short, unsigned short, unsigned short, unsigned short);                              //constructor
      int clock(int);                              //a step will be applied by each clock cycle
      
      void init(int, int, int, int, int);            //initialize each motor
      
private:
      void writeBits(unsigned short);            //writes the bits to the latches
      void latchData(void);                        //gives out the data
      
      int _MotorStates[MAX_NUMBER_OF_MOTORS];                        //keeps track of the current motor state
      Motor motor[MAX_NUMBER_OF_MOTORS];                              //motor classes
      
      unsigned short _NumberOfMotors;
      unsigned short _PinData;
      unsigned short _PinShift;
      unsigned short _PinStore;
      
      unsigned short _ClockCounter;
};

#endif

cpp-file:

have to post it later, didn’t fit in this post

one could use it with arduino in such a way:

//MasterVerwalter Objektname(int PinData, int PinShift, int PinStore, int NumberOfMotors)
MasterVerwalter master(PINDATA,PINSHIFT,PINSTORE,4);

//Objektname.init(int MotorNumber, int StepsToMove, int MovementMode, int Speed, int Direction)
  master.init(1,100,ONCE,4,LINKS);
    for(int i = 0; i < 100; i++){
      master.clock(1);
    }

in the constructor one tells, which pins have been used for connecting the board to the arduino,

the init method tells which motor will be used in what kind of a manner (for example here: motor 1, 100 steps, once, every fourth cycle, to the left)

.clock() moves the motors…

one can choose between several movement methods:

more later have to go

how can I upload images?

To post an image you'll need to upload it to somewhere like Flicker first, then click the 3r4d button above (when you type into teh text box) and type the URL of the Photo on Flicker (or whatever photo service you use) inbetween the img commands.

here is the hardware schematic:

http://www.stud.tu-ilmenau.de/~jawi-mt/pics/schematic_stepper_array_small.png

and an image of the etched board:

http://www.stud.tu-ilmenau.de/~jawi-mt/pics/SDIM0154.JPG

here is the cpp file:

 /*
  MasterVerwalter.cpp - Hardware Stepper Library
  Copyright (c) 2008 Jan Winter jwinter@gmx.net.  All right reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

//#define debug      //for text output

#include "WProgram.h"
#include "MasterVerwalter.h"
#include <avr/io.h>

//assigned pins of the L293D at the74HC595
#define MOTOR_1A 0
#define MOTOR_1B 1
#define MOTOR_2A 2
#define MOTOR_2B 3

#define CLOCKCYCLE 16000000


//should be assigned as variables per init o s.th. in the future...
#define MAX_STEPS 2            
#define VELOCITY_DELAY 2
#define MOVEMENT_DELAY 0

//Fullstep movement pattern of one stepper motor
int M[4] = {
      (_BV(MOTOR_1A) | _BV(MOTOR_2A)),
      (_BV(MOTOR_1B) | _BV(MOTOR_2A)),
      (_BV(MOTOR_1B) | _BV(MOTOR_2B)),
      (_BV(MOTOR_1A) | _BV(MOTOR_2B))
};


//###############################
//      Motor Class
//###############################
//constructor
Motor::Motor(void) {
      this->_StepIndex = 0;
      this->_NumberOfStepsMoved = 0;
      this->_NumberOfStepsToMove = 1;
      this->_Inversion = 0;
      this->_MovementMode = ENDLESS;
      this->_Speed = 1;
}

//stepper function: saves the actual stepper state and returns ist
//      direction < 0 : anti-clockwise rotation
//      direction > 0 : clockwise rotation
//      direction = 0 : no rotation
int Motor::step(int direction) {
      switch(this->_MovementMode){
            case INVERSE:
                  if(this->_NumberOfStepsMoved  > this->_NumberOfStepsToMove){
                        this->_NumberOfStepsMoved = 0;
                        this->_Inversion = ~(this->_Inversion);
                  }
                  if(this->_Inversion){
                        direction = - direction;
                  }
                  break;
            case WAIT:
                  if(this->_NumberOfStepsMoved  > this->_NumberOfStepsToMove){
                        this->_NumberOfStepsMoved = 0;
                        this->_Inversion = ~(this->_Inversion);
                  }
                  if(this->_Inversion){
                        this->_NumberOfStepsMoved++;
                        direction = 0;
                        this->release();
                        return -1;
                  }
                  break;
            case ONCE:
                  direction = this->_Direction;
                  if(this->_NumberOfStepsMoved  > this->_NumberOfStepsToMove){
                        direction = 0;
                  }
                  break;
            case ENDLESS:
                  break;
            case POSITION:
                  int Difference = this->_NumberOfStepsMoved - this->_NumberOfStepsToMove;
                  if(Difference > 0){
                        direction = -1;
                        this->_NumberOfStepsMoved--;
                  }
                  else if(Difference < 0){
                        direction = 1;
                        this->_NumberOfStepsMoved++;
                  }
                  else {
                        direction = 0;
                  }
      }
                  
      //ONCE gewegt sich nach Überlauf wieder!
      if(direction > 0) {
            this->_StepIndex++;
            this->_NumberOfStepsMoved++;
      }
      else if(direction < 0){
            this->_StepIndex--;
            this->_NumberOfStepsMoved++;
      }
      else {
            return _StepIndex;
      }
      if(_StepIndex > 3) {
            this->_StepIndex = 0;
      }
      else if (_StepIndex < 0) {
            this->_StepIndex = 3;
      }
      return _StepIndex;
}

int Motor::release(void) {
      this->_StepIndex = 0;
      return -1;
}

void Motor::SetStepsToMove(int NumberOfStepsToMove, int MovementMode, int Speed, int Direction){
      this->_NumberOfStepsToMove = NumberOfStepsToMove;
      if(MovementMode != POSITION){
            this->_NumberOfStepsMoved = 0;
      }
      this->_MovementMode = MovementMode;
      this->_Speed = Speed;
      this->_Direction = Direction;
}

int Motor::getSpeed(void){
      return this->_Speed;
}

int Motor::getStepsFinished(void){
      if((this->_NumberOfStepsMoved - this->_NumberOfStepsToMove) > 0){
            return 1;
      }
      else {
            return 0;
      }

}

//###############################
//      MasterVerwalter Class
//###############################

//constructor
MasterVerwalter::MasterVerwalter(unsigned short PinData, unsigned short PinShift, unsigned short PinStore,
                                                 unsigned short NumberOfMotors){
      this->_PinData = PinData;
      this->_PinShift = PinShift;
      this->_PinStore = PinStore;      
      pinMode(PinData, OUTPUT);
      pinMode(PinShift, OUTPUT);
      pinMode(PinStore, OUTPUT);
      this->_NumberOfMotors = NumberOfMotors;            //muss bisher in 2er Kombos auftreten
      this->_ClockCounter = 0;
      
}

void MasterVerwalter::init(int MotorNumber, int StepsToMove, int MovementMode, int Speed, int Direction){
      if((MotorNumber > MAX_NUMBER_OF_MOTORS) || (MotorNumber < 1)){
            return;
      }
      this->motor[MotorNumber - 1].SetStepsToMove(StepsToMove, MovementMode, Speed, Direction);
      //Serial.println("new init");
}

int MasterVerwalter::clock(int direction){
      //in switch umwandeln: 1, 0, -1, sowie release
      short isFinished = 1;
      //isFinished funktoiniert noch nicht ganz (1 motor 2 motoren...)
      for(int i = (this->_NumberOfMotors - 1); i >= 0; i--){
            if(direction > 0){
                  if(this->_ClockCounter % (motor[i].getSpeed()) == 0){
                        this->_MotorStates[0] = motor[i].step(1);
                        isFinished = isFinished & motor[i].getStepsFinished();
                  }
                  else {
                        this->_MotorStates[0] = motor[i].step(0);
                  }

                  if(this->_ClockCounter % (motor[i - 1].getSpeed()) == 0){
                        this->_MotorStates[1] = motor[i - 1].step(1);
                        isFinished = isFinished & motor[i].getStepsFinished();
                  }
                  else {
                        this->_MotorStates[1] = motor[i - 1].step(0);
                  }

            }
            else if(direction < 0){
                  if(this->_ClockCounter % (motor[i].getSpeed()) == 0){
                        this->_MotorStates[0] = motor[i].step(-1);
                        isFinished = isFinished & motor[i].getStepsFinished();
                  }
                  else {
                        this->_MotorStates[0] = motor[i].step(0);
                  }
                  
                  if(this->_ClockCounter % (motor[i - 1].getSpeed()) == 0){
                        this->_MotorStates[1] = motor[i - 1].step(-1);
                        isFinished = isFinished & motor[i].getStepsFinished();
                  }
                  else {
                        this->_MotorStates[1] = motor[i - 1].step(0);
                  }                              
            }
            else{
                  this->_MotorStates[0] = motor[i].release();      
                  this->_MotorStates[1] = motor[i - 1].release();      
            }
            unsigned short State1;
            unsigned short State2;
            if(this->_MotorStates[0] == -1){
                  State2 = 0;
            }
            else {
                  State2 = M[this->_MotorStates[0]];
            }
            if(this->_MotorStates[1] == -1){
                  State1 = 0;
            }
            else {
                  State1 = M[this->_MotorStates[1]];
            }            
            this->writeBits(State1 | (State2 << 4));
            i--;
      }

      this->latchData();
      (this->_ClockCounter)++;
      delay(2);
      return (isFinished);
}



void MasterVerwalter::latchData(void){
      digitalWrite(_PinStore,LOW);
      digitalWrite(_PinStore,HIGH);
      digitalWrite(_PinStore,LOW);
}

//writes the data to the latches
void MasterVerwalter::writeBits(unsigned short dataValue){
      //8 bits ausgeben
      unsigned short bitValue = 0;
      
      for(int k = 0; k < 8; k++)
      {
        //generate output, MSB first 
        bitValue = ((dataValue) & (1 << (7 - k)));     
        if(bitValue > 0)
        {
                  digitalWrite(_PinData,HIGH);
        }   
        else
        {
                  digitalWrite(_PinData,LOW);
        }
            digitalWrite(_PinShift,LOW);
        digitalWrite(_PinShift,HIGH);
        digitalWrite(_PinShift,LOW);            
      }
}

there are several movement modes:

INVERSE: each motor will do the programmed movement and will inverse it's direction after reaching the desired number of steps

ONCE: each motor will do the programmed movement once, and will stop until he get's new instructions

WAIT: each motor will do the programmed movement, and will wait the programmed number of steps and do the movement again

POSTITION and ENDLESS are not implemented yet.

i would say, the software is kind of version 0.6... couldn't do to many tests till now, the project was on hold for the last 6 months...

the etched board works with 3 of the 4 possible stepper connections, have spent hours of debugging and soldering point checking and wire pinging, but haven't found out whether it is a design error or soldering/etching/hardware error.

Have never tried out more than 1 board in a row, cause I only etched 1 board till now...

so if you have any suggestions for improvement please let me know, maybe other people might be interested in this as well.

it might be possible to create something like this:

http://www.youtube.com/watch?v=HVhVClFMg6Y

(of course in a much smaller realisation)

Looks cool. One of those boards made into a proper PCB would look nice. This would be very useful for robotics projects or reprap stuff.

thanks,

I got inspired by http://www.ladyada.net/make/mshield/ for designing the board, but I don't know whether the dimensions of the blocking capacitors are chosen right. Does anyone know something about choosing the right blocking capacitors?

What do you mean with a proper PCB? A more sophisticated, smaller design? At the moment it is half the size of a euro board.