Pages: [1]   Go Down
Author Topic: Stepper PWM and unexpected results  (Read 780 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Code:
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!  smiley-eek
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think I found the answer to the problem.

Quote
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?
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 627
Posts: 34220
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

It is not PWM you are using.

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

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 627
Posts: 34220
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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?
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 627
Posts: 34220
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

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

Quote
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.
Logged

South Texas
Offline Offline
Edison Member
*
Karma: 8
Posts: 1025
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.
Logged

Offline Offline
Newbie
*
Karma: 0
Posts: 21
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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)

Code:
#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.
Logged

Pages: [1]   Go Up
Jump to: