Industruino as stepper motor driver

Connect a unipolar stepper motor to INDIO

Tom

INDIO digital outputs

The Industruino INDIO has 8 digital outputs of the type: galvanically isolated high-side driver (Charge pump NFET). Each channel can deliver 2.6A at the supply voltage (typically 12 or 24V). Maximum switching frequency is 400Hz.

This allows us to drive a UNIPOLAR stepper motor directly from 4 pins (1-4 in the picture on the left), with the common wires connected to GND. The common wires cannot be connected to the supply voltage because the output pins, as high-side drivers, cannot sink any current. This is also why as far as i understand, it is impossible to drive a bipolar stepper motor with high-side drivers only. For reference see this excellent explanation on steppers.

Unipolar stepper motors have 5 or 6 wires. Below test was done with a 5-wire PM35S-048 stepper which has 48 steps per rotation and is rated for 24V, but we did the test with around 10V.

The maximum switching frequency of 400Hz would allow 400 steps per second in theory. For 48 steps per rotation, that means max 400/48=8.3 rotations per second, or 500 RPM. Indeed my test confirmed that the stepper works accurately up to 500 RPM; 600 RPM did not work.

Code

As the INDIO's digital channels need the Indio library, we cannot use the standard Arduino Stepper library. I did borrow the relevant parts of this library and put them in the main sketch so it is very transparent how the stepper works, see below.

The sketch only works for unipolar stepper motors, as described above, and uses 4 digital output channels.

Here are 2 videos showing the stepper at 60 RPM and 300 RPM.

 
 

This is the code used of the above example:

  • a startup screen that allows to move the stepper using the up/down buttons
  • press enter to start a standard routine: full 360 rotation clockwise, back anti-clockwise with stops at 240 and 120 degrees
  • press enter to change the speed (rpm)
/*
 * test Industruino INDIO 1286 with stepper motor directly to 4 digital outputs
 * to work with UNIPOLAR stepper motor = 5 or 6 wires
 * 2 wires per coil
 * extra wire(s) to GND (Indio has hig side drivers, cannot sink current)
 *
 * see http://www.tigoe.net/pcomp/code/circuits/motors/stepper-motors/
 * code based on the Arduino standard Stepper library, modified to work with Industruino INDIO digital channels
 *
 * Tom Tobback March 2017
 */

#include <Indio.h>
#include <Wire.h>
#include <UC1701.h>
static UC1701 lcd;

// CONFIGURATION
const int motor_pin_1 = 1;
const int motor_pin_2 = 2;
const int motor_pin_3 = 3;
const int motor_pin_4 = 4;
int motor_speed = 60;          // the motor speed, in revolutions per minute
int number_of_steps = 48;   // total number of steps for this motor

// INTERNAL
int step_number = 0;    // which step the motor is on
unsigned long last_step_time = 0; // time stamp in us of the last step taken
const int button_enter_pin = 24;
const int button_up_pin = 25;
const int button_down_pin = 23;

////////////////////////////////////////////////////////////////////////////////

void setup() {

  lcd.begin();
  lcd.setCursor(10, 1);
  lcd.print("Unipolar Stepper");
  lcd.setCursor(50, 2);
  lcd.print("TEST");
  lcd.setCursor(0, 4);
  lcd.print("press Up/Down for pos");
  lcd.setCursor(0, 6);
  lcd.print("press Enter to start");

  pinMode(26, OUTPUT);    // LED
  digitalWrite(26, LOW);    // LOW = backlight ON

  pinMode(button_enter_pin, INPUT);
  pinMode(button_up_pin, INPUT);
  pinMode(button_down_pin, INPUT);

  Indio.digitalMode(motor_pin_1, OUTPUT);
  Indio.digitalMode(motor_pin_2, OUTPUT);
  Indio.digitalMode(motor_pin_3, OUTPUT);
  Indio.digitalMode(motor_pin_4, OUTPUT);

  Indio.digitalWrite(motor_pin_1, LOW);
  Indio.digitalWrite(motor_pin_2, LOW);
  Indio.digitalWrite(motor_pin_3, LOW);
  Indio.digitalWrite(motor_pin_4, LOW);

  while (digitalRead(button_enter_pin)) {
    if (!digitalRead(button_up_pin)) step(1);
    if (!digitalRead(button_down_pin)) step(-1);
  }

  lcd.clear();
  lcd.setCursor(10, 1);
  lcd.print("Unipolar Stepper");
  lcd.setCursor(10, 4);
  lcd.print("Speed (rpm): ");
  lcd.setCursor(10, 6);
  lcd.print("hold Enter to");
  lcd.setCursor(10, 7);
  lcd.print("change speed");
}

