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.