Skip to content Skip to sidebar Skip to footer

Stepper Motor Serial Monitor Arduino Running At Same Time

Control Stepper Motor Serial Monitor Arduino 

Stepper Motor Serial Monitor Arduino Running At Same Time - Previously I have made an article about Keyes CNC Shield V4 GRBL and how to control via a4988 driver. But a problem arises when we can't control the stepper motor to rotate at the same time.

Therefore, I tried several methods and using a timer interrupt was the right choice after learning the program code from the video Coordinated stepper motor control (arduino) and then I edited a few lines of the program and set the right interrupt to run on the Arduino Uno / Nano.

 

A. A4988 CNC Shield Stepper Motor Schematic

In this article, I still use the CNC Shield V4 a4988 as a stepper motor controller to facilitate this test. For the schematic, please look at the following picture:

Keyes CNC Shield Wiring Schematics Chip Piko Stepper Motor Serial Monitor Arduino Running At Same Time
CNC Shield Stepper Motor Schematic

The stepper motor has 4 wires, red blue green and black, please make sure you connect it to the CNC Shield correctly. If the connection is wrong, it could be that the stepper is spinning incorrectly. I have provided the program code for you below.

 

B. Arduino Code Motor Stepper 

This is a program that I modified, you can copy and paste into your arduino IDE, then upload it to arduino.

#define X_DIR_PIN          2
#define X_STEP_PIN         5
#define X_ENABLE_PIN       8

#define Y_DIR_PIN          3
#define Y_STEP_PIN         6
#define Y_ENABLE_PIN       8

#define Z_DIR_PIN          4
#define Z_STEP_PIN         7
#define Z_ENABLE_PIN       8


#define X_STEP_HIGH             PORTD |=  (1 << X_STEP_PIN);
#define X_STEP_LOW              PORTD &= ~(1 << X_STEP_PIN);

#define Y_STEP_HIGH             PORTD |=  (1 << Y_STEP_PIN);
#define Y_STEP_LOW              PORTD &= ~(1 << Y_STEP_PIN);
//
#define Z_STEP_HIGH             PORTD |=  (1 << Z_STEP_PIN);
#define Z_STEP_LOW              PORTD &= ~(1 << Z_STEP_PIN);


#define TIMER1_INTERRUPTS_ON    TIMSK1 |=  (1 << OCIE1A);
#define TIMER1_INTERRUPTS_OFF   TIMSK1 &= ~(1 << OCIE1A);

byte a;
int x = 0;
int y = 0;
int z = 0;

String get_data[3];
String data;
int stringData;
String per_data;

struct stepperInfo {
  // externally defined parameters
  float acceleration;
  volatile unsigned int minStepInterval;   // ie. max speed, smaller is faster
  void (*dirFunc)(int);
  void (*stepFunc)();

  // derived parameters
  unsigned int c0;                // step interval for first step, determines acceleration
  long stepPosition;              // current position of stepper (total of all movements taken so far)

  // per movement variables (only changed once per movement)
  volatile int dir;                        // current direction of movement, used to keep track of position
  volatile unsigned int totalSteps;        // number of steps requested for current movement
  volatile bool movementDone = false;      // true if the current movement has been completed (used by main program to wait for completion)
  volatile unsigned int rampUpStepCount;   // number of steps taken to reach either max speed, or half-way to the goal (will be zero until this number is known)

  // per iteration variables (potentially changed every interrupt)
  volatile unsigned int n;                 // index in acceleration curve, used to calculate next interval
  volatile float d;                        // current interval length
  volatile unsigned long di;               // above variable truncated
  volatile unsigned int stepCount;         // number of steps completed in current movement
};

void xStep() {
  X_STEP_HIGH
  X_STEP_LOW
}
void xDir(int dir) {
  digitalWrite(X_DIR_PIN, dir);
}

void yStep() {
  Y_STEP_HIGH
  Y_STEP_LOW
}
void yDir(int dir) {
  digitalWrite(Y_DIR_PIN, dir);
}

void zStep() {
  Z_STEP_HIGH
  Z_STEP_LOW
}
void zDir(int dir) {
  digitalWrite(Z_DIR_PIN, dir);
}


void resetStepperInfo( stepperInfo& si ) {
  si.n = 0;
  si.d = 0;
  si.di = 0;
  si.stepCount = 0;
  si.rampUpStepCount = 0;
  si.totalSteps = 0;
  si.stepPosition = 0;
  si.movementDone = false;
}

#define NUM_STEPPERS 3

volatile stepperInfo steppers[NUM_STEPPERS];

void setup() {

  Serial.begin(9600);

  pinMode(X_STEP_PIN,   OUTPUT);
  pinMode(X_DIR_PIN,    OUTPUT);
  pinMode(X_ENABLE_PIN, OUTPUT);

  pinMode(Y_STEP_PIN,   OUTPUT);
  pinMode(Y_DIR_PIN,    OUTPUT);
  pinMode(Y_ENABLE_PIN, OUTPUT);

  pinMode(Z_STEP_PIN,   OUTPUT);
  pinMode(Z_DIR_PIN,    OUTPUT);
  pinMode(Z_ENABLE_PIN, OUTPUT);


  digitalWrite(X_ENABLE_PIN, LOW);
  digitalWrite(Y_ENABLE_PIN, LOW);
  digitalWrite(Z_ENABLE_PIN, LOW);

  noInterrupts();
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 1000;                             // compare value
  TCCR1B |= (1 << WGM12);                   // CTC mode
  TCCR1B |= ((1 << CS11) | (1 << CS10));    // 64 prescaler
  interrupts();


  steppers[0].dirFunc = xDir;
  steppers[0].stepFunc = xStep;
  steppers[0].acceleration = 1000;
  steppers[0].minStepInterval = 250;

  steppers[1].dirFunc = yDir;
  steppers[1].stepFunc = yStep;
  steppers[1].acceleration = 1000;
  steppers[1].minStepInterval = 250;

  steppers[2].dirFunc = zDir;
  steppers[2].stepFunc = zStep;
  steppers[2].acceleration = 1000;
  steppers[2].minStepInterval = 250;
}