////////////////////////////////////////////////////////////////////////////////

void loop() {

  lcd.setCursor(100, 4);
  lcd.print(motor_speed);
  lcd.print("  ");

  step(number_of_steps);
  delay(2000); changeSpeed();

  step(-number_of_steps / 3);
  delay(1000); changeSpeed();

  step(-number_of_steps / 3);
  delay(1000); changeSpeed();

  step(-number_of_steps / 3);
  delay(1000); changeSpeed();

}

////////////////////////////////////////////////////////////////////////////////

void step(int steps_to_move)
{
  unsigned long step_delay = 60L * 1000L * 1000L / number_of_steps / motor_speed;
  int steps_left = abs(steps_to_move);  // how many steps to take
  int direction;      // motor direction

  // determine direction based on whether steps_to_mode is + or -:
  if (steps_to_move > 0) {
    direction = 1;
  }
  if (steps_to_move < 0) {
    direction = 0;
  }

  // decrement the number of steps, moving one step each time:
  while (steps_left > 0)
  {
    unsigned long now = micros();
    // move only if the appropriate delay has passed:
    if (now - last_step_time >= step_delay)
    {
      // get the timeStamp of when you stepped:
      last_step_time = now;
      // increment or decrement the step number,
      // depending on direction:
      if (direction == 1)
      {
        step_number++;
        if (step_number == number_of_steps) {
          step_number = 0;
        }
      }
      else
      {
        if (step_number == 0) {
          step_number = number_of_steps;
        }
        step_number--;
      }
      // decrement the steps left:
      steps_left--;
      // step the motor to step number 0, 1, ..., {3 or 10}
      stepMotor(step_number % 4);
    }
  }
}

////////////////////////////////////////////////////////////////////////////////

void stepMotor(int thisStep)
{
  switch (thisStep) {
    case 0:  // 1010
      Indio.digitalWrite(motor_pin_1, HIGH);
      Indio.digitalWrite(motor_pin_2, LOW);
      Indio.digitalWrite(motor_pin_3, HIGH);
      Indio.digitalWrite(motor_pin_4, LOW);
      break;
    case 1:  // 0110
      Indio.digitalWrite(motor_pin_1, LOW);
      Indio.digitalWrite(motor_pin_2, HIGH);
      Indio.digitalWrite(motor_pin_3, HIGH);
      Indio.digitalWrite(motor_pin_4, LOW);
      break;
    case 2:  //0101
      Indio.digitalWrite(motor_pin_1, LOW);
      Indio.digitalWrite(motor_pin_2, HIGH);
      Indio.digitalWrite(motor_pin_3, LOW);
      Indio.digitalWrite(motor_pin_4, HIGH);
      break;
    case 3:  //1001
      Indio.digitalWrite(motor_pin_1, HIGH);
      Indio.digitalWrite(motor_pin_2, LOW);
      Indio.digitalWrite(motor_pin_3, LOW);
      Indio.digitalWrite(motor_pin_4, HIGH);
      break;
  }
}

void changeSpeed() {
  if (!digitalRead(button_enter_pin)) {
    lcd.setCursor(10, 6);
    lcd.print("change speed  ");
    lcd.setCursor(10, 7);
    lcd.print("               ");
    delay(2000);
    while (digitalRead(button_enter_pin)) {
      if (!digitalRead(button_up_pin)) motor_speed++;
      if (!digitalRead(button_down_pin)) motor_speed--;
      motor_speed = constrain(motor_speed, 0, 600);
      lcd.setCursor(100, 4);
      lcd.print(motor_speed);
      lcd.print("  ");
      delay(50);
    }
    lcd.setCursor(10, 6);
    lcd.print("hold Enter to  ");
    lcd.setCursor(10, 7);
    lcd.print("change speed  ");
  }
}