stepper: accelerating with new arduino library

Next up, a way of accelerating and decelerating a stepper motor. The mount can’t just whack in to top speed it needs to start off slow and speed up. So the idea is to be able to set the min speed, max speed, and the number of steps taken to accelerate. The result looks something like this:

An example sketch

/*
 * Accelerating Stepper
 * uses the Stepper_Aclr8 libruary
 *
 * created by Ben @ cape ealing
 * 25 May 2008
 *
 */

// include the libraries
#include 

// set up the stepper motor
// 200 steps per rev, wired to pins 4, 6, 5, and 7
Stepper_Aclr8 Stepper(200, 4, 6, 5, 7);

void setup()
{
  // set the minimum speed of the motor (5 revs per min)
  Stepper.setMinSpeed(5);
  // set the maximum speed of the motor (30 revs per min)
  Stepper.setMaxSpeed(30);
  // Accelerate over 200 steps
  Stepper.setAcc(200);
}

void loop()
{
// Take 600 steps forward
// 1st 200 steps:  will accelerate from 5 to 30 revs per min 
// 2nd 200 steps: will run at max speed
// 3rd 200 steps: will decelerate from 30 to 5 revs per min
Stepper.step(600);

// wait 2 seconds
delay(2000);

// and in the reverse direction
// this time it works out it can only accelerate
// for 100 steps, then steady for 100, then decelerate for 100
Stepper.step(-300);

// wait 2 seconds
delay(2000);

}

The Maths

The motor accelerates from the minimum to the maximum speed at a constant rate over the set number of steps. With the speed at a constant acceleration the delay between each steps changes at a non-constant rate as shown in the chart below.

Within the library, the maths is as follows:

creating a library

All of this needed a new library: StepperAxlr8.


The starting point is this tutorial. I won’t repeat what’s set out there but basically you need to write a header file (.h) and a cpp file (.cpp). As the tutorial explains: “You need at least two files for a library: a header file (w/ the extension .h) and the source file (w/ extension .cpp). The header file has definitions for the library: basically a listing of everything that’s inside; while the source file has the actual code.”

To do all this I used the free visual c++ express (download link above).

The Header File

/*
  StepperAxlr8.h - Stepper library with acceleration / deceleration 
  for Wiring/Arduino 
  - Version 1.0
 */

// ensure this library description is only included once
#ifndef Stepper_Aclr8_h
#define Stepper_Aclr8_h

// library interface description
class Stepper_Aclr8 {
  public:
 
	// constructor: 4 wire stepper motors only
	Stepper_Aclr8(int number_of_steps, int motor_pin_1, \
		int motor_pin_2, int motor_pin_3, int motor_pin_4);

	// min speed setter method:
    void setMinSpeed(long minSpeed);

    // max speed setter method:
    void setMaxSpeed(long maxSpeed);

    // accelerator method:
	void setAcc(double Accel);

    // mover method:
    void step(int steps_to_move);

    int version(void);

  private:
	// setup the motor
    void stepMotor(int thisStep);
   
	// Direction of rotation
	int direction;        
    // Speed in RPMs
	int speed;          
    // delay between steps, in ms, based on speed
    double step_delay;    
	// total number of steps this motor can take
	int number_of_steps;      
    // delay at 1 rev per minute
    double delay_1revpermin;
	// which step the motor is on
	int step_number;        
	//steps left
	int steps_left;    

	// motor pin numbers:
    int motor_pin_1;
    int motor_pin_2;
    int motor_pin_3;
    int motor_pin_4;
    
	// time stamp in ms of when the last step was taken
	long last_step_time;      

	//acceleration
	long motorSpeed;
	long motorMinSpeed;
	long motorMaxSpeed;
	long motorAccSteps;
	double motorAccelVal;

	// total steps for motor
	int total_steps_left;
	
	// which step number of steps to take
	int steps_no;

};

#endif

The CPP file

/*
  StepperAxlr8.cpp - Stepper library with acceleration / deceleration
  for Wiring/Arduino
  - Version 1.0
 */

#include "WProgram.h"
#include "Stepper_Aclr8.h"

/*
 *   Constructor for four-pin stepper motor
 *   Sets which wires should control the motor and
 *   primary variables used by motor.
 */

