Arduino Uno/Mega maximum supported Stepper Motor Pulse rate with AccelStepper?

Hi everyone,

Thanks for any advice and time you spend trying to help me out here, much appreciated!

So I've been working on a camera slider project with this NEMA 34 stepper motor:

85BYGH450C-060

and I've been using this driver to control the motor at 51200 steps per rotation (I've used another more simple driver option, but I purchased this one to help diagnose a previous problem):

And I've been using the AccelStepper library to run the motor.

My question is, what is the steps per second limit the Arduino Uno/Mega can support? I know the AccelStepper library says the Uno can handle up to 4000 steps per second with its 16Mhz clock speed, which means it would take a good 12 seconds or so to get a full rotation of the motor at 51200 steps per second (selected for its smoother operation). And does the lower processing speed of the Uno/Mega (16MHz) compared to the Due (84MHz) have an impact on the smoothness of the motors operation at 51200 steps per revolution?

Side Note
Along with this issue, I've found running the motor at the maximum speed I can with AccelStepper (regardless of steps per revolution e.g. 1000 or 51200) still produces a somewhat choppy operation with a 21cm (diameter) timing gear. Running at lower Amperage's helps a lot, but it's still a little choppy. Am I expecting too much of a 51200 step per resolution, have I chosen a timing gear that's too large, or have I probably done something wrong?

Also the stepper.setSpeed command seems to affect the acceleration rate even though I've locked the stepper.setAcceleration command to specific figures, and have the setSpeed command mapped to a potentiometer. So when I change the value of the potentiometer it changes the acceleration, the the top speed remains fixed... what am I missing here?

Below is the full code I'm using (in the next message due to work count issues), please excuse random entries of set.Speed and set.Acceleration here and there, I've just been trying to raise the importance of setting the speed and acceleration in case I wasn't using them enough and it wasn't sticking... not how it works I know :confused: Also attached is a wiring diagram with the wrong driver, but an explanation of the right driver.

Thanks for any assistance!

2019-07-20 14_47_41-Camera Slider Wiring.fzz_ - Fritzing - [Breadboard View].png

/* This code allows a bipolar stepper motor driven by a microstep driver to be manually positioned to start and end positions using push buttons.
    These start and end positions are then used to move the stepper motor continuously between for the purposes of being used as a camera slider.
    To keep the motion smooth the AccelStepper library has been used.
    The intention was to use a VL53L0X, Ultrasonic, or IR sensor for position guidance, but I couldn't get that to work due to the nuances of
    using a stepper motor like the "blocking" situation... but I'm sure there's a way somehow without compromising acceleration.
*/



// libraries
#include <AccelStepper.h>


/**********Control Buttons**********/
//declare all push buttons to pins.
const int btn1 = 8; // moves motor in one direction - see case 1
const int btn2 = 9; // moces motor in oposite direction to case 1 - see case 2
const int btn3 = 10; // logs current position value to start position - see case 3
const int btn4 = 11; // logs current position to end position - see case 4
const int btn5 = 12; // activates continuous movement of motor between start and end position - see case 5 and "the simplest button switch" code

//create containers to track button state (low or high). Not constant so value can change at the push and release of a button.
int btn1State = 0;
int btn2State = 0;
int btn3State = 0;
int btn4State = 0;
int btn5State = 0; // combines with "the simplest push button switch later

// delcare 'mode' to contain and trigger switch.case functions
int mode = 0;


/**********Speed Control - Pot**********/
//Speed Control
int pot = 0; // declaring the linear slider
long potMap = 0; // figure from the mapping of the pot to the stepper speed
long highSpeed; // used to hold the high speed value of the stepper motor
long lowSpeed; // used to hold the high speed value of the stepper motor



/**********Stepper Setup**********/
//declare all STEPPER motor wires to pins.
const int PUL = 2;
const int DIR = 3;
const int ENBL = 4;

AccelStepper STEPPER (1, PUL, DIR); // Defining Stepper Motor 1 - Driver, PUL - Pulse, DIR - Direction
long motorSpeed = 0; // stores the stepping frequency of the motor driven by the potentiometer
long motorAcceleration = 0; // stores the acceleration rate

