It spins now.

For anyone wanting to experiment with driving a stepper motor using an Arduino, you might want to try modifying the sketch below.

You’ll probably need to modify the bit masks in directions() and enable() to get it running on any other model of Arduino than the Duemilanove (which uses an Atmega328P).

/*
 * Open-ended stepper driver for David Cuartielles' Motor Shield v3.0
 * (http://blushingboy.net/p/motorShieldV3/)
 *
 * This enables the board to drive a single two-phase, four-wire stepper.
 * Make sure to match the bit masks in directions() and enable() with the
 * actual IC-to-digital line mapping for your particular board.
 *
 * (c) 2012 Antonio VA Hilario ([email protected])
 * Creative Commons CC-BY-SA License
 * See https://creativecommons.org/
 * 
 * Coil energization sequence for SN754410 follows the guide published at 
 * http://mechatronics.mech.northwestern.edu/design_ref/actuators/stepper_drive1.html
 * 
 */

#define BTN_DN 6
#define BTN_UP 7

// PWM-capable, PORTB[2:SS,AtMEGA328p 16]
#define ENA_A 10
// PWM-capable PORTB[3:MOSI,AtMEGA328p 17]
#define ENA_B 11

// PORTB[bit4:MISO,AtMEGA328p 18]
#define DIR_A 12
// PORTB[bit5:SCK,AtMEGA328p 19]
#define DIR_B 13

// 'N'orth and 'S'outh. Because of the way David's motor shield wires up the ENABLE lines
// on the SN 754410 / L293D, current flows in one of two directions depending on the state of
// the bits set in directions(), below. 
#define N HIGH
#define S LOW

//
// About the L293D / SN754410 chip:
//
// This chip has some back-EMF protection, 
// and hence can drive a stepper directly at 
// low speed. However, the TI application note SLRS007B
// (2005) indicates need for external back-EMF shunt diodes
// on outputs Y.
//
// Inputs      Output
// A    EN     Y
// DIR  ENA    motorpair
// 1    1      1
// 0    1      0
// X    0      Z
// Because of this, the DIR inputs are fed to both A pins: 
// when one is high, the other is low; and the EN (ENA) inputs
// determine whether a current will flow through a given coil.
// Cuartielles' shield is thus able to be used to drive unipolar
// steppers at full-step increments. 

uint16_t n = 0;
int direction = 1;

void setup() {

  pinMode(BTN_UP,INPUT_PULLUP);
  pinMode(BTN_DN,INPUT_PULLUP);
  pinMode(ENA_A,OUTPUT);
  pinMode(ENA_B,OUTPUT);
  pinMode(DIR_A,OUTPUT);
  pinMode(DIR_B,OUTPUT);

}

void directions( int A, int B ) {
  // Ports DIR_A and DIR_B are on digital pins 12 and 13;
  // these map to PORTB bits 4 and 5.
  uint8_t db = PORTB & ~B00110000;
  enable( LOW, LOW );
  db |= A == HIGH ? B00010000 : 0;
  db |= B == HIGH ? B00100000 : 0;
  PORTB = db;
  delayMicroseconds(10);
  enable( HIGH, HIGH );
}

void enable( int A, int B ) {
  // Ports ENA_A and ENA_B are on digital pins 10 and 11;
  // these map to PORTB bits 2 and 3, respectively.
  uint8_t db = PORTB & ~B00001100;
  db |= A == HIGH ? B00000100 : 0;
  db |= B == HIGH ? B00001000 : 0;
  PORTB = db;
  // This 700 microsecond delay gives time for current to rise sufficiently in each phase coil.
  // Reducing this value significantly below this amount causes an EM295 stepper to simply whine without 
  // generating torque.
  delayMicroseconds(700);
}

void step() {
  // Depending on the value of [direction] (set in the loop() function below),
  // we polarize the coils in forward or reverse order. This is only slightly less
  // awkward than decrementing [n] and working backward through the cases below.
  // Notice the order in which coils are energized in the forward direction just
  // mirrors that going in the opposite direction.
  switch ( n++ % 4 ) {
    case 0:
      if ( direction > 0 ) directions(N,N); else directions(N,S);
      break;
    case 1:
      if ( direction > 0 ) directions(S,N); else directions(S,S);
      break;
    case 2:
      if ( direction > 0 ) directions(S,S); else directions(S,N);
      break;
    case 3:
      if ( direction > 0 ) directions(N,S); else directions(N,N);
      break;
  }
}

void loop() {
  // Read both buttons on the board: Each button causes the motor to rotate in one or the other direction.
  int nowdir = (digitalRead(BTN_UP) ? 0 : -1) + (digitalRead(BTN_DN) ? 0 : 1);
  if ( 0 == nowdir ) {
    if ( 0 != direction ) {
      enable( LOW, LOW );
      Serial.println("Stopped");
    }
    direction = 0;
  } else {
    direction = nowdir;
    step();
  }
  // Notice that we don't use a delay at the end of this loop.
  // We're counting on the hundreds of microseconds delay in the enable() function
  // to ensure that coil currents have risen to a sufficient magnitude to overcome mechanical losses.
}

One thought on “It spins now.”

Comments are closed.