Stepper motor speed ramp

I am about as green as can be with steppers, stepper drivers, and Arduino. If that admission is not enough to send you away I'll be grateful! I have a Stepperonline Nema 23 stepper motor, one of their CL57Y drivers, an S-350 48v power supply and a uno. I have some code working but observe a behavior I don't understand. My expected result is the stepper should start with 1000 microseconds between pulses and decrement down to 1, and I should observe see the motor smoothly ramping up in rpm to its max rpm or the max the uno can send out pulses. In execution, what I see is the motor starts out as expected, but at around 200 rpm, suddenly it flips to its max rpm. If I uncomment the serial print, the motor will just barely rotate, and no speed increase is seen. It also happens if I add a delay (500) and the end of the code. Any suggestion will be appreciated.

Richard

//Pins definitions
#define pulPin 9 //step pin
#define dirPin 8 //direction

//Declare variables
float speedC=0; // current speed

//Setting parameters
uint16_t maxSpeed=0; //min time between pulses
uint16_t minSpeed=1000; //max time between pulses
float accel=1; //acceleration of the motor
bool leftDir=0; //direction value for left direction
bool rightDir=1; //direction value for right direction

void setup() {
//pins as input

//pins as output
pinMode(pulPin,OUTPUT); // set Pin9 as PUL
pinMode(dirPin,OUTPUT); // set Pin8 as DIR

Serial.begin(9600); //initialize the serial transmition at 9600 bauds

digitalWrite(dirPin,leftDir);
speedC=1000;
}

void loop() {

        digitalWrite(pulPin,HIGH); // Output high
        delayMicroseconds(speedC); // set rotate speed by delay
        digitalWrite(pulPin,LOW);  // Output low
        delayMicroseconds(speedC); // set rotate speed by delay

        speedC-=1;
        if(speedC<1) speedC=1;     // clumsey method to run motor as fast as Arduino can pump out pulses.
             
       // Serial.print("speedC="); Serial.println(speedC);
       // Serial.print(" speedM="); Serial.println(speedM);
 }

Why not use int, it’s greener.

1 Like

Thanks for the suggestion. The behavior remains mostly the same. The motor's top speed is higher. Now I can overdrive (1 microsecond will cause an alarm 5 will not and generate over 3,000rpm) the stepper driver causing it to alarm, so clearly, int v float is an improvement—however, the rapid ramping remains.

Ps. I'm not planning on using the stepper at 3000 rpm. The anticipated speed range is more in the realm of 60 to 1000 rpm.

Video demonstrating the issue: https://photos.app.goo.gl/FWc1ptmF5Snb2Lsu9

I’ve never seen a stepper that can go over 1500 RPM

According to the data sheet of this stepper motor driver...
Oops! The maximum step rate is 200kHz.
I think driver is PANIC when pulse rate from Arduino when exceeds it.

1 Like

Set this to a much higher baud rate, try 115200 then see what can be printed ?

1 Like

Serial print can be sloooow.
See my Multi-tasking in Arduino tutorial that has a complete stepper example using the AccelStepper library

For the problem with Serial.prints see my Arduino Serial I/O for the Real World tutorial.

1 Like

Does 200 kHz convert to 5 microseconds? So if I understand that, that would be the min delay between pulses to the stepper driver to not exceed 200kHz?

That helped until it lost a few bits and started spewing gibberish. Good info.

Yes it is.
But, as @LarryD says, whether the motor can follow is another matter.

1 Like

For your amusement, a stepper online stepper running at 3,200+ rpm. I do get the practical side as steppers as I understand as rpm increases lose torque and a fraction of the range for my application is useful. Here's a ten-second video of my stepper https://photos.app.goo.gl/cyec6StEjGcAUH6eA

1 Like

Very Nice !

1 Like

I tried adding a inelegant and sloppy (I have only been writing in the IDE for a few days so I am very confused and deficit in the language) counter (to only decrement speedC n iterations) to slow down the ramp. Didn't work so its commented out. Also I added a delay (500) as the only change and the motor doesn't run at all. Comment the delay out and all goes back to the original behavior.

void loop() {

        digitalWrite(pulPin,HIGH); // Output high
        delayMicroseconds(speedC); // set rotate speed by delay
        digitalWrite(pulPin,LOW);  // Output low
        delayMicroseconds(speedC); // set rotate speed by delay
        //pulse+=1;
        //if (pulse=500000);{
        speedC-=1;
        if(speedC<30) speedC=30;   // clumsy method  run limit motor speed.
        //pulse=0;}
        delay (500);
        
             
       //Serial.print("speedC="); Serial.println(speedC);
       // Serial.print(" speedM="); Serial.println(speedM);
 }

