Go Down

Topic: I need that loop insanely fast - any ideas? [CODE] (Read 3833 times) previous topic - next topic

MaxBlitz

I am controlling a stepper motor with an analog joystick. Its already pretty fast, but the motor can go faster  :smiley-mr-green: . Just the code isnt fast enough. Its running on Arduino Due. Any ideas?

Code: [Select]
#include <AccelStepper.h>
#include <limits.h>

#define PIN_MOTORENA 5                // Motor enable pin
#define PIN_MOTORDIR 6                 // Motor direction pin
#define PIN_MOTORPUL 7                 // Motor pulse pin
#define PIN_XAXIS A4     // Joystick X-axis read on analog pin
#define CALIBRATION 0     // 1 for calibration, 0 for normal operation
#define CALIBRATION_VALUE 32     // Lowest Value from calibration. Used in map function.
#define MICROSTEP 16                     // Microstepping value (1-128)
#define SPEEDLIMIT 2048*MICROSTEP // stepper speed limit
#define DEADBAND 32*MICROSTEP     // +/- this value the stepper does not move

/*
Set up the stepper. Its using a stepper motor driver (first param =1)
*/
AccelStepper stepper(1, PIN_MOTORPUL, PIN_MOTORDIR);
 
 
void setup()

  /*
  Initial settings of the stepper motor
  */
  stepper.setPinsInverted(1,1,1); // Invert signals (common-anode driver configuration)
  stepper.setMaxSpeed(SPEEDLIMIT);
  stepper.setSpeed(0);
 
  /*
  Serial communication
  */
  #if CALIBRATION == 1
  Serial.begin(19200);   
  #endif

  /*
  12-bit resolution on analogRead(). Values 0 - 4095
  */
  analogReadResolution(12);     
}
 
void loop()

  /*
  Run the stepper with constant speed mode.
  */
  stepper.runSpeed();

  /*
  Read the joystick, map and set the speed of the stepper.
  */
  #if CALIBRATION == 0
  register int xAxis;
    xAxis = analogRead(PIN_XAXIS);
    xAxis = map(xAxis, CALIBRATION_VALUE, 4095, SPEEDLIMIT, -SPEEDLIMIT);
stepper.setSpeed(my_abs(xAxis) > DEADBAND ? xAxis : 0);
  #endif
 
  /*
  Calibration mode for raw joystick data output
  */
  #if CALIBRATION == 1
    /*
    Observe value when joystick untouched. Use this value as
CALIBRATION_VALUE
*/
    Serial.println(analogRead(PIN_XAXIS));
    delay(500);
  #endif
}

/*
Experimental function for faster?? computing of abs.
cdq
add eax, edx
xor eax, edx
*/
int my_abs(int a)
{
   int mask = (a >> (sizeof(int) * CHAR_BIT - 1));
   return (a + mask) ^ mask;
}

MorganS

How does writing your own abs() function change the speed of the motor? The speed is defined by the AccelStepper library and the SPEEDLIMIT which you set for yourself. Microstepping the motor is probably the main cause of your slowness.

Now the speed of your loop is related to the speed of the abs() function but that's only affecting how often the speed can be updated by your loop, not the absolute speed of the motor turning.

I suspect that the compiler's version of abs() is many times faster than yours. You are calling a function which has some overhead. If you didn't use the function, the compiler might be able to use the code shown in the SAM3X datasheet in section 12.11.73 with no function overhead.

On the AVR Arduinos, map() can be slow because it uses division. This will be fast on the Due. Once again, this looks like a function but it isn't. This will compile to very fast code with no function-calling overhead.

I would advise you to stay away from Serial.print() on the Due. This can be mind-bogglingly slow.

"The problem is in the code you didn't post."

MarkT

Drop the library and hard code your step driving (maybe using timers and interrupts even)?

I favour DDA/DDS techniques for this sort of control - its instructive to look at the RepRap
engine code for stepping, and to understand Direct Digital Synthesis (DDS) technique of
maintaining a phase variable that's regularly incrmented by a frequency variable, often cleaner
and easier than working with delays and the time-domain directly, and only needs a simple
interrupt handler.

For multiaxis stepping Bressenham's line algorithm is useful, its one of a class of DDA
(Digital Differential Analysis IIRC) algorithms which allow simple additions/subtraction
and comparisons to replace division for keeping steps in arbitrary ratios to each other.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