const long STEPPERsteps = 51200; // logs the steps per rotation is the motor should run at
long CURRENTpos = 0; // keeps track of the current position of the slider
long STARTpos = 0; // used to log the desired start position with btn3
long ENDpos = 0; // used to log the desired end position with btn4
long STEPPERsetup = (STEPPERsteps / 100); // used to hold the steps the motor should move during the STARTpos and ENDpos setup phase



void setup() { // VOID SETUP 
  Serial.begin (115200); // starts up serial com to track code state

  /**********Control Buttons**********/
  //declare push buttons are inputs.
  pinMode (btn1, INPUT);
  pinMode (btn2, INPUT);
  pinMode (btn3, INPUT);
  pinMode (btn4, INPUT);
  pinMode (btn5, INPUT);


  /**********Speed Control - Pot**********/
  // declaring A0 (pot) as an input
  pinMode (A0, INPUT);

}

void loop() { //VOID LOOP

  /**********Pot Speed Control**********/
  highSpeed = STEPPERsteps; // high speed value
  lowSpeed = (STEPPERsteps / 100); // low speed value


  /**********Stepper Setup**********/
  pot = analogRead (A0);
  //Stepper settings refering back to pre-void setup data
  potMap = map(pot, 0, 1023, lowSpeed, highSpeed); // mapping pot to desired motor speeds
  motorSpeed = potMap;
  motorAcceleration = (potMap / 4);

  /* below was creating a "deadzone" for the potentiometer, but this was removed for simplicity. It did work well enough though.
    //  if (motorSpeed > (highSpeed * 0.99)) { // high speed deadzone
    //    motorSpeed = highSpeed ;
    //  }
    //  if (motorSpeed < (highSpeed * 0.03)) { // low speed deadzone
    //    motorSpeed = lowSpeed ;
    //  }
  */

  Serial.print ("Motor Speed = "); // keeping track of stepper speed
  Serial.println (motorSpeed);


  //preparing button pins to be monitored
  btn1State = digitalRead (btn1);
  btn2State = digitalRead (btn2);
  btn3State = digitalRead (btn3);
  btn4State = digitalRead (btn4);

  //btn5State
  if (digitalRead(btn5) == true) { // the simplest button switch. Allows btn5State to switch between HIGH and LOW at the push/toggle of a button.
    btn5State = !btn5State;
    digitalWrite (btn5, btn5State);
  } while (digitalRead (btn5) == true);
  Serial.print ("btn5State = ");
  Serial.println (btn5State);


  // checking the state of each button to determine what 'mode' should be.
  // Serial.println has been added to each to visually keep track of the stae of 'mode'. Disabled for clarity.
  if (btn1State == HIGH) {
    mode = 1;
    //    Serial.println ("MODE = 1");
  }
  else if (btn2State == HIGH) {
    mode = 2;
    //   Serial.println ("MODE = 2");
  }
  else if (btn3State == HIGH) {
    mode = 3;
    //    Serial.println ("MODE = 3");
  }
  else if (btn4State == HIGH) {
    mode = 4;
    //   Serial.println ("MODE = 4");
  }
  else if (btn5State == HIGH) {
    mode = 5;
    //Serial.println ("MODE = 5");
  } else {
    mode = 0;
    //   Serial.println ("MODE = 0");
  }


  Serial.print ("CURRENTpos = "); // keeping track of current position before a case is activated
  Serial.println (CURRENTpos);


  /**********Modes - functions**********/
  // switch.case action, depending on 'mode' an action will execute
  switch (mode) {
      break;
    case 0: // no instruciton the motor to ensure there's no accidental movement. Not really necassary...
      Serial.println ("CASE = 0");
      STEPPER.setSpeed (motorSpeed); // initiating stepper speed
      STEPPER.setAcceleration (motorAcceleration);
      break;

    case 1:
      //  Serial.println ("CASE = 1"); // MANUAL CW rotation - moves the motor cloockwise a set number of steps
      STEPPER.setSpeed (motorSpeed); // initiating stepper speed
      STEPPER.setAcceleration (motorAcceleration);
      CURRENTpos = (CURRENTpos + STEPPERsetup);
      STEPPER.runToNewPosition (CURRENTpos);
      STEPPER.run();
      Serial.print ("Case 1 CURRENTpos = ");
      Serial.println (CURRENTpos);
      break;

    case 2:
      // Serial.println ("CASE = 2"); // MANUAL CCW rotation - moves the motor counter clockwise a set number of steps
      STEPPER.setSpeed (motorSpeed); // initiating stepper speed
      STEPPER.setAcceleration (motorAcceleration);
      CURRENTpos = (CURRENTpos - STEPPERsetup);
      STEPPER.runToNewPosition (CURRENTpos);
      STEPPER.run();
      Serial.print ("Case 2 CURRENTpos = ");
      Serial.println (CURRENTpos);
      break;

    case 3:
      //   Serial.println ("CASE = 3"); // MANUAL start position - logs the current position to start position
      STARTpos = CURRENTpos;
      Serial.print ("Case 3 STARTpos = ");
      Serial.println (STARTpos);
      break;

    case 4:
      //   Serial.println ("CASE = 4"); // MANUAL end position - logs the current position to end position
      ENDpos = CURRENTpos;
      Serial.print ("Case 4 ENDpos = ");
      Serial.println (ENDpos);
      break;

    case 5:
      //   Serial.println ("CASE = 5"); // Backwards and Forwards - moves the motor back and forward between the start and end postitions

      Serial.print ("Case 5 STARTpos = ");
      Serial.println (STARTpos);
      STEPPER.setSpeed (motorSpeed); // initiating stepper speed
      STEPPER.setAcceleration (motorAcceleration);
      STEPPER.runToNewPosition (STARTpos);
      STEPPER.run();

      Serial.print ("Case 5 ENDpos = ");
      Serial.println (ENDpos);
      STEPPER.setSpeed (motorSpeed); // initiating stepper speed
      STEPPER.setAcceleration (motorAcceleration);
      STEPPER.runToNewPosition (ENDpos);
      STEPPER.run();
      break;
  }


  // FIN
}

