stepper runs much slower than commanded stepper.setSpeed. Why?

Hi guys, I’m experimenting with a stepper setup on my Uno. First sketch:

#include <AccelStepper.h>
AccelStepper stepper(1, 13, 11);

void setup()
{
  stepper.setMaxSpeed(2000);
  stepper.setAcceleration(2000);
}

void loop()
{
  stepper.setSpeed(1000);
  stepper.runSpeed();
}

runs fine, pulses are spaced at 1ms. The next sketch is closer to the final product involving some user I/O elements. It’s not finished yet, I copy/pasted the stepper code from sketch 1 to the bottom of the void loop of sketch 2 just as a test. (last 2 lines)

//LCD setup
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

//Stepper setup
#include <AccelStepper.h>
AccelStepper stepper(1, 13, 11);

int ppr = 800; //steper pulses per rev, allow for microstepping!

//User interface
int distance; //feed and return distance, units are 1/100mm
int dmm; //distance in whole mm
int drem; //remainder of distance after decimal point
int pot; //potentiometer read value
int rate; //commanded feed rate mm/rev,
int dir; //feed and return direction ????? 1=feed left, 0=feed right


//General timing


unsigned long previousMillis = 0;        // will store last time LED was updated
const long interval = 200;


int rpm = 1000; //spindle rpm, temporarily assigned here

//TEMPORARY

int spd;

//END TEMPORARY

void setup() {

  lcd.begin(16, 2);
}

void loop() {

  unsigned long currentMillis = millis();

  //General LCD printing
  analogWrite(10, 27);
  lcd.setCursor(0, 0);
  lcd.print("Feed");
  lcd.setCursor(12, 0);
  lcd.print("Rate");
  lcd.setCursor(6, 1);
  lcd.print("mm");
  lcd.setCursor(3, 1);
  lcd.print(".");

  //Calculate and print feed rate (rate)
  int pot = analogRead(A1);
  rate = map(pot, 0, 1020, 2, 25);
  if (rate < 10) {
    lcd.setCursor(12, 1);
    lcd.print("0.0 ");
    lcd.setCursor(15, 1);
    lcd.print(rate);
  }

  if (rate >= 10) {
    lcd.setCursor(12, 1);
    lcd.print("0. ");
    lcd.setCursor(14, 1);
    lcd.print(rate);
  }



  //Calculate and print feed direction
  dir = analogRead(A0);
  dir = map(dir, 0, 1020, 0, 1);
  lcd.setCursor(5, 0);
  if (dir == 0) {
    lcd.print("Left ");
  }
  if (dir == 1) {
    lcd.print("Right");
  }


  //Calculate and print feed/return distance
  //first, dmm (full mm units)

  distance = map(pot, 0, 1020, 95, 150);
  dmm = (distance / 100);

  if (dmm < 10) {
    lcd.setCursor(0, 1);
    lcd.print("  ");
    lcd.setCursor(2, 1);
    lcd.print(dmm);
  }

  else

    if (dmm < 100) {
      lcd.setCursor(0, 1);
      lcd.print(" ");
      lcd.setCursor(1, 1);
      lcd.print(dmm);
    }


    else

    {
      lcd.setCursor(0, 1);
      lcd.print(dmm);
    }


  //then, drem (1/100 mm units)

  drem = (distance - distance / 100 * 100);

  if (drem < 10) {
    lcd.setCursor(4, 1);
    lcd.print("0");
    lcd.setCursor(5, 1);
    lcd.print(drem);
  }

  else  {

    lcd.setCursor(4, 1);
    lcd.print(drem);
  }

  //Timing

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
  }


stepper.setSpeed(1000);
  stepper.runSpeed();


}

The stepper runs waaaay slower than the first example, I looked at the pulses on the oscilloscope and the spacing increased from 1ms to almost 40. What gives?

My theory is that the CPU simply can’t handle the workload. Am I right?

If so, is there a way to let the cpu concentrate on just running the stepper when commanded? It’s ok to pause the user I/O part of the code while the stepper runs.

Many thanks

Florian

My guess is that there is too much going on in your loop() so that stepper.runSpeed() is not being called often enough.

All those lcd calls may be the culprit. loop() should be repeating 100s or 1000s of times per second. There is no need to update the LCD that often. Put the LCD code into a separate function and call it once or twice per second.

...R

Robin2:
Put the LCD code into a separate function and call it once or twice per second.

...R

Cool, I'm going to read up on that. Thanks!

Yes, the AccelStepper library can only output pulses as often as run() is called - you want to
ensure that you never block loop(). If you were writing to Serial, you could use the
Serial.availableToWrite () call to tell you how many characters are free in the hardware
buffer for writing serial.

With the LCD object I don't know how long it takes - if it takes too long you may have to
figure out a way to drive AccelStepper run() method from a timer interrupt (this may or
may not work depending on how its implemented, but I suspect you can).

I had the same issue with this library. Here is the Print function that I used. You have to bring your local variables to your print function or create global variables. Put your if statements in the Print Function as well and it will work as you intended. Now it will only print twice a second.

unsigned long Print_Interval = 500;
void setup() {
lcd.begin(16, 2); 
}
void loop() 
{
 Print ();
  
}
void Print ()
{
  
       
 if ((unsigned long)(millis() - previousMillis) >= Print_Interval)
  {
    previousMillis = millis(); 

 
    lcd.clear(); 
    lcd.setCursor(0, 0);
    lcd.print("Blah");
    lcd.setCursor(0, 1);
    lcd.print("Blah Blah");    
   
  }   
}

The problem is that the motor will stutter every time the lcd is being accessed
if the run() method is neglected. I don't know what the latencies of this library
are - perhaps you can buffer the stuff to be sent and print one character at a time
to get less latency?

MarkT:
The problem is that the motor will stutter every time the lcd is being accessed
if the run() method is neglected.

EXACTLY! I had this intermittent stuttering in the motor, and I couldn't figure out wth was going on it realized the stutter was at about the same frequency with the lcd.Print. This is slowly leading me to experiment with running the stepper as part of an interrupt subroutine. Lots to read up on about that.

How much time (in microseconds) is there between steps?

...R

for testing, 1000, i.e. 1000 steps per second if my math is correct. I'd like to get closer to 8000 in the end. Might not be realistic, I'll see.

One millisec (1000 µsess) should be plenty of time to write a little bit of stuff to the LCD.

However if you reduce it to 125 µsecs you may be creating a challenge for a 16MHz Arduino.

Keep in mind, also, that interrupts use additional MCU cycles to save and restore the system before and after attending to the code in the ISR. They do not automatically solve every problem.

...R

You can run interrupts at upto about 30kHz on the ATmega328 at 16MHz, if its a few instructions.

If you use a print function Like Robin suggested it will not shudder. There will be no need to run on an interrupt. It is a good practice to call the stepper.run () at the top of the print function. You can put it in each function and many times in your main loop if it is extensive. A quick test is to // out the print lines of code and you will see the performance improve.

MarkT:
You can run interrupts at upto about 30kHz on the ATmega328 at 16MHz, if its a few instructions.

Will it also be able to update the LCD between interrupts?

…R