MaxBlitz

#3
Jan 25, 2015, 07:40 pm Last Edit: Jan 25, 2015, 07:45 pm by MaxBlitz
How does writing your own abs() function change the speed of the motor? The speed is defined by the AccelStepper library and the SPEEDLIMIT which you set for yourself. Microstepping the motor is probably the main cause of your slowness.

Now the speed of your loop is related to the speed of the abs() function but that's only affecting how often the speed can be updated by your loop, not the absolute speed of the motor turning.

I suspect that the compiler's version of abs() is many times faster than yours. You are calling a function which has some overhead. If you didn't use the function, the compiler might be able to use the code shown in the SAM3X datasheet in section 12.11.73 with no function overhead.

On the AVR Arduinos, map() can be slow because it uses division. This will be fast on the Due. Once again, this looks like a function but it isn't. This will compile to very fast code with no function-calling overhead.

I would advise you to stay away from Serial.print() on the Due. This can be mind-bogglingly slow.


Thanks for the reply. I used both abs() and my_abs(), it didnt really show a difference. I will use the built in abs() then.

The speed of my loop affects the speed of my stepper, since it only executes one step when stepper.runSpeed() is called. The library works like that.

The Serial.println() is only used for debugging purposes and currently not active since its ignored by the preprocessor when compiling.

So lets reduce the problem to the following code:
Code: [Select]

#include <AccelStepper.h>
#include <limits.h>

#define PIN_MOTORENA 5                // Motor enable pin
#define PIN_MOTORDIR 6                // Motor direction pin
#define PIN_MOTORPUL 7                // Motor pulse pin
#define PIN_XAXIS A4           // Joystick X-axis read on analog pin
#define CALIBRATION_VALUE 32       // Lowest Value from calibration. Used in map function.
#define MICROSTEP 16                                 // Microstepping value (1-128)
#define SPEEDLIMIT 2048*MICROSTEP     // stepper speed limit
#define DEADBAND 32*MICROSTEP     // +/- this value the stepper does not move

/*
Set up the stepper. Its using a stepper motor driver (first param =1)
*/
AccelStepper stepper(1, PIN_MOTORPUL, PIN_MOTORDIR);
 
 
void setup()

  /*
  Initial settings of the stepper motor
  */
  stepper.setPinsInverted(1,1,1); // Invert signals (common-anode driver configuration)
  stepper.setMaxSpeed(SPEEDLIMIT);
  stepper.setSpeed(0);
 
  /*
  12-bit resolution on analogRead(). Values 0 - 4095
  */
  analogReadResolution(12);     
}
 
void loop()

  /*
  Run the stepper with constant speed mode.
  */
  stepper.runSpeed();

  /*
  Read the joystick, map and set the speed of the stepper.
  */
  register int xAxis;
        xAxis = analogRead(PIN_XAXIS);
        xAxis = map(xAxis, CALIBRATION_VALUE, 4095, SPEEDLIMIT, -SPEEDLIMIT);
stepper.setSpeed(abs(xAxis) > DEADBAND ? xAxis : 0);
}

robtillaart

#4
Jan 25, 2015, 08:23 pm Last Edit: Jan 25, 2015, 08:27 pm by robtillaart
try this:
Code: [Select]

void loop()

  stepper.runSpeed();

  int xAxis = analogRead(PIN_XAXIS) - 2048;  // make 0 the middle, makes the math simpler

  if ((xAxis < -DEADBAND) || (xAxis > DEADBAND))  // check if in DEADBAND
  {
    stepper.setSpeed(xAxis * 16);  // max = 2047*16 ,... should do the trick
  }
  else
  {
    stepper.setSpeed(0);
  }
}


note this way it allows your dead band even to be asymmetric.
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

jurs

So lets reduce the problem to the following code:
I see two possibilities to make things faster.

