Motor shield - stepper speed

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
drcrmic
 
Posts: 6
Joined: Wed Dec 18, 2013 12:02 pm

Motor shield - stepper speed

Post by drcrmic »

Hi,

I’m using the Adafruit 324 stepper (200 steps/rev, 12 V, 0.35 A) on an Adafruit Motor/Stepper/Servo Shield for Arduino v2 Kit - v2.0. The stepper is powered directly via a 12 VDC/1 amp power supply connected to the shield. I’m using the newest version of Adafruit_Motorshield to control the stepper.
For the intended application (movement of a linear stage with a defined speed) I require rather exact speed control of my stepper.

However, I noticed that the actual speed (rpm) of the stepper deviates from the set speed, independent of the operation mode (Double/Interleave/Microstep). With increasing speeds, the discrepancy increases significantly (e.g. in the case of the sample code below, the actual forward speed is around 3.7 rpm, compared to a set speed of 5 rpm; the backward speed is around 6.6 rpm instead of 12 rpm).
In all cases, the total number of steps (= the moving distance of my linear stage) remains correct.

After some tests at various operation parameters and some calculations, I found out that for each step (or in the case of microstepping, microstep), I “lose” about 1.3 ms, independent of the operation mode and the set speed, so I have a cumulative effect - each step takes 1.3 ms longer than it should. Where does this value come from? Is there a possibility to correct for this?