void resetStepper(volatile stepperInfo& si) {
  si.c0 = si.acceleration;
  si.d = si.c0;
  si.di = si.d;
  si.stepCount = 0;
  si.n = 0;
  si.rampUpStepCount = 0;
  si.movementDone = false;
}

volatile byte remainingSteppersFlag = 0;

void prepareMovement(int whichMotor, int steps) {
  volatile stepperInfo& si = steppers[whichMotor];
  si.dirFunc( steps < 0 ? HIGH : LOW );
  si.dir = steps > 0 ? 1 : -1;
  si.totalSteps = abs(steps);
  resetStepper(si);
  remainingSteppersFlag |= (1 << whichMotor);
}

volatile byte nextStepperFlag = 0;

volatile int ind = 0;
volatile unsigned int intervals[100];

void setNextInterruptInterval() {

  bool movementComplete = true;

  unsigned int mind = 999999;
  for (int i = 0; i < NUM_STEPPERS; i++) {
    if ( ((1 << i) & remainingSteppersFlag) && steppers[i].di < mind ) {
      mind = steppers[i].di;
    }
  }

  nextStepperFlag = 0;
  for (int i = 0; i < NUM_STEPPERS; i++) {
    if ( ! steppers[i].movementDone )
      movementComplete = false;

    if ( ((1 << i) & remainingSteppersFlag) && steppers[i].di == mind )
      nextStepperFlag |= (1 << i);
  }

  if ( remainingSteppersFlag == 0 ) {
    OCR1A = 65500;
  }

  OCR1A = mind;
}

ISR(TIMER1_COMPA_vect)
{
  unsigned int tmpCtr = OCR1A;

  OCR1A = 65500;

  for (int i = 0; i < NUM_STEPPERS; i++) {

    if ( ! ((1 << i) & remainingSteppersFlag) )
      continue;

    if ( ! (nextStepperFlag & (1 << i)) ) {
      steppers[i].di -= tmpCtr;
      continue;
    }

    volatile stepperInfo& s = steppers[i];

    if ( s.stepCount < s.totalSteps ) {
      s.stepFunc();
      s.stepCount++;
      s.stepPosition += s.dir;
      if ( s.stepCount >= s.totalSteps ) {
        s.movementDone = true;
        remainingSteppersFlag &= ~(1 << i);
      }
    }

    if ( s.rampUpStepCount == 0 ) {
      s.n++;
      s.d = s.d - (2 * s.d) / (4 * s.n + 1);
      if ( s.d <= s.minStepInterval ) {
        s.d = s.minStepInterval;
        s.rampUpStepCount = s.stepCount;
      }
      if ( s.stepCount >= s.totalSteps / 2 ) {
        s.rampUpStepCount = s.stepCount;
      }
    }
    else if ( s.stepCount >= s.totalSteps - s.rampUpStepCount ) {
      s.d = (s.d * (4 * s.n + 1)) / (4 * s.n + 1 - 2);
      s.n--;
    }

    s.di = s.d; // integer
  }

  setNextInterruptInterval();

  TCNT1  = 0;
}

void runAndWait() {
  setNextInterruptInterval();
  while ( remainingSteppersFlag );
}

void loop() {

  TIMER1_INTERRUPTS_ON

  while (Serial.available() !=  0)
  {
    data = Serial.readString();

    for (a = 0; a < 3; a++)
    {
      get_data[a] = split(data, ' ', a);

      if (get_data[a] != NULL)
      {
        if (a == 0)
        {
          x = get_data[0].toInt();
        }

        if (a == 1)
        {
          y = get_data[1].toInt();
        }

        if (a == 2)
        {
          z = get_data[2].toInt();
        }
      }
    }

    if (x != NULL)
    {
      prepareMovement( 0, x );
      Serial.print("Stepper X : ");
      Serial.print(x);
      Serial.println(" steps");
    }

    if (y != 0)
    {
      prepareMovement( 1, y );
      Serial.print("Stepper Y : ");
      Serial.print(y);
      Serial.println(" steps");
    }

    if (z != 0)
    {
      prepareMovement( 2, z );
      Serial.print("Stepper Z : ");
      Serial.print(z);
      Serial.println(" steps");
      Serial.println();
    }

    runAndWait();

    x = 0;
    y = 0;
    z = 0;
  }



}

String split(String data, char character, int pos)
{
  stringData = 0;
  per_data = "";

  for (int i = 0; i < data.length() - 1; i++)
  {

    if (data[i] == character)
    {
      stringData++;
    }

    else if (stringData == pos)
    {
      per_data.concat(data[i]);
    }

    else if (stringData > pos)
    {
      return per_data;
      break;
    }
  }

  return per_data;
}

After that, open your Arduino Serial Monitor, and send the steps value in "x y z" format (make sure you are using space). For example, you want to rotate the stepper motor together with the steps position:

  • X is 30 steps
  • Y is 100 steps
  • Z is 360 steps

In the serial monitor form, please write 30 100 360, then Enter. You can see the motors spinning together.

To simplify this illustration, I attached a video of the program above below.

I understand that the program above still has many shortcomings, but it all went well and I have tested it with my stepper.
 
If this article is useful, please share it with your friends, hopefully this can help your project.
 
Have a nice day.

Post a Comment for "Stepper Motor Serial Monitor Arduino Running At Same Time"