Stepper PWM and unexpected results

I have a stepper motor in my project that I want to drive at a number of different user defined speeds across a predefined distance. The speeds I want to achieve range from a 15 second trip, to 24 hours across this distance. I am having success with the trips from 15 seconds all the way up to 25 minutes, however when I get to 30 minutes I start to get unexpected results.

The user defined speeds are as follows:

Seconds Minutes Hours
15 1 1
20 2 2
25 3 3
30 4 4
35 5 5
40 10 6
45 15 9
50 20 12
55 25 18
30 24
35
40
45
50
55

To implement the stepper speed, I used a microsecond delay in the 'for' loop below to generate the pulse and define the stepper speed. I calibrated the distance traveled based on the 15 second trip, and then did a linear extrapolation to find the speedValue for the rest of the trips. As stated above, it works great up until the 30 minute trip.

for(long i = 0; i < maxStepsForTrip; i++){
     digitalWrite(pinStep, HIGH);
     delayMicroseconds(speedValue);
     digitalWrite(pinStep, LOW);
     delayMicroseconds(speedValue);
     stepsTaken++;
   }

I'm having trouble trying to understand why the motor is acting up when the microsecond delay gets too large. Shouldn't the longer delay simply cause the pulse to be larger and the step to be slower? I haven't been able to find any data anywhere that says a stepper should have a minimum speed. I looked at the values of speedValue at 25 minutes (which is 15500 microseconds) and at 30 minutes (which was 18600) and in my mind these shouldn't produce terribly different results. I mean it's not like we're changing orders of magnitude or anything.

To try and remedy this situation, I've tried a 'for' loop with a millisecond delay and the stepper doesn't like that at all.

Can someone please help with this?

Thanks in advance! :astonished:

I think I found the answer to the problem.

delayMicroseconds()

Description
Pauses the program for the amount of time (in microseconds) specified as parameter. There are a thousand microseconds in a millisecond, and a million microseconds in a second.
Currently, the largest value that will produce an accurate delay is 16383. This could change in future Arduino releases. For delays longer than a few thousand microseconds, you should use delay() instead.

But I'm still wondering why a regular delay() won't work for stepper PWM?

It is not PWM you are using.

There is no reason why the normal delay will not work but your top speed is limited.

So if it's not PWM what is it? I thought PWM was changing the pulse frequency to the coils...ie adjusting the delay between pulses. And I'm not worried about max speed...it's minimum speed or very low speeds I need.

Do you have any suggestions as to why I'm experiencing these problems?

PWM stands for Pulse Width Modulation so the ratio of the on to the off time controls the average value of the waveform. This is nothing to do with what you are doing.
From the very littl you have posted all you are doing is producing a variable period square wave. This will be the same no matter how you generate the delay.
So how is your stepping motor being driven? What driver are you using? How is it wired up? What sort of stepping motor do you have? What is the full code?

For more information about what PWM is see:-
http://www.thebox.myzen.co.uk/Tutorial/PWM.html
For information about stepping motors see:-
http://www.thebox.myzen.co.uk/Workshop/Motors_3.html
and
http://www.thebox.myzen.co.uk/Workshop/Motors_4.html

however when I get to 30 minutes I start to get unexpected results.

It also helps to know what results these are and what you expect.

Not seeing your entire code... It would appear to be something called OVERFLOW. You are getting too big a number for the operation and what is really being worked with is a truncated form of the number you gave it.

Take a look at the BlinkWithotDelay sktch and see how they did the timing there. Only way to have really long delays.

Hey guys, thanks for the replies.

@Grumpy_Mike, thanks for the info regarding PWM. As far as the results go, here is a sample around the breakdown area...I've omitted a lot of other data as it is working correctly.

EXPECTED (ms) ACTUAL (ms)
. .
. .
. .
1200000 1166457
1500000 1457923
1800000 208947
2100000 500412
2400000 791878
. .
. .
. .

The data from the first three results is not entirely accurate but it's much closer than the last three results.

@kf2qd, I believe you're right about the overflow. If you check out my second post you'll see the Arduino reference entry for delayMicroseconds(). It only provides accurate values for numbers up to 16383. I see in the blink without delay example that they are using unsigned long variables but delayMicroseconds() cannot use values of that magnitude anyway.

I'll attach relevant sections of my code and maybe someone can spot what I'm doing wrong. (I have omitted certain irrelevant sections of my code...just in case you see variables or function calls with no other reference)

#include <Stepper.h>

const int swStart = 1;            
const int pinStep = 5;          
const int pinDir = 6;       
const int pot = A0;               

int potValue;              
int units = 1;                  
int EDsleepFlag = 0;              
int lockLastState;              
long speedValue;  
long stepsTaken;

void setup(){  
  Serial.begin(9600);
  pinMode(swStart, INPUT);
  pinMode(pinStep, OUTPUT);
  pinMode(pinDir, OUTPUT);
  pinMode(pot, INPUT);
}