I already increased the i2c bus rate to 400 kHz, as recommended in the FAQ (http://learn.adafruit.com/adafruit-moto ... rduino/faq), but I could not notice any improvements. Also, I tried to see whether the voltage of the power supply could be a problem, but I obtained the exact same results with a 9 V supply instead of 12 V.

Here’s the sample code I used for the project (I used a button push to start the routine):

Code: Select all

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 

Adafruit_StepperMotor *myMotor = AFMS.getStepper(200, 2);

int switchPin = 6;              
int val;                        

void setup() {
  digitalWrite(switchPin, HIGH);       
  Serial.begin(9600);           
  Serial.println("Stepper test!");
  AFMS.begin();
  myMotor->setSpeed(320);
}

void loop() {
  while (digitalRead(switchPin) == LOW)
      {
       myMotor->setSpeed(5);                
       myMotor->step(2000, FORWARD, MICROSTEP);
       delay(1000);                           
       myMotor->setSpeed(12);                   
       myMotor->step(2000, BACKWARD, MICROSTEP);    
       }
}
Do you have any ideas/suggestions how to get the correct speed?

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Motor shield - stepper speed

Post by adafruit_support_bill »

Interesting question. Looking at the library code, I see a few things that could cause a deviation from the desired step-rate:

The step period is implemented as a delay, so any overhead (including the i2c communication with the shield) will be added to the step period.
The delay is in increments of 1 mS, so fractions of a millisecond will be truncated. There is code to compensate for this, but it is only applied after 1000 steps. Up to that point, the error will be cumulative.

This overhead shouid not add up to 1.3mS. But considering that you are using Microstep mode, you have to multiply the overhead by 16 and it starts to sound possible. Do you get less speed error using Single or Double?

Another variable is the speed of the Arduino clock itself. Most current models use a resonator instead of a crystal for the master clock. These are less precise than crystals, but should still be within a few percent.

drcrmic
 
Posts: 6
Joined: Wed Dec 18, 2013 12:02 pm

Re: Motor shield - stepper speed

Post by drcrmic »

The speed error per step is present in all operation modes, so there's the same step error in Double or Interleave mode as well - and it's always the exact same amount of about 1.3 ms per step or half-step, respectively.
In microstep mode, the error is 1.3 ms per microstep (so the error per full step is actually 1.3 x 16 = 20.8 ms, in case of the 16x microstep mode).

The error is independent of the number of steps and independent of the speed chosen.

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Motor shield - stepper speed

Post by adafruit_support_bill »

What Arduino are you using to control the shield?

drcrmic
 
Posts: 6
Joined: Wed Dec 18, 2013 12:02 pm

Re: Motor shield - stepper speed

Post by drcrmic »

I use a pre-assembled Arduino Uno R3 (http://www.adafruit.com/products/68).

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Motor shield - stepper speed

Post by adafruit_support_bill »

Not sure what would account for that level of overhead. I'll have to try to reproduce this in the lab, although it will probably be a while before I can get to it.
In any case, the delay-based timing is never going to be ultra-precise. The best way to assure a precise step rate is to set up a timer interrupt at the desired step frequency and call "onestep()" on each timer tick.

drcrmic
 
Posts: 6
Joined: Wed Dec 18, 2013 12:02 pm

Re: Motor shield - stepper speed

Post by drcrmic »

Ok, I'll try the suggested method of setting up the timer interrupt.

Thanks for the suggestion!

drcrmic
 
Posts: 6
Joined: Wed Dec 18, 2013 12:02 pm

Re: Motor shield - stepper speed

Post by drcrmic »

Just a quick follow-up:

I tried the timer interrupt method suggested above, and it works very nicely. The speed is now correct.
What I did was I first set up timer2 with a frequency of 10 kHz. In ISR I included a "stepRate" counter setting itself back to 0 after a certain amount of steps corresponding to my steprate interval in milliseconds multiplied by 10 (the multiplication comes from the 10 kHz above - I chose this value for increased accuracy, i.e. 0.1 ms steps).

In my loop, I finally included an if-statement calling for a onestep() operation after the above mentioned "stepRate" counter sets itself back to 0.

I attached the code below, in case someone is interested - I know it's probably not the most elegant solution, but for me it seems to be working. It basically calls for a defined forward motion with 30 rpm for 10 revolutions once a button is pressed.

Code: Select all

#include <Wire.h>
#include <Adafruit_MotorShield.h>
#include "utility/Adafruit_PWMServoDriver.h"

Adafruit_MotorShield AFMS = Adafruit_MotorShield(); 

// Connect a stepper motor with 200 steps per revolution (1.8 degree)
// to motor port #2 (M3 and M4)
Adafruit_StepperMotor *myMotor = AFMS.getStepper(200, 2);

int switchPin = 6;               // switch C is connected to pin 6
int val;                         // variable for reading the pin status

int currState = HIGH;
int prevState = HIGH;

long maxSteps = 2000;            // total number of steps to be taken, in this case 2000 (=10 revolutions)
int stepsTaken = 0;
long lastStepTime = 0;
int var = 0;
volatile int stepRate = 0;       // introduction of a volatile variable to be read/changed in ISR

void setup() {
  pinMode(switchPin, INPUT);     // Set the switch pin as input
  digitalWrite(switchPin, HIGH); // turn on pullup resistors


  cli();         //set timer2 interrupt at 10kHz
  TCCR2A = 0;    // set TCCR2A to 0
  TCCR2B = 0;    // set TCCR2B to 0
  TCNT2  = 0;    // set counter value to 0
  // set the compare match register for 10khz
  OCR2A = 199;// = (16*10^6) / (10000*8) - 1 (for timer2, this must be <256)
  TCCR2A |= (1 << WGM21);   // turn on CTC mode
  TCCR2B |= (1 << CS21);    // Set prescaler to 8: 1 for the CS21 bit
  TIMSK2 |= (1 << OCIE2A);  // enable timer compare interrupt
  sei();

  Serial.begin(9600);     
  AFMS.begin();
}

ISR(TIMER2_COMPA_vect) {
  //increment stepRate; increases every 0.1 ms
  stepRate+=1;
  if (stepRate==100){    //insert the desired stepRateA interval in milliseconds multiplied by 10, in this case 10 ms - corresponds to 30 rpm
    stepRate=0;
  }
}

void loop() {
   currState = digitalRead(switchPin);
   if(currState != prevState)
   {
      if(currState == LOW)  // when the button is pushed, the ProgramButton routine is started and executed exactly one time
      { 
        ProgramButton();
      }
      else
      { 
      }
   }
   prevState = currState;
}

void ProgramButton(){
   var = 0;
   while(var < 1)
   {
       if (stepRate==0)   // Time to take another step
      {
        myMotor->onestep(FORWARD, DOUBLE);
        if (++stepsTaken >= maxSteps)
        {
          var++;
          stepsTaken = 0;
        }
      }    
   }
}
On a sidenote, before I tried the interrupt method, I tried to call for onestep() by using a millis() command. In this case, the overhead for each step was exactly 1.1 ms, independent of using double or microstep mode. Could it have to do something with the timer that's used for this command? When I start my program, the Serial monitor reports "Estimated pre-scale: 2.81, Final pre-scale: 3". What does that mean?

User avatar
adafruit_support_bill
 
Posts: 88093
Joined: Sat Feb 07, 2009 10:11 am

Re: Motor shield - stepper speed

Post by adafruit_support_bill »

Nice work. Thanks for posting your results. I'm still curious about the step overhead. But I have a few more tasks in the queue before I can dig into it.
When I start my program, the Serial monitor reports "Estimated pre-scale: 2.81, Final pre-scale: 3". What does that mean?
The prescale calculation is for the PWM frequency. It does not affect step-rate (that I know of).

drcrmic
 
Posts: 6
Joined: Wed Dec 18, 2013 12:02 pm

Re: Motor shield - stepper speed

Post by drcrmic »

Okay, good to know.

Again, thanks for your suggestions, they really helped me gain insight in some of the more "advanced" functions (at least, to me as a beginner).

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”