By the way, it's a fundamental story.
This driver is a closed loop stepper driver.
Looks like to be used in open loop, does it work correctly without an encoder signal?

The motor has an encoder Closed Loop Stepper Motor

BTW, if speedC is = 1, delayMicroseconds(speedC) will could be 4 µs.

“This function works very accurately in the range 3 microseconds and up.

I see, then there seems to be another reason why the lamp-up is failed.
Do you have an oscilloscope?

For a 16Mhz UNO
It takes 1.13µs to go from then end of loop() back to the beginning.

A digitalWrite() takes ≈ 6µs.


So I have learned a few things. I decided to create some hardcoded examples of speed in relationship to delays. Surprisingly (to me), it was non-linear and went off the rails at the 100-microsecond delay, then sort of comes to be predictable again by 60-microseconds delay. I have no idea if that kind of behavior is normal or not in steppers. This is the latest iteration of the and code, and the problems persist.

//Pins definitions
#define pulPin 9        // step pin 
#define dirPin 8        // direction 
#define buttonLPin 6    // left push button
#define buttonRPin 7    // right push button

//Declare variables
int speedC=0;        
int speedM=0;
int pulse=0;
int stepC=0;

//Setting parameters
uint16_t mindelay=100;    // the minium delay between pulses
uint16_t maxdelay=1000;   // the maxium delay between pulses
int delaystep=1;          // acceleration of the motor
bool leftDir=0;           // direction value for left direction
bool rightDir=1;          // direction value for right direction
int delayG=500;           // read somewhere that Delay can be sick and by declaring a global variable will cure.
int stepM=10;             // number of steps per intial/one-time momentary press of button

void setup() {
  //pins as input
  pinMode(buttonLPin,INPUT_PULLUP);         // set as input with pullup
  pinMode(buttonRPin,INPUT_PULLUP);         // set as input with pullup
  
  //pins as output
  pinMode(pulPin,OUTPUT);                   // set Pin9 as stepper driver PULSE
  pinMode(dirPin,OUTPUT);                   // set Pin8 as stepper driver DIRECTION

  //Serial.begin(38400); // initialize the serial transmition at 9600 bauds
  
  speedC=maxdelay ;                         // not needed
}

void loop() {

  if(digitalRead(buttonLPin)==LOW){         // if left button has been pressed 
    speedC=maxdelay;
    speedM=speedC/2;
    stepC=stepM;
      while (stepC > 0) { 
        digitalWrite(dirPin,leftDir);       // set direction
        digitalWrite(pulPin,HIGH);          // Initial step to give adequate time to release button befre continious operation.
        delayMicroseconds(mindelay);  
        digitalWrite(pulPin,LOW); 
        delayMicroseconds(mindelay);        // this should be 1 tenth/second in total delay. The motor doesn't pay attention to this block of code.
        stepC=stepC-1;
      }
    delay (delayG);                         // this delaty give time to release button before continious operaitons commences.  Used for preeise alignment. 
  
      while(digitalRead(buttonLPin)==LOW){  // Left button has been depressed
        digitalWrite(pulPin,HIGH);          // Output high
        delayMicroseconds(speedM);          // delay interval
        digitalWrite(pulPin,LOW);           // Output low
        delayMicroseconds(speedM);          // delay interval
        speedC-=delaystep;
        speedM=speedC/2;  
          if(speedC<mindelay) speedC=mindelay;   // clumsey method to limit speed

           //Serial.print("speedC="); Serial.print(speedC);
           //Serial.print(" speedM="); Serial.println(speedM);
           //Serial.print(" delaystep="); Serial.println(delaystep);
      }    
   }

  if(digitalRead(buttonRPin)==LOW){   // if right button has been pressed 
    speedC=maxdelay;
    speedM=speedC/2;
    stepC=stepM;
      while (stepC > 0) { 
        digitalWrite(dirPin,rightDir);     
        digitalWrite(pulPin,HIGH);        
        delayMicroseconds(1000);  
        digitalWrite(pulPin,LOW); 
        delayMicroseconds(1000);          
        stepC=stepC-1;
      }
    delay (delayG);
      
      while(digitalRead(buttonRPin)==LOW){
        speedM=speedC/2;
        digitalWrite(pulPin,HIGH); 
        delayMicroseconds(speedM); 
        digitalWrite(pulPin,LOW);  
        delayMicroseconds(speedM); 
        speedC-=delaystep;
    
          if(speedC<mindelay) speedC=mindelay;   
      }    
   }

}

I do wonder if creating an array with known values to rpm range and then walk through the array could be the solution I'm searching for. I don't know yet how to deal with an array in Arduino, but that is solvable.

In addition, I have found the verb delay (integer) does not seem to work. If I use a constant, it did not work. If I use a variable it does.

arrayPreformatted text