void displaySpeed(){
  lcd.setCursor(0,1);  
  if(units == 1){    
    lcd.print("seconds  ");
    if(potValue >= 0 && potValue < 113){lcd.setCursor(0,0);     lcd.print("15     ");    speedValue = 115;}
    if(potValue >= 113 && potValue < 226){lcd.setCursor(0,0);   lcd.print("20     ");    speedValue = 207;}
    if(potValue >= 226 && potValue < 339){lcd.setCursor(0,0);   lcd.print("25     ");    speedValue = 258;}
    if(potValue >= 339 && potValue < 452){lcd.setCursor(0,0);   lcd.print("30     ");    speedValue = 310;}
    if(potValue >= 452 && potValue < 565){lcd.setCursor(0,0);   lcd.print("35     ");    speedValue = 362;}
    if(potValue >= 565 && potValue < 678){lcd.setCursor(0,0);   lcd.print("40     ");    speedValue = 413;}
    if(potValue >= 678 && potValue < 791){lcd.setCursor(0,0);   lcd.print("45     ");    speedValue = 465;}
    if(potValue >= 791 && potValue < 904){lcd.setCursor(0,0);   lcd.print("50     ");    speedValue = 517;}
    if(potValue >= 904 && potValue < 1023){lcd.setCursor(0,0);  lcd.print("55     ");    speedValue = 568;}
  }
  if(units == 2){
    lcd.print("minutes  ");
    if(potValue >= 0 && potValue < 68){lcd.setCursor(0,0);      lcd.print("1      ");    speedValue = 620;}
    if(potValue >= 68 && potValue < 136){lcd.setCursor(0,0);    lcd.print("2      ");    speedValue = 1240;}
    if(potValue >= 136 && potValue < 204){lcd.setCursor(0,0);   lcd.print("3      ");    speedValue = 1860;}
    if(potValue >= 204 && potValue < 272){lcd.setCursor(0,0);   lcd.print("4      ");    speedValue = 2480;}
    if(potValue >= 272 && potValue < 340){lcd.setCursor(0,0);   lcd.print("5      ");    speedValue = 3100;}
    if(potValue >= 340 && potValue < 408){lcd.setCursor(0,0);   lcd.print("10     ");    speedValue = 6200;}
    if(potValue >= 408 && potValue < 476){lcd.setCursor(0,0);   lcd.print("15     ");    speedValue = 9300;}
    if(potValue >= 476 && potValue < 544){lcd.setCursor(0,0);   lcd.print("20     ");    speedValue = 12400;}
    if(potValue >= 544 && potValue < 612){lcd.setCursor(0,0);   lcd.print("25     ");    speedValue = 15500;}
    if(potValue >= 612 && potValue < 680){lcd.setCursor(0,0);   lcd.print("30     ");    speedValue = 18600;}
    if(potValue >= 680 && potValue < 748){lcd.setCursor(0,0);   lcd.print("35     ");    speedValue = 21700;}
    if(potValue >= 748 && potValue < 816){lcd.setCursor(0,0);   lcd.print("40     ");    speedValue = 24800;}
    if(potValue >= 816 && potValue < 884){lcd.setCursor(0,0);   lcd.print("45     ");    speedValue = 27900;}
    if(potValue >= 884 && potValue < 952){lcd.setCursor(0,0);   lcd.print("50     ");    speedValue = 31000;}
    if(potValue >= 952 && potValue < 1023){lcd.setCursor(0,0);  lcd.print("55     ");    speedValue = 34100;}
  }
  if(units == 3){
    lcd.print("hours    ");
    if(potValue >= 0 && potValue < 102){lcd.setCursor(0,0);     lcd.print("1      ");    speedValue = 37200;}
    if(potValue >= 102 && potValue < 204){lcd.setCursor(0,0);   lcd.print("2      ");    speedValue = 74400;}
    if(potValue >= 204 && potValue < 306){lcd.setCursor(0,0);   lcd.print("3      ");    speedValue = 111600;}
    if(potValue >= 306 && potValue < 408){lcd.setCursor(0,0);   lcd.print("4      ");    speedValue = 148800;}
    if(potValue >= 408 && potValue < 510){lcd.setCursor(0,0);   lcd.print("5      ");    speedValue = 186000;}
    if(potValue >= 510 && potValue < 612){lcd.setCursor(0,0);   lcd.print("6      ");    speedValue = 223200;}
    if(potValue >= 612 && potValue < 714){lcd.setCursor(0,0);   lcd.print("9      ");    speedValue = 334800;}
    if(potValue >= 714 && potValue < 816){lcd.setCursor(0,0);   lcd.print("12     ");    speedValue = 446400;}
    if(potValue >= 816 && potValue < 918){lcd.setCursor(0,0);   lcd.print("18     ");    speedValue = 669600;}
    if(potValue >= 918 && potValue < 1023){lcd.setCursor(0,0);  lcd.print("24     ");    speedValue = 892800;}
  }
}

void stepperMove(){
   stepsTaken = 0;
   long time1 = millis();
   
   for(long i = 0; i < 46400; i++){
     digitalWrite(pinStep, HIGH);
     delayMicroseconds(speedValue);
     digitalWrite(pinStep, LOW);
     delayMicroseconds(speedValue);
     stepsTaken++;
   }
   
   long time2 = millis();
   long timeForStep = (time2 - time1);
   Serial.print(timeForStep);
   Serial.print(" ms");
   Serial.print("\n");
}

void loop(){  
  potValue = analogRead(pot);
  displaySpeed();  
  startSw.update();
  
  if(startSw.read()){
   ledBlinkFive();
   stepperMove();
  }  
}

I'm still not sure why a regular delay won't work in between the digitalWrite() calls in my stepperMove() function.