Possibility #1: Use a faster controller board!
As you are already using the DUE, there is only one Arduino compatible board that is faster than a DUE. It is the Intel Galileo. Unfortunately your preferred third-party library might not be compatible with that (I don't know).

Possibility #2: If controller power doesn't help you at all, learn writing effective code!

Let me ask you five simple questions:

Question #1:
How many steps per second is the desired maximum stepping speed of the stepper motor?

Question #2:
How many times per second do you want to set a different stepping speed using your joystick?

Question #3:
Wich number is higher: Answer to question #1 or answer to question #2?

Question #4:
If the answer to question #1 is a much higher number: Why to you read the joystick as often as you try stepping the stepper motor?

Question #5:
Would it not be enough to read the joystick 10, 20 or 50 times per second or so?



bobcousins

What puzzles me about AccelStepper is why it doesn't use a timer, instead of having the user call a polling function.

Anyway, the user can probably still use a timer interrupt to call runSpeed, then the main loop can do any old stuff it wants.

One day I will pull the code out of Marlin and create an interrupt driven multi-axis stepper library.
Please ask questions in the forum so everyone can benefit. PM me for paid work.

MaxBlitz

What puzzles me about AccelStepper is why it doesn't use a timer, instead of having the user call a polling function.

Anyway, the user can probably still use a timer interrupt to call runSpeed, then the main loop can do any old stuff it wants.

One day I will pull the code out of Marlin and create an interrupt driven multi-axis stepper library.
A super fast stepper library would be fantastic! +1

MaxBlitz

I see two possibilities to make things faster.

Possibility #1: Use a faster controller board!
As you are already using the DUE, there is only one Arduino compatible board that is faster than a DUE. It is the Intel Galileo. Unfortunately your preferred third-party library might not be compatible with that (I don't know).

Possibility #2: If controller power doesn't help you at all, learn writing effective code!

Let me ask you five simple questions:

Question #1:
How many steps per second is the desired maximum stepping speed of the stepper motor?

Question #2:
How many times per second do you want to set a different stepping speed using your joystick?

Question #3:
Wich number is higher: Answer to question #1 or answer to question #2?

Question #4:
If the answer to question #1 is a much higher number: Why to you read the joystick as often as you try stepping the stepper motor?

Question #5:
Would it not be enough to read the joystick 10, 20 or 50 times per second or so?



#1: 128000

#2: Not really sure, it must seem like totally instant to a human.

#3: The step pulses are critical

#4: good point, i could use a counter or millis to read the joystick. Do you think the additional conditional statements wont outweigh the benefit? There has to be minimum one more if statement if i do it like this, plus one assertment of the current millis() value to a variable. That costs time to. The question is: is it faster than reading an analog value?

#5: As pointed out in #2, i guess 50 times should be enough.


corollary: I think for my purposes a faster method of controlling the stepper than with AccelStepper library would be great. Is there another good library out there? Coding my own would be the last resort, since i am not the best coder...

MaxBlitz

try this:
Code: [Select]

void loop()

  stepper.runSpeed();

  int xAxis = analogRead(PIN_XAXIS) - 2048;  // make 0 the middle, makes the math simpler

  if ((xAxis < -DEADBAND) || (xAxis > DEADBAND))  // check if in DEADBAND
  {
    stepper.setSpeed(xAxis * 16);  // max = 2047*16 ,... should do the trick
  }
  else
  {
    stepper.setSpeed(0);
  }
}


note this way it allows your dead band even to be asymmetric.
Tried it. My code is much faster though.

robtillaart

> tried it. My code is much faster though.

how did you measure?
Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

MaxBlitz

> tried it. My code is much faster though.

how did you measure?
I didnt measure with micros(), but i am running a linear slider with the stepper motor. its about 30 % percent faster with the code i posted. Yeah i now, thats not the perfect way to measure, but it was so considerable, that there was no doubt about it. I dont know where the difference comes from, maybe your code can be optimized to run faster, since the general approach is cool. Dont know why its slower though...

Whats puzzling me is that my map(xAxis,0,4095, -40000, 40000) returns as boundary values -39180 to 40000. I want the lower limit to be also 40000 when the joystick is fully turned to the low side.

The biggest issue right now is though, that the stepper is much louder with manual control compared to when i run it in software. This is at full throttle, so no finger jitter. But i see the lower value constantly jumping from -39180 to -39200. I think this comes from noise. Would be great if there would be a way to smooth out the joystick signal.

I also tried reading the joystick only every 500ms (if statement with millis() function), but that lead to a very jerky motion of the stepper, and i need silk smooth and silent, so i abandoned that idea.

Go Up