Stepper_Aclr8::Stepper_Aclr8(int number_of_steps, \
						   int motor_pin_1, int motor_pin_2, \
						   int motor_pin_3, int motor_pin_4)
{
  // Stepper Motor
  // which step the motor is on
  this->step_number = 0;
  // the motor speed, in revolutions per minute
  this->speed = 0;
  // motor direction
  this->direction = 0;
  // time stamp in ms of the last step taken
  this->last_step_time = 0;
  // total number of steps for this motor
  this->number_of_steps = number_of_steps;
  // find the delay nec at 1 rev per min
  this->delay_1revpermin = 60L * 1000L  / number_of_steps;

  // Arduino pins for the motor control connection
  this->motor_pin_1 = motor_pin_1;
  this->motor_pin_2 = motor_pin_2;
  this->motor_pin_3 = motor_pin_3;
  this->motor_pin_4 = motor_pin_4;

  // setup the pins on the microcontroller
  pinMode(this->motor_pin_1, OUTPUT);
  pinMode(this->motor_pin_2, OUTPUT);
  pinMode(this->motor_pin_3, OUTPUT);
  pinMode(this->motor_pin_4, OUTPUT);
}

/*
  Sets the minimum speed in revs per minute
*/
void Stepper_Aclr8::setMinSpeed(long minSpeed)
{
	this->motorMinSpeed = minSpeed;
}

/*
  Sets the maximum speed in revs per minute
*/
void Stepper_Aclr8::setMaxSpeed(long maxSpeed)
{
	this->motorMaxSpeed = maxSpeed;
}


/*
	Set the acceleration rate
 */
void Stepper_Aclr8::setAcc(double Accel)
{
	// Number of steps to accelerate over
	this->motorAccSteps = Accel;
	// Change in speed needed per step
	this->motorAccelVal = (this->motorMaxSpeed -this->motorMinSpeed) / (Accel-1);
}

void Stepper_Aclr8::step(int steps_to_move)
{
  // steps taken
  int steps_no= 1;
  // Steps for each motor to complete, and the total
  // how many steps to take for the motor
  int steps_left = abs(steps_to_move);
  // how many steps to take
  int total_steps_left = steps_left;

  // determine direction based on whether steps_to_mode is + or -:
  // First motor
  if (steps_to_move > 0) {this->direction = 1;}
  else
  {this->direction = 0;}

  // reset the motor speed
  this->speed = this->motorMinSpeed;

  // this is the loop for motor
  while(total_steps_left > 0) {

    //If the motor steps not completed it's steps then
    if (steps_left > 0) {

	  //1.a. if in the first third of total steps AND
	  if ((steps_left > abs(steps_to_move/3)) && \
		  (steps_left > (abs(steps_to_move/3)*2))) {
			  // 1.b if not yet at max speed
			  if (this->speed < this->motorMaxSpeed){
				  // get the new speed
				  this->speed = ((steps_no -1) * this->motorAccelVal) + this->motorMinSpeed; 
				  // get the new delay
				  step_delay = (this->delay_1revpermin / this->speed); 
              } // i.b.  end if: not yet max speed
	  } //1.a. end if: first third of total steps
			  
	  //2. if in middle third of steps do nothing

	  //3.a. if in the last third of total steps AND
	  if (steps_left <= (abs(steps_to_move)/3)) {
		  //3.b. if within deceleration zone
		  if (steps_left <= this->motorAccSteps) {
			  // get the new speed
			  this->speed = ((steps_left-1) * this->motorAccelVal) + this->motorMinSpeed; 
			  // get the new delay
			  step_delay = (this->delay_1revpermin / this->speed); 
		} //end if: within deceleration zone
	  } //3.a. end if: last third of total steps

      // move only if the appropriate delay has passed:
      if (millis() - this->last_step_time >= this->step_delay) {
        // get the timeStamp of when you stepped:
        this->last_step_time = millis();
          // increment or decrement the step number,
          // depending on direction:
          if (this->direction == 1) {
            this->step_number++;
            if (this->step_number == this->number_of_steps) {
            this->step_number = 0;
            }
          }
          else
		  {
            if (this->step_number == 0) {
            this->step_number = this->number_of_steps;
            }
          this->step_number--;
          }
      // in/decrement the steps left:
      steps_no++;
      steps_left--;
	  total_steps_left--;
      
	  // step the motor to step number 0, 1, 2, or 3:
      stepMotor(this->step_number % 4);
      }
    } //End: if (steps_left > 0)
  } //End: WHILE statement
} //End: void step()


