From 9e5ca94ea7e1284af0bda8d0b387fe6644c129ba Mon Sep 17 00:00:00 2001 From: smike Date: Fri, 6 Jan 2012 03:41:17 -0500 Subject: [PATCH 1/8] Calculating stepper delays in micros instead of millis to get better speed resolution. Also optimized step routine a bit by removing extra call to get time. --- libraries/Stepper/Stepper.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/libraries/Stepper/Stepper.cpp b/libraries/Stepper/Stepper.cpp index 5d6b5e53664..66761168b1e 100644 --- a/libraries/Stepper/Stepper.cpp +++ b/libraries/Stepper/Stepper.cpp @@ -37,12 +37,8 @@ 3 1 0 4 0 0 - The circuits can be found at - -http://www.arduino.cc/en/Tutorial/Stepper - - - */ + The circuits can be found at http://www.arduino.cc/en/Tutorial/Stepper +*/ #include "Arduino.h" @@ -112,7 +108,7 @@ Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2, int moto */ void Stepper::setSpeed(long whatSpeed) { - this->step_delay = 60L * 1000L / this->number_of_steps / whatSpeed; + this->step_delay = 60L * 1000000L / this->number_of_steps / whatSpeed; } /* @@ -126,14 +122,15 @@ void Stepper::step(int steps_to_move) // determine direction based on whether steps_to_mode is + or -: if (steps_to_move > 0) {this->direction = 1;} if (steps_to_move < 0) {this->direction = 0;} - - + // decrement the number of steps, moving one step each time: while(steps_left > 0) { - // move only if the appropriate delay has passed: - if (millis() - this->last_step_time >= this->step_delay) { + unsigned long now = micros(); + long elapsed_time = now - this->last_step_time; + // move only if the appropriate delay has passed: + if (elapsed_time - this->last_step_time >= this->step_delay) { // get the timeStamp of when you stepped: - this->last_step_time = millis(); + this->last_step_time = now; // increment or decrement the step number, // depending on direction: if (this->direction == 1) { From eda1bc4d7d0efcfd7e2ba24f17ce3c7e4b97f338 Mon Sep 17 00:00:00 2001 From: smike Date: Fri, 6 Jan 2012 04:42:34 -0500 Subject: [PATCH 2/8] Implementing half-stepping and wave drive. --- libraries/Stepper/Stepper.cpp | 163 ++++++++++++++++++++++----------- libraries/Stepper/Stepper.h | 50 +++++++++- libraries/Stepper/keywords.txt | 1 + 3 files changed, 159 insertions(+), 55 deletions(-) diff --git a/libraries/Stepper/Stepper.cpp b/libraries/Stepper/Stepper.cpp index 66761168b1e..90c6f9b7109 100644 --- a/libraries/Stepper/Stepper.cpp +++ b/libraries/Stepper/Stepper.cpp @@ -37,13 +37,56 @@ 3 1 0 4 0 0 + For 4 wire configurations only, the motor can be driven with full-stepping (the default), + half-stepping, or a wave drive. + + Half-stepping will halve the size of the motor's step, giving it double the resolution. + Remember that the amount of steps you pass to step() will be interpreted based on the current + value of the drive type. The sequence of control signals is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 0 1 0 + 3 0 1 1 0 + 4 0 1 0 0 + 5 0 1 0 1 + 6 0 0 0 1 + 7 1 0 0 1 + 8 1 0 0 0 + + Wave drive mode only keeps one phase on a time. This means that the motor will use less power, but + will also only have half of the torque of full-stepping. The sequence of control signals is as + follows: + + Step C0 C1 C2 C3 + 1 0 0 1 0 + 2 0 1 0 0 + 3 0 0 0 1 + 4 1 0 0 0 + The circuits can be found at http://www.arduino.cc/en/Tutorial/Stepper + More info on the different types of drives can be found at + http://www.wikipedia.org/wiki/Stepper_motor#Phase_current_waveforms */ #include "Arduino.h" #include "Stepper.h" +// Only even rows are used for full-stepping. +// Only odd rows are used for wave-stepping. +// All rows are used for half-stepping. +const int Stepper::STEP_VALUES[8][4] = { + {HIGH, LOW, HIGH, LOW}, // not used for wave-stepping + {LOW, LOW, HIGH, LOW}, // not used for full-stepping + {LOW, HIGH, HIGH, LOW}, // not used for wave-stepping + {LOW, HIGH, LOW, LOW}, // not used for full-stepping + {LOW, HIGH, LOW, HIGH}, // not used for wave-stepping + {LOW, LOW, LOW, HIGH}, // not used for full-stepping + {HIGH, LOW, LOW, HIGH}, // not used for wave-stepping + {HIGH, LOW, LOW, LOW} // not used for full-stepping +}; + /* * two-wire constructor. * Sets which wires should control the motor. @@ -55,6 +98,7 @@ Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2) this->direction = 0; // motor direction this->last_step_time = 0; // time stamp in ms of the last step taken this->number_of_steps = number_of_steps; // total number of steps for this motor + this->drive_type = FullStep; // Arduino pins for the motor control connection: this->motor_pin_1 = motor_pin_1; @@ -85,6 +129,7 @@ Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2, int moto this->direction = 0; // motor direction this->last_step_time = 0; // time stamp in ms of the last step taken this->number_of_steps = number_of_steps; // total number of steps for this motor + this->drive_type = FullStep; // Arduino pins for the motor control connection: this->motor_pin_1 = motor_pin_1; @@ -104,13 +149,20 @@ Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2, int moto /* Sets the speed in revs per minute - */ void Stepper::setSpeed(long whatSpeed) { this->step_delay = 60L * 1000000L / this->number_of_steps / whatSpeed; } +/* + Sets the drive type +*/ +void Stepper::setDriveType(DriveType drive_type) +{ + this->drive_type = drive_type; +} + /* Moves the motor steps_to_move steps. If the number is negative, the motor moves in the reverse direction. @@ -123,32 +175,40 @@ void Stepper::step(int steps_to_move) if (steps_to_move > 0) {this->direction = 1;} if (steps_to_move < 0) {this->direction = 0;} + int number_of_steps = this->number_of_steps; + unsigned long step_delay = this->step_delay; + if (this->drive_type == HalfStep) { + // There are twice as many steps, adjust appropriately. + number_of_steps *= 2; + step_delay /= 2; + } + // decrement the number of steps, moving one step each time: while(steps_left > 0) { unsigned long now = micros(); long elapsed_time = now - this->last_step_time; // move only if the appropriate delay has passed: - if (elapsed_time - this->last_step_time >= this->step_delay) { + if (elapsed_time >= step_delay) { // get the timeStamp of when you stepped: this->last_step_time = now; // increment or decrement the step number, // depending on direction: if (this->direction == 1) { this->step_number++; - if (this->step_number == this->number_of_steps) { + if (this->step_number >= number_of_steps) { this->step_number = 0; } } else { - if (this->step_number == 0) { - this->step_number = this->number_of_steps; + if (this->step_number <= 0) { + this->step_number = number_of_steps; } this->step_number--; } // decrement the steps left: steps_left--; - // step the motor to step number 0, 1, 2, or 3: - stepMotor(this->step_number % 4); + // step the motor: + stepMotor(); } } } @@ -156,55 +216,56 @@ void Stepper::step(int steps_to_move) /* * Moves the motor forward or backwards. */ -void Stepper::stepMotor(int thisStep) -{ - if (this->pin_count == 2) { - switch (thisStep) { - case 0: /* 01 */ - digitalWrite(motor_pin_1, LOW); - digitalWrite(motor_pin_2, HIGH); +void Stepper::stepMotor() { + switch(this->drive_type) { + case FullStep: + fullStepMotor(); break; - case 1: /* 11 */ - digitalWrite(motor_pin_1, HIGH); - digitalWrite(motor_pin_2, HIGH); + case HalfStep: + halfStepMotor(); break; - case 2: /* 10 */ - digitalWrite(motor_pin_1, HIGH); - digitalWrite(motor_pin_2, LOW); + case Wave: + waveStepMotor(); break; - case 3: /* 00 */ - digitalWrite(motor_pin_1, LOW); - digitalWrite(motor_pin_2, LOW); - break; - } } +} + +void Stepper::fullStepMotor() +{ + const int* step_row = STEP_VALUES[(this->step_number % 4) * 2]; // full-stepping only uses even rows + if (this->pin_count == 2) { + // For 2-wire configurations we take the middle two pins + digitalWrite(motor_pin_2, step_row[1]); + digitalWrite(motor_pin_3, step_row[2]); + } else if (this->pin_count == 4) { + digitalWrite(motor_pin_1, step_row[0]); + digitalWrite(motor_pin_2, step_row[1]); + digitalWrite(motor_pin_3, step_row[2]); + digitalWrite(motor_pin_4, step_row[3]); + } +} + +void Stepper::halfStepMotor() +{ + // Only 4-wire configurations can be half-stepped. + if (this->pin_count == 4) { + const int* step_row = STEP_VALUES[this->step_number % 8]; + digitalWrite(motor_pin_1, step_row[0]); + digitalWrite(motor_pin_2, step_row[1]); + digitalWrite(motor_pin_3, step_row[2]); + digitalWrite(motor_pin_4, step_row[3]); + } +} + +void Stepper::waveStepMotor() +{ + // Only 4-wire configurations can be wave-driven. if (this->pin_count == 4) { - 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; - } + const int* step_row = STEP_VALUES[(this->step_number % 4) * 2 + 1]; // wave drive only uses odd rows + digitalWrite(motor_pin_1, step_row[0]); + digitalWrite(motor_pin_2, step_row[1]); + digitalWrite(motor_pin_3, step_row[2]); + digitalWrite(motor_pin_4, step_row[3]); } } diff --git a/libraries/Stepper/Stepper.h b/libraries/Stepper/Stepper.h index 4094aee9077..55fef1e5967 100644 --- a/libraries/Stepper/Stepper.h +++ b/libraries/Stepper/Stepper.h @@ -37,8 +37,36 @@ 3 1 0 4 0 0 - The circuits can be found at - http://www.arduino.cc/en/Tutorial/Stepper + For 4 wire configurations only, the motor can be driven with full-stepping (the default), + half-stepping, or a wave drive. + + Half-stepping will halve the size of the motor's step, giving it double the resolution. + Remember that the amount of steps you pass to step() will be interpreted based on the current + value of the drive type. The sequence of control signals is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 0 1 0 + 3 0 1 1 0 + 4 0 1 0 0 + 5 0 1 0 1 + 6 0 0 0 1 + 7 1 0 0 1 + 8 1 0 0 0 + + Wave drive mode only keeps one phase on a time. This means that the motor will use less power, but + will also only have half of the torque of full-stepping. The sequence of control signals is as + follows: + + Step C0 C1 C2 C3 + 1 0 0 1 0 + 2 0 1 0 0 + 3 0 0 0 1 + 4 1 0 0 0 + + The circuits can be found at http://www.arduino.cc/en/Tutorial/Stepper + More info on the different types of drives can be found at + http://www.wikipedia.org/wiki/Stepper_motor#Phase_current_waveforms */ // ensure this library description is only included once @@ -48,6 +76,8 @@ // library interface description class Stepper { public: + enum DriveType { FullStep, HalfStep, Wave }; + // constructors: Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2); Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4); @@ -55,13 +85,26 @@ class Stepper { // speed setter method: void setSpeed(long whatSpeed); + // drive type setter method: + void setDriveType(DriveType drive_type); + // mover method: void step(int number_of_steps); int version(void); private: - void stepMotor(int this_step); + void stepMotor(); + void fullStepMotor(); + void halfStepMotor(); + void waveStepMotor(); + + // Only even rows are used for full-stepping. + // Only odd rows are used for wave-stepping. + // All rows are used for half-stepping. + static const int STEP_VALUES[8][4]; + + DriveType drive_type; // What kind of stepping to use. Defaults to full. int direction; // Direction of rotation int speed; // Speed in RPMs @@ -80,4 +123,3 @@ class Stepper { }; #endif - diff --git a/libraries/Stepper/keywords.txt b/libraries/Stepper/keywords.txt index 19a0fadf2bd..32e8ea44c3f 100644 --- a/libraries/Stepper/keywords.txt +++ b/libraries/Stepper/keywords.txt @@ -14,6 +14,7 @@ Stepper KEYWORD1 step KEYWORD2 setSpeed KEYWORD2 +setDriveType KEYWORD2 version KEYWORD2 ###################################### From 9d62deccbd2742bcf5fa6e573f4da54a3b4305b4 Mon Sep 17 00:00:00 2001 From: smike Date: Tue, 10 Jan 2012 00:23:32 -0500 Subject: [PATCH 3/8] Improvements to Stepper library: - drive multiple steppers in unison - halt in the middle of a step sequence - callback in step loop - some optimizations for code size. code size is still ~2k larger than the previous version, however. Also, importing a simplified vector impl found on arduino forums. --- libraries/Stepper/Stepper.cpp | 345 ++++++++++++++++++----------- libraries/Stepper/Stepper.h | 118 +++++++--- libraries/Stepper/keywords.txt | 19 +- libraries/Stepper/utility/Vector.h | 73 ++++++ 4 files changed, 384 insertions(+), 171 deletions(-) create mode 100644 libraries/Stepper/utility/Vector.h diff --git a/libraries/Stepper/Stepper.cpp b/libraries/Stepper/Stepper.cpp index 90c6f9b7109..0ebfaa5354f 100644 --- a/libraries/Stepper/Stepper.cpp +++ b/libraries/Stepper/Stepper.cpp @@ -1,19 +1,19 @@ /* Stepper.cpp - - Stepper library for Wiring/Arduino - Version 0.4 - + Original library (0.1) by Tom Igoe. Two-wire modifications (0.2) by Sebastian Gassner Combination version (0.3) by Tom Igoe and David Mellis - Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley + Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley Drives a unipolar or bipolar stepper motor using 2 wires or 4 wires When wiring multiple stepper motors to a microcontroller, - you quickly run out of output pins, with each motor requiring 4 connections. + you quickly run out of output pins, with each motor requiring 4 connections. By making use of the fact that at any time two of the four motor coils are the inverse of the other two, the number of - control connections can be reduced from 4 to 2. + control connections can be reduced from 4 to 2. A slightly modified circuit around a Darlington transistor array or an L293 H-bridge connects to only 2 microcontroler pins, inverts the signals received, @@ -69,14 +69,18 @@ http://www.wikipedia.org/wiki/Stepper_motor#Phase_current_waveforms */ +#include +#include + +#include +#include "utility/vector.h" -#include "Arduino.h" #include "Stepper.h" // Only even rows are used for full-stepping. // Only odd rows are used for wave-stepping. // All rows are used for half-stepping. -const int Stepper::STEP_VALUES[8][4] = { +const uint8_t Stepper::STEP_VALUES[8][4] = { {HIGH, LOW, HIGH, LOW}, // not used for wave-stepping {LOW, LOW, HIGH, LOW}, // not used for full-stepping {LOW, HIGH, HIGH, LOW}, // not used for wave-stepping @@ -87,192 +91,275 @@ const int Stepper::STEP_VALUES[8][4] = { {HIGH, LOW, LOW, LOW} // not used for full-stepping }; +Stepper::Stepper() : should_halt(false), callback(NULL) {} + /* * two-wire constructor. * Sets which wires should control the motor. */ -Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2) +Stepper::Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) + : should_halt(false), callback(NULL) { - this->step_number = 0; // which step the motor is on - this->speed = 0; // the motor speed, in revolutions per minute - this->direction = 0; // motor direction - this->last_step_time = 0; // time stamp in ms of the last step taken - this->number_of_steps = number_of_steps; // total number of steps for this motor - this->drive_type = FullStep; - - // Arduino pins for the motor control connection: - this->motor_pin_1 = motor_pin_1; - this->motor_pin_2 = motor_pin_2; - - // setup the pins on the microcontroller: - pinMode(this->motor_pin_1, OUTPUT); - pinMode(this->motor_pin_2, OUTPUT); - - // When there are only 2 pins, set the other two to 0: - this->motor_pin_3 = 0; - this->motor_pin_4 = 0; - - // pin_count is used by the stepMotor() method: - this->pin_count = 2; + registerMotor(steps_per_rev, motor_pin_1, motor_pin_2); } - /* * constructor for four-pin version * Sets which wires should control the motor. */ +Stepper::Stepper(int steps_per_rev, + uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4) + : should_halt(false), callback(NULL) +{ + registerMotor(steps_per_rev, motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4); +} + +Stepper::~Stepper() +{ + uint8_t num_motors = numMotors(); + for(uint8_t i = 0; i < num_motors; i++) { + free(motor_infos[i]->motor_pins); + delete motor_infos[i]; + } +} + +uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) +{ + uint8_t motor_pins[] = { motor_pin_1, motor_pin_2 }; + return registerMotor(steps_per_rev, motor_pins, 2); +} + +uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4) +{ + uint8_t motor_pins[] = { motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4 }; + return registerMotor(steps_per_rev, motor_pins, 4); +} -Stepper::Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4) +uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins) { - this->step_number = 0; // which step the motor is on - this->speed = 0; // the motor speed, in revolutions per minute - this->direction = 0; // motor direction - this->last_step_time = 0; // time stamp in ms of the last step taken - this->number_of_steps = number_of_steps; // total number of steps for this motor - this->drive_type = FullStep; - - // 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); - - // pin_count is used by the stepMotor() method: - this->pin_count = 4; + MotorInfo* motor_info = new MotorInfo; + motor_info->drive_type = FullStep; + motor_info->last_step_time = 0UL; + + motor_info->motor_pins = (uint8_t*)malloc(num_pins * sizeof(uint8_t)); + for (uint8_t i = 0; i < num_pins; i++) { + motor_info->motor_pins[i] = motor_pins[i]; + } + + motor_info->num_pins = num_pins; + motor_info->step_delay = 0UL; + motor_info->step_number = 0; + motor_info->steps_per_rev = steps_per_rev; + + setupMotorPins(motor_pins, num_pins); + + this->motor_infos.push_back(motor_info); + return motor_infos.size() - 1; +} + +void Stepper::setupMotorPins(uint8_t motor_pins[], uint8_t num_pins) { + // Setup the pins on the microcontroller. + // Write LOW to each pin initially so the motor is not driven and current is not wasted. + for (uint8_t i = 0; i < num_pins; i++) { + pinMode(motor_pins[i], OUTPUT); + digitalWrite(motor_pins[i], LOW); + } } /* Sets the speed in revs per minute */ -void Stepper::setSpeed(long whatSpeed) +void Stepper::setSpeed(long speed) { - this->step_delay = 60L * 1000000L / this->number_of_steps / whatSpeed; + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + setSpeed(i, speed); + } +} + +void Stepper::setSpeed(uint8_t motor_id, long speed) +{ + MotorInfo* motor_info = this->motor_infos[motor_id]; + motor_info->step_delay = 60UL * 1000000UL / motor_info->steps_per_rev / speed; } /* Sets the drive type -*/ + */ void Stepper::setDriveType(DriveType drive_type) { - this->drive_type = drive_type; + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + setDriveType(i, drive_type); + } +} + +void Stepper::setDriveType(uint8_t motor_id, DriveType drive_type) +{ + this->motor_infos[motor_id]->drive_type = drive_type; } /* - Moves the motor steps_to_move steps. If the number is negative, + Moves the motor steps_to_move steps. If the number is negative, the motor moves in the reverse direction. */ -void Stepper::step(int steps_to_move) -{ - int steps_left = abs(steps_to_move); // how many steps to take - - // determine direction based on whether steps_to_mode is + or -: - if (steps_to_move > 0) {this->direction = 1;} - if (steps_to_move < 0) {this->direction = 0;} - - int number_of_steps = this->number_of_steps; - unsigned long step_delay = this->step_delay; - if (this->drive_type == HalfStep) { - // There are twice as many steps, adjust appropriately. - number_of_steps *= 2; - step_delay /= 2; +void Stepper::step(int steps_to_move) { + uint8_t num_motors = numMotors(); + int steps_to_move_array[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + steps_to_move_array[i] = steps_to_move; } + step(steps_to_move_array); +} + +void Stepper::step(uint8_t motor_id, int steps_to_move) { + uint8_t num_motors = numMotors(); + int steps_to_move_array[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + steps_to_move_array[i] = i == motor_id ? steps_to_move : 0; + } + step(steps_to_move_array); +} - // decrement the number of steps, moving one step each time: - while(steps_left > 0) { - unsigned long now = micros(); - long elapsed_time = now - this->last_step_time; - // move only if the appropriate delay has passed: - if (elapsed_time >= step_delay) { - // get the timeStamp of when you stepped: - this->last_step_time = now; - // increment or decrement the step number, - // depending on direction: - if (this->direction == 1) { - this->step_number++; - if (this->step_number >= number_of_steps) { - this->step_number = 0; +void Stepper::step(int steps_to_move[]) +{ + uint8_t num_motors = numMotors(); + + int steps_left[num_motors]; + bool direction_positive[num_motors]; + int steps_per_rev[num_motors]; + unsigned long step_delay[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + const MotorInfo* motor_info = this->motor_infos[i]; + steps_left[i] = abs(steps_to_move[i]); // how many steps to take + + // determine direction based on whether steps_to_mode is + or -: + direction_positive[i] = steps_to_move[i] > 0; + + steps_per_rev[i] = motor_info->steps_per_rev; + step_delay[i] = motor_info->step_delay; + if (motor_info->drive_type == HalfStep) { + // There are twice as many steps, adjust appropriately. + steps_per_rev[i] *= 2; + step_delay[i] /= 2UL; + } + } + + while(!this->should_halt) { + const unsigned long now = micros(); + bool any_steps_left = false; + // look at each motor to see if it should be moved now. + for (uint8_t i = 0; i < num_motors; i++) { + MotorInfo* motor_info = this->motor_infos[i]; + if (steps_left[i] > 0) { + any_steps_left = true; + + unsigned long elapsed_time = now - motor_info->last_step_time; + // micros has rolled over. Assume that it has only rolled over once. + if (now < motor_info->last_step_time) { + // 0xFFFF is 2^32-1, the maximum value of an unsigned long + elapsed_time += 0xFFFFUL; } - } - else { - if (this->step_number <= 0) { - this->step_number = number_of_steps; + + // move only if the appropriate delay has passed: + if (elapsed_time >= step_delay[i]) { + // get the timeStamp of when you stepped: + motor_info->last_step_time = now; + // increment or decrement the step number, + // depending on direction: + if (direction_positive[i]) { + motor_info->step_number++; + } else { + motor_info->step_number--; + } + motor_info->step_number %= steps_per_rev[i]; + // decrement the steps left: + steps_left[i]--; + // step the motor: + stepMotor(i); } - this->step_number--; } - // decrement the steps left: - steps_left--; - // step the motor: - stepMotor(); + } + + // if there are no steps left for any motors, we're done! + if (!any_steps_left) { + break; + } + + // if it is set, call the callback. + if (this->callback != NULL) { + this->callback(); } } + + // reset halt signal + this->should_halt = false; } /* * Moves the motor forward or backwards. */ -void Stepper::stepMotor() { - switch(this->drive_type) { +void Stepper::stepMotor(uint8_t motor_id) +{ + const MotorInfo* motor_info = this->motor_infos[motor_id]; + uint8_t* motor_pins = motor_info->motor_pins; + + // Only write out the values if the configuration is valid. + if (motor_info->num_pins != 4 && + motor_info->num_pins != 2 && motor_info->drive_type != FullStep) { + return; + } + + const uint8_t* step_row; + switch(motor_info->drive_type) { case FullStep: - fullStepMotor(); + step_row = Stepper::STEP_VALUES[(motor_info->step_number % 4) * 2]; + if (motor_info->num_pins == 2) { + // For 2-wire configurations we take the middle two values, so we move the pointer up by + // one so that the first two values are really elements 1 and 2, instead of 0 and 1. + step_row++; + } break; case HalfStep: - halfStepMotor(); + step_row = Stepper::STEP_VALUES[motor_info->step_number % 8]; break; case Wave: - waveStepMotor(); + step_row = Stepper::STEP_VALUES[(motor_info->step_number % 4) * 2 + 1]; break; + default: + // If the this is an unknown drive type do nothing. + return; } + + writeMotorPins(motor_pins, step_row, motor_info->num_pins); } -void Stepper::fullStepMotor() +void Stepper::writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins) { - const int* step_row = STEP_VALUES[(this->step_number % 4) * 2]; // full-stepping only uses even rows - if (this->pin_count == 2) { - // For 2-wire configurations we take the middle two pins - digitalWrite(motor_pin_2, step_row[1]); - digitalWrite(motor_pin_3, step_row[2]); - } else if (this->pin_count == 4) { - digitalWrite(motor_pin_1, step_row[0]); - digitalWrite(motor_pin_2, step_row[1]); - digitalWrite(motor_pin_3, step_row[2]); - digitalWrite(motor_pin_4, step_row[3]); + for (uint8_t i = 0; i < num_pins; i++) { + digitalWrite(motor_pins[i], values[i]); } } -void Stepper::halfStepMotor() +void Stepper::halt() { - // Only 4-wire configurations can be half-stepped. - if (this->pin_count == 4) { - const int* step_row = STEP_VALUES[this->step_number % 8]; - digitalWrite(motor_pin_1, step_row[0]); - digitalWrite(motor_pin_2, step_row[1]); - digitalWrite(motor_pin_3, step_row[2]); - digitalWrite(motor_pin_4, step_row[3]); - } + this->should_halt = true; } -void Stepper::waveStepMotor() +void Stepper::setCallback(void (*callback)()) { - // Only 4-wire configurations can be wave-driven. - if (this->pin_count == 4) { - const int* step_row = STEP_VALUES[(this->step_number % 4) * 2 + 1]; // wave drive only uses odd rows - digitalWrite(motor_pin_1, step_row[0]); - digitalWrite(motor_pin_2, step_row[1]); - digitalWrite(motor_pin_3, step_row[2]); - digitalWrite(motor_pin_4, step_row[3]); - } + this->callback = callback; +} + +uint8_t Stepper::numMotors() +{ + return this->motor_infos.size(); } /* version() returns the version of the library: */ -int Stepper::version(void) +uint8_t Stepper::version(void) { - return 4; + return 5; } diff --git a/libraries/Stepper/Stepper.h b/libraries/Stepper/Stepper.h index 55fef1e5967..357b33201ea 100644 --- a/libraries/Stepper/Stepper.h +++ b/libraries/Stepper/Stepper.h @@ -1,19 +1,23 @@ /* Stepper.h - - Stepper library for Wiring/Arduino - Version 0.4 - + Original library (0.1) by Tom Igoe. Two-wire modifications (0.2) by Sebastian Gassner Combination version (0.3) by Tom Igoe and David Mellis Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley - Drives a unipolar or bipolar stepper motor using 2 wires or 4 wires + Drives unipolar or bipolar stepper motors using 2 wires or 4 wires + + You can drive multiple motors simultaneously by calling registerMotor() multiple times on the same + instance of Stepper. You can then either move one motor at a time, or all of them in unison using + void step(int[] steps) function. When wiring multiple stepper motors to a microcontroller, - you quickly run out of output pins, with each motor requiring 4 connections. + you quickly run out of output pins, with each motor requiring 4 connections. By making use of the fact that at any time two of the four motor coils are the inverse of the other two, the number of - control connections can be reduced from 4 to 2. + control connections can be reduced from 4 to 2. A slightly modified circuit around a Darlington transistor array or an L293 H-bridge connects to only 2 microcontroler pins, inverts the signals received, @@ -73,53 +77,97 @@ #ifndef Stepper_h #define Stepper_h +#include +#include "utility/vector.h" + // library interface description class Stepper { public: enum DriveType { FullStep, HalfStep, Wave }; // constructors: - Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2); - Stepper(int number_of_steps, int motor_pin_1, int motor_pin_2, int motor_pin_3, int motor_pin_4); + // Empty constructor + Stepper(); + // The two constructors below will also add a motor as motor_id 0. + // steps_per_rev should be in full steps + Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); + Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4); + + // destructor + ~Stepper(); + + // Registers an extra motor. The return value is the motor's id, and should be used to + // reference it in the future. + // steps_per_rev should be in full steps + uint8_t registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); + uint8_t registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4); + + // speed setter methods: + // Set the speed for all motors. in RPM + void setSpeed(long speed); + // Set the speed for a specific motor, in RPM + void setSpeed(uint8_t motor_id, long speed); + + // drive type setter methods: + // Set the drive type for all motors. + void setDriveType(DriveType drive_type); + // Set the drive type for a specific motor + void setDriveType(uint8_t motor_id, DriveType drive_type); - // speed setter method: - void setSpeed(long whatSpeed); + // mover methods: + // Move all motors the number_of_steps + void step(int steps_to_move); + // Move only the motor with motor_id number_of_steps + void step(uint8_t motor_id, int steps_to_move); + // Move all of the motors simultaneously. Each element in the number_of_steps array corresponds + // to the number of steps the motor with motor_id being the index of that element should be + // moved. steps_to_move must have exactly numMotors() elements. If you dont want some motors + // to move, set those values to 0. + void step(int steps_to_move[]); - // drive type setter method: - void setDriveType(DriveType drive_type); + // Aborts the step() loop once. + void halt(); + + // Sets a callback to call after each iteration of the step() loop. Set to NULL to disable. + // Remember that the callback should execute quickly and not block. The longer the callback + // takes the slower steps can be taken. + void setCallback(void (*callback)()); - // mover method: - void step(int number_of_steps); + // Returns the number of motors registered. + uint8_t numMotors(void); - int version(void); + uint8_t version(void); private: - void stepMotor(); - void fullStepMotor(); - void halfStepMotor(); - void waveStepMotor(); + struct MotorInfo { + uint8_t* motor_pins; // motor pin numbers + uint8_t num_pins; + + Stepper::DriveType drive_type; // What kind of stepping to use. Defaults to full. + unsigned long step_delay; // delay between steps, in micros, based on speed + int steps_per_rev; // total number of steps this motor can take + + int step_number; // which step the motor is on + unsigned long last_step_time; // time stamp in micros of when the last step was taken + }; + + uint8_t registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins); + void setupMotorPins(uint8_t motor_pins[], uint8_t num_pins); + + // moves the motor specified one step. + void stepMotor(uint8_t motor_id); + + // writes the given values to the motor pins. + void writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins); // Only even rows are used for full-stepping. // Only odd rows are used for wave-stepping. // All rows are used for half-stepping. - static const int STEP_VALUES[8][4]; - - DriveType drive_type; // What kind of stepping to use. Defaults to full. - - int direction; // Direction of rotation - int speed; // Speed in RPMs - unsigned long step_delay; // delay between steps, in ms, based on speed - int number_of_steps; // total number of steps this motor can take - int pin_count; // whether you're driving the motor with 2 or 4 pins - int step_number; // which step the motor is on - - // motor pin numbers: - int motor_pin_1; - int motor_pin_2; - int motor_pin_3; - int motor_pin_4; - - long last_step_time; // time stamp in ms of when the last step was taken + static const uint8_t STEP_VALUES[8][4]; + + Vector motor_infos; + bool should_halt; + void (*callback)(); }; #endif diff --git a/libraries/Stepper/keywords.txt b/libraries/Stepper/keywords.txt index 32e8ea44c3f..6ffdb733643 100644 --- a/libraries/Stepper/keywords.txt +++ b/libraries/Stepper/keywords.txt @@ -6,24 +6,29 @@ # Datatypes (KEYWORD1) ####################################### -Stepper KEYWORD1 +Stepper KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -step KEYWORD2 -setSpeed KEYWORD2 +registerMotor KEYWORD2 +halt KEYWORD2 +numMotors KEYWORD2 +setCallback KEYWORD2 setDriveType KEYWORD2 -version KEYWORD2 +setSpeed KEYWORD2 +step KEYWORD2 +version KEYWORD2 ###################################### # Instances (KEYWORD2) ####################################### -direction KEYWORD2 -speed KEYWORD2 - ####################################### # Constants (LITERAL1) ####################################### + +FullStep LITERAL1 +HalfStep LITERAL1 +Wave LITERAL1 \ No newline at end of file diff --git a/libraries/Stepper/utility/Vector.h b/libraries/Stepper/utility/Vector.h new file mode 100644 index 00000000000..3b10d1529be --- /dev/null +++ b/libraries/Stepper/utility/Vector.h @@ -0,0 +1,73 @@ +// Adapted from a post on the Arduino Forums by Gabb: +// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1289151491 + +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include + +// Minimal class to replace std::vector +template +class Vector { + size_t d_size; // Stores no. of actually stored objects + size_t d_capacity; // Stores allocated capacity + Data *d_data; // Stores data +public: + // Default constructor + Vector() : d_size(0), d_capacity(0), d_data(0) {}; + // Copy constuctor + Vector(Vector const &other) : d_size(other.d_size), d_capacity(other.d_capacity), d_data(0) { + d_data = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(d_data, other.d_data, d_size * sizeof(Data)); + }; + + // Destructor + ~Vector() { + free(d_data); + }; + + // Needed for memory management + Vector &operator=(Vector const &other) { + free(d_data); + d_size = other.d_size; + d_capacity = other.d_capacity; + d_data = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(d_data, other.d_data, d_size * sizeof(Data)); + return *this; + }; + + // Const getter + Data const &operator[](size_t idx) const { + return d_data[idx]; + }; + // Changeable getter + Data &operator[](size_t idx) { + return d_data[idx]; + }; + + // Adds new value. If needed, allocates more space + void push_back(Data const &x) { + if (d_capacity == d_size) { + resize(); + } + d_data[d_size++] = x; + }; + + // Size getter + size_t size() const { + return d_size; + }; + +private: + // Allocates double the old space + void resize() { + d_capacity = d_capacity ? d_capacity * 2 : 1; + Data *newdata = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(newdata, d_data, d_size * sizeof(Data)); + free(d_data); + d_data = newdata; + }; +}; + +#endif From 2152e3e5c4d7b8a329dec6d1c85b8bc3518dbcd7 Mon Sep 17 00:00:00 2001 From: smike Date: Wed, 11 Jan 2012 01:12:51 -0500 Subject: [PATCH 4/8] Now you can set whether you want a motor to hold its position when it's done stepping. --- libraries/Stepper/Stepper.cpp | 40 ++++++++++++++++++++++++++++++++--- libraries/Stepper/Stepper.h | 13 ++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/libraries/Stepper/Stepper.cpp b/libraries/Stepper/Stepper.cpp index 0ebfaa5354f..8ff7ca7e816 100644 --- a/libraries/Stepper/Stepper.cpp +++ b/libraries/Stepper/Stepper.cpp @@ -6,7 +6,11 @@ Combination version (0.3) by Tom Igoe and David Mellis Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley - Drives a unipolar or bipolar stepper motor using 2 wires or 4 wires + Drives unipolar or bipolar stepper motors using 2 wires or 4 wires + + You can drive multiple motors simultaneously by calling registerMotor() multiple times on the same + instance of Stepper. You can then either move one motor at a time, or all of them in unison using + void step(int[] steps) function. When wiring multiple stepper motors to a microcontroller, you quickly run out of output pins, with each motor requiring 4 connections. @@ -91,6 +95,10 @@ const uint8_t Stepper::STEP_VALUES[8][4] = { {HIGH, LOW, LOW, LOW} // not used for full-stepping }; +// Pin values that turn off current to a motor so that it does not hold its position. +// This is only possible for 4-pin configurations +const uint8_t Stepper::OFF_VALUES[] = { LOW, LOW, LOW, LOW }; + Stepper::Stepper() : should_halt(false), callback(NULL) {} /* @@ -150,6 +158,7 @@ uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t motor_info->step_delay = 0UL; motor_info->step_number = 0; motor_info->steps_per_rev = steps_per_rev; + motor_info->should_hold = true; setupMotorPins(motor_pins, num_pins); @@ -159,11 +168,14 @@ uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t void Stepper::setupMotorPins(uint8_t motor_pins[], uint8_t num_pins) { // Setup the pins on the microcontroller. - // Write LOW to each pin initially so the motor is not driven and current is not wasted. for (uint8_t i = 0; i < num_pins; i++) { pinMode(motor_pins[i], OUTPUT); - digitalWrite(motor_pins[i], LOW); } + + // Write LOW to each pin initially so the motor is not driven and current is not wasted. + // This only really makes sense for a 4-pin motor, but it's still better than letting the pins of + // a 2-pin motor have random values. + writeMotorPins(motor_pins, Stepper::OFF_VALUES, num_pins); } /* @@ -199,6 +211,19 @@ void Stepper::setDriveType(uint8_t motor_id, DriveType drive_type) this->motor_infos[motor_id]->drive_type = drive_type; } +void Stepper::setHold(bool hold) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + setHold(i, hold); + } +} + +void Stepper::setHold(uint8_t motor_id, bool hold) +{ + this->motor_infos[motor_id]->should_hold = hold; +} + /* Moves the motor steps_to_move steps. If the number is negative, the motor moves in the reverse direction. @@ -294,6 +319,15 @@ void Stepper::step(int steps_to_move[]) // reset halt signal this->should_halt = false; + + for (uint8_t i = 0; i < num_motors; i++) { + MotorInfo* motor_info = this->motor_infos[i]; + // Only makes sense to do for 4-pin motors, but we still pass num_pins to writeMotorPins since a + // SEGFAULT would be really bad. + if (!motor_info->should_hold && motor_info->num_pins == 4) { + writeMotorPins(motor_info->motor_pins, Stepper::OFF_VALUES, motor_info->num_pins); + } + } } /* diff --git a/libraries/Stepper/Stepper.h b/libraries/Stepper/Stepper.h index 357b33201ea..796dbfb5d45 100644 --- a/libraries/Stepper/Stepper.h +++ b/libraries/Stepper/Stepper.h @@ -128,6 +128,13 @@ class Stepper { // Aborts the step() loop once. void halt(); + // Sets whether the steppers should hold their position after they finish stepping. + // This can only be done for 4-pin motor configurations, and will be ignored otherwise. + // This will only take effect after the next step() call finishes. Call step(0) if you want to + // have it take immediately. + void setHold(bool hold); + void setHold(uint8_t motor_id, bool hold); + // Sets a callback to call after each iteration of the step() loop. Set to NULL to disable. // Remember that the callback should execute quickly and not block. The longer the callback // takes the slower steps can be taken. @@ -149,6 +156,8 @@ class Stepper { int step_number; // which step the motor is on unsigned long last_step_time; // time stamp in micros of when the last step was taken + + bool should_hold; // whether to hold the motor position when not stepping. }; uint8_t registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins); @@ -165,6 +174,10 @@ class Stepper { // All rows are used for half-stepping. static const uint8_t STEP_VALUES[8][4]; + // Pin values that turn off current to a motor so that it does not hold its position. + // This is only possible for 4-pin configurations + static const uint8_t OFF_VALUES[4]; + Vector motor_infos; bool should_halt; void (*callback)(); From 0a9bc427491fa5b9e9ce34af3c592a4b27250b3c Mon Sep 17 00:00:00 2001 From: smike Date: Thu, 19 Jan 2012 03:22:06 -0500 Subject: [PATCH 5/8] refactoring steppers to separate single motor stepping from multi-motor. also refactored so to expose the meet of the step loop so people can roll their own if they like. --- libraries/Stepper/MultiStepper.cpp | 100 +++++++++ libraries/Stepper/MultiStepper.h | 72 ++++++ libraries/Stepper/Stepper.cpp | 348 ++++++++--------------------- libraries/Stepper/Stepper.h | 93 ++++---- libraries/Stepper/keywords.txt | 22 +- 5 files changed, 324 insertions(+), 311 deletions(-) create mode 100644 libraries/Stepper/MultiStepper.cpp create mode 100644 libraries/Stepper/MultiStepper.h diff --git a/libraries/Stepper/MultiStepper.cpp b/libraries/Stepper/MultiStepper.cpp new file mode 100644 index 00000000000..e54a3139f5a --- /dev/null +++ b/libraries/Stepper/MultiStepper.cpp @@ -0,0 +1,100 @@ +#include +#include + +#include +#include "Stepper.h" +#include "utility/vector.h" + +#include "MultiStepper.h" + +MultiStepper::MultiStepper() {} + +MultiStepper::~MultiStepper() {} + +uint8_t MultiStepper::registerStepper(Stepper* stepper) +{ + this->steppers.push_back(stepper); + return this->steppers.size() - 1; +} + +Stepper* MultiStepper::getStepper(uint8_t motor_id) +{ + return this->steppers[motor_id]; +} + +void MultiStepper::setSpeed(int speed) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setSpeed(speed); + } +} + +void MultiStepper::setDriveType(Stepper::DriveType drive_type) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setDriveType(drive_type); + } +} + +void MultiStepper::setHold(bool hold) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setHold(hold); + } +} + +void MultiStepper::step(int steps_to_move) { + uint8_t num_motors = numMotors(); + int steps_to_move_array[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + steps_to_move_array[i] = steps_to_move; + } + step(steps_to_move_array); +} + +void MultiStepper::step(int steps_to_move[]) +{ + uint8_t num_motors = numMotors(); + + for (uint8_t i = 0; i < num_motors; i++) { + Stepper* stepper = this->steppers[i]; + + stepper->setStepsToMove(abs(steps_to_move[i])); + // determine direction based on whether steps_to_mode is + or -: + stepper->setDirectionPositive(steps_to_move[i] > 0); + } + + while(true) { + if (!stepSlice()) { + // no steps left for any motor. We're done! + break; + } + } +} + +bool MultiStepper::stepSlice() +{ + uint8_t num_motors = numMotors(); + + bool any_steps_left = false; + const unsigned long now = micros(); + for (uint8_t i = 0; i < num_motors; i++) { + Stepper* stepper = this->steppers[i]; + // if there are no steps left for any motors, we're done! + if (stepper->getStepsLeftToMove() > 0) { + any_steps_left = true; + + stepper->stepSlice(now); + } + } + + return any_steps_left; +} + +uint8_t MultiStepper::numMotors() +{ + return this->steppers.size(); +} diff --git a/libraries/Stepper/MultiStepper.h b/libraries/Stepper/MultiStepper.h new file mode 100644 index 00000000000..b3f219f704a --- /dev/null +++ b/libraries/Stepper/MultiStepper.h @@ -0,0 +1,72 @@ +/* + Multi.h - - Stepper library for Wiring/Arduino - Version 0.4 + + Original library (0.1) by Tom Igoe. + Two-wire modifications (0.2) by Sebastian Gassner + Combination version (0.3) by Tom Igoe and David Mellis + Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley + + Drives unipolar or bipolar stepper motors using 2 wires or 4 wires + + You can drive multiple motors simultaneously by calling registerStepper() multiple times + You can move all of them in unison using the void step(int[]) or void step(int) functions. +*/ + +// ensure this library description is only included once +#ifndef MultiStepper_h +#define MultiStepper_h + +#include +#include "utility/Vector.h" +#include "Stepper.h" + +// library interface description +class MultiStepper { + public: + // constructors: + // Empty constructor + MultiStepper(); + + // destructor + ~MultiStepper(); + + // Registers an extra stepper. The return value is the motor's id, and should be used to + // reference it in the future. + uint8_t registerStepper(Stepper* stepper); + + Stepper* getStepper(uint8_t motor_id); + + // speed setter methods: + // Set the speed for all motors. in RPM + void setSpeed(int speed); + + // drive type setter methods: + // Set the drive type for all motors. + void setDriveType(Stepper::DriveType drive_type); + + // mover methods: + // Move all motors the number_of_steps + // Note that steps_to_move is an integer, with a max value of 2^15-1 + void step(int steps_to_move); + // Move all of the motors simultaneously. Each element in the number_of_steps array corresponds + // to the number of steps the motor with motor_id being the index of that element should be + // moved. steps_to_move must have exactly numMotors() elements. If you dont want some motors + // to move, set those values to 0. + void step(int steps_to_move[]); + + bool stepSlice(); + + // Sets whether the steppers should hold their position after they finish stepping. + // This can only be done for 4-pin motor configurations, and will be ignored otherwise. + // This will only take effect after the next step() call finishes. Call step(0) if you want to + // have it take immediately. + void setHold(bool hold); + + // Returns the number of motors registered. + uint8_t numMotors(void); + + private: + Vector steppers; +}; + +#endif diff --git a/libraries/Stepper/Stepper.cpp b/libraries/Stepper/Stepper.cpp index 8ff7ca7e816..db85d541a27 100644 --- a/libraries/Stepper/Stepper.cpp +++ b/libraries/Stepper/Stepper.cpp @@ -1,78 +1,3 @@ -/* - Stepper.cpp - - Stepper library for Wiring/Arduino - Version 0.4 - - Original library (0.1) by Tom Igoe. - Two-wire modifications (0.2) by Sebastian Gassner - Combination version (0.3) by Tom Igoe and David Mellis - Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley - - Drives unipolar or bipolar stepper motors using 2 wires or 4 wires - - You can drive multiple motors simultaneously by calling registerMotor() multiple times on the same - instance of Stepper. You can then either move one motor at a time, or all of them in unison using - void step(int[] steps) function. - - When wiring multiple stepper motors to a microcontroller, - you quickly run out of output pins, with each motor requiring 4 connections. - - By making use of the fact that at any time two of the four motor - coils are the inverse of the other two, the number of - control connections can be reduced from 4 to 2. - - A slightly modified circuit around a Darlington transistor array or an L293 H-bridge - connects to only 2 microcontroler pins, inverts the signals received, - and delivers the 4 (2 plus 2 inverted ones) output signals required - for driving a stepper motor. - - The sequence of control signals for 4 control wires is as follows: - - Step C0 C1 C2 C3 - 1 1 0 1 0 - 2 0 1 1 0 - 3 0 1 0 1 - 4 1 0 0 1 - - The sequence of controls signals for 2 control wires is as follows - (columns C1 and C2 from above): - - Step C0 C1 - 1 0 1 - 2 1 1 - 3 1 0 - 4 0 0 - - For 4 wire configurations only, the motor can be driven with full-stepping (the default), - half-stepping, or a wave drive. - - Half-stepping will halve the size of the motor's step, giving it double the resolution. - Remember that the amount of steps you pass to step() will be interpreted based on the current - value of the drive type. The sequence of control signals is as follows: - - Step C0 C1 C2 C3 - 1 1 0 1 0 - 2 0 0 1 0 - 3 0 1 1 0 - 4 0 1 0 0 - 5 0 1 0 1 - 6 0 0 0 1 - 7 1 0 0 1 - 8 1 0 0 0 - - Wave drive mode only keeps one phase on a time. This means that the motor will use less power, but - will also only have half of the torque of full-stepping. The sequence of control signals is as - follows: - - Step C0 C1 C2 C3 - 1 0 0 1 0 - 2 0 1 0 0 - 3 0 0 0 1 - 4 1 0 0 0 - - The circuits can be found at http://www.arduino.cc/en/Tutorial/Stepper - More info on the different types of drives can be found at - http://www.wikipedia.org/wiki/Stepper_motor#Phase_current_waveforms -*/ - #include #include @@ -99,16 +24,14 @@ const uint8_t Stepper::STEP_VALUES[8][4] = { // This is only possible for 4-pin configurations const uint8_t Stepper::OFF_VALUES[] = { LOW, LOW, LOW, LOW }; -Stepper::Stepper() : should_halt(false), callback(NULL) {} - /* * two-wire constructor. * Sets which wires should control the motor. */ Stepper::Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) - : should_halt(false), callback(NULL) { - registerMotor(steps_per_rev, motor_pin_1, motor_pin_2); + uint8_t motor_pins[] = {motor_pin_1, motor_pin_2}; + init(steps_per_rev, motor_pins, 2); } /* @@ -117,56 +40,34 @@ Stepper::Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) */ Stepper::Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4) - : should_halt(false), callback(NULL) { - registerMotor(steps_per_rev, motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4); + uint8_t motor_pins[] = {motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4}; + init(steps_per_rev, motor_pins, 4); } Stepper::~Stepper() { - uint8_t num_motors = numMotors(); - for(uint8_t i = 0; i < num_motors; i++) { - free(motor_infos[i]->motor_pins); - delete motor_infos[i]; - } + free(this->motor_pins); } -uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) +void Stepper::init(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins) { - uint8_t motor_pins[] = { motor_pin_1, motor_pin_2 }; - return registerMotor(steps_per_rev, motor_pins, 2); -} + this->direction_positive = true; + this->drive_type = FullStep; + this->last_step_time = 0UL; -uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4) -{ - uint8_t motor_pins[] = { motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4 }; - return registerMotor(steps_per_rev, motor_pins, 4); -} - -uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins) -{ - MotorInfo* motor_info = new MotorInfo; - motor_info->drive_type = FullStep; - motor_info->last_step_time = 0UL; - - motor_info->motor_pins = (uint8_t*)malloc(num_pins * sizeof(uint8_t)); + this->motor_pins = (uint8_t*)malloc(num_pins * sizeof(uint8_t)); for (uint8_t i = 0; i < num_pins; i++) { - motor_info->motor_pins[i] = motor_pins[i]; + this->motor_pins[i] = motor_pins[i]; } - motor_info->num_pins = num_pins; - motor_info->step_delay = 0UL; - motor_info->step_number = 0; - motor_info->steps_per_rev = steps_per_rev; - motor_info->should_hold = true; - - setupMotorPins(motor_pins, num_pins); + this->num_pins = num_pins; + this->step_delay = 0UL; + this->step_number = 0; + this->steps_per_rev = steps_per_rev; + this->steps_to_move = 0; + this->should_hold = true; - this->motor_infos.push_back(motor_info); - return motor_infos.size() - 1; -} - -void Stepper::setupMotorPins(uint8_t motor_pins[], uint8_t num_pins) { // Setup the pins on the microcontroller. for (uint8_t i = 0; i < num_pins; i++) { pinMode(motor_pins[i], OUTPUT); @@ -181,18 +82,10 @@ void Stepper::setupMotorPins(uint8_t motor_pins[], uint8_t num_pins) { /* Sets the speed in revs per minute */ -void Stepper::setSpeed(long speed) -{ - uint8_t num_motors = numMotors(); - for (uint8_t i = 0; i < num_motors; i++) { - setSpeed(i, speed); - } -} - -void Stepper::setSpeed(uint8_t motor_id, long speed) +void Stepper::setSpeed(unsigned int speed) { - MotorInfo* motor_info = this->motor_infos[motor_id]; - motor_info->step_delay = 60UL * 1000000UL / motor_info->steps_per_rev / speed; + this->step_delay = 60UL * 1000000UL / this->steps_per_rev / speed; + this->step_delay = this->step_delay; } /* @@ -200,28 +93,38 @@ void Stepper::setSpeed(uint8_t motor_id, long speed) */ void Stepper::setDriveType(DriveType drive_type) { - uint8_t num_motors = numMotors(); - for (uint8_t i = 0; i < num_motors; i++) { - setDriveType(i, drive_type); + this->drive_type = drive_type; +} + +void Stepper::setHold(bool hold) +{ + this->should_hold = hold; + + // Only makes sense to do for 4-pin motors, but we still pass num_pins to writeMotorPins since a + // SEGFAULT would be really bad. + if (this->num_pins == 4) { + if (this->should_hold) { + // This will rewrite the latest pin values + stepMotor(); + } else { + writeMotorPins(this->motor_pins, Stepper::OFF_VALUES, this->num_pins); + } } } -void Stepper::setDriveType(uint8_t motor_id, DriveType drive_type) +void Stepper::setDirectionPositive(bool direction_positive) { - this->motor_infos[motor_id]->drive_type = drive_type; + this->direction_positive = direction_positive; } -void Stepper::setHold(bool hold) +void Stepper::setStepsToMove(unsigned int steps_to_move) { - uint8_t num_motors = numMotors(); - for (uint8_t i = 0; i < num_motors; i++) { - setHold(i, hold); - } + this->steps_to_move = steps_to_move; } -void Stepper::setHold(uint8_t motor_id, bool hold) +unsigned int Stepper::getStepsLeftToMove() { - this->motor_infos[motor_id]->should_hold = hold; + return this->steps_to_move; } /* @@ -229,143 +132,101 @@ void Stepper::setHold(uint8_t motor_id, bool hold) the motor moves in the reverse direction. */ void Stepper::step(int steps_to_move) { - uint8_t num_motors = numMotors(); - int steps_to_move_array[num_motors]; - for (uint8_t i = 0; i < num_motors; i++) { - steps_to_move_array[i] = steps_to_move; + setStepsToMove(abs(steps_to_move)); + // determine direction based on whether steps_to_mode is + or -: + setDirectionPositive(steps_to_move > 0); + + while(this->steps_to_move > 0) { + stepSlice(); } - step(steps_to_move_array); + + // TODO(smike): Since this happens right after the last step, it will effectively cancel it out. + // Add a delay so the motor has time to complete its last step. + setHold(this->should_hold); } -void Stepper::step(uint8_t motor_id, int steps_to_move) { - uint8_t num_motors = numMotors(); - int steps_to_move_array[num_motors]; - for (uint8_t i = 0; i < num_motors; i++) { - steps_to_move_array[i] = i == motor_id ? steps_to_move : 0; - } - step(steps_to_move_array); +void Stepper::stepSlice() { + stepSlice(micros()); } -void Stepper::step(int steps_to_move[]) +void Stepper::stepSlice(const unsigned long now) { - uint8_t num_motors = numMotors(); - - int steps_left[num_motors]; - bool direction_positive[num_motors]; - int steps_per_rev[num_motors]; - unsigned long step_delay[num_motors]; - for (uint8_t i = 0; i < num_motors; i++) { - const MotorInfo* motor_info = this->motor_infos[i]; - steps_left[i] = abs(steps_to_move[i]); // how many steps to take - - // determine direction based on whether steps_to_mode is + or -: - direction_positive[i] = steps_to_move[i] > 0; - - steps_per_rev[i] = motor_info->steps_per_rev; - step_delay[i] = motor_info->step_delay; - if (motor_info->drive_type == HalfStep) { - // There are twice as many steps, adjust appropriately. - steps_per_rev[i] *= 2; - step_delay[i] /= 2UL; - } + if (this->steps_to_move == 0) { + // we have nothing to do + return; } - while(!this->should_halt) { - const unsigned long now = micros(); - bool any_steps_left = false; - // look at each motor to see if it should be moved now. - for (uint8_t i = 0; i < num_motors; i++) { - MotorInfo* motor_info = this->motor_infos[i]; - if (steps_left[i] > 0) { - any_steps_left = true; - - unsigned long elapsed_time = now - motor_info->last_step_time; - // micros has rolled over. Assume that it has only rolled over once. - if (now < motor_info->last_step_time) { - // 0xFFFF is 2^32-1, the maximum value of an unsigned long - elapsed_time += 0xFFFFUL; - } - - // move only if the appropriate delay has passed: - if (elapsed_time >= step_delay[i]) { - // get the timeStamp of when you stepped: - motor_info->last_step_time = now; - // increment or decrement the step number, - // depending on direction: - if (direction_positive[i]) { - motor_info->step_number++; - } else { - motor_info->step_number--; - } - motor_info->step_number %= steps_per_rev[i]; - // decrement the steps left: - steps_left[i]--; - // step the motor: - stepMotor(i); - } - } - } - - // if there are no steps left for any motors, we're done! - if (!any_steps_left) { - break; - } + int steps_per_rev; + unsigned long step_delay; - // if it is set, call the callback. - if (this->callback != NULL) { - this->callback(); - } + steps_per_rev = this->steps_per_rev; + step_delay = this->step_delay; + if (this->drive_type == HalfStep) { + // There are twice as many steps, adjust appropriately. + steps_per_rev *= 2; + step_delay /= 2UL; } - // reset halt signal - this->should_halt = false; + unsigned long elapsed_time = now - this->last_step_time; - for (uint8_t i = 0; i < num_motors; i++) { - MotorInfo* motor_info = this->motor_infos[i]; - // Only makes sense to do for 4-pin motors, but we still pass num_pins to writeMotorPins since a - // SEGFAULT would be really bad. - if (!motor_info->should_hold && motor_info->num_pins == 4) { - writeMotorPins(motor_info->motor_pins, Stepper::OFF_VALUES, motor_info->num_pins); + // micros has rolled over. Assume that it has only rolled over once. + if (now < this->last_step_time) { + // 0xFFFF is 2^32-1, the maximum value of an unsigned long + elapsed_time += 0xFFFFUL; + } + + // move only if the appropriate delay has passed: + if (elapsed_time >= step_delay) { + // get the timeStamp of when you stepped: + this->last_step_time = now; + // increment or decrement the step number, + // depending on direction: + if (this->direction_positive) { + this->step_number++; + } else { + this->step_number--; } + this->step_number %= steps_per_rev; + // decrement the steps left: + this->steps_to_move--; + // step the motor: + stepMotor(); } } /* * Moves the motor forward or backwards. */ -void Stepper::stepMotor(uint8_t motor_id) +void Stepper::stepMotor() { - const MotorInfo* motor_info = this->motor_infos[motor_id]; - uint8_t* motor_pins = motor_info->motor_pins; - // Only write out the values if the configuration is valid. - if (motor_info->num_pins != 4 && - motor_info->num_pins != 2 && motor_info->drive_type != FullStep) { + if (this->num_pins != 4 && + this->num_pins != 2 && this->drive_type != FullStep) { return; } const uint8_t* step_row; - switch(motor_info->drive_type) { + switch(this->drive_type) { case FullStep: - step_row = Stepper::STEP_VALUES[(motor_info->step_number % 4) * 2]; - if (motor_info->num_pins == 2) { + step_row = Stepper::STEP_VALUES[(this->step_number % 4) * 2]; + if (this->num_pins == 2) { // For 2-wire configurations we take the middle two values, so we move the pointer up by // one so that the first two values are really elements 1 and 2, instead of 0 and 1. step_row++; } break; case HalfStep: - step_row = Stepper::STEP_VALUES[motor_info->step_number % 8]; + step_row = Stepper::STEP_VALUES[this->step_number % 8]; break; case Wave: - step_row = Stepper::STEP_VALUES[(motor_info->step_number % 4) * 2 + 1]; + step_row = Stepper::STEP_VALUES[(this->step_number % 4) * 2 + 1]; break; default: // If the this is an unknown drive type do nothing. return; } - writeMotorPins(motor_pins, step_row, motor_info->num_pins); + writeMotorPins(this->motor_pins, step_row, this->num_pins); } void Stepper::writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins) @@ -375,21 +236,6 @@ void Stepper::writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8 } } -void Stepper::halt() -{ - this->should_halt = true; -} - -void Stepper::setCallback(void (*callback)()) -{ - this->callback = callback; -} - -uint8_t Stepper::numMotors() -{ - return this->motor_infos.size(); -} - /* version() returns the version of the library: */ diff --git a/libraries/Stepper/Stepper.h b/libraries/Stepper/Stepper.h index 796dbfb5d45..fd6e33ecd2c 100644 --- a/libraries/Stepper/Stepper.h +++ b/libraries/Stepper/Stepper.h @@ -8,10 +8,6 @@ Drives unipolar or bipolar stepper motors using 2 wires or 4 wires - You can drive multiple motors simultaneously by calling registerMotor() multiple times on the same - instance of Stepper. You can then either move one motor at a time, or all of them in unison using - void step(int[] steps) function. - When wiring multiple stepper motors to a microcontroller, you quickly run out of output pins, with each motor requiring 4 connections. @@ -80,14 +76,15 @@ #include #include "utility/vector.h" +// forward declaration so it can be counted as a friend class. +class MultiStepper; + // library interface description class Stepper { public: enum DriveType { FullStep, HalfStep, Wave }; // constructors: - // Empty constructor - Stepper(); // The two constructors below will also add a motor as motor_id 0. // steps_per_rev should be in full steps Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); @@ -96,75 +93,51 @@ class Stepper { // destructor ~Stepper(); - // Registers an extra motor. The return value is the motor's id, and should be used to - // reference it in the future. - // steps_per_rev should be in full steps - uint8_t registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); - uint8_t registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4); - // speed setter methods: - // Set the speed for all motors. in RPM - void setSpeed(long speed); - // Set the speed for a specific motor, in RPM - void setSpeed(uint8_t motor_id, long speed); + // Set the speed of the motor, in RPM + void setSpeed(unsigned int speed); // drive type setter methods: - // Set the drive type for all motors. + // Set the drive type of the motor. void setDriveType(DriveType drive_type); - // Set the drive type for a specific motor - void setDriveType(uint8_t motor_id, DriveType drive_type); // mover methods: - // Move all motors the number_of_steps + // Note that steps_to_move is an integer, with a max value of 2^15-1 void step(int steps_to_move); - // Move only the motor with motor_id number_of_steps - void step(uint8_t motor_id, int steps_to_move); - // Move all of the motors simultaneously. Each element in the number_of_steps array corresponds - // to the number of steps the motor with motor_id being the index of that element should be - // moved. steps_to_move must have exactly numMotors() elements. If you dont want some motors - // to move, set those values to 0. - void step(int steps_to_move[]); - - // Aborts the step() loop once. - void halt(); // Sets whether the steppers should hold their position after they finish stepping. // This can only be done for 4-pin motor configurations, and will be ignored otherwise. // This will only take effect after the next step() call finishes. Call step(0) if you want to // have it take immediately. void setHold(bool hold); - void setHold(uint8_t motor_id, bool hold); - // Sets a callback to call after each iteration of the step() loop. Set to NULL to disable. - // Remember that the callback should execute quickly and not block. The longer the callback - // takes the slower steps can be taken. - void setCallback(void (*callback)()); + void setDirectionPositive(bool direction_positive); - // Returns the number of motors registered. - uint8_t numMotors(void); + // sets a limit on how many steps should be moved + void setStepsToMove(unsigned int steps_to_move); - uint8_t version(void); + // returns how many steps are left to move. + // This is scaled based on the drive type (1 FullStep = 2 HalfSteps) + unsigned int getStepsLeftToMove(); - private: - struct MotorInfo { - uint8_t* motor_pins; // motor pin numbers - uint8_t num_pins; + void stepSlice(); - Stepper::DriveType drive_type; // What kind of stepping to use. Defaults to full. - unsigned long step_delay; // delay between steps, in micros, based on speed - int steps_per_rev; // total number of steps this motor can take + uint8_t version(void); - int step_number; // which step the motor is on - unsigned long last_step_time; // time stamp in micros of when the last step was taken + friend class MultiStepper; - bool should_hold; // whether to hold the motor position when not stepping. - }; + protected: + // Helper method for stepSlice(). Takes the current time in microseconds. + // It is useful when needing to move multiple motors simultaneously. Having each motor calculate + // can make them out of sync, and slow since micros() take a long time to evaluate. + void stepSlice(const unsigned long now); - uint8_t registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins); - void setupMotorPins(uint8_t motor_pins[], uint8_t num_pins); + private: + // initialization + void init(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins); // moves the motor specified one step. - void stepMotor(uint8_t motor_id); + void stepMotor(); // writes the given values to the motor pins. void writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins); @@ -178,9 +151,19 @@ class Stepper { // This is only possible for 4-pin configurations static const uint8_t OFF_VALUES[4]; - Vector motor_infos; - bool should_halt; - void (*callback)(); + uint8_t* motor_pins; // motor pin numbers + uint8_t num_pins; + + Stepper::DriveType drive_type; // What kind of stepping to use. Defaults to full. + unsigned long step_delay; // delay between steps, in micros, based on speed + int steps_per_rev; // total number of steps this motor can take + bool direction_positive; // if the direction to move is positive + + unsigned int step_number; // which step the motor is on + unsigned int steps_to_move; // the number of steps left to take + unsigned long last_step_time; // time stamp in micros of when the last step was taken + + bool should_hold; // whether to hold the motor position when not stepping. }; #endif diff --git a/libraries/Stepper/keywords.txt b/libraries/Stepper/keywords.txt index 6ffdb733643..e8d9ad3088f 100644 --- a/libraries/Stepper/keywords.txt +++ b/libraries/Stepper/keywords.txt @@ -6,20 +6,32 @@ # Datatypes (KEYWORD1) ####################################### +MultiStepper KEYWORD1 Stepper KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### -registerMotor KEYWORD2 -halt KEYWORD2 +getStepper KEYWORD2 numMotors KEYWORD2 -setCallback KEYWORD2 +registerStepper KEYWORD2 +setDriveType KEYWORD2 +setHold KEYWORD2 +setSpeed KEYWORD2 +step KEYWORD2 +stepSlice KEYWORD2 + +getStepsLeftToMove KEYWORD2 +halt KEYWORD2 setDriveType KEYWORD2 +setDirectionPositive KEYWORD2 +setStepsToMove KEYWORD2 +setHold KEYWORD2 setSpeed KEYWORD2 -step KEYWORD2 -version KEYWORD2 +step KEYWORD2 +stepSlice KEYWORD2 +version KEYWORD2 ###################################### # Instances (KEYWORD2) From 6742fac75297d6733596ddac652dd3cb949bb530 Mon Sep 17 00:00:00 2001 From: smike Date: Thu, 19 Jan 2012 03:59:09 -0500 Subject: [PATCH 6/8] adding a couple more convenience methods to multistepper --- libraries/Stepper/MultiStepper.cpp | 16 ++++++++++++++++ libraries/Stepper/MultiStepper.h | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/libraries/Stepper/MultiStepper.cpp b/libraries/Stepper/MultiStepper.cpp index e54a3139f5a..309d970a3c8 100644 --- a/libraries/Stepper/MultiStepper.cpp +++ b/libraries/Stepper/MultiStepper.cpp @@ -46,6 +46,22 @@ void MultiStepper::setHold(bool hold) } } +void MultiStepper::setDirectionPositive(bool direction_positive) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setDirectionPositive(direction_positive); + } +} + +void MultiStepper::setStepsToMove(unsigned int steps_to_move) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + this->steppers[i]->setStepsToMove(steps_to_move); + } +} + void MultiStepper::step(int steps_to_move) { uint8_t num_motors = numMotors(); int steps_to_move_array[num_motors]; diff --git a/libraries/Stepper/MultiStepper.h b/libraries/Stepper/MultiStepper.h index b3f219f704a..267e92a6fc2 100644 --- a/libraries/Stepper/MultiStepper.h +++ b/libraries/Stepper/MultiStepper.h @@ -44,6 +44,11 @@ class MultiStepper { // Set the drive type for all motors. void setDriveType(Stepper::DriveType drive_type); + void setDirectionPositive(bool direction_positive); + + // sets a limit on how many steps should be moved + void setStepsToMove(unsigned int steps_to_move); + // mover methods: // Move all motors the number_of_steps // Note that steps_to_move is an integer, with a max value of 2^15-1 From 57b1f2fc533a207d0f48c6f3eca303ffe66d5f42 Mon Sep 17 00:00:00 2001 From: smike Date: Fri, 27 Jan 2012 01:23:37 -0500 Subject: [PATCH 7/8] Adding empty consturctor, copy consturctor, = operator so that we dont segfault. Also renaming the setDirection method and factoring out a method to force a step. --- libraries/Stepper/MultiStepper.cpp | 6 ++-- libraries/Stepper/MultiStepper.h | 2 +- libraries/Stepper/Stepper.cpp | 49 ++++++++++++++++++++---------- libraries/Stepper/Stepper.h | 13 ++++++-- libraries/Stepper/keywords.txt | 22 +++++++------- 5 files changed, 58 insertions(+), 34 deletions(-) diff --git a/libraries/Stepper/MultiStepper.cpp b/libraries/Stepper/MultiStepper.cpp index 309d970a3c8..b09c875c2dd 100644 --- a/libraries/Stepper/MultiStepper.cpp +++ b/libraries/Stepper/MultiStepper.cpp @@ -46,11 +46,11 @@ void MultiStepper::setHold(bool hold) } } -void MultiStepper::setDirectionPositive(bool direction_positive) +void MultiStepper::setDirection(bool forward) { uint8_t num_motors = numMotors(); for (uint8_t i = 0; i < num_motors; i++) { - this->steppers[i]->setDirectionPositive(direction_positive); + this->steppers[i]->setDirection(forward); } } @@ -80,7 +80,7 @@ void MultiStepper::step(int steps_to_move[]) stepper->setStepsToMove(abs(steps_to_move[i])); // determine direction based on whether steps_to_mode is + or -: - stepper->setDirectionPositive(steps_to_move[i] > 0); + stepper->setDirection(steps_to_move[i] > 0); } while(true) { diff --git a/libraries/Stepper/MultiStepper.h b/libraries/Stepper/MultiStepper.h index 267e92a6fc2..7a6f7c05732 100644 --- a/libraries/Stepper/MultiStepper.h +++ b/libraries/Stepper/MultiStepper.h @@ -44,7 +44,7 @@ class MultiStepper { // Set the drive type for all motors. void setDriveType(Stepper::DriveType drive_type); - void setDirectionPositive(bool direction_positive); + void setDirection(bool forward); // sets a limit on how many steps should be moved void setStepsToMove(unsigned int steps_to_move); diff --git a/libraries/Stepper/Stepper.cpp b/libraries/Stepper/Stepper.cpp index db85d541a27..0d289162ebb 100644 --- a/libraries/Stepper/Stepper.cpp +++ b/libraries/Stepper/Stepper.cpp @@ -2,7 +2,7 @@ #include #include -#include "utility/vector.h" +#include "utility/Vector.h" #include "Stepper.h" @@ -24,6 +24,16 @@ const uint8_t Stepper::STEP_VALUES[8][4] = { // This is only possible for 4-pin configurations const uint8_t Stepper::OFF_VALUES[] = { LOW, LOW, LOW, LOW }; +Stepper::Stepper(const Stepper& copy) +{ + init(copy.steps_per_rev, copy.motor_pins, copy.num_pins); +} + +Stepper& Stepper::operator=(const Stepper& copy) { + free(this->motor_pins); + init(copy.steps_per_rev, copy.motor_pins, copy.num_pins); +} + /* * two-wire constructor. * Sets which wires should control the motor. @@ -52,7 +62,7 @@ Stepper::~Stepper() void Stepper::init(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins) { - this->direction_positive = true; + this->forward = true; this->drive_type = FullStep; this->last_step_time = 0UL; @@ -112,9 +122,9 @@ void Stepper::setHold(bool hold) } } -void Stepper::setDirectionPositive(bool direction_positive) +void Stepper::setDirection(bool forward) { - this->direction_positive = direction_positive; + this->forward = forward; } void Stepper::setStepsToMove(unsigned int steps_to_move) @@ -134,7 +144,7 @@ unsigned int Stepper::getStepsLeftToMove() void Stepper::step(int steps_to_move) { setStepsToMove(abs(steps_to_move)); // determine direction based on whether steps_to_mode is + or -: - setDirectionPositive(steps_to_move > 0); + setDirection(steps_to_move > 0); while(this->steps_to_move > 0) { stepSlice(); @@ -179,21 +189,28 @@ void Stepper::stepSlice(const unsigned long now) if (elapsed_time >= step_delay) { // get the timeStamp of when you stepped: this->last_step_time = now; - // increment or decrement the step number, - // depending on direction: - if (this->direction_positive) { - this->step_number++; - } else { - this->step_number--; - } - this->step_number %= steps_per_rev; + + stepNow(); + // decrement the steps left: this->steps_to_move--; - // step the motor: - stepMotor(); } } +void Stepper::stepNow() +{ + // increment or decrement the step number, + // depending on direction: + if (this->forward) { + this->step_number++; + } else { + this->step_number--; + } + this->step_number %= steps_per_rev; + // step the motor: + stepMotor(); +} + /* * Moves the motor forward or backwards. */ @@ -201,7 +218,7 @@ void Stepper::stepMotor() { // Only write out the values if the configuration is valid. if (this->num_pins != 4 && - this->num_pins != 2 && this->drive_type != FullStep) { + !(this->num_pins == 2 && this->drive_type == FullStep)) { return; } diff --git a/libraries/Stepper/Stepper.h b/libraries/Stepper/Stepper.h index fd6e33ecd2c..5dff30eaf73 100644 --- a/libraries/Stepper/Stepper.h +++ b/libraries/Stepper/Stepper.h @@ -74,7 +74,7 @@ #define Stepper_h #include -#include "utility/vector.h" +#include "utility/Vector.h" // forward declaration so it can be counted as a friend class. class MultiStepper; @@ -87,12 +87,16 @@ class Stepper { // constructors: // The two constructors below will also add a motor as motor_id 0. // steps_per_rev should be in full steps + Stepper() {} + Stepper(const Stepper& copy); Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4); // destructor ~Stepper(); + Stepper& operator=(const Stepper& copy); + // speed setter methods: // Set the speed of the motor, in RPM void setSpeed(unsigned int speed); @@ -104,6 +108,9 @@ class Stepper { // mover methods: // Note that steps_to_move is an integer, with a max value of 2^15-1 void step(int steps_to_move); + // Does a step without regard for speed or the steps left to move. After this method it will be + // just like no steps have happened with respect to speed and number of steps left. + void stepNow(); // Sets whether the steppers should hold their position after they finish stepping. // This can only be done for 4-pin motor configurations, and will be ignored otherwise. @@ -111,7 +118,7 @@ class Stepper { // have it take immediately. void setHold(bool hold); - void setDirectionPositive(bool direction_positive); + void setDirection(bool forward); // sets a limit on how many steps should be moved void setStepsToMove(unsigned int steps_to_move); @@ -157,7 +164,7 @@ class Stepper { Stepper::DriveType drive_type; // What kind of stepping to use. Defaults to full. unsigned long step_delay; // delay between steps, in micros, based on speed int steps_per_rev; // total number of steps this motor can take - bool direction_positive; // if the direction to move is positive + bool forward; // whether to move forward or backward unsigned int step_number; // which step the motor is on unsigned int steps_to_move; // the number of steps left to take diff --git a/libraries/Stepper/keywords.txt b/libraries/Stepper/keywords.txt index e8d9ad3088f..1f98a38c724 100644 --- a/libraries/Stepper/keywords.txt +++ b/libraries/Stepper/keywords.txt @@ -13,26 +13,26 @@ Stepper KEYWORD1 # Methods and Functions (KEYWORD2) ####################################### -getStepper KEYWORD2 -numMotors KEYWORD2 -registerStepper KEYWORD2 -setDriveType KEYWORD2 -setHold KEYWORD2 -setSpeed KEYWORD2 -step KEYWORD2 -stepSlice KEYWORD2 -getStepsLeftToMove KEYWORD2 -halt KEYWORD2 +# Common betwen Stepper and MultiStepper setDriveType KEYWORD2 -setDirectionPositive KEYWORD2 +setDirection KEYWORD2 setStepsToMove KEYWORD2 setHold KEYWORD2 setSpeed KEYWORD2 step KEYWORD2 stepSlice KEYWORD2 + +# Unique to Stepper +getStepsLeftToMove KEYWORD2 +stepNow KEYWORD2 version KEYWORD2 +# Unique to MultiStepper +getStepper KEYWORD2 +numMotors KEYWORD2 +registerStepper KEYWORD2 + ###################################### # Instances (KEYWORD2) ####################################### From ffa57480d682ce2b394a6684939e7e169fa12947 Mon Sep 17 00:00:00 2001 From: smike Date: Mon, 28 Oct 2013 03:19:43 -0400 Subject: [PATCH 8/8] getting all changes in before switching computers...i dont remember what is actually in here. --- .gitignore | 4 +- .project | 102 ++++- app/src/processing/app/Sketch.java | 156 +++---- app/src/processing/app/debug/Sizer.java | 108 +++-- libraries/Stepper3/Stepper3.cpp | 402 ++++++++++++++++++ libraries/Stepper3/Stepper3.h | 187 ++++++++ .../Stepper3/examples/MotorKnob/MotorKnob.ino | 41 ++ .../stepper_oneRevolution.ino | 44 ++ .../stepper_oneStepAtATime.ino | 44 ++ .../stepper_speedControl.ino | 49 +++ libraries/Stepper3/keywords.txt | 34 ++ libraries/Stepper3/utility/Vector.h | 73 ++++ 12 files changed, 1094 insertions(+), 150 deletions(-) create mode 100644 libraries/Stepper3/Stepper3.cpp create mode 100644 libraries/Stepper3/Stepper3.h create mode 100644 libraries/Stepper3/examples/MotorKnob/MotorKnob.ino create mode 100644 libraries/Stepper3/examples/stepper_oneRevolution/stepper_oneRevolution.ino create mode 100644 libraries/Stepper3/examples/stepper_oneStepAtATime/stepper_oneStepAtATime.ino create mode 100644 libraries/Stepper3/examples/stepper_speedControl/stepper_speedControl.ino create mode 100644 libraries/Stepper3/keywords.txt create mode 100644 libraries/Stepper3/utility/Vector.h diff --git a/.gitignore b/.gitignore index c4509976609..9c3d7df58e3 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ app/bin/ app/pde.jar build/macosx/work/ core/bin/ -core/core.jar \ No newline at end of file +core/core.jar +/.cproject +/.project diff --git a/.project b/.project index 0b76a40ba80..a3f29214ef7 100644 --- a/.project +++ b/.project @@ -1,17 +1,85 @@ - - - processing-head - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - + + + processing-head + + + Arduino_Uno + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + ?name? + + + + org.eclipse.cdt.make.core.append_environment + true + + + org.eclipse.cdt.make.core.autoBuildTarget + all + + + org.eclipse.cdt.make.core.buildArguments + + + + org.eclipse.cdt.make.core.buildCommand + make + + + org.eclipse.cdt.make.core.buildLocation + ${workspace_loc:/second eclipse test/Release} + + + org.eclipse.cdt.make.core.cleanBuildTarget + clean + + + org.eclipse.cdt.make.core.contents + org.eclipse.cdt.make.core.activeConfigSettings + + + org.eclipse.cdt.make.core.enableAutoBuild + false + + + org.eclipse.cdt.make.core.enableCleanBuild + true + + + org.eclipse.cdt.make.core.enableFullBuild + true + + + org.eclipse.cdt.make.core.fullBuildTarget + all + + + org.eclipse.cdt.make.core.stopOnError + true + + + org.eclipse.cdt.make.core.useDefaultBuildCmd + true + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + it.baeyens.arduinonature + + diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index 1d540c3741c..8e347454298 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -98,7 +98,7 @@ public class Sketch { */ private String libraryPath; /** - * List of library folders. + * List of library folders. */ private ArrayList importedLibraries; @@ -365,8 +365,8 @@ protected void nameCode(String newName) { if (!validExtension(newExtension)) { Base.showWarning(_("Problem with rename"), I18n.format( - _("\".{0}\" is not a valid extension."), newExtension - ), null); + _("\".{0}\" is not a valid extension."), newExtension + ), null); return; } @@ -399,33 +399,33 @@ protected void nameCode(String newName) { if (newName.equalsIgnoreCase(c.getFileName())) { Base.showMessage(_("Nope"), I18n.format( - _("A file named \"{0}\" already exists in \"{1}\""), - c.getFileName(), - folder.getAbsolutePath() - )); + _("A file named \"{0}\" already exists in \"{1}\""), + c.getFileName(), + folder.getAbsolutePath() + )); return; } } - + // In Arduino, don't allow a .cpp file with the same name as the sketch, // because the sketch is concatenated into a file with that name as part - // of the build process. + // of the build process. if (newName.equals(getName() + ".cpp")) { Base.showMessage(_("Nope"), _("You can't have a .cpp file with the same name as the sketch.")); return; } - + if (renamingCode && currentIndex == 0) { for (int i = 1; i < codeCount; i++) { if (sanitaryName.equalsIgnoreCase(code[i].getPrettyName()) && code[i].getExtension().equalsIgnoreCase("cpp")) { Base.showMessage(_("Nope"), I18n.format( - _("You can't rename the sketch to \"{0}\"\n" + - "because the sketch already has a .cpp file with that name."), - sanitaryName - )); + _("You can't rename the sketch to \"{0}\"\n" + + "because the sketch already has a .cpp file with that name."), + sanitaryName + )); return; } } @@ -458,10 +458,10 @@ protected void nameCode(String newName) { if (newFolder.exists()) { Base.showWarning(_("Cannot Rename"), I18n.format( - _("Sorry, a sketch (or folder) named " + + _("Sorry, a sketch (or folder) named " + "\"{0}\" already exists."), - newName - ), null); + newName + ), null); return; } @@ -484,10 +484,10 @@ protected void nameCode(String newName) { if (!current.renameTo(newFile, newExtension)) { Base.showWarning(_("Error"), I18n.format( - _("Could not rename \"{0}\" to \"{1}\""), - current.getFileName(), - newFile.getName() - ), null); + _("Could not rename \"{0}\" to \"{1}\""), + current.getFileName(), + newFile.getName() + ), null); return; } @@ -529,10 +529,10 @@ protected void nameCode(String newName) { if (!current.renameTo(newFile, newExtension)) { Base.showWarning(_("Error"), I18n.format( - _("Could not rename \"{0}\" to \"{1}\""), - current.getFileName(), - newFile.getName() - ), null); + _("Could not rename \"{0}\" to \"{1}\""), + current.getFileName(), + newFile.getName() + ), null); return; } } @@ -545,11 +545,11 @@ protected void nameCode(String newName) { } } catch (IOException e) { Base.showWarning(_("Error"), - I18n.format( + I18n.format( "Could not create the file \"{0}\" in \"{1}\"", - newFile, - folder.getAbsolutePath() - ), e); + newFile, + folder.getAbsolutePath() + ), e); return; } SketchCode newCode = new SketchCode(newFile, newExtension); @@ -735,7 +735,7 @@ public boolean accept(File dir, String name) { return name.toLowerCase().endsWith(".pde"); } }); - + if (pdeFiles != null && pdeFiles.length > 0) { if (Preferences.get("editor.update_extension") == null) { Object[] options = { _("OK"), _("Cancel") }; @@ -753,12 +753,12 @@ public boolean accept(File dir, String name) { null, options, options[0]); - + if (result != JOptionPane.OK_OPTION) return false; // save cancelled - + Preferences.setBoolean("editor.update_extension", true); } - + if (Preferences.getBoolean("editor.update_extension")) { // Do rename of all .pde files to new .ino extension for (File pdeFile : pdeFiles) @@ -768,14 +768,14 @@ public boolean accept(File dir, String name) { } for (int i = 0; i < codeCount; i++) { - if (code[i].isModified()) + if (code[i].isModified()) code[i].save(); } calcModified(); return true; } - + protected boolean renameCodeToInoExtension(File pdeFile) { for (SketchCode c : code) { if (!c.getFile().equals(pdeFile)) @@ -787,7 +787,7 @@ protected boolean renameCodeToInoExtension(File pdeFile) { } return false; } - + /** * Handles 'Save As' for a sketch. @@ -861,11 +861,11 @@ protected boolean saveAs() throws IOException { if (newName.equalsIgnoreCase(code[i].getPrettyName()) && code[i].getExtension().equalsIgnoreCase("cpp")) { Base.showMessage(_("Nope"), - I18n.format( + I18n.format( _("You can't save the sketch as \"{0}\"\n" + "because the sketch already has a .cpp file with that name."), - newName - )); + newName + )); return false; } } @@ -1068,7 +1068,7 @@ public boolean addFile(File sourceFile) { if (!muchSuccess) { Base.showWarning(_("Error adding file"), I18n.format(_("Could not delete the existing ''{0}'' file."), filename), - null); + null); return false; } } @@ -1091,7 +1091,7 @@ public boolean addFile(File sourceFile) { } catch (IOException e) { Base.showWarning(_("Error adding file"), I18n.format(_("Could not add ''{0}'' to the sketch."), filename), - e); + e); return false; } } @@ -1216,13 +1216,13 @@ protected void cleanup() { // need to be recompiled, or if the board does not // use setting build.dependency //Base.removeDir(tempBuildFolder); - + // note that we can't remove the builddir itself, otherwise // the next time we start up, internal runs using Runner won't // work because the build dir won't exist at startup, so the classloader // will ignore the fact that that dir is in the CLASSPATH in run.sh Base.removeDescendants(tempBuildFolder); - + deleteFilesOnNextBuild = false; } else { // delete only stale source files, from the previously @@ -1240,7 +1240,7 @@ protected void cleanup() { } } } - + // Create a fresh applet folder (needed before preproc is run below) //tempBuildFolder.mkdirs(); } @@ -1282,8 +1282,8 @@ public static void buildSettingChanged() { private static boolean deleteFilesOnNextBuild = true; /** - * When running from the editor, take care of preparations before running - * the build. + * When running from the editor, take care of preparations before running + * the build. */ public void prepare() { // make sure the user didn't hide the sketch folder @@ -1479,7 +1479,7 @@ public ArrayList getImportedLibraries() { return importedLibraries; } - + /** * Map an error from a set of processed .java files back to its location * in the actual sketch. @@ -1489,7 +1489,7 @@ public ArrayList getImportedLibraries() { * @return A RunnerException to be sent to the editor, or null if it wasn't * possible to place the exception to the sketch code. */ -// public RunnerException placeExceptionAlt(String message, +// public RunnerException placeExceptionAlt(String message, // String filename, int line) { // String appletJavaFile = appletClassName + ".java"; // SketchCode errorCode = null; @@ -1521,14 +1521,14 @@ public ArrayList getImportedLibraries() { // line--; // // // getMessage() will be what's shown in the editor -// RunnerException exception = +// RunnerException exception = // new RunnerException(message, codeIndex, line, -1); // exception.hideStackTrace(); // return exception; // } // return null; // } - + /** * Map an error from a set of processed .java files back to its location @@ -1539,8 +1539,8 @@ public ArrayList getImportedLibraries() { * @return A RunnerException to be sent to the editor, or null if it wasn't * possible to place the exception to the sketch code. */ - public RunnerException placeException(String message, - String dotJavaFilename, + public RunnerException placeException(String message, + String dotJavaFilename, int dotJavaLine) { int codeIndex = 0; //-1; int codeLine = -1; @@ -1582,7 +1582,7 @@ public RunnerException placeException(String message, } } // could not find a proper line number, so deal with this differently. - // but if it was in fact the .java file we're looking for, though, + // but if it was in fact the .java file we're looking for, though, // send the error message through. // this is necessary because 'import' statements will be at a line // that has a lower number than the preproc offset, for instance. @@ -1614,7 +1614,7 @@ public String build(boolean verbose) throws RunnerException { */ public String build(String buildPath, boolean verbose) throws RunnerException { - + // run the preprocessor editor.status.progressUpdate(20); String primaryClassName = preprocess(buildPath); @@ -1628,8 +1628,8 @@ public String build(String buildPath, boolean verbose) } return null; } - - + + protected boolean exportApplet(boolean usingProgrammer) throws Exception { return exportApplet(tempBuildFolder.getAbsolutePath(), usingProgrammer); } @@ -1642,7 +1642,7 @@ public boolean exportApplet(String appletPath, boolean usingProgrammer) throws RunnerException, IOException, SerialException { prepare(); - + // build the sketch editor.status.progressNotice(_("Compiling sketch...")); String foundName = build(appletPath, false); @@ -1664,34 +1664,46 @@ public boolean exportApplet(String appletPath, boolean usingProgrammer) return true; } - + public void setCompilingProgress(int percent) { editor.status.progressUpdate(percent); } - + protected void size(String buildPath, String suggestedClassName) - throws RunnerException { + throws RunnerException { long size = 0; String maxsizeString = Base.getBoardPreferences().get("upload.maximum_size"); if (maxsizeString == null) return; long maxsize = Integer.parseInt(maxsizeString); + + String maxramString = Base.getBoardPreferences().get("upload.maximum_ram_size"); + long maxram = maxramString == null ? 1024 : Integer.parseInt(maxramString); + + String warningramString = Base.getBoardPreferences().get("upload.warning_ram_size"); + long warningram = warningramString == null ? maxram/2 : Integer.parseInt(warningramString); + Sizer sizer = new Sizer(buildPath, suggestedClassName); - try { + try { size = sizer.computeSize(); - System.out.println( - I18n.format( - _("Binary sketch size: {0} bytes (of a {1} byte maximum)"), - size, maxsize - ) - ); + System.out.println("Binary sketch size: " + size + " bytes (of a " + + maxsize + " byte maximum)"); + System.out.println("Chip memory sram: " + sizer.data + + " bytes (of a " +maxram+ " byte maximum)"); } catch (RunnerException e) { - System.err.println(I18n.format(_("Couldn't determine program size: {0}"), e.getMessage())); + e.printStackTrace(); } + if (sizer.data > maxram) + throw new RunnerException( + "Allowable chip memory exceeded; see http://www.arduino.cc/en/Reference/PROGMEM to reduce ram size"); + + if (sizer.data > warningram) + System.err.println("Warning Large amount of chip memory used. Consider using PROGMEM, http://www.arduino.cc/en/Reference/PROGMEM, to reduce ram size "); + if (size > maxsize) throw new RunnerException( - _("Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it.")); + "Sketch too big; see http://www.arduino.cc/en/Guide/Troubleshooting#size for tips on reducing it."); } @@ -1766,9 +1778,9 @@ public boolean exportApplicationPrompt() throws IOException, RunnerException { return false; } - + /** - * Export to application via GUI. + * Export to application via GUI. */ protected boolean exportApplication() throws IOException, RunnerException { return false; @@ -1902,7 +1914,7 @@ public String getDefaultExtension() { public List getHiddenExtensions() { return hiddenExtensions; } - + /** * Returns a String[] array of proper extensions. */ diff --git a/app/src/processing/app/debug/Sizer.java b/app/src/processing/app/debug/Sizer.java index d67728a3c85..b6a544f9f3d 100644 --- a/app/src/processing/app/debug/Sizer.java +++ b/app/src/processing/app/debug/Sizer.java @@ -1,35 +1,10 @@ -/* -*- mode: jde; c-basic-offset: 2; indent-tabs-mode: nil -*- */ - -/* - Sizer - computes the size of a .hex file - Part of the Arduino project - http://www.arduino.cc/ - - Copyright (c) 2006 David A. Mellis - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software Foundation, - Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - $Id$ -*/ - package processing.app.debug; -import processing.app.Base; - import java.io.*; import java.util.*; +import processing.app.*; + public class Sizer implements MessageConsumer { private String buildPath, sketchName; private String firstLine; @@ -40,34 +15,40 @@ public Sizer(String buildPath, String sketchName) { this.buildPath = buildPath; this.sketchName = sketchName; } - + public long computeSize() throws RunnerException { - String avrBasePath = Base.getAvrBasePath(); + String userdir = System.getProperty("user.dir") + File.separator; + String avrBasePath; + if(Base.isMacOS()) { + avrBasePath = new String("Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/"); + } + else if(Base.isLinux()) { + avrBasePath = new String(""); + } + else { + avrBasePath = new String(userdir + "hardware/tools/avr/bin/"); + } String commandSize[] = new String[] { - avrBasePath + "avr-size", - " " + avrBasePath + "avr-objdump", + "-h", + "" }; - - commandSize[1] = buildPath + File.separator + sketchName + ".hex"; - int r = 0; + commandSize[2] = buildPath + File.separator + sketchName + ".elf"; + try { exception = null; size = -1; firstLine = null; Process process = Runtime.getRuntime().exec(commandSize); - MessageSiphon in = new MessageSiphon(process.getInputStream(), this); - MessageSiphon err = new MessageSiphon(process.getErrorStream(), this); - + MessageSiphon messageSiphon = new MessageSiphon(process.getInputStream(), this); boolean running = true; - while(running) { try { - if (in.thread != null) - in.thread.join(); - if (err.thread != null) - err.thread.join(); - r = process.waitFor(); + process.waitFor(); + if (messageSiphon.getThread() != null) { + Thread.sleep(100); + } running = false; } catch (InterruptedException intExc) { } } @@ -76,33 +57,40 @@ public long computeSize() throws RunnerException { // some sub-class has overridden it to do so, thus we need to check for // it. See: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1166589459 exception = new RunnerException( - (e.toString() == null) ? e.getClass().getName() + r : e.toString() + r); + (e.toString() == null) ? e.getClass().getName() : e.toString()); } - + if (exception != null) throw exception; - + if (size == -1) throw new RunnerException(firstLine); - - return size; + + return size+1; } - + + public int data = 0; public void message(String s) { if (firstLine == null) firstLine = s; else { - StringTokenizer st = new StringTokenizer(s, " "); - try { - st.nextToken(); - st.nextToken(); - st.nextToken(); - size = (new Integer(st.nextToken().trim())).longValue(); - } catch (NoSuchElementException e) { - exception = new RunnerException(e.toString()); - } catch (NumberFormatException e) { - exception = new RunnerException(e.toString()); - } + size+=checkTag(s," .text "); + data+=checkTag(s," .data "); + data+=checkTag(s," .bss "); + // exception = new RunnerException(e.toString()); + + } + } + + private int checkTag(String s, String tag){ + int size=0; + int p = s.indexOf(tag); + if(p != -1){ + s=s.substring(p+6).trim(); + p=s.indexOf(" "); + if(p != -1) + size = Integer.parseInt(s.substring(0,p).trim(),16); } + return size; } } \ No newline at end of file diff --git a/libraries/Stepper3/Stepper3.cpp b/libraries/Stepper3/Stepper3.cpp new file mode 100644 index 00000000000..c6b26ec2781 --- /dev/null +++ b/libraries/Stepper3/Stepper3.cpp @@ -0,0 +1,402 @@ +/* + Stepper.cpp - - Stepper library for Wiring/Arduino - Version 0.4 + + Original library (0.1) by Tom Igoe. + Two-wire modifications (0.2) by Sebastian Gassner + Combination version (0.3) by Tom Igoe and David Mellis + Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley + + Drives unipolar or bipolar stepper motors using 2 wires or 4 wires + + You can drive multiple motors simultaneously by calling registerMotor() multiple times on the same + instance of Stepper. You can then either move one motor at a time, or all of them in unison using + void step(int[] steps) function. + + When wiring multiple stepper motors to a microcontroller, + you quickly run out of output pins, with each motor requiring 4 connections. + + By making use of the fact that at any time two of the four motor + coils are the inverse of the other two, the number of + control connections can be reduced from 4 to 2. + + A slightly modified circuit around a Darlington transistor array or an L293 H-bridge + connects to only 2 microcontroler pins, inverts the signals received, + and delivers the 4 (2 plus 2 inverted ones) output signals required + for driving a stepper motor. + + The sequence of control signals for 4 control wires is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 1 1 0 + 3 0 1 0 1 + 4 1 0 0 1 + + The sequence of controls signals for 2 control wires is as follows + (columns C1 and C2 from above): + + Step C0 C1 + 1 0 1 + 2 1 1 + 3 1 0 + 4 0 0 + + For 4 wire configurations only, the motor can be driven with full-stepping (the default), + half-stepping, or a wave drive. + + Half-stepping will halve the size of the motor's step, giving it double the resolution. + Remember that the amount of steps you pass to step() will be interpreted based on the current + value of the drive type. The sequence of control signals is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 0 1 0 + 3 0 1 1 0 + 4 0 1 0 0 + 5 0 1 0 1 + 6 0 0 0 1 + 7 1 0 0 1 + 8 1 0 0 0 + + Wave drive mode only keeps one phase on a time. This means that the motor will use less power, but + will also only have half of the torque of full-stepping. The sequence of control signals is as + follows: + + Step C0 C1 C2 C3 + 1 0 0 1 0 + 2 0 1 0 0 + 3 0 0 0 1 + 4 1 0 0 0 + + The circuits can be found at http://www.arduino.cc/en/Tutorial/Stepper + More info on the different types of drives can be found at + http://www.wikipedia.org/wiki/Stepper_motor#Phase_current_waveforms +*/ + +#include +#include + +#include +#include "utility/vector.h" + +#include "Stepper3.h" + +// Only even rows are used for full-stepping. +// Only odd rows are used for wave-stepping. +// All rows are used for half-stepping. +const uint8_t Stepper::STEP_VALUES[8][4] = { + {HIGH, LOW, HIGH, LOW}, // not used for wave-stepping + {LOW, LOW, HIGH, LOW}, // not used for full-stepping + {LOW, HIGH, HIGH, LOW}, // not used for wave-stepping + {LOW, HIGH, LOW, LOW}, // not used for full-stepping + {LOW, HIGH, LOW, HIGH}, // not used for wave-stepping + {LOW, LOW, LOW, HIGH}, // not used for full-stepping + {HIGH, LOW, LOW, HIGH}, // not used for wave-stepping + {HIGH, LOW, LOW, LOW} // not used for full-stepping +}; + +// Pin values that turn off current to a motor so that it does not hold its position. +// This is only possible for 4-pin configurations +const uint8_t Stepper::OFF_VALUES[] = { LOW, LOW, LOW, LOW }; + +Stepper::Stepper() : should_halt(false), callback(NULL) {} + +/* + * two-wire constructor. + * Sets which wires should control the motor. + */ +Stepper::Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) + : should_halt(false), callback(NULL) +{ + registerMotor(steps_per_rev, motor_pin_1, motor_pin_2); +} + +/* + * constructor for four-pin version + * Sets which wires should control the motor. + */ +Stepper::Stepper(int steps_per_rev, + uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4) + : should_halt(false), callback(NULL) +{ + registerMotor(steps_per_rev, motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4); +} + +Stepper::~Stepper() +{ + uint8_t num_motors = numMotors(); + for(uint8_t i = 0; i < num_motors; i++) { + free(motor_infos[i]->motor_pins); + delete motor_infos[i]; + } +} + +uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2) +{ + uint8_t motor_pins[] = { motor_pin_1, motor_pin_2 }; + return registerMotor(steps_per_rev, motor_pins, 2); +} + +uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4) +{ + uint8_t motor_pins[] = { motor_pin_1, motor_pin_2, motor_pin_3, motor_pin_4 }; + return registerMotor(steps_per_rev, motor_pins, 4); +} + +uint8_t Stepper::registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins) +{ + MotorInfo* motor_info = new MotorInfo; + motor_info->drive_type = FullStep; + motor_info->last_step_time = 0UL; + + motor_info->motor_pins = (uint8_t*)malloc(num_pins * sizeof(uint8_t)); + for (uint8_t i = 0; i < num_pins; i++) { + motor_info->motor_pins[i] = motor_pins[i]; + } + + motor_info->num_pins = num_pins; + motor_info->step_delay = 0UL; + motor_info->step_number = 0; + motor_info->steps_per_rev = steps_per_rev; + motor_info->should_hold = true; + + setupMotorPins(motor_pins, num_pins); + + this->motor_infos.push_back(motor_info); + return motor_infos.size() - 1; +} + +void Stepper::setupMotorPins(uint8_t motor_pins[], uint8_t num_pins) { + // Setup the pins on the microcontroller. + for (uint8_t i = 0; i < num_pins; i++) { + pinMode(motor_pins[i], OUTPUT); + } + + // Write LOW to each pin initially so the motor is not driven and current is not wasted. + // This only really makes sense for a 4-pin motor, but it's still better than letting the pins of + // a 2-pin motor have random values. + writeMotorPins(motor_pins, Stepper::OFF_VALUES, num_pins); +} + +/* + Sets the speed in revs per minute +*/ +void Stepper::setSpeed(int speed) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + setSpeed(i, speed); + } +} + +void Stepper::setSpeed(uint8_t motor_id, int speed) +{ + MotorInfo* motor_info = this->motor_infos[motor_id]; + motor_info->step_delay = 60UL * 1000000UL / motor_info->steps_per_rev / speed; + motor_info->step_delay = abs(motor_info->step_delay); +} + +/* + Sets the drive type + */ +void Stepper::setDriveType(DriveType drive_type) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + setDriveType(i, drive_type); + } +} + +void Stepper::setDriveType(uint8_t motor_id, DriveType drive_type) +{ + this->motor_infos[motor_id]->drive_type = drive_type; +} + +void Stepper::setHold(bool hold) +{ + uint8_t num_motors = numMotors(); + for (uint8_t i = 0; i < num_motors; i++) { + setHold(i, hold); + } +} + +void Stepper::setHold(uint8_t motor_id, bool hold) +{ + this->motor_infos[motor_id]->should_hold = hold; +} + +/* + Moves the motor steps_to_move steps. If the number is negative, + the motor moves in the reverse direction. + */ +void Stepper::step(int steps_to_move) { + uint8_t num_motors = numMotors(); + int steps_to_move_array[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + steps_to_move_array[i] = steps_to_move; + } + step(steps_to_move_array); +} + +void Stepper::step(uint8_t motor_id, int steps_to_move) { + uint8_t num_motors = numMotors(); + int steps_to_move_array[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + steps_to_move_array[i] = i == motor_id ? steps_to_move : 0; + } + step(steps_to_move_array); +} + +void Stepper::step(int steps_to_move[]) +{ + uint8_t num_motors = numMotors(); + + unsigned int steps_left[num_motors]; + bool direction_positive[num_motors]; + int steps_per_rev[num_motors]; + unsigned long step_delay[num_motors]; + for (uint8_t i = 0; i < num_motors; i++) { + const MotorInfo* motor_info = this->motor_infos[i]; + steps_left[i] = abs(steps_to_move[i]); // how many steps to take + + // determine direction based on whether steps_to_mode is + or -: + direction_positive[i] = steps_to_move[i] > 0; + + steps_per_rev[i] = motor_info->steps_per_rev; + step_delay[i] = motor_info->step_delay; + if (motor_info->drive_type == HalfStep) { + // There are twice as many steps, adjust appropriately. + steps_per_rev[i] *= 2; + step_delay[i] /= 2UL; + } + } + + while(!this->should_halt) { + const unsigned long now = micros(); + bool any_steps_left = false; + // look at each motor to see if it should be moved now. + for (uint8_t i = 0; i < num_motors; i++) { + MotorInfo* motor_info = this->motor_infos[i]; + if (steps_left[i] > 0) { + any_steps_left = true; + + unsigned long elapsed_time = now - motor_info->last_step_time; + // micros has rolled over. Assume that it has only rolled over once. + if (now < motor_info->last_step_time) { + // 0xFFFF is 2^32-1, the maximum value of an unsigned long + elapsed_time += 0xFFFFUL; + } + + // move only if the appropriate delay has passed: + if (elapsed_time >= step_delay[i]) { + // get the timeStamp of when you stepped: + motor_info->last_step_time = now; + // increment or decrement the step number, + // depending on direction: + if (direction_positive[i]) { + motor_info->step_number++; + } else { + motor_info->step_number--; + } + motor_info->step_number %= steps_per_rev[i]; + // decrement the steps left: + steps_left[i]--; + // step the motor: + stepMotor(i); + } + } + } + + // if there are no steps left for any motors, we're done! + if (!any_steps_left) { + break; + } + + // if it is set, call the callback. + if (this->callback != NULL) { + this->callback(); + } + } + + // reset halt signal + this->should_halt = false; + + // TODO(smike): Since this happens right after the last step, it will effectively cancel it out. + // Add a delay so the motor has time to complete its last step. + for (uint8_t i = 0; i < num_motors; i++) { + MotorInfo* motor_info = this->motor_infos[i]; + // Only makes sense to do for 4-pin motors, but we still pass num_pins to writeMotorPins since a + // SEGFAULT would be really bad. + if (!motor_info->should_hold && motor_info->num_pins == 4) { + writeMotorPins(motor_info->motor_pins, Stepper::OFF_VALUES, motor_info->num_pins); + } + } +} + +/* + * Moves the motor forward or backwards. + */ +void Stepper::stepMotor(uint8_t motor_id) +{ + const MotorInfo* motor_info = this->motor_infos[motor_id]; + uint8_t* motor_pins = motor_info->motor_pins; + + // Only write out the values if the configuration is valid. + if (motor_info->num_pins != 4 && + motor_info->num_pins != 2 && motor_info->drive_type != FullStep) { + return; + } + + const uint8_t* step_row; + switch(motor_info->drive_type) { + case FullStep: + step_row = Stepper::STEP_VALUES[(motor_info->step_number % 4) * 2]; + if (motor_info->num_pins == 2) { + // For 2-wire configurations we take the middle two values, so we move the pointer up by + // one so that the first two values are really elements 1 and 2, instead of 0 and 1. + step_row++; + } + break; + case HalfStep: + step_row = Stepper::STEP_VALUES[motor_info->step_number % 8]; + break; + case Wave: + step_row = Stepper::STEP_VALUES[(motor_info->step_number % 4) * 2 + 1]; + break; + default: + // If the this is an unknown drive type do nothing. + return; + } + + writeMotorPins(motor_pins, step_row, motor_info->num_pins); +} + +void Stepper::writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins) +{ + for (uint8_t i = 0; i < num_pins; i++) { + digitalWrite(motor_pins[i], values[i]); + } +} + +void Stepper::halt() +{ + this->should_halt = true; +} + +void Stepper::setCallback(void (*callback)()) +{ + this->callback = callback; +} + +uint8_t Stepper::numMotors() +{ + return this->motor_infos.size(); +} + +/* + version() returns the version of the library: +*/ +uint8_t Stepper::version(void) +{ + return 5; +} diff --git a/libraries/Stepper3/Stepper3.h b/libraries/Stepper3/Stepper3.h new file mode 100644 index 00000000000..b0c0cf3ed01 --- /dev/null +++ b/libraries/Stepper3/Stepper3.h @@ -0,0 +1,187 @@ +/* + Stepper.h - - Stepper library for Wiring/Arduino - Version 0.4 + + Original library (0.1) by Tom Igoe. + Two-wire modifications (0.2) by Sebastian Gassner + Combination version (0.3) by Tom Igoe and David Mellis + Bug fix for four-wire (0.4) by Tom Igoe, bug fix from Noah Shibley + + Drives unipolar or bipolar stepper motors using 2 wires or 4 wires + + You can drive multiple motors simultaneously by calling registerMotor() multiple times on the same + instance of Stepper. You can then either move one motor at a time, or all of them in unison using + void step(int[] steps) function. + + When wiring multiple stepper motors to a microcontroller, + you quickly run out of output pins, with each motor requiring 4 connections. + + By making use of the fact that at any time two of the four motor + coils are the inverse of the other two, the number of + control connections can be reduced from 4 to 2. + + A slightly modified circuit around a Darlington transistor array or an L293 H-bridge + connects to only 2 microcontroler pins, inverts the signals received, + and delivers the 4 (2 plus 2 inverted ones) output signals required + for driving a stepper motor. + + The sequence of control signals for 4 control wires is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 1 1 0 + 3 0 1 0 1 + 4 1 0 0 1 + + The sequence of controls signals for 2 control wires is as follows + (columns C1 and C2 from above): + + Step C0 C1 + 1 0 1 + 2 1 1 + 3 1 0 + 4 0 0 + + For 4 wire configurations only, the motor can be driven with full-stepping (the default), + half-stepping, or a wave drive. + + Half-stepping will halve the size of the motor's step, giving it double the resolution. + Remember that the amount of steps you pass to step() will be interpreted based on the current + value of the drive type. The sequence of control signals is as follows: + + Step C0 C1 C2 C3 + 1 1 0 1 0 + 2 0 0 1 0 + 3 0 1 1 0 + 4 0 1 0 0 + 5 0 1 0 1 + 6 0 0 0 1 + 7 1 0 0 1 + 8 1 0 0 0 + + Wave drive mode only keeps one phase on a time. This means that the motor will use less power, but + will also only have half of the torque of full-stepping. The sequence of control signals is as + follows: + + Step C0 C1 C2 C3 + 1 0 0 1 0 + 2 0 1 0 0 + 3 0 0 0 1 + 4 1 0 0 0 + + The circuits can be found at http://www.arduino.cc/en/Tutorial/Stepper + More info on the different types of drives can be found at + http://www.wikipedia.org/wiki/Stepper_motor#Phase_current_waveforms +*/ + +// ensure this library description is only included once +#ifndef Stepper_h +#define Stepper_h + +#include +#include "utility/vector.h" + +// library interface description +class Stepper { + public: + enum DriveType { FullStep, HalfStep, Wave }; + + // constructors: + // Empty constructor + Stepper(); + // The two constructors below will also add a motor as motor_id 0. + // steps_per_rev should be in full steps + Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); + Stepper(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4); + + // destructor + ~Stepper(); + + // Registers an extra motor. The return value is the motor's id, and should be used to + // reference it in the future. + // steps_per_rev should be in full steps + uint8_t registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2); + uint8_t registerMotor(int steps_per_rev, uint8_t motor_pin_1, uint8_t motor_pin_2, uint8_t motor_pin_3, uint8_t motor_pin_4); + + // speed setter methods: + // Set the speed for all motors. in RPM + void setSpeed(int speed); + // Set the speed for a specific motor, in RPM + void setSpeed(uint8_t motor_id, int speed); + + // drive type setter methods: + // Set the drive type for all motors. + void setDriveType(DriveType drive_type); + // Set the drive type for a specific motor + void setDriveType(uint8_t motor_id, DriveType drive_type); + + // mover methods: + // Move all motors the number_of_steps + // Note that steps_to_move is an integer, with a max value of 2^15-1 + void step(int steps_to_move); + // Move only the motor with motor_id number_of_steps + void step(uint8_t motor_id, int steps_to_move); + // Move all of the motors simultaneously. Each element in the number_of_steps array corresponds + // to the number of steps the motor with motor_id being the index of that element should be + // moved. steps_to_move must have exactly numMotors() elements. If you dont want some motors + // to move, set those values to 0. + void step(int steps_to_move[]); + + // Aborts the step() loop once. + void halt(); + + // Sets whether the steppers should hold their position after they finish stepping. + // This can only be done for 4-pin motor configurations, and will be ignored otherwise. + // This will only take effect after the next step() call finishes. Call step(0) if you want to + // have it take immediately. + void setHold(bool hold); + void setHold(uint8_t motor_id, bool hold); + + // Sets a callback to call after each iteration of the step() loop. Set to NULL to disable. + // Remember that the callback should execute quickly and not block. The longer the callback + // takes the slower steps can be taken. + void setCallback(void (*callback)()); + + // Returns the number of motors registered. + uint8_t numMotors(void); + + uint8_t version(void); + + private: + struct MotorInfo { + uint8_t* motor_pins; // motor pin numbers + uint8_t num_pins; + + Stepper::DriveType drive_type; // What kind of stepping to use. Defaults to full. + unsigned long step_delay; // delay between steps, in micros, based on speed + int steps_per_rev; // total number of steps this motor can take + + unsigned int step_number; // which step the motor is on + unsigned long last_step_time; // time stamp in micros of when the last step was taken + + bool should_hold; // whether to hold the motor position when not stepping. + }; + + uint8_t registerMotor(int steps_per_rev, uint8_t motor_pins[], uint8_t num_pins); + void setupMotorPins(uint8_t motor_pins[], uint8_t num_pins); + + // moves the motor specified one step. + void stepMotor(uint8_t motor_id); + + // writes the given values to the motor pins. + void writeMotorPins(uint8_t motor_pins[], const uint8_t values[], uint8_t num_pins); + + // Only even rows are used for full-stepping. + // Only odd rows are used for wave-stepping. + // All rows are used for half-stepping. + static const uint8_t STEP_VALUES[8][4]; + + // Pin values that turn off current to a motor so that it does not hold its position. + // This is only possible for 4-pin configurations + static const uint8_t OFF_VALUES[4]; + + Vector motor_infos; + bool should_halt; + void (*callback)(); +}; + +#endif diff --git a/libraries/Stepper3/examples/MotorKnob/MotorKnob.ino b/libraries/Stepper3/examples/MotorKnob/MotorKnob.ino new file mode 100644 index 00000000000..d428186342d --- /dev/null +++ b/libraries/Stepper3/examples/MotorKnob/MotorKnob.ino @@ -0,0 +1,41 @@ +/* + * MotorKnob + * + * A stepper motor follows the turns of a potentiometer + * (or other sensor) on analog input 0. + * + * http://www.arduino.cc/en/Reference/Stepper + * This example code is in the public domain. + */ + +#include + +// change this to the number of steps on your motor +#define STEPS 100 + +// create an instance of the stepper class, specifying +// the number of steps of the motor and the pins it's +// attached to +Stepper stepper(STEPS, 8, 9, 10, 11); + +// the previous reading from the analog input +int previous = 0; + +void setup() +{ + // set the speed of the motor to 30 RPMs + stepper.setSpeed(30); +} + +void loop() +{ + // get the sensor value + int val = analogRead(0); + + // move a number of steps equal to the change in the + // sensor reading + stepper.step(val - previous); + + // remember the previous value of the sensor + previous = val; +} \ No newline at end of file diff --git a/libraries/Stepper3/examples/stepper_oneRevolution/stepper_oneRevolution.ino b/libraries/Stepper3/examples/stepper_oneRevolution/stepper_oneRevolution.ino new file mode 100644 index 00000000000..2dbb57d7af3 --- /dev/null +++ b/libraries/Stepper3/examples/stepper_oneRevolution/stepper_oneRevolution.ino @@ -0,0 +1,44 @@ + +/* + Stepper Motor Control - one revolution + + This program drives a unipolar or bipolar stepper motor. + The motor is attached to digital pins 8 - 11 of the Arduino. + + The motor should revolve one revolution in one direction, then + one revolution in the other direction. + + + Created 11 Mar. 2007 + Modified 30 Nov. 2009 + by Tom Igoe + + */ + +#include + +const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution + // for your motor + +// initialize the stepper library on pins 8 through 11: +Stepper myStepper(stepsPerRevolution, 8,9,10,11); + +void setup() { + // set the speed at 60 rpm: + myStepper.setSpeed(60); + // initialize the serial port: + Serial.begin(9600); +} + +void loop() { + // step one revolution in one direction: + Serial.println("clockwise"); + myStepper.step(stepsPerRevolution); + delay(500); + + // step one revolution in the other direction: + Serial.println("counterclockwise"); + myStepper.step(-stepsPerRevolution); + delay(500); +} + diff --git a/libraries/Stepper3/examples/stepper_oneStepAtATime/stepper_oneStepAtATime.ino b/libraries/Stepper3/examples/stepper_oneStepAtATime/stepper_oneStepAtATime.ino new file mode 100644 index 00000000000..36d32991dc9 --- /dev/null +++ b/libraries/Stepper3/examples/stepper_oneStepAtATime/stepper_oneStepAtATime.ino @@ -0,0 +1,44 @@ + +/* + Stepper Motor Control - one step at a time + + This program drives a unipolar or bipolar stepper motor. + The motor is attached to digital pins 8 - 11 of the Arduino. + + The motor will step one step at a time, very slowly. You can use this to + test that you've got the four wires of your stepper wired to the correct + pins. If wired correctly, all steps should be in the same direction. + + Use this also to count the number of steps per revolution of your motor, + if you don't know it. Then plug that number into the oneRevolution + example to see if you got it right. + + Created 30 Nov. 2009 + by Tom Igoe + + */ + +#include + +const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution + // for your motor + +// initialize the stepper library on pins 8 through 11: +Stepper myStepper(stepsPerRevolution, 8,9,10,11); + +int stepCount = 0; // number of steps the motor has taken + +void setup() { + // initialize the serial port: + Serial.begin(9600); +} + +void loop() { + // step one step: + myStepper.step(1); + Serial.print("steps:" ); + Serial.println(stepCount); + stepCount++; + delay(500); +} + diff --git a/libraries/Stepper3/examples/stepper_speedControl/stepper_speedControl.ino b/libraries/Stepper3/examples/stepper_speedControl/stepper_speedControl.ino new file mode 100644 index 00000000000..dbd0f7f02a8 --- /dev/null +++ b/libraries/Stepper3/examples/stepper_speedControl/stepper_speedControl.ino @@ -0,0 +1,49 @@ + +/* + Stepper Motor Control - speed control + + This program drives a unipolar or bipolar stepper motor. + The motor is attached to digital pins 8 - 11 of the Arduino. + A potentiometer is connected to analog input 0. + + The motor will rotate in a clockwise direction. The higher the potentiometer value, + the faster the motor speed. Because setSpeed() sets the delay between steps, + you may notice the motor is less responsive to changes in the sensor value at + low speeds. + + Created 30 Nov. 2009 + Modified 28 Oct 2010 + by Tom Igoe + + */ + +#include + +const int stepsPerRevolution = 200; // change this to fit the number of steps per revolution +// for your motor + + +// initialize the stepper library on pins 8 through 11: +Stepper myStepper(stepsPerRevolution, 8,9,10,11); + +int stepCount = 0; // number of steps the motor has taken + +void setup() { + // initialize the serial port: + Serial.begin(9600); +} + +void loop() { + // read the sensor value: + int sensorReading = analogRead(A0); + // map it to a range from 0 to 100: + int motorSpeed = map(sensorReading, 0, 1023, 0, 100); + // set the motor speed: + if (motorSpeed > 0) { + myStepper.setSpeed(motorSpeed); + // step 1/100 of a revolution: + myStepper.step(stepsPerRevolution/100); + } +} + + diff --git a/libraries/Stepper3/keywords.txt b/libraries/Stepper3/keywords.txt new file mode 100644 index 00000000000..6ffdb733643 --- /dev/null +++ b/libraries/Stepper3/keywords.txt @@ -0,0 +1,34 @@ +####################################### +# Syntax Coloring Map For Test +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Stepper KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +registerMotor KEYWORD2 +halt KEYWORD2 +numMotors KEYWORD2 +setCallback KEYWORD2 +setDriveType KEYWORD2 +setSpeed KEYWORD2 +step KEYWORD2 +version KEYWORD2 + +###################################### +# Instances (KEYWORD2) +####################################### + +####################################### +# Constants (LITERAL1) +####################################### + +FullStep LITERAL1 +HalfStep LITERAL1 +Wave LITERAL1 \ No newline at end of file diff --git a/libraries/Stepper3/utility/Vector.h b/libraries/Stepper3/utility/Vector.h new file mode 100644 index 00000000000..3b10d1529be --- /dev/null +++ b/libraries/Stepper3/utility/Vector.h @@ -0,0 +1,73 @@ +// Adapted from a post on the Arduino Forums by Gabb: +// http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1289151491 + +#ifndef VECTOR_H +#define VECTOR_H + +#include +#include + +// Minimal class to replace std::vector +template +class Vector { + size_t d_size; // Stores no. of actually stored objects + size_t d_capacity; // Stores allocated capacity + Data *d_data; // Stores data +public: + // Default constructor + Vector() : d_size(0), d_capacity(0), d_data(0) {}; + // Copy constuctor + Vector(Vector const &other) : d_size(other.d_size), d_capacity(other.d_capacity), d_data(0) { + d_data = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(d_data, other.d_data, d_size * sizeof(Data)); + }; + + // Destructor + ~Vector() { + free(d_data); + }; + + // Needed for memory management + Vector &operator=(Vector const &other) { + free(d_data); + d_size = other.d_size; + d_capacity = other.d_capacity; + d_data = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(d_data, other.d_data, d_size * sizeof(Data)); + return *this; + }; + + // Const getter + Data const &operator[](size_t idx) const { + return d_data[idx]; + }; + // Changeable getter + Data &operator[](size_t idx) { + return d_data[idx]; + }; + + // Adds new value. If needed, allocates more space + void push_back(Data const &x) { + if (d_capacity == d_size) { + resize(); + } + d_data[d_size++] = x; + }; + + // Size getter + size_t size() const { + return d_size; + }; + +private: + // Allocates double the old space + void resize() { + d_capacity = d_capacity ? d_capacity * 2 : 1; + Data *newdata = (Data *) malloc(d_capacity * sizeof(Data)); + memcpy(newdata, d_data, d_size * sizeof(Data)); + free(d_data); + d_data = newdata; + }; +}; + +#endif