I’ve run FOUR steppers simultaneously in loop() @ 10kHz* without thinking about the code, so if accelstepper is tight enough, it should be able to beat that.

  • while doing a few other things at the same time.

I believe the AccelStepper speed limit is because it uses floating point maths.

IIRC some others have got much higher step rates with this Simple Stepper Code - obviously by modifying the interval between steps.

If you also use the DigitalWriteFast library there should be a further performance improvement as it is much faster than the regular digitalWrite().

...R

@lastchancename Was that with the AccelStepper library or something else? I'm trying to gauge what kind of clock speed is required to run a stepper at 51200 steps per revolution, which I don't think should be limited by the clock speed since one step is just lined up behind the other so it should just be a hit on rotation speed (if there's a clock speed limitation at all) rather than a hit on the ability to run a higher resolution steps per revolution.

What has your experience been with running stepper motors as smoothly as possible, and what do you think the realism in getting very smooth operation (enough to not shudder a camera mounded to the belt) with a 21cm diameter timing gear linked to a timing belt for the camera slider is? And lastly, is there a way to gauge whether there's a limitation of the Arduino with regards to driving the stepper motor at different resolutions? Appreciate the insight btw :slight_smile:

@Robin2 From what AccelStepper says calculating the Acceleration curve is an expensive function that should be limited, and that should have some sort of impact on performance. But would the DigitalWriteFast library be able to improve stepper motor performance or reduce its impact on of the AccelStepper library? I'm not sure if I've even got the experience to handle some of the nuances of that library right now, I'm barely getting by with the AccelStepper library :stuck_out_tongue:

AlexF16:
But would the DigitalWriteFast library be able to improve stepper motor performance or reduce its impact on of the AccelStepper library?

The DigitalWriteFast library is for use when writing your own stepper code. It has no relevance for the AccelStepper library.

Writing your own code for simple acceleration is not difficult as illustrated in this simple acceleration code

If you want to get a sense of the level of performance that can be achieved with an Uno read up about the specs for the GRBL program that is used to control CNC machines

...R

Agree completely with Robin.
Code like this fits right in the middle of the Quality / Cost / Simplicity triangle.

My code, as Robin also suggested - used direct pin manipulation rather than a library or class to generate the direction / step and speed profiling.

With some thought - and depending on what else is happening, you might find a way to 51kHz stepping on an 8-bit Arduino