/*
 * Moves the Motor forward or backwards
 */
void Stepper_Aclr8::stepMotor(int thisStep)
{
    switch (thisStep) {
      case 0:    // 1010
      digitalWrite(motor_pin_1, HIGH);
      digitalWrite(motor_pin_2, LOW);
      digitalWrite(motor_pin_3, HIGH);
      digitalWrite(motor_pin_4, LOW);
      break;
      case 1:    // 0110
      digitalWrite(motor_pin_1, LOW);
      digitalWrite(motor_pin_2, HIGH);
      digitalWrite(motor_pin_3, HIGH);
      digitalWrite(motor_pin_4, LOW);
      break;
      case 2:    //0101
      digitalWrite(motor_pin_1, LOW);
      digitalWrite(motor_pin_2, HIGH);
      digitalWrite(motor_pin_3, LOW);
      digitalWrite(motor_pin_4, HIGH);
      break;
      case 3:    //1001
      digitalWrite(motor_pin_1, HIGH);
      digitalWrite(motor_pin_2, LOW);
      digitalWrite(motor_pin_3, LOW);
      digitalWrite(motor_pin_4, HIGH);
      break;
    }
}

/*
  version() returns the version of the library:
*/
int Stepper_Aclr8::version(void)
{
  return 1;
}

Download

To use the library, just download the Stepper_Aclr8 files, drop them into the hardware/libraries folder, and unzip them.

Enjoy.

Ben

Post-script

In due course, I needed to find a way of ‘interrupting’ the step – ie the user needed to be able to cancel a step command part-way through. So, I added the following method:

  // Interrupt: set pin 13 high and read input on pin 18
  Stepper.setIpins(13, 18);

This is to be used with a matrix keypad. The idea is that one button on a keypad is the button a user presses to stop the movement. So, the library now has to know which pin (ie column or row on the keypad) is to be set ‘high’ and which pin (ie column or row) is to be read to see if a button has been pressed. So, on my set-up, I set pin 13 high (that sets column 2 high), and then I read pin 18 (analog pin 4). The library was amended so that as the ‘step’ command starts, the pin13 is set high, and then pin 18 is read after each step. Easy.

However, the tricky bit was as follows: if the motor is to be interrupted, then I need to know how many steps were taken before the interruption. The idea in due course is to have two stepper motors, so I couldn’t just turn the class into a function (although that’s the obvious and easy thing to do). Instead, after a bit of searching and a query on the arduino website, I learnt how to use a ‘pointer’ to pass a variable (and more) between the sketch and the class.

The important code in the sketch is this addition:

int StepsToDo = 0;

void setup()
{
[...]
  // Pass the address of the StepsToDo variable
  Stepper.setToDoAddress(&StepsToDo);
}

That line passes the ‘address’ of the StepsToDo integer – ie the physical address of the integer on the chip. Then, the library can amend the data at that ‘address’. That means that when the sketch asks for the value of the integer, it ‘reads’ the value at that address – which has been changed by the library. This basically provides a rather convulted way of passing variables between the sketch and the library.

This explanation on wikipedia sets it out pretty well, and this explanation from arduino helped piece it together.

Insofar as the library itself is concerned, the method looks like this in the header file:

    // define method and global variable to hold data of step address
	void setToDoAddress(int *StepsAddress);
	int *gStepAdd;

And this in the cpp file:

void Stepper_Aclr8_i::setToDoAddress(int* StepsAddress)
{
	gStepAdd = StepsAddress;
}
[...]
    // set a new value at the StepsLeft address (ie StepsToDo variable)
    *gStepAdd = total_steps_left;

The example sketch with the library prints the number of steps to be stepped, and then how many are left to step after the interrupt. Like this:

StepsToDo = 200;
// Print the StepsToDo value before starting steps
Serial.println(StepsToDo);
// Step 200 steps
Stepper.step(StepsToDo);
// Print the StepsToDo value after starting steps,
// if the steps complete then this will be 0, if
// the steps are interupted then this is the number of
// steps left to do.
Serial.println(StepsToDo);

The whole library, Stepper_Axlr8_i, can be downloaded here.

B

post a comment...

you must be logged in to post a comment.