From 287a0d1dba4a4f59c38f54a295fcdb15ad4a4b62 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 21 May 2017 13:36:42 -0700 Subject: [PATCH 01/21] First attempt at driving multiple motors via a container (#2) All motors are driven in parallel but some may finish before others. Had to expose all protected properties of BasicStepperDriver and copy some internals, which should be moved back in. Task-Url: https://github.com/laurb9/StepperDriver/issues/2 --- examples/MultiAxis/MultiAxis.ino | 58 ++++++++++++++++++ src/BasicStepperDriver.h | 3 +- src/MultiDriver.cpp | 100 +++++++++++++++++++++++++++++++ src/MultiDriver.h | 58 ++++++++++++++++++ 4 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 examples/MultiAxis/MultiAxis.ino create mode 100644 src/MultiDriver.cpp create mode 100644 src/MultiDriver.h diff --git a/examples/MultiAxis/MultiAxis.ino b/examples/MultiAxis/MultiAxis.ino new file mode 100644 index 0000000..1401d76 --- /dev/null +++ b/examples/MultiAxis/MultiAxis.ino @@ -0,0 +1,58 @@ +/* + * Multi-motor control + * + * Move two or three motors at the same time. + * + * Copyright (C)2017 Laurentiu Badea + * + * This file may be redistributed under the terms of the MIT license. + * A copy of this license has been included with this distribution in the file LICENSE. + */ +#include +#include "BasicStepperDriver.h" +#include "MultiDriver.h" + +// Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step +#define MOTOR_STEPS 200 + +// X motor +#define DIR_X 5 +#define STEP_X 9 + +// Y motor +#define DIR_Y 8 +#define STEP_Y 6 + +// If microstepping is set externally, make sure this matches the selected mode +// 1=full step, 2=half step etc. +#define MICROSTEPS 32 + +// 2-wire basic config, microstepping is hardwired on the driver +// Other drivers can be mixed and matched but must be configured individually +BasicStepperDriver stepperX(MOTOR_STEPS, DIR_X, STEP_X, 10); +BasicStepperDriver stepperY(MOTOR_STEPS, DIR_Y, STEP_Y); + +MultiDriver controller(stepperX, stepperY); + +void setup() { + Serial.begin(38400); + /* + * Set target motors RPM. + * The actual speed may be slower if another axis has more travel + */ + stepperX.setRPM(30); + stepperY.setRPM(90); + + controller.setMicrostep(MICROSTEPS); +} + +void loop() { + + controller.move(4000, 12000); + + controller.move(-4000, -6000); + + controller.move(0, -6000); + + delay(30000); +} diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 8f9c6bd..0676db9 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -10,7 +10,6 @@ #ifndef STEPPER_DRIVER_BASE_H #define STEPPER_DRIVER_BASE_H #include -#include "BasicStepperDriver.h" // used internally by the library to mark unconnected pins #define PIN_UNCONNECTED -1 @@ -33,7 +32,7 @@ inline void microWaitUntil(unsigned long target_micros){ * Microstepping level should be externally controlled or hardwired. */ class BasicStepperDriver { -protected: +public: int motor_steps; int rpm = 60; int dir_pin; diff --git a/src/MultiDriver.cpp b/src/MultiDriver.cpp new file mode 100644 index 0000000..42e2bf8 --- /dev/null +++ b/src/MultiDriver.cpp @@ -0,0 +1,100 @@ +/* + * Multi-motor group driver + * + * Copyright (C)2017 Laurentiu Badea + * + * This file may be redistributed under the terms of the MIT license. + * A copy of this license has been included with this distribution in the file LICENSE. + */ +#include "MultiDriver.h" + +#define FOREACH_MOTOR(action) for (short i=count-1; i >= 0; i--){action;} + +/* + * Move each motor the requested number of steps, in parallel + * positive to move forward, negative to reverse, 0 to remain still + */ +void MultiDriver::move(long steps1, long steps2, long steps3){ + long steps[3] = {steps1, steps2, steps3}; + short states[MAX_MOTORS]; + unsigned long event_timers[MAX_MOTORS]; + + /* + * Initialize state and trigger STEP for all active motors + */ + enable(); + FOREACH_MOTOR( + if (steps[i]){ + if (steps[i] > 0){ + motors[i]->setDirection(1); + } else { + motors[i]->setDirection(-1); + steps[i] = -steps[i]; + } + states[i] = HIGH; + digitalWrite(motors[i]->step_pin, HIGH); + /* + * We try to do a 50% duty cycle so it's easy to see. + * Other option is step_high_min, pulse_duration-step_high_min. + */ + event_timers[i] = motors[i]->step_pulse / 2; + } + ); + + bool done = false; + while (!done){ + // Find the time when the next pulse needs to fire + // this is the smallest timer value from all active motors + unsigned long next_event = ~0L; + done = true; + FOREACH_MOTOR( + if (steps[i]){ + done = false; + if (event_timers[i] < next_event && steps[i]){ + next_event = event_timers[i]; + } + } + ); + if (done){ + break; + } + + // wait until the next event + microWaitUntil(micros() + next_event); + + FOREACH_MOTOR( + if (steps[i]){ + if (event_timers[i] == next_event){ // motor ready for next action + + // Toggle STEP + states[i] = (states[i] == LOW) ? HIGH : LOW; + digitalWrite(motors[i]->step_pin, states[i]); + + // If final state is LOW, decrement remaining steps for this motor + if (states[i] == LOW){ + steps[i] -= 1; + } + + // set timer for the next pulse + event_timers[i] = motors[i]->step_pulse / 2; + + } else { + // Reduce all other event timers by the current left time + event_timers[i] -= next_event; + }; + } + ); + } + disable(); +} + +void MultiDriver::setMicrostep(unsigned microsteps){ + FOREACH_MOTOR(motors[i]->setMicrostep(microsteps)); +} + +void MultiDriver::enable(void){ + FOREACH_MOTOR(motors[i]->enable()); +} +void MultiDriver::disable(void){ + FOREACH_MOTOR(motors[i]->disable()); +} diff --git a/src/MultiDriver.h b/src/MultiDriver.h new file mode 100644 index 0000000..70a864e --- /dev/null +++ b/src/MultiDriver.h @@ -0,0 +1,58 @@ +/* + * Multi-motor group driver + * + * Copyright (C)2017 Laurentiu Badea + * + * This file may be redistributed under the terms of the MIT license. + * A copy of this license has been included with this distribution in the file LICENSE. + */ +#ifndef MULTI_DRIVER_BASE_H +#define MULTI_DRIVER_BASE_H +#include +#include "BasicStepperDriver.h" + +#define MAX_MOTORS 3 // a reasonable but arbitrary limit +#define Motor BasicStepperDriver +/* + * Multi-motor group driver class. + */ +class MultiDriver { +public: + unsigned short count; + Motor* const *motors; + /* + * Generic initializer, will be called by the others + */ + MultiDriver(const unsigned short count, Motor* const *motors) + :count(count), motors(motors) + {}; + +public: + /* + * Two-motor setup + */ + MultiDriver(Motor& motor1, Motor& motor2) + :MultiDriver(2, new Motor* const[2]{&motor1, &motor2}) + {}; + /* + * Three-motor setup (X, Y, Z for example) + */ + MultiDriver(Motor& motor1, Motor& motor2, Motor& motor3) + :MultiDriver(3, new Motor* const[3]{&motor1, &motor2, &motor3}) + {}; + /* + * Move the motors a given number of steps. + * positive to move forward, negative to reverse + */ + void move(long steps1, long steps2, long steps3=0); + /* + * Set the same microstepping level on all motors + */ + void setMicrostep(unsigned microsteps); + /* + * Turn all motors on or off + */ + void enable(void); + void disable(void); +}; +#endif // MULTI_DRIVER_BASE_H From 28d4090fdf2a0a0d1a952d821f207d11642e6c16 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 21 May 2017 15:38:34 -0700 Subject: [PATCH 02/21] Get timing information from drivers instead of touching internal vars This should allow for each driver to control its own pulse timing. Task-Url: https://github.com/laurb9/StepperDriver/issues/2 #2 --- examples/MultiAxis/MultiAxis.ino | 1 - src/BasicStepperDriver.cpp | 27 ++++----------------------- src/BasicStepperDriver.h | 31 +++++++++++++++++++++++++++++-- src/MultiDriver.cpp | 31 +++++++++---------------------- 4 files changed, 42 insertions(+), 48 deletions(-) diff --git a/examples/MultiAxis/MultiAxis.ino b/examples/MultiAxis/MultiAxis.ino index 1401d76..12262f6 100644 --- a/examples/MultiAxis/MultiAxis.ino +++ b/examples/MultiAxis/MultiAxis.ino @@ -38,7 +38,6 @@ void setup() { Serial.begin(38400); /* * Set target motors RPM. - * The actual speed may be slower if another axis has more travel */ stepperX.setRPM(30); stepperY.setRPM(90); diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index e437484..466adae 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -71,35 +71,16 @@ unsigned BasicStepperDriver::setMicrostep(unsigned microsteps){ return this->microsteps; } -/* - * DIR: forward HIGH, reverse LOW - */ -void BasicStepperDriver::setDirection(int direction){ - digitalWrite(dir_pin, (direction<0) ? LOW : HIGH); -} - /* * Move the motor a given number of steps. * positive to move forward, negative to reverse */ void BasicStepperDriver::move(long steps){ - if (steps >= 0){ - setDirection(1); - } else { - setDirection(-1); - steps = -steps; - } - /* - * We currently try to do a 50% duty cycle so it's easy to see. - * Other option is step_high_min, pulse_duration-step_high_min. - */ - unsigned long pulse_duration = step_pulse/2; + Direction direction = (steps >= 0) ? DIR_FORWARD : DIR_REVERSE; + steps = abs(steps); while (steps--){ - digitalWrite(step_pin, HIGH); - unsigned long next_edge = micros() + pulse_duration; - microWaitUntil(next_edge); - digitalWrite(step_pin, LOW); - microWaitUntil(next_edge + pulse_duration); + microWaitUntil(micros() + step(HIGH, direction)); + microWaitUntil(micros() + step(LOW, direction)); } } diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 0676db9..5e780b3 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -15,6 +15,8 @@ #define PIN_UNCONNECTED -1 #define IS_CONNECTED(pin) (pin != PIN_UNCONNECTED) +enum Direction {DIR_FORWARD, DIR_REVERSE}; + /* * calculate the step pulse in microseconds for a given rpm value. * 60[s/min] * 1000000[us/s] / microsteps / steps / rpm @@ -32,7 +34,7 @@ inline void microWaitUntil(unsigned long target_micros){ * Microstepping level should be externally controlled or hardwired. */ class BasicStepperDriver { -public: +protected: int motor_steps; int rpm = 60; int dir_pin; @@ -45,7 +47,12 @@ class BasicStepperDriver { // step pulse duration (microseconds), depends on rpm and microstep level unsigned long step_pulse; - void setDirection(int direction); + /* + * DIR: forward HIGH, reverse LOW + */ + void setDirection(Direction direction){ + digitalWrite(dir_pin, (direction == DIR_FORWARD) ? HIGH : LOW); + }; void init(void); void calcStepPulse(void); @@ -99,5 +106,25 @@ class BasicStepperDriver { */ void enable(void); void disable(void); + /* + * Methods to allow external timing control. + * These should not be needed for normal use. + */ + /* + * toggle step and return time until next change is needed (micros) + */ + unsigned long step(const short value, Direction direction){ + /* + * DIR pin is sampled on rising STEP edge, so we need to set it first + */ + setDirection(direction); + digitalWrite(step_pin, value); + /* + * We currently try to do a 50% duty cycle so it's easy to see. + * Other option is step_high_min, pulse_duration-step_high_min. + */ + return step_pulse/2; + } + }; #endif // STEPPER_DRIVER_BASE_H diff --git a/src/MultiDriver.cpp b/src/MultiDriver.cpp index 42e2bf8..66ec9be 100644 --- a/src/MultiDriver.cpp +++ b/src/MultiDriver.cpp @@ -17,6 +17,7 @@ void MultiDriver::move(long steps1, long steps2, long steps3){ long steps[3] = {steps1, steps2, steps3}; short states[MAX_MOTORS]; + Direction dirs[MAX_MOTORS]; unsigned long event_timers[MAX_MOTORS]; /* @@ -25,32 +26,22 @@ void MultiDriver::move(long steps1, long steps2, long steps3){ enable(); FOREACH_MOTOR( if (steps[i]){ - if (steps[i] > 0){ - motors[i]->setDirection(1); - } else { - motors[i]->setDirection(-1); - steps[i] = -steps[i]; - } + dirs[i] = (steps[i] > 0) ? DIR_FORWARD : DIR_REVERSE; + steps[i] = abs(steps[i]); states[i] = HIGH; - digitalWrite(motors[i]->step_pin, HIGH); - /* - * We try to do a 50% duty cycle so it's easy to see. - * Other option is step_high_min, pulse_duration-step_high_min. - */ - event_timers[i] = motors[i]->step_pulse / 2; + event_timers[i] = motors[i]->step(HIGH, dirs[i]); } ); - bool done = false; - while (!done){ + while (true){ // Find the time when the next pulse needs to fire // this is the smallest timer value from all active motors unsigned long next_event = ~0L; - done = true; + bool done = true; FOREACH_MOTOR( if (steps[i]){ done = false; - if (event_timers[i] < next_event && steps[i]){ + if (event_timers[i] < next_event){ next_event = event_timers[i]; } } @@ -66,18 +57,14 @@ void MultiDriver::move(long steps1, long steps2, long steps3){ if (steps[i]){ if (event_timers[i] == next_event){ // motor ready for next action - // Toggle STEP + // Toggle STEP and set timer for next pulse states[i] = (states[i] == LOW) ? HIGH : LOW; - digitalWrite(motors[i]->step_pin, states[i]); + event_timers[i] = motors[i]->step(states[i], dirs[i]); // If final state is LOW, decrement remaining steps for this motor if (states[i] == LOW){ steps[i] -= 1; } - - // set timer for the next pulse - event_timers[i] = motors[i]->step_pulse / 2; - } else { // Reduce all other event timers by the current left time event_timers[i] -= next_event; From 10a1876dd7ae2c565741e817beb0ca185caa9aa8 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 21 May 2017 16:30:27 -0700 Subject: [PATCH 03/21] Add SyncDriver for synchronized movement. RPMs are altered such that all motors finish that (approximately*) the same time. *) because rpm is an integer, if it were fractional then we could be more precise. TODO: can we exceed (long) ? should we do double ? #2 Task-Url: https://github.com/laurb9/StepperDriver/issues/2 --- examples/MultiAxis/MultiAxis.ino | 4 +-- src/BasicStepperDriver.h | 11 +++++-- src/MultiDriver.h | 6 ++-- src/SyncDriver.cpp | 52 ++++++++++++++++++++++++++++++++ src/SyncDriver.h | 45 +++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 7 deletions(-) create mode 100644 src/SyncDriver.cpp create mode 100644 src/SyncDriver.h diff --git a/examples/MultiAxis/MultiAxis.ino b/examples/MultiAxis/MultiAxis.ino index 12262f6..6341485 100644 --- a/examples/MultiAxis/MultiAxis.ino +++ b/examples/MultiAxis/MultiAxis.ino @@ -10,7 +10,7 @@ */ #include #include "BasicStepperDriver.h" -#include "MultiDriver.h" +#include "SyncDriver.h" // Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step #define MOTOR_STEPS 200 @@ -32,7 +32,7 @@ BasicStepperDriver stepperX(MOTOR_STEPS, DIR_X, STEP_X, 10); BasicStepperDriver stepperY(MOTOR_STEPS, DIR_Y, STEP_Y); -MultiDriver controller(stepperX, stepperY); +SyncDriver controller(stepperX, stepperY); void setup() { Serial.begin(38400); diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 5e780b3..d99cb4c 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -2,7 +2,7 @@ * Generic Stepper Motor Driver Driver * Indexer mode only. * - * Copyright (C)2015 Laurentiu Badea + * Copyright (C)2015-2017 Laurentiu Badea * * This file may be redistributed under the terms of the MIT license. * A copy of this license has been included with this distribution in the file LICENSE. @@ -101,6 +101,7 @@ class BasicStepperDriver { * Set target motor RPM (1-200 is a reasonable range) */ void setRPM(unsigned rpm); + unsigned getRPM(void){ return rpm; }; /* * Turn off/on motor to allow the motor to be moved by hand/hold the position in place */ @@ -111,7 +112,7 @@ class BasicStepperDriver { * These should not be needed for normal use. */ /* - * toggle step and return time until next change is needed (micros) + * Toggle step and return time until next change is needed (micros) */ unsigned long step(const short value, Direction direction){ /* @@ -125,6 +126,12 @@ class BasicStepperDriver { */ return step_pulse/2; } + /* + * Return the step interval (micros) + */ + unsigned long getTimePerStep(void){ + return step_pulse; + } }; #endif // STEPPER_DRIVER_BASE_H diff --git a/src/MultiDriver.h b/src/MultiDriver.h index 70a864e..1ba1a00 100644 --- a/src/MultiDriver.h +++ b/src/MultiDriver.h @@ -6,8 +6,8 @@ * This file may be redistributed under the terms of the MIT license. * A copy of this license has been included with this distribution in the file LICENSE. */ -#ifndef MULTI_DRIVER_BASE_H -#define MULTI_DRIVER_BASE_H +#ifndef MULTI_DRIVER_H +#define MULTI_DRIVER_H #include #include "BasicStepperDriver.h" @@ -55,4 +55,4 @@ class MultiDriver { void enable(void); void disable(void); }; -#endif // MULTI_DRIVER_BASE_H +#endif // MULTI_DRIVER_H diff --git a/src/SyncDriver.cpp b/src/SyncDriver.cpp new file mode 100644 index 0000000..4fe19e7 --- /dev/null +++ b/src/SyncDriver.cpp @@ -0,0 +1,52 @@ +/* + * Synchronous Multi-motor group driver + * All motors reach their target at the same time. + * + * Copyright (C)2017 Laurentiu Badea + * + * This file may be redistributed under the terms of the MIT license. + * A copy of this license has been included with this distribution in the file LICENSE. + */ +#include "SyncDriver.h" + +#define FOREACH_MOTOR(action) for (short i=count-1; i >= 0; i--){action;} + +void SyncDriver::move(long steps1, long steps2, long steps3){ + long steps[3] = {steps1, steps2, steps3}; + long timing[MAX_MOTORS]; + unsigned rpms[MAX_MOTORS]; + /* + * find which motor would take the longest to finish, + */ + long m = 0; + FOREACH_MOTOR( + long move_time = abs(steps[i]) * motors[i]->getTimePerStep(); + timing[i] = move_time; + Serial.print(i); Serial.print(" = "); Serial.println(move_time); + if (move_time > m){ + m = move_time; + } + ); + /* + * Stretch timing for all others by adjusting rpm proportionally + */ + if (m){ + FOREACH_MOTOR( + if (steps[i]){ + rpms[i] = motors[i]->getRPM(); + motors[i]->setRPM(rpms[i] * timing[i] / m); + } + ); + } + MultiDriver::move(steps1, steps2, steps3); + /* + * Restore original rpm settings + */ + if (m){ + FOREACH_MOTOR( + if (rpms[i]){ + motors[i]->setRPM(rpms[i]); + } + ); + } +} diff --git a/src/SyncDriver.h b/src/SyncDriver.h new file mode 100644 index 0000000..4d3382a --- /dev/null +++ b/src/SyncDriver.h @@ -0,0 +1,45 @@ +/* + * Synchronous Multi-motor group driver + * + * Copyright (C)2017 Laurentiu Badea + * + * This file may be redistributed under the terms of the MIT license. + * A copy of this license has been included with this distribution in the file LICENSE. + */ +#ifndef SYNC_DRIVER_H +#define SYNC_DRIVER_H +#include +#include "MultiDriver.h" + +/* + * Synchronous Multi-motor group driver class. + * This driver sets up timing so all motors reach their target at the same time. + */ +class SyncDriver : public MultiDriver { + /* + * Generic initializer, will be called by the others + */ + SyncDriver(const unsigned short count, Motor* const *motors) + :MultiDriver(count, motors) + {}; + +public: + /* + * Two-motor setup + */ + SyncDriver(Motor& motor1, Motor& motor2) + :SyncDriver(2, new Motor* const[2]{&motor1, &motor2}) + {}; + /* + * Three-motor setup (X, Y, Z for example) + */ + SyncDriver(Motor& motor1, Motor& motor2, Motor& motor3) + :SyncDriver(3, new Motor* const[3]{&motor1, &motor2, &motor3}) + {}; + /* + * Move the motors a given number of steps. + * positive to move forward, negative to reverse + */ + void move(long steps1, long steps2, long steps3=0); +}; +#endif // SYNC_DRIVER_H From 4f5d3a44c083612d05df5828e19ac189cd22a0fd Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 21 May 2017 19:57:55 -0700 Subject: [PATCH 04/21] Cleanup --- examples/MultiAxis/MultiAxis.ino | 6 +++--- src/MultiDriver.cpp | 2 -- src/SyncDriver.cpp | 17 ++++++++--------- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/examples/MultiAxis/MultiAxis.ino b/examples/MultiAxis/MultiAxis.ino index 6341485..a47a4bc 100644 --- a/examples/MultiAxis/MultiAxis.ino +++ b/examples/MultiAxis/MultiAxis.ino @@ -29,13 +29,12 @@ // 2-wire basic config, microstepping is hardwired on the driver // Other drivers can be mixed and matched but must be configured individually -BasicStepperDriver stepperX(MOTOR_STEPS, DIR_X, STEP_X, 10); +BasicStepperDriver stepperX(MOTOR_STEPS, DIR_X, STEP_X); BasicStepperDriver stepperY(MOTOR_STEPS, DIR_Y, STEP_Y); SyncDriver controller(stepperX, stepperY); void setup() { - Serial.begin(38400); /* * Set target motors RPM. */ @@ -48,10 +47,11 @@ void setup() { void loop() { controller.move(4000, 12000); + delay(1000); controller.move(-4000, -6000); + delay(1000); controller.move(0, -6000); - delay(30000); } diff --git a/src/MultiDriver.cpp b/src/MultiDriver.cpp index 66ec9be..efdcaf7 100644 --- a/src/MultiDriver.cpp +++ b/src/MultiDriver.cpp @@ -23,7 +23,6 @@ void MultiDriver::move(long steps1, long steps2, long steps3){ /* * Initialize state and trigger STEP for all active motors */ - enable(); FOREACH_MOTOR( if (steps[i]){ dirs[i] = (steps[i] > 0) ? DIR_FORWARD : DIR_REVERSE; @@ -72,7 +71,6 @@ void MultiDriver::move(long steps1, long steps2, long steps3){ } ); } - disable(); } void MultiDriver::setMicrostep(unsigned microsteps){ diff --git a/src/SyncDriver.cpp b/src/SyncDriver.cpp index 4fe19e7..cce3e5f 100644 --- a/src/SyncDriver.cpp +++ b/src/SyncDriver.cpp @@ -18,23 +18,22 @@ void SyncDriver::move(long steps1, long steps2, long steps3){ /* * find which motor would take the longest to finish, */ - long m = 0; + long move_time = 0; FOREACH_MOTOR( - long move_time = abs(steps[i]) * motors[i]->getTimePerStep(); - timing[i] = move_time; - Serial.print(i); Serial.print(" = "); Serial.println(move_time); - if (move_time > m){ - m = move_time; + long m = abs(steps[i]) * motors[i]->getTimePerStep(); + timing[i] = m; + if (m > move_time){ + move_time = m; } ); /* * Stretch timing for all others by adjusting rpm proportionally */ - if (m){ + if (move_time){ FOREACH_MOTOR( if (steps[i]){ rpms[i] = motors[i]->getRPM(); - motors[i]->setRPM(rpms[i] * timing[i] / m); + motors[i]->setRPM(rpms[i] * timing[i] / move_time); } ); } @@ -42,7 +41,7 @@ void SyncDriver::move(long steps1, long steps2, long steps3){ /* * Restore original rpm settings */ - if (m){ + if (move_time){ FOREACH_MOTOR( if (rpms[i]){ motors[i]->setRPM(rpms[i]); From 7a8833b595d781a3b4bd41b01c2b5bc12f8db3b2 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 21 May 2017 20:12:04 -0700 Subject: [PATCH 05/21] Fixed issue which caused incorrect motor rpms to be restored. --- src/SyncDriver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/SyncDriver.cpp b/src/SyncDriver.cpp index cce3e5f..9951962 100644 --- a/src/SyncDriver.cpp +++ b/src/SyncDriver.cpp @@ -34,6 +34,8 @@ void SyncDriver::move(long steps1, long steps2, long steps3){ if (steps[i]){ rpms[i] = motors[i]->getRPM(); motors[i]->setRPM(rpms[i] * timing[i] / move_time); + } else { + rpms[i] = 0; } ); } From 3a1c859b6824900616a3cc537bcaebe508508e22 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 21 May 2017 22:01:12 -0700 Subject: [PATCH 06/21] Added rotation methods --- src/BasicStepperDriver.cpp | 6 ++---- src/BasicStepperDriver.h | 10 +++++++++- src/MultiDriver.cpp | 9 +++++++++ src/MultiDriver.h | 7 ++++++- 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index 466adae..a7d306f 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -88,8 +88,7 @@ void BasicStepperDriver::move(long steps){ * Move the motor a given number of degrees (1-360) */ void BasicStepperDriver::rotate(long deg){ - long steps = deg * motor_steps * (long)microsteps / 360; - move(steps); + move(calcStepsForRotation(deg)); } /* * Move the motor with sub-degree precision. @@ -97,8 +96,7 @@ void BasicStepperDriver::rotate(long deg){ * due to inclusion of float support. */ void BasicStepperDriver::rotate(double deg){ - long steps = deg * motor_steps * microsteps / 360; - move(steps); + move(calcStepsForRotation(deg)); } /* diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index d99cb4c..6efecd0 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -132,6 +132,14 @@ class BasicStepperDriver { unsigned long getTimePerStep(void){ return step_pulse; } - + /* + * Calculate steps needed to rotate requested angle, given in degrees + */ + long calcStepsForRotation(long deg){ + return deg * motor_steps * (long)microsteps / 360; + } + long calcStepsForRotation(double deg){ + return deg * motor_steps * microsteps / 360; + } }; #endif // STEPPER_DRIVER_BASE_H diff --git a/src/MultiDriver.cpp b/src/MultiDriver.cpp index efdcaf7..466a4fb 100644 --- a/src/MultiDriver.cpp +++ b/src/MultiDriver.cpp @@ -73,6 +73,15 @@ void MultiDriver::move(long steps1, long steps2, long steps3){ } } +#define CALC_STEPS(i, deg) ((motors[i] && deg) ? motors[i]->calcStepsForRotation(deg) : 0) +void MultiDriver::rotate(long deg1, long deg2, long deg3){ + move(CALC_STEPS(0, deg1), CALC_STEPS(1, deg2), CALC_STEPS(2, deg3)); +} + +void MultiDriver::rotate(double deg1, double deg2, double deg3){ + move(CALC_STEPS(0, deg1), CALC_STEPS(1, deg2), CALC_STEPS(2, deg3)); +} + void MultiDriver::setMicrostep(unsigned microsteps){ FOREACH_MOTOR(motors[i]->setMicrostep(microsteps)); } diff --git a/src/MultiDriver.h b/src/MultiDriver.h index 1ba1a00..7e6b9c3 100644 --- a/src/MultiDriver.h +++ b/src/MultiDriver.h @@ -44,7 +44,12 @@ class MultiDriver { * Move the motors a given number of steps. * positive to move forward, negative to reverse */ - void move(long steps1, long steps2, long steps3=0); + virtual void move(long steps1, long steps2, long steps3=0); + void rotate(int deg1, int deg2, int deg3=0){ + rotate((long)deg1, (long)deg2, (long)deg3); + }; + void rotate(long deg1, long deg2, long deg3=0); + void rotate(double deg1, double deg2, double deg3=0); /* * Set the same microstepping level on all motors */ From 567e997c53f97f52d811561478bde4cb592d7b40 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 21 May 2017 22:26:43 -0700 Subject: [PATCH 07/21] Move movement state into the class. Allows breaking the movement to do something else while waiting, like BasicStepperDriver.step() --- src/MultiDriver.cpp | 13 ++++++------- src/MultiDriver.h | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/MultiDriver.cpp b/src/MultiDriver.cpp index 466a4fb..b865385 100644 --- a/src/MultiDriver.cpp +++ b/src/MultiDriver.cpp @@ -15,10 +15,9 @@ * positive to move forward, negative to reverse, 0 to remain still */ void MultiDriver::move(long steps1, long steps2, long steps3){ - long steps[3] = {steps1, steps2, steps3}; - short states[MAX_MOTORS]; - Direction dirs[MAX_MOTORS]; - unsigned long event_timers[MAX_MOTORS]; + steps[0] = steps1; + steps[1] = steps2; + steps[2] = steps3; /* * Initialize state and trigger STEP for all active motors @@ -36,16 +35,16 @@ void MultiDriver::move(long steps1, long steps2, long steps3){ // Find the time when the next pulse needs to fire // this is the smallest timer value from all active motors unsigned long next_event = ~0L; - bool done = true; + ready = true; FOREACH_MOTOR( if (steps[i]){ - done = false; + ready = false; if (event_timers[i] < next_event){ next_event = event_timers[i]; } } ); - if (done){ + if (ready){ break; } diff --git a/src/MultiDriver.h b/src/MultiDriver.h index 7e6b9c3..6898a2e 100644 --- a/src/MultiDriver.h +++ b/src/MultiDriver.h @@ -17,7 +17,10 @@ * Multi-motor group driver class. */ class MultiDriver { -public: +protected: + /* + * Configuration + */ unsigned short count; Motor* const *motors; /* @@ -27,6 +30,20 @@ class MultiDriver { :count(count), motors(motors) {}; + /* + * Movement state + */ + // ready to start a new move + bool ready = true; + // how many steps are left for each motor (if ready=false) + long steps[MAX_MOTORS]; + // DIR needed for each motor + Direction dirs[MAX_MOTORS]; + // STEP states for each motor + short states[MAX_MOTORS]; + // when next state change is due for each motor + unsigned long event_timers[MAX_MOTORS]; + public: /* * Two-motor setup From 17be1e23238b8e8237c400bafba3f10609aa0c8a Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 4 Jun 2017 19:05:01 -0700 Subject: [PATCH 08/21] Split move() into a setup phase and step pulse phase for #19 --- src/BasicStepperDriver.cpp | 47 ++++++++++++++++++++++++++++++++------ src/BasicStepperDriver.h | 37 ++++++++++++++---------------- src/MultiDriver.cpp | 26 +++++++-------------- src/MultiDriver.h | 6 ----- src/SyncDriver.h | 3 ++- 5 files changed, 67 insertions(+), 52 deletions(-) diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index a7d306f..d40e548 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -76,14 +76,13 @@ unsigned BasicStepperDriver::setMicrostep(unsigned microsteps){ * positive to move forward, negative to reverse */ void BasicStepperDriver::move(long steps){ - Direction direction = (steps >= 0) ? DIR_FORWARD : DIR_REVERSE; - steps = abs(steps); - while (steps--){ - microWaitUntil(micros() + step(HIGH, direction)); - microWaitUntil(micros() + step(LOW, direction)); - } + unsigned long next_event; + startMove(steps); + do { + next_event = nextAction(); + microWaitUntil(micros() + next_event); + } while (next_event); } - /* * Move the motor a given number of degrees (1-360) */ @@ -99,6 +98,40 @@ void BasicStepperDriver::rotate(double deg){ move(calcStepsForRotation(deg)); } +/* + * Initiate a move (calculate and save the parameters) + */ +void BasicStepperDriver::startMove(long steps){ + dir_state = (steps >= 0) ? HIGH : LOW; + step_state = LOW; + steps_remaining = abs(steps); +} +/* + * Toggle step and return time until next change is needed (micros) + */ +unsigned long BasicStepperDriver::nextAction(void){ + if (steps_remaining > 0){ + /* + * DIR pin is sampled on rising STEP edge, so it is set first + */ + digitalWrite(dir_pin, dir_state); + if (step_state == LOW){ + step_state = HIGH; + } else { + step_state = LOW; + steps_remaining--; + } + digitalWrite(step_pin, step_state); + /* + * We currently try to do a 50% duty cycle so it's easy to see. + * Other option is step_high_min, pulse_duration-step_high_min. + */ + return step_pulse/2; + } else { + return 0; // end of move + } +} + /* * Enable/Disable the motor by setting a digital flag */ diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 6efecd0..b48801f 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -41,18 +41,24 @@ class BasicStepperDriver { int step_pin; int enable_pin = PIN_UNCONNECTED; + /* + * Movement state + */ + // how many steps are left to complete the current move (absolute value) + long steps_remaining; + // DIR pin state + short dir_state; + // STEP pin state (HIGH / LOW) + short step_state = LOW; + // microseconds until next state change + unsigned long next_pulse_time; + // current microstep level, must be < getMaxMicrostep() // for 1:16 microsteps is 16 unsigned microsteps = 1; // step pulse duration (microseconds), depends on rpm and microstep level unsigned long step_pulse; - /* - * DIR: forward HIGH, reverse LOW - */ - void setDirection(Direction direction){ - digitalWrite(dir_pin, (direction == DIR_FORWARD) ? HIGH : LOW); - }; void init(void); void calcStepPulse(void); @@ -112,23 +118,14 @@ class BasicStepperDriver { * These should not be needed for normal use. */ /* - * Toggle step and return time until next change is needed (micros) + * Initiate a move (calculate and save the parameters) */ - unsigned long step(const short value, Direction direction){ - /* - * DIR pin is sampled on rising STEP edge, so we need to set it first - */ - setDirection(direction); - digitalWrite(step_pin, value); - /* - * We currently try to do a 50% duty cycle so it's easy to see. - * Other option is step_high_min, pulse_duration-step_high_min. - */ - return step_pulse/2; - } + void startMove(long steps); /* - * Return the step interval (micros) + * Toggle step and return time until next change is needed (micros) */ + unsigned long nextAction(void); + unsigned long getTimePerStep(void){ return step_pulse; } diff --git a/src/MultiDriver.cpp b/src/MultiDriver.cpp index b865385..8febaa1 100644 --- a/src/MultiDriver.cpp +++ b/src/MultiDriver.cpp @@ -15,19 +15,17 @@ * positive to move forward, negative to reverse, 0 to remain still */ void MultiDriver::move(long steps1, long steps2, long steps3){ - steps[0] = steps1; - steps[1] = steps2; - steps[2] = steps3; + long steps[3] = {steps1, steps2, steps3}; /* * Initialize state and trigger STEP for all active motors */ FOREACH_MOTOR( if (steps[i]){ - dirs[i] = (steps[i] > 0) ? DIR_FORWARD : DIR_REVERSE; - steps[i] = abs(steps[i]); - states[i] = HIGH; - event_timers[i] = motors[i]->step(HIGH, dirs[i]); + motors[i]->startMove(steps[i]); + event_timers[i] = motors[i]->nextAction(); + } else { + event_timers[i] = 0; } ); @@ -37,7 +35,7 @@ void MultiDriver::move(long steps1, long steps2, long steps3){ unsigned long next_event = ~0L; ready = true; FOREACH_MOTOR( - if (steps[i]){ + if (event_timers[i]){ ready = false; if (event_timers[i] < next_event){ next_event = event_timers[i]; @@ -52,17 +50,9 @@ void MultiDriver::move(long steps1, long steps2, long steps3){ microWaitUntil(micros() + next_event); FOREACH_MOTOR( - if (steps[i]){ + if (event_timers[i]){ if (event_timers[i] == next_event){ // motor ready for next action - - // Toggle STEP and set timer for next pulse - states[i] = (states[i] == LOW) ? HIGH : LOW; - event_timers[i] = motors[i]->step(states[i], dirs[i]); - - // If final state is LOW, decrement remaining steps for this motor - if (states[i] == LOW){ - steps[i] -= 1; - } + event_timers[i] = motors[i]->nextAction(); } else { // Reduce all other event timers by the current left time event_timers[i] -= next_event; diff --git a/src/MultiDriver.h b/src/MultiDriver.h index 6898a2e..e5b93de 100644 --- a/src/MultiDriver.h +++ b/src/MultiDriver.h @@ -35,12 +35,6 @@ class MultiDriver { */ // ready to start a new move bool ready = true; - // how many steps are left for each motor (if ready=false) - long steps[MAX_MOTORS]; - // DIR needed for each motor - Direction dirs[MAX_MOTORS]; - // STEP states for each motor - short states[MAX_MOTORS]; // when next state change is due for each motor unsigned long event_timers[MAX_MOTORS]; diff --git a/src/SyncDriver.h b/src/SyncDriver.h index 4d3382a..536423a 100644 --- a/src/SyncDriver.h +++ b/src/SyncDriver.h @@ -16,6 +16,7 @@ * This driver sets up timing so all motors reach their target at the same time. */ class SyncDriver : public MultiDriver { +protected: /* * Generic initializer, will be called by the others */ @@ -40,6 +41,6 @@ class SyncDriver : public MultiDriver { * Move the motors a given number of steps. * positive to move forward, negative to reverse */ - void move(long steps1, long steps2, long steps3=0); + void move(long steps1, long steps2, long steps3=0) override; }; #endif // SYNC_DRIVER_H From f33b63963bf97c5b10d4975cb88f1effaac85175 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Mon, 5 Jun 2017 00:29:54 -0700 Subject: [PATCH 09/21] Implemented non-blocking move in MultiDriver (#19) Also added non-blocking rotate methods. --- src/BasicStepperDriver.cpp | 14 +++++++ src/BasicStepperDriver.h | 3 ++ src/MultiDriver.cpp | 82 +++++++++++++++++++++++--------------- src/MultiDriver.h | 15 +++++++ 4 files changed, 81 insertions(+), 33 deletions(-) diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index d40e548..12677b3 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -106,6 +106,20 @@ void BasicStepperDriver::startMove(long steps){ step_state = LOW; steps_remaining = abs(steps); } +/* + * Move the motor a given number of degrees (1-360) + */ +void BasicStepperDriver::startRotate(long deg){ + startMove(calcStepsForRotation(deg)); +} +/* + * Move the motor with sub-degree precision. + * Note that using this function even once will add 1K to your program size + * due to inclusion of float support. + */ +void BasicStepperDriver::startRotate(double deg){ + startMove(calcStepsForRotation(deg)); +} /* * Toggle step and return time until next change is needed (micros) */ diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index b48801f..913da06 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -121,6 +121,9 @@ class BasicStepperDriver { * Initiate a move (calculate and save the parameters) */ void startMove(long steps); + inline void startRotate(int deg){ startRotate((long)deg); }; + void startRotate(long deg); + void startRotate(double deg); /* * Toggle step and return time until next change is needed (micros) */ diff --git a/src/MultiDriver.cpp b/src/MultiDriver.cpp index 8febaa1..50adb73 100644 --- a/src/MultiDriver.cpp +++ b/src/MultiDriver.cpp @@ -11,54 +11,62 @@ #define FOREACH_MOTOR(action) for (short i=count-1; i >= 0; i--){action;} /* - * Move each motor the requested number of steps, in parallel - * positive to move forward, negative to reverse, 0 to remain still + * Initialize motor parameters */ -void MultiDriver::move(long steps1, long steps2, long steps3){ +void MultiDriver::startMove(long steps1, long steps2, long steps3){ long steps[3] = {steps1, steps2, steps3}; - /* * Initialize state and trigger STEP for all active motors */ FOREACH_MOTOR( if (steps[i]){ motors[i]->startMove(steps[i]); + }; + event_timers[i] = 0; + ); + ready = false; +} +/* + * Trigger next step action + */ +unsigned long MultiDriver::nextAction(void){ + // Trigger all the motors that need it (event timer = 0) + FOREACH_MOTOR( + if (event_timers[i] == 0){ event_timers[i] = motors[i]->nextAction(); - } else { - event_timers[i] = 0; } ); - - while (true){ - // Find the time when the next pulse needs to fire - // this is the smallest timer value from all active motors - unsigned long next_event = ~0L; - ready = true; - FOREACH_MOTOR( - if (event_timers[i]){ - ready = false; - if (event_timers[i] < next_event){ - next_event = event_timers[i]; - } + // Find the time when the next pulse needs to fire + // this is the smallest non-zero timer value from all active motors + unsigned long next_event = ~0L; + ready = true; + FOREACH_MOTOR( + if (event_timers[i]){ + ready = false; + if (event_timers[i] < next_event){ + next_event = event_timers[i]; } - ); - if (ready){ - break; } - + ); + // Reduce all event timers by the current left time so 0 marks next + FOREACH_MOTOR( + if (event_timers[i]){ + event_timers[i] -= next_event; + } + ); + return (ready) ? 0 : next_event; +} +/* + * Move each motor the requested number of steps, in parallel + * positive to move forward, negative to reverse, 0 to remain still + */ +void MultiDriver::move(long steps1, long steps2, long steps3){ + unsigned long next_event; + startMove(steps1, steps2, steps3); + while (!ready){ + next_event = nextAction(); // wait until the next event microWaitUntil(micros() + next_event); - - FOREACH_MOTOR( - if (event_timers[i]){ - if (event_timers[i] == next_event){ // motor ready for next action - event_timers[i] = motors[i]->nextAction(); - } else { - // Reduce all other event timers by the current left time - event_timers[i] -= next_event; - }; - } - ); } } @@ -71,6 +79,14 @@ void MultiDriver::rotate(double deg1, double deg2, double deg3){ move(CALC_STEPS(0, deg1), CALC_STEPS(1, deg2), CALC_STEPS(2, deg3)); } +void MultiDriver::startRotate(long deg1, long deg2, long deg3){ + startMove(CALC_STEPS(0, deg1), CALC_STEPS(1, deg2), CALC_STEPS(2, deg3)); +} + +void MultiDriver::startRotate(double deg1, double deg2, double deg3){ + startMove(CALC_STEPS(0, deg1), CALC_STEPS(1, deg2), CALC_STEPS(2, deg3)); +} + void MultiDriver::setMicrostep(unsigned microsteps){ FOREACH_MOTOR(motors[i]->setMicrostep(microsteps)); } diff --git a/src/MultiDriver.h b/src/MultiDriver.h index e5b93de..c77f2ab 100644 --- a/src/MultiDriver.h +++ b/src/MultiDriver.h @@ -61,6 +61,21 @@ class MultiDriver { }; void rotate(long deg1, long deg2, long deg3=0); void rotate(double deg1, double deg2, double deg3=0); + + /* + * Motor movement with external control of timing + */ + virtual void startMove(long steps1, long steps2, long steps3=0); + void startRotate(int deg1, int deg2, int deg3=0){ + startRotate((long)deg1, (long)deg2, (long)deg3); + }; + void startRotate(long deg1, long deg2, long deg3=0); + void startRotate(double deg1, double deg2, double deg3=0); + /* + * Toggle step and return time until next change is needed (micros) + */ + unsigned long nextAction(void); + /* * Set the same microstepping level on all motors */ From a99a494bb6b8a83bb8c19d0e6f605539de4696b3 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Mon, 5 Jun 2017 22:14:17 -0700 Subject: [PATCH 10/21] Replace getTimePerStep with an estimation of total move time. --- src/BasicStepperDriver.h | 4 ++-- src/SyncDriver.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 913da06..73e1739 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -129,8 +129,8 @@ class BasicStepperDriver { */ unsigned long nextAction(void); - unsigned long getTimePerStep(void){ - return step_pulse; + unsigned long getTimeForMove(long steps){ + return step_pulse * abs(steps); } /* * Calculate steps needed to rotate requested angle, given in degrees diff --git a/src/SyncDriver.cpp b/src/SyncDriver.cpp index 9951962..019ac10 100644 --- a/src/SyncDriver.cpp +++ b/src/SyncDriver.cpp @@ -20,7 +20,7 @@ void SyncDriver::move(long steps1, long steps2, long steps3){ */ long move_time = 0; FOREACH_MOTOR( - long m = abs(steps[i]) * motors[i]->getTimePerStep(); + long m = motors[i]->getTimeForMove(abs(steps[i])); timing[i] = m; if (m > move_time){ move_time = m; From 603adafbdd81da36da003fed26bfcb9ddd08dbd6 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Mon, 5 Jun 2017 23:02:48 -0700 Subject: [PATCH 11/21] Implement startMove/nextAction in SyncDriver --- src/MultiDriver.cpp | 2 +- src/MultiDriver.h | 4 ++-- src/SyncDriver.cpp | 26 +++++++++++++++++++++----- src/SyncDriver.h | 9 ++++----- 4 files changed, 28 insertions(+), 13 deletions(-) diff --git a/src/MultiDriver.cpp b/src/MultiDriver.cpp index 50adb73..bf445d4 100644 --- a/src/MultiDriver.cpp +++ b/src/MultiDriver.cpp @@ -16,7 +16,7 @@ void MultiDriver::startMove(long steps1, long steps2, long steps3){ long steps[3] = {steps1, steps2, steps3}; /* - * Initialize state and trigger STEP for all active motors + * Initialize state for all active motors */ FOREACH_MOTOR( if (steps[i]){ diff --git a/src/MultiDriver.h b/src/MultiDriver.h index c77f2ab..b041825 100644 --- a/src/MultiDriver.h +++ b/src/MultiDriver.h @@ -55,7 +55,7 @@ class MultiDriver { * Move the motors a given number of steps. * positive to move forward, negative to reverse */ - virtual void move(long steps1, long steps2, long steps3=0); + void move(long steps1, long steps2, long steps3=0); void rotate(int deg1, int deg2, int deg3=0){ rotate((long)deg1, (long)deg2, (long)deg3); }; @@ -74,7 +74,7 @@ class MultiDriver { /* * Toggle step and return time until next change is needed (micros) */ - unsigned long nextAction(void); + virtual unsigned long nextAction(void); /* * Set the same microstepping level on all motors diff --git a/src/SyncDriver.cpp b/src/SyncDriver.cpp index 019ac10..ca4e8ee 100644 --- a/src/SyncDriver.cpp +++ b/src/SyncDriver.cpp @@ -11,10 +11,12 @@ #define FOREACH_MOTOR(action) for (short i=count-1; i >= 0; i--){action;} -void SyncDriver::move(long steps1, long steps2, long steps3){ +/* + * Initialize motor parameters + */ +void SyncDriver::startMove(long steps1, long steps2, long steps3){ long steps[3] = {steps1, steps2, steps3}; long timing[MAX_MOTORS]; - unsigned rpms[MAX_MOTORS]; /* * find which motor would take the longest to finish, */ @@ -39,15 +41,29 @@ void SyncDriver::move(long steps1, long steps2, long steps3){ } ); } - MultiDriver::move(steps1, steps2, steps3); /* - * Restore original rpm settings + * Initialize state for all active motors */ - if (move_time){ + FOREACH_MOTOR( + if (steps[i]){ + motors[i]->startMove(steps[i]); + }; + event_timers[i] = 0; + ); + ready = false; +} + +unsigned long SyncDriver::nextAction(void){ + unsigned long next_event = MultiDriver::nextAction(); + if (!next_event){ + /* + * Restore original rpm settings + */ FOREACH_MOTOR( if (rpms[i]){ motors[i]->setRPM(rpms[i]); } ); } + return next_event; } diff --git a/src/SyncDriver.h b/src/SyncDriver.h index 536423a..8ae8097 100644 --- a/src/SyncDriver.h +++ b/src/SyncDriver.h @@ -17,6 +17,7 @@ */ class SyncDriver : public MultiDriver { protected: + unsigned short rpms[MAX_MOTORS]; /* * Generic initializer, will be called by the others */ @@ -37,10 +38,8 @@ class SyncDriver : public MultiDriver { SyncDriver(Motor& motor1, Motor& motor2, Motor& motor3) :SyncDriver(3, new Motor* const[3]{&motor1, &motor2, &motor3}) {}; - /* - * Move the motors a given number of steps. - * positive to move forward, negative to reverse - */ - void move(long steps1, long steps2, long steps3=0) override; + + void startMove(long steps1, long steps2, long steps3=0) override; + unsigned long nextAction(void) override; }; #endif // SYNC_DRIVER_H From 6d67f0bcb90a13528793cbf7d11cc198150b2720 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Wed, 7 Jun 2017 23:18:52 -0700 Subject: [PATCH 12/21] Use begin(rpm, microsteps) to initalize pins and setup timing (#14) Make setMicrostep() virtual so calling it from BasicStepperDriver::begin() actually does the right thing in subclasses. --- .../BasicStepperDriver/BasicStepperDriver.ino | 11 ++++------- examples/ClockStepper/ClockStepper.ino | 5 ++--- examples/MicroStepping/MicroStepping.ino | 2 +- examples/MultiAxis/MultiAxis.ino | 14 +++++--------- src/A4988.cpp | 10 +++++----- src/A4988.h | 4 ++-- src/BasicStepperDriver.cpp | 19 +++++++++---------- src/BasicStepperDriver.h | 7 +++++-- src/DRV8834.h | 3 +-- 9 files changed, 34 insertions(+), 41 deletions(-) diff --git a/examples/BasicStepperDriver/BasicStepperDriver.ino b/examples/BasicStepperDriver/BasicStepperDriver.ino index 3410ec8..ce41c7c 100644 --- a/examples/BasicStepperDriver/BasicStepperDriver.ino +++ b/examples/BasicStepperDriver/BasicStepperDriver.ino @@ -35,8 +35,11 @@ void setup() { * Set target motor RPM. * These motors can do up to about 200rpm. * Too high will result in a high pitched whine and the motor does not move. + * + * Also tell the driver the microstep level we selected. + * If mismatched, the motor will move at a different RPM than chosen. */ - stepper.setRPM(120); + stepper.begin(120, MICROSTEPS); } void loop() { @@ -44,12 +47,6 @@ void loop() { // energize coils - the motor will hold position // stepper.enable(); - /* - * Tell the driver the microstep level we selected. - * If mismatched, the motor will move at a different RPM than chosen. - */ - stepper.setMicrostep(MICROSTEPS); - /* * Moving motor one full revolution using the degree notation */ diff --git a/examples/ClockStepper/ClockStepper.ino b/examples/ClockStepper/ClockStepper.ino index e9b3271..aee4949 100644 --- a/examples/ClockStepper/ClockStepper.ino +++ b/examples/ClockStepper/ClockStepper.ino @@ -43,10 +43,9 @@ DRV8834 stepper(MOTOR_STEPS, DIR, STEP, M0, M1); void setup() { /* - * Set target motor RPM. + * Set target motor RPM=1 and microstepping=1 */ - stepper.setRPM(1); - stepper.setMicrostep(1); // make sure we are in full speed mode + stepper.begin(1, 1); } void loop() { diff --git a/examples/MicroStepping/MicroStepping.ino b/examples/MicroStepping/MicroStepping.ino index ced0998..8e6d539 100644 --- a/examples/MicroStepping/MicroStepping.ino +++ b/examples/MicroStepping/MicroStepping.ino @@ -49,7 +49,7 @@ void setup() { * These motors can do up to about 200rpm. * Too high will result in a high pitched whine and the motor does not move. */ - stepper.setRPM(120); + stepper.begin(120); } void loop() { diff --git a/examples/MultiAxis/MultiAxis.ino b/examples/MultiAxis/MultiAxis.ino index a47a4bc..4852906 100644 --- a/examples/MultiAxis/MultiAxis.ino +++ b/examples/MultiAxis/MultiAxis.ino @@ -38,20 +38,16 @@ void setup() { /* * Set target motors RPM. */ - stepperX.setRPM(30); - stepperY.setRPM(90); - - controller.setMicrostep(MICROSTEPS); + stepperX.begin(30, MICROSTEPS); + stepperY.begin(90, MICROSTEPS); } void loop() { - controller.move(4000, 12000); + controller.rotate(90*5, 60*15); delay(1000); - - controller.move(-4000, -6000); + controller.rotate(-90*5, -30*15); delay(1000); - - controller.move(0, -6000); + controller.rotate(0, -30*15); delay(30000); } diff --git a/src/A4988.cpp b/src/A4988.cpp index b9ba490..7d543d1 100644 --- a/src/A4988.cpp +++ b/src/A4988.cpp @@ -41,8 +41,8 @@ A4988::A4988(int steps, int dir_pin, int step_pin, int enable_pin, int ms1_pin, ms1_pin(ms1_pin), ms2_pin(ms2_pin), ms3_pin(ms3_pin) {} -void A4988::init(void){ - BasicStepperDriver::init(); +void A4988::begin(int rpm, unsigned microsteps){ + BasicStepperDriver::begin(rpm, microsteps); if (!IS_CONNECTED(ms1_pin) || !IS_CONNECTED(ms2_pin) || !IS_CONNECTED(ms3_pin)){ return; @@ -65,10 +65,10 @@ unsigned A4988::setMicrostep(unsigned microsteps){ return this->microsteps; } - const uint8_t* ms_table = this->getMicrostepTable(); - size_t ms_table_size = this->getMicrostepTableSize(); + const uint8_t* ms_table = getMicrostepTable(); + size_t ms_table_size = getMicrostepTableSize(); - int i = 0; + unsigned i = 0; while (i < ms_table_size){ if (this->microsteps & (1<getMaxMicrostep(); ms<<=1){ + for (unsigned ms=1; ms <= getMaxMicrostep(); ms<<=1){ if (microsteps == ms){ this->microsteps = microsteps; break; diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 73e1739..5ac5699 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -59,7 +59,6 @@ class BasicStepperDriver { // step pulse duration (microseconds), depends on rpm and microstep level unsigned long step_pulse; - void init(void); void calcStepPulse(void); // tWH(STEP) pulse duration, STEP high, min value (us) @@ -82,11 +81,15 @@ class BasicStepperDriver { */ BasicStepperDriver(int steps, int dir_pin, int step_pin); BasicStepperDriver(int steps, int dir_pin, int step_pin, int enable_pin); + /* + * Initialize pins, calculate timings etc + */ + void begin(int rpm=60, unsigned microsteps=1); /* * Set current microstep level, 1=full speed, 32=fine microstepping * Returns new level or previous level if value out of range */ - unsigned setMicrostep(unsigned microsteps); + virtual unsigned setMicrostep(unsigned microsteps); /* * Move the motor a given number of steps. * positive to move forward, negative to reverse diff --git a/src/DRV8834.h b/src/DRV8834.h index 2b4433d..711a279 100644 --- a/src/DRV8834.h +++ b/src/DRV8834.h @@ -16,7 +16,6 @@ class DRV8834 : public BasicStepperDriver { protected: int m0_pin = PIN_UNCONNECTED; int m1_pin = PIN_UNCONNECTED; - void init(void); // tWH(STEP) pulse duration, STEP high, min value (1.9us) static const int step_high_min = 2; // tWL(STEP) pulse duration, STEP low, min value (1.9us) @@ -44,6 +43,6 @@ class DRV8834 : public BasicStepperDriver { */ DRV8834(int steps, int dir_pin, int step_pin, int m0_pin, int m1_pin); DRV8834(int steps, int dir_pin, int step_pin, int enable_pin, int m0_pin, int m1_pin); - unsigned setMicrostep(unsigned microsteps); + unsigned setMicrostep(unsigned microsteps) override; }; #endif // DRV8834_H From 41e7d04d743212b2498148ff82fa7d160637d36d Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Tue, 13 Jun 2017 23:22:23 -0700 Subject: [PATCH 13/21] Add linear speed profile (#1) Added setSpeedProfile(LINEAR_SPEED, acccel, decel) to switch mode. The rest works the same way as for CONSTANT_SPEED. Acceleration is given in steps/s^2, not a very intuitive unit. --- examples/AccelTest/AccelTest.ino | 53 +++++++++++++++ src/BasicStepperDriver.cpp | 111 ++++++++++++++++++++++++++++--- src/BasicStepperDriver.h | 83 ++++++++++++++--------- 3 files changed, 206 insertions(+), 41 deletions(-) create mode 100644 examples/AccelTest/AccelTest.ino diff --git a/examples/AccelTest/AccelTest.ino b/examples/AccelTest/AccelTest.ino new file mode 100644 index 0000000..dbff7b0 --- /dev/null +++ b/examples/AccelTest/AccelTest.ino @@ -0,0 +1,53 @@ +/* + * Demo / test acceleration parameters + * + * Copyright (C)2015-2017 Laurentiu Badea + * + * This file may be redistributed under the terms of the MIT license. + * A copy of this license has been included with this distribution in the file LICENSE. + */ +#include +#include "BasicStepperDriver.h" + +// Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step +#define MOTOR_STEPS 200 + +// Since microstepping is set externally, make sure this matches the selected mode +// 1=full step, 2=half step etc. +#define MICROSTEPS 16 + +#define DIR 5 +#define STEP 9 + +// 2-wire basic config, microstepping is hardwired on the driver +BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP, 10); + +void setup() { + Serial.begin(115200); + + stepper.begin(120, MICROSTEPS); + + /* + * LINEAR_SPEED profile needs the acceleration and deceleration values + * in full steps / s^2. + */ + stepper.setSpeedProfile(LINEAR_SPEED, 1000, 1000); + + Serial.println("START"); + stepper.startRotate(-360); +} + +void loop() { + static int step = 0; + unsigned wait_time = stepper.nextAction(); + if (wait_time){ + Serial.print(" step="); Serial.print(step++/2); + Serial.print(" dt="); Serial.print(wait_time); + Serial.print(" rpm="); Serial.print(stepper.getCurrentRPM()); + Serial.println(); + microWaitUntil(micros() + wait_time); + } else { + Serial.println("END"); + delay(3600000); + } +} diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index 96ec163..11b8dc8 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -2,10 +2,14 @@ * Generic Stepper Motor Driver Driver * Indexer mode only. - * Copyright (C)2015 Laurentiu Badea + * Copyright (C)2015-2017 Laurentiu Badea * * This file may be redistributed under the terms of the MIT license. * A copy of this license has been included with this distribution in the file LICENSE. + * + * Linear speed profile calculations based on + * - Generating stepper-motor speed profiles in real time - David Austin, 2004 + * - Atmel AVR446: Linear speed control of stepper motor, 2006 */ #include "BasicStepperDriver.h" @@ -42,11 +46,6 @@ void BasicStepperDriver::begin(int rpm, unsigned microsteps){ enable(); } - -void BasicStepperDriver::calcStepPulse(void){ - step_pulse = STEP_PULSE(rpm, motor_steps, microsteps); -} - /* * Set target motor RPM (1-200 is a reasonable range) */ @@ -70,6 +69,15 @@ unsigned BasicStepperDriver::setMicrostep(unsigned microsteps){ return this->microsteps; } +/* + * Set speed profile - CONSTANT_SPEED, LINEAR_SPEED (accelerated) + */ +void BasicStepperDriver::setSpeedProfile(Mode mode, unsigned accel, unsigned decel){ + this->mode = mode; + this->accel = accel; + this->decel = decel; +} + /* * Move the motor a given number of steps. * positive to move forward, negative to reverse @@ -98,27 +106,108 @@ void BasicStepperDriver::rotate(double deg){ } /* - * Initiate a move (calculate and save the parameters) + * Set up a new move (calculate and save the parameters) */ void BasicStepperDriver::startMove(long steps){ + unsigned long speed; dir_state = (steps >= 0) ? HIGH : LOW; step_state = LOW; steps_remaining = abs(steps); + step_count = 0; + switch (mode){ + case LINEAR_SPEED: + // speed is in [steps/s] + speed = rpm * motor_steps * microsteps / 60; + // how many steps from 0 to target rpm + steps_to_cruise = speed * speed / (2 * accel * microsteps); + // how many steps from 0 til we need to begin slowing down + steps_to_brake = steps_remaining * decel / (accel + decel); + if (steps_to_cruise < steps_to_brake){ + // will reach max speed before needing to brake + steps_to_brake = steps_to_cruise * accel / decel; + } else { + // cannot reach max speed, will need to brake early + steps_to_cruise = steps_to_brake; + steps_to_brake = steps_remaining - steps_to_cruise; + } + // Initial pulse (c0) including error correction factor 0.676 [us] + step_pulse = (1e+6)*0.676*sqrt(2.0f/(accel*microsteps)); + Serial.print("max speed = "); Serial.println(speed); + Serial.print("steps_to_cruise = "); Serial.println(steps_to_cruise); + Serial.print("steps_to_brake = "); Serial.println(steps_to_brake); + break; + case CONSTANT_SPEED: + default: + step_pulse = STEP_PULSE(rpm, motor_steps, microsteps); + } } /* - * Move the motor a given number of degrees (1-360) + * Return calculated time to complete the given move + */ +unsigned long BasicStepperDriver::getTimeForMove(long steps){ + unsigned long t; + switch (mode){ + case LINEAR_SPEED: + startMove(steps); + t = sqrt(2 * steps_to_cruise / accel) + + (steps_remaining - steps_to_cruise - steps_to_brake) * STEP_PULSE(rpm, motor_steps, microsteps) + + sqrt(2 * steps_to_brake / decel); + break; + case CONSTANT_SPEED: + default: + t = step_pulse * steps_remaining; + } + return t; +} +/* + * Move the motor an integer number of degrees (360 = full rotation) + * This has poor precision for small amounts, since step is usually 1.8deg */ void BasicStepperDriver::startRotate(long deg){ startMove(calcStepsForRotation(deg)); } /* * Move the motor with sub-degree precision. - * Note that using this function even once will add 1K to your program size + * Note that calling this function will increase program size substantially * due to inclusion of float support. */ void BasicStepperDriver::startRotate(double deg){ startMove(calcStepsForRotation(deg)); } + +/* + * calculate the interval til the next pulse + */ +void BasicStepperDriver::calcStepPulse(void){ + // feed remainder into successive steps to increase accuracy (Atmel DOC8017) + static long rest = 0; + // if constant speed + if (mode == LINEAR_SPEED){ + if (step_count <= steps_to_cruise){ + /* + * accelerating + */ + step_pulse = step_pulse - (2*step_pulse+rest)/(4*step_count+1); + rest = (2*step_pulse+rest) % (4*step_count+1); + } else if (steps_remaining > steps_to_brake){ + /* + * cruising (no speed changes) + */ + } else { + /* + * decelerating + */ + Serial.print(" i="); Serial.print(step_count); + Serial.print(" r="); Serial.print(steps_remaining); + Serial.print(" t="); Serial.print(step_pulse); + Serial.print(" rpm="); + Serial.print(getCurrentRPM()); + step_pulse = step_pulse + (2*step_pulse-rest)/(4*steps_remaining+1); + rest = (2*step_pulse-rest) % (4*step_count+1); + Serial.print(" rest="); Serial.print(rest); + } + } +} /* * Toggle step and return time until next change is needed (micros) */ @@ -133,13 +222,15 @@ unsigned long BasicStepperDriver::nextAction(void){ } else { step_state = LOW; steps_remaining--; + step_count++; + calcStepPulse(); } digitalWrite(step_pin, step_state); /* * We currently try to do a 50% duty cycle so it's easy to see. * Other option is step_high_min, pulse_duration-step_high_min. */ - return step_pulse/2; + return step_pulse/2; // FIXME: precision loss (1us) } else { return 0; // end of move } diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 5ac5699..3f538cf 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -16,6 +16,7 @@ #define IS_CONNECTED(pin) (pin != PIN_UNCONNECTED) enum Direction {DIR_FORWARD, DIR_REVERSE}; +enum Mode {CONSTANT_SPEED, LINEAR_SPEED}; /* * calculate the step pulse in microseconds for a given rpm value. @@ -35,42 +36,49 @@ inline void microWaitUntil(unsigned long target_micros){ */ class BasicStepperDriver { protected: - int motor_steps; - int rpm = 60; + /* + * Motor Configuration + */ + int motor_steps; // motor steps per revolution (usually 200) + unsigned accel = 1000; // maximum acceleration [steps/s^2] + unsigned decel = 1000; // maximum deceleration [steps/s^2] + + /* + * Driver Configuration + */ int dir_pin; int step_pin; int enable_pin = PIN_UNCONNECTED; + // Get max microsteps supported by the device + virtual unsigned getMaxMicrostep(); + // current microstep level (1,2,4,8,...), must be < getMaxMicrostep() + unsigned microsteps = 1; + // tWH(STEP) pulse duration, STEP high, min value (us) + static const int step_high_min = 1; + // tWL(STEP) pulse duration, STEP low, min value (us) + static const int step_low_min = 1; + // tWAKE wakeup time, nSLEEP inactive to STEP (us) + static const int wakeup_time = 0; + + int rpm = 60; /* * Movement state */ - // how many steps are left to complete the current move (absolute value) - long steps_remaining; + Mode mode = CONSTANT_SPEED; + long step_count; // current position + long steps_remaining; // to complete the current move (absolute value) + long steps_to_cruise; // steps to reach cruising (max) rpm + long steps_to_brake; // steps needed to come to a full stop + unsigned long step_pulse;// step pulse duration (microseconds) + // DIR pin state short dir_state; // STEP pin state (HIGH / LOW) short step_state = LOW; - // microseconds until next state change - unsigned long next_pulse_time; - - // current microstep level, must be < getMaxMicrostep() - // for 1:16 microsteps is 16 - unsigned microsteps = 1; - // step pulse duration (microseconds), depends on rpm and microstep level - unsigned long step_pulse; void calcStepPulse(void); - // tWH(STEP) pulse duration, STEP high, min value (us) - static const int step_high_min = 1; - // tWL(STEP) pulse duration, STEP low, min value (us) - static const int step_low_min = 1; - // tWAKE wakeup time, nSLEEP inactive to STEP (us) - static const int wakeup_time = 0; - - // Get max microsteps supported by the device - virtual unsigned getMaxMicrostep(); - private: // microstep range (1, 16, 32 etc) static const unsigned MAX_MICROSTEP = 128; @@ -90,6 +98,21 @@ class BasicStepperDriver { * Returns new level or previous level if value out of range */ virtual unsigned setMicrostep(unsigned microsteps); + /* + * Set target motor RPM (1-200 is a reasonable range) + */ + void setRPM(unsigned rpm); + unsigned getRPM(void){ + return rpm; + }; + unsigned getCurrentRPM(void){ + return 60*1000000L / step_pulse / microsteps / motor_steps; + } + /* + * Set speed profile - CONSTANT_SPEED, LINEAR_SPEED (accelerated) + * accel and decel are given in [full steps/s^2] + */ + void setSpeedProfile(Mode mode, unsigned accel=1000, unsigned decel=1000); /* * Move the motor a given number of steps. * positive to move forward, negative to reverse @@ -106,11 +129,6 @@ class BasicStepperDriver { * Rotate using a float or double for increased movement precision. */ void rotate(double deg); - /* - * Set target motor RPM (1-200 is a reasonable range) - */ - void setRPM(unsigned rpm); - unsigned getRPM(void){ return rpm; }; /* * Turn off/on motor to allow the motor to be moved by hand/hold the position in place */ @@ -124,7 +142,9 @@ class BasicStepperDriver { * Initiate a move (calculate and save the parameters) */ void startMove(long steps); - inline void startRotate(int deg){ startRotate((long)deg); }; + inline void startRotate(int deg){ + startRotate((long)deg); + }; void startRotate(long deg); void startRotate(double deg); /* @@ -132,9 +152,10 @@ class BasicStepperDriver { */ unsigned long nextAction(void); - unsigned long getTimeForMove(long steps){ - return step_pulse * abs(steps); - } + /* + * Return calculated time to complete the given move + */ + unsigned long getTimeForMove(long steps); /* * Calculate steps needed to rotate requested angle, given in degrees */ From d31e6d7d923de48a87c9cb78a73826b10e9914ad Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Wed, 14 Jun 2017 21:52:42 -0700 Subject: [PATCH 14/21] Fix deceleration code, remove debugging statements 1) unsigned step_pulse causes wrong results in the decel formula, made signed. 2) reset rest at end of accel series --- examples/AccelTest/AccelTest.ino | 3 ++- src/BasicStepperDriver.cpp | 15 +++------------ src/BasicStepperDriver.h | 4 ++-- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/examples/AccelTest/AccelTest.ino b/examples/AccelTest/AccelTest.ino index dbff7b0..c546862 100644 --- a/examples/AccelTest/AccelTest.ino +++ b/examples/AccelTest/AccelTest.ino @@ -34,7 +34,7 @@ void setup() { stepper.setSpeedProfile(LINEAR_SPEED, 1000, 1000); Serial.println("START"); - stepper.startRotate(-360); + stepper.startRotate(360); } void loop() { @@ -47,6 +47,7 @@ void loop() { Serial.println(); microWaitUntil(micros() + wait_time); } else { + stepper.disable(); Serial.println("END"); delay(3600000); } diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index 11b8dc8..6c79bd1 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -132,9 +132,6 @@ void BasicStepperDriver::startMove(long steps){ } // Initial pulse (c0) including error correction factor 0.676 [us] step_pulse = (1e+6)*0.676*sqrt(2.0f/(accel*microsteps)); - Serial.print("max speed = "); Serial.println(speed); - Serial.print("steps_to_cruise = "); Serial.println(steps_to_cruise); - Serial.print("steps_to_brake = "); Serial.println(steps_to_brake); break; case CONSTANT_SPEED: default: @@ -188,7 +185,7 @@ void BasicStepperDriver::calcStepPulse(void){ * accelerating */ step_pulse = step_pulse - (2*step_pulse+rest)/(4*step_count+1); - rest = (2*step_pulse+rest) % (4*step_count+1); + rest = (step_count < steps_to_cruise) ? (2*step_pulse+rest) % (4*step_count+1) : 0; } else if (steps_remaining > steps_to_brake){ /* * cruising (no speed changes) @@ -197,14 +194,8 @@ void BasicStepperDriver::calcStepPulse(void){ /* * decelerating */ - Serial.print(" i="); Serial.print(step_count); - Serial.print(" r="); Serial.print(steps_remaining); - Serial.print(" t="); Serial.print(step_pulse); - Serial.print(" rpm="); - Serial.print(getCurrentRPM()); - step_pulse = step_pulse + (2*step_pulse-rest)/(4*steps_remaining+1); - rest = (2*step_pulse-rest) % (4*step_count+1); - Serial.print(" rest="); Serial.print(rest); + step_pulse = step_pulse - (2*step_pulse+rest)/(-4*steps_remaining+1); + rest = (2*step_pulse+rest) % (-4*steps_remaining+1); } } } diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 3f538cf..f7cdf2b 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -69,8 +69,8 @@ class BasicStepperDriver { long step_count; // current position long steps_remaining; // to complete the current move (absolute value) long steps_to_cruise; // steps to reach cruising (max) rpm - long steps_to_brake; // steps needed to come to a full stop - unsigned long step_pulse;// step pulse duration (microseconds) + long steps_to_brake; // steps needed to come to a full stop + long step_pulse; // step pulse duration (microseconds) // DIR pin state short dir_state; From 91122fae614c1c44fc0b9caf7a1a5268ff2f1796 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Wed, 14 Jun 2017 22:18:12 -0700 Subject: [PATCH 15/21] Use short instead of int or unsigned where it makes sense. This saves some storage space. --- src/A4988.cpp | 16 +++++++-------- src/A4988.h | 22 ++++++++++---------- src/BasicStepperDriver.cpp | 26 +++++++++++------------ src/BasicStepperDriver.h | 42 +++++++++++++++++++------------------- src/DRV8825.cpp | 10 ++++----- src/DRV8825.h | 12 +++++------ src/DRV8834.cpp | 12 +++++------ src/DRV8834.h | 18 ++++++++-------- 8 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/A4988.cpp b/src/A4988.cpp index 7d543d1..08b0ad1 100644 --- a/src/A4988.cpp +++ b/src/A4988.cpp @@ -19,11 +19,11 @@ const uint8_t A4988::MS_TABLE[] = {0b000, 0b001, 0b010, 0b011, 0b111}; * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ -A4988::A4988(int steps, int dir_pin, int step_pin) +A4988::A4988(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) {} -A4988::A4988(int steps, int dir_pin, int step_pin, int enable_pin) +A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) {} @@ -31,17 +31,17 @@ A4988::A4988(int steps, int dir_pin, int step_pin, int enable_pin) * Fully wired. * All the necessary control pins for A4988 are connected. */ -A4988::A4988(int steps, int dir_pin, int step_pin, int ms1_pin, int ms2_pin, int ms3_pin) +A4988::A4988(short steps, short dir_pin, short step_pin, short ms1_pin, short ms2_pin, short ms3_pin) :BasicStepperDriver(steps, dir_pin, step_pin), ms1_pin(ms1_pin), ms2_pin(ms2_pin), ms3_pin(ms3_pin) {} -A4988::A4988(int steps, int dir_pin, int step_pin, int enable_pin, int ms1_pin, int ms2_pin, int ms3_pin) +A4988::A4988(short steps, short dir_pin, short step_pin, short enable_pin, short ms1_pin, short ms2_pin, short ms3_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), ms1_pin(ms1_pin), ms2_pin(ms2_pin), ms3_pin(ms3_pin) {} -void A4988::begin(int rpm, unsigned microsteps){ +void A4988::begin(short rpm, short microsteps){ BasicStepperDriver::begin(rpm, microsteps); if (!IS_CONNECTED(ms1_pin) || !IS_CONNECTED(ms2_pin) || !IS_CONNECTED(ms3_pin)){ @@ -58,7 +58,7 @@ void A4988::begin(int rpm, unsigned microsteps){ * Allowed ranges for A4988 are 1:1 to 1:16 * If the control pins are not connected, we recalculate the timing only */ -unsigned A4988::setMicrostep(unsigned microsteps){ +short A4988::setMicrostep(short microsteps){ BasicStepperDriver::setMicrostep(microsteps); if (!IS_CONNECTED(ms1_pin) || !IS_CONNECTED(ms2_pin) || !IS_CONNECTED(ms3_pin)){ @@ -68,7 +68,7 @@ unsigned A4988::setMicrostep(unsigned microsteps){ const uint8_t* ms_table = getMicrostepTable(); size_t ms_table_size = getMicrostepTableSize(); - unsigned i = 0; + unsigned short i = 0; while (i < ms_table_size){ if (this->microsteps & (1<rpm = rpm; calcStepPulse(); } @@ -58,8 +58,8 @@ void BasicStepperDriver::setRPM(unsigned rpm){ * Set stepping mode (1:microsteps) * Allowed ranges for BasicStepperDriver are 1:1 to 1:128 */ -unsigned BasicStepperDriver::setMicrostep(unsigned microsteps){ - for (unsigned ms=1; ms <= getMaxMicrostep(); ms<<=1){ +short BasicStepperDriver::setMicrostep(short microsteps){ + for (short ms=1; ms <= getMaxMicrostep(); ms<<=1){ if (microsteps == ms){ this->microsteps = microsteps; break; @@ -72,7 +72,7 @@ unsigned BasicStepperDriver::setMicrostep(unsigned microsteps){ /* * Set speed profile - CONSTANT_SPEED, LINEAR_SPEED (accelerated) */ -void BasicStepperDriver::setSpeedProfile(Mode mode, unsigned accel, unsigned decel){ +void BasicStepperDriver::setSpeedProfile(Mode mode, short accel, short decel){ this->mode = mode; this->accel = accel; this->decel = decel; @@ -83,7 +83,7 @@ void BasicStepperDriver::setSpeedProfile(Mode mode, unsigned accel, unsigned dec * positive to move forward, negative to reverse */ void BasicStepperDriver::move(long steps){ - unsigned long next_event; + long next_event; startMove(steps); do { next_event = nextAction(); @@ -109,7 +109,7 @@ void BasicStepperDriver::rotate(double deg){ * Set up a new move (calculate and save the parameters) */ void BasicStepperDriver::startMove(long steps){ - unsigned long speed; + long speed; dir_state = (steps >= 0) ? HIGH : LOW; step_state = LOW; steps_remaining = abs(steps); @@ -141,8 +141,8 @@ void BasicStepperDriver::startMove(long steps){ /* * Return calculated time to complete the given move */ -unsigned long BasicStepperDriver::getTimeForMove(long steps){ - unsigned long t; +long BasicStepperDriver::getTimeForMove(long steps){ + long t; switch (mode){ case LINEAR_SPEED: startMove(steps); @@ -202,7 +202,7 @@ void BasicStepperDriver::calcStepPulse(void){ /* * Toggle step and return time until next change is needed (micros) */ -unsigned long BasicStepperDriver::nextAction(void){ +long BasicStepperDriver::nextAction(void){ if (steps_remaining > 0){ /* * DIR pin is sampled on rising STEP edge, so it is set first @@ -242,6 +242,6 @@ void BasicStepperDriver::disable(void){ } } -unsigned BasicStepperDriver::getMaxMicrostep(){ +short BasicStepperDriver::getMaxMicrostep(){ return BasicStepperDriver::MAX_MICROSTEP; } diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index f7cdf2b..97e3a71 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -39,20 +39,20 @@ class BasicStepperDriver { /* * Motor Configuration */ - int motor_steps; // motor steps per revolution (usually 200) - unsigned accel = 1000; // maximum acceleration [steps/s^2] - unsigned decel = 1000; // maximum deceleration [steps/s^2] + short motor_steps; // motor steps per revolution (usually 200) + short accel = 1000; // maximum acceleration [steps/s^2] + short decel = 1000; // maximum deceleration [steps/s^2] /* * Driver Configuration */ - int dir_pin; - int step_pin; - int enable_pin = PIN_UNCONNECTED; + short dir_pin; + short step_pin; + short enable_pin = PIN_UNCONNECTED; // Get max microsteps supported by the device - virtual unsigned getMaxMicrostep(); + virtual short getMaxMicrostep(); // current microstep level (1,2,4,8,...), must be < getMaxMicrostep() - unsigned microsteps = 1; + short microsteps = 1; // tWH(STEP) pulse duration, STEP high, min value (us) static const int step_high_min = 1; // tWL(STEP) pulse duration, STEP low, min value (us) @@ -60,7 +60,7 @@ class BasicStepperDriver { // tWAKE wakeup time, nSLEEP inactive to STEP (us) static const int wakeup_time = 0; - int rpm = 60; + short rpm = 60; /* * Movement state @@ -81,38 +81,38 @@ class BasicStepperDriver { private: // microstep range (1, 16, 32 etc) - static const unsigned MAX_MICROSTEP = 128; + static const short MAX_MICROSTEP = 128; public: /* * Basic connection: DIR, STEP are connected. */ - BasicStepperDriver(int steps, int dir_pin, int step_pin); - BasicStepperDriver(int steps, int dir_pin, int step_pin, int enable_pin); + BasicStepperDriver(short steps, short dir_pin, short step_pin); + BasicStepperDriver(short steps, short dir_pin, short step_pin, short enable_pin); /* * Initialize pins, calculate timings etc */ - void begin(int rpm=60, unsigned microsteps=1); + void begin(short rpm=60, short microsteps=1); /* * Set current microstep level, 1=full speed, 32=fine microstepping * Returns new level or previous level if value out of range */ - virtual unsigned setMicrostep(unsigned microsteps); + virtual short setMicrostep(short microsteps); /* * Set target motor RPM (1-200 is a reasonable range) */ - void setRPM(unsigned rpm); - unsigned getRPM(void){ + void setRPM(short rpm); + short getRPM(void){ return rpm; }; - unsigned getCurrentRPM(void){ - return 60*1000000L / step_pulse / microsteps / motor_steps; + short getCurrentRPM(void){ + return (short)(60*1000000L / step_pulse / microsteps / motor_steps); } /* * Set speed profile - CONSTANT_SPEED, LINEAR_SPEED (accelerated) * accel and decel are given in [full steps/s^2] */ - void setSpeedProfile(Mode mode, unsigned accel=1000, unsigned decel=1000); + void setSpeedProfile(Mode mode, short accel=1000, short decel=1000); /* * Move the motor a given number of steps. * positive to move forward, negative to reverse @@ -150,12 +150,12 @@ class BasicStepperDriver { /* * Toggle step and return time until next change is needed (micros) */ - unsigned long nextAction(void); + long nextAction(void); /* * Return calculated time to complete the given move */ - unsigned long getTimeForMove(long steps); + long getTimeForMove(long steps); /* * Calculate steps needed to rotate requested angle, given in degrees */ diff --git a/src/DRV8825.cpp b/src/DRV8825.cpp index 359ce73..d0800b7 100644 --- a/src/DRV8825.cpp +++ b/src/DRV8825.cpp @@ -15,22 +15,22 @@ */ const uint8_t DRV8825::MS_TABLE[] = {0b000, 0b001, 0b010, 0b011, 0b100, 0b111}; -DRV8825::DRV8825(int steps, int dir_pin, int step_pin) +DRV8825::DRV8825(short steps, short dir_pin, short step_pin) :A4988(steps, dir_pin, step_pin) {} -DRV8825::DRV8825(int steps, int dir_pin, int step_pin, int enable_pin) +DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short enable_pin) :A4988(steps, dir_pin, step_pin, enable_pin) {} /* * A4988-DRV8825 Compatibility map: MS1-MODE0, MS2-MODE1, MS3-MODE2 */ -DRV8825::DRV8825(int steps, int dir_pin, int step_pin, int mode0_pin, int mode1_pin, int mode2_pin) +DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short mode0_pin, short mode1_pin, short mode2_pin) :A4988(steps, dir_pin, step_pin, mode0_pin, mode1_pin, mode2_pin) {} -DRV8825::DRV8825(int steps, int dir_pin, int step_pin, int enable_pin, int mode0_pin, int mode1_pin, int mode2_pin) +DRV8825::DRV8825(short steps, short dir_pin, short step_pin, short enable_pin, short mode0_pin, short mode1_pin, short mode2_pin) :A4988(steps, dir_pin, step_pin, enable_pin, mode0_pin, mode1_pin, mode2_pin) {} @@ -44,6 +44,6 @@ size_t DRV8825::getMicrostepTableSize() return sizeof(DRV8825::MS_TABLE); } -unsigned DRV8825::getMaxMicrostep(){ +short DRV8825::getMaxMicrostep(){ return DRV8825::MAX_MICROSTEP; } diff --git a/src/DRV8825.h b/src/DRV8825.h index fe0c0b7..68afe8f 100644 --- a/src/DRV8825.h +++ b/src/DRV8825.h @@ -28,16 +28,16 @@ class DRV8825 : public A4988 { size_t getMicrostepTableSize() override; // Get max microsteps supported by the device - unsigned getMaxMicrostep() override; + short getMaxMicrostep() override; private: // microstep range (1, 16, 32 etc) - static const unsigned MAX_MICROSTEP = 32; + static const short MAX_MICROSTEP = 32; public: - DRV8825(int steps, int dir_pin, int step_pin); - DRV8825(int steps, int dir_pin, int step_pin, int enable_pin); - DRV8825(int steps, int dir_pin, int step_pin, int mode0_pin, int mode1_pin, int mode2_pin); - DRV8825(int steps, int dir_pin, int step_pin, int enable_pin, int mode0_pin, int mode1_pin, int mode2_pin); + DRV8825(short steps, short dir_pin, short step_pin); + DRV8825(short steps, short dir_pin, short step_pin, short enable_pin); + DRV8825(short steps, short dir_pin, short step_pin, short mode0_pin, short mode1_pin, short mode2_pin); + DRV8825(short steps, short dir_pin, short step_pin, short enable_pin, short mode0_pin, short mode1_pin, short mode2_pin); }; #endif // DRV8825_H diff --git a/src/DRV8834.cpp b/src/DRV8834.cpp index 917fdeb..6a048c3 100644 --- a/src/DRV8834.cpp +++ b/src/DRV8834.cpp @@ -13,22 +13,22 @@ * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ -DRV8834::DRV8834(int steps, int dir_pin, int step_pin) +DRV8834::DRV8834(short steps, short dir_pin, short step_pin) :BasicStepperDriver(steps, dir_pin, step_pin) {} -DRV8834::DRV8834(int steps, int dir_pin, int step_pin, int enable_pin) +DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short enable_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin) {} /* * Fully wired. All the necessary control pins for DRV8834 are connected. */ -DRV8834::DRV8834(int steps, int dir_pin, int step_pin, int m0_pin, int m1_pin) +DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short m0_pin, short m1_pin) :BasicStepperDriver(steps, dir_pin, step_pin), m0_pin(m0_pin), m1_pin(m1_pin) {} -DRV8834::DRV8834(int steps, int dir_pin, int step_pin, int enable_pin, int m0_pin, int m1_pin) +DRV8834::DRV8834(short steps, short dir_pin, short step_pin, short enable_pin, short m0_pin, short m1_pin) :BasicStepperDriver(steps, dir_pin, step_pin, enable_pin), m0_pin(m0_pin), m1_pin(m1_pin) {} @@ -38,7 +38,7 @@ DRV8834::DRV8834(int steps, int dir_pin, int step_pin, int enable_pin, int m0_pi * If the control pins are not connected, we recalculate the timing only * */ -unsigned DRV8834::setMicrostep(unsigned microsteps){ +short DRV8834::setMicrostep(short microsteps){ BasicStepperDriver::setMicrostep(microsteps); if (!IS_CONNECTED(m0_pin) || !IS_CONNECTED(m1_pin)){ @@ -80,6 +80,6 @@ unsigned DRV8834::setMicrostep(unsigned microsteps){ return this->microsteps; } -unsigned DRV8834::getMaxMicrostep(){ +short DRV8834::getMaxMicrostep(){ return DRV8834::MAX_MICROSTEP; } diff --git a/src/DRV8834.h b/src/DRV8834.h index 711a279..7aeeec6 100644 --- a/src/DRV8834.h +++ b/src/DRV8834.h @@ -14,8 +14,8 @@ class DRV8834 : public BasicStepperDriver { protected: - int m0_pin = PIN_UNCONNECTED; - int m1_pin = PIN_UNCONNECTED; + short m0_pin = PIN_UNCONNECTED; + short m1_pin = PIN_UNCONNECTED; // tWH(STEP) pulse duration, STEP high, min value (1.9us) static const int step_high_min = 2; // tWL(STEP) pulse duration, STEP low, min value (1.9us) @@ -25,24 +25,24 @@ class DRV8834 : public BasicStepperDriver { // also 200ns between ENBL/DIR/Mx changes and STEP HIGH // Get max microsteps supported by the device - unsigned getMaxMicrostep() override; + short getMaxMicrostep() override; private: // microstep range (1, 16, 32 etc) - static const unsigned MAX_MICROSTEP = 32; + static const short MAX_MICROSTEP = 32; public: /* * Basic connection: only DIR, STEP are connected. * Microstepping controls should be hardwired. */ - DRV8834(int steps, int dir_pin, int step_pin); - DRV8834(int steps, int dir_pin, int step_pin, int enable_pin); + DRV8834(short steps, short dir_pin, short step_pin); + DRV8834(short steps, short dir_pin, short step_pin, short enable_pin); /* * Fully wired. All the necessary control pins for DRV8834 are connected. */ - DRV8834(int steps, int dir_pin, int step_pin, int m0_pin, int m1_pin); - DRV8834(int steps, int dir_pin, int step_pin, int enable_pin, int m0_pin, int m1_pin); - unsigned setMicrostep(unsigned microsteps) override; + DRV8834(short steps, short dir_pin, short step_pin, short m0_pin, short m1_pin); + DRV8834(short steps, short dir_pin, short step_pin, short enable_pin, short m0_pin, short m1_pin); + short setMicrostep(short microsteps) override; }; #endif // DRV8834_H From db1c1f4be400b48ef287227bba4de0bd747ce735 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Wed, 14 Jun 2017 23:35:43 -0700 Subject: [PATCH 16/21] Timing calculation fixes. Initialize rest where necessary Don't call calcStepPulse() in setRPM() / setMicrostep() --- src/BasicStepperDriver.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index 677262d..87a1842 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -51,7 +51,6 @@ void BasicStepperDriver::begin(short rpm, short microsteps){ */ void BasicStepperDriver::setRPM(short rpm){ this->rpm = rpm; - calcStepPulse(); } /* @@ -65,12 +64,12 @@ short BasicStepperDriver::setMicrostep(short microsteps){ break; } } - calcStepPulse(); return this->microsteps; } /* * Set speed profile - CONSTANT_SPEED, LINEAR_SPEED (accelerated) + * accel and decel are given in [full steps/s^2] */ void BasicStepperDriver::setSpeedProfile(Mode mode, short accel, short decel){ this->mode = mode; @@ -152,7 +151,7 @@ long BasicStepperDriver::getTimeForMove(long steps){ break; case CONSTANT_SPEED: default: - t = step_pulse * steps_remaining; + t = STEP_PULSE(rpm, motor_steps, microsteps); } return t; } @@ -176,11 +175,22 @@ void BasicStepperDriver::startRotate(double deg){ * calculate the interval til the next pulse */ void BasicStepperDriver::calcStepPulse(void){ - // feed remainder into successive steps to increase accuracy (Atmel DOC8017) - static long rest = 0; + // remainder to be fed into successive steps to increase accuracy (Atmel DOC8017) + static long rest; + + if (steps_remaining <= 0){ // this should not happen, but avoids strange calculations + return; + } + + steps_remaining--; + step_count++; + // if constant speed if (mode == LINEAR_SPEED){ if (step_count <= steps_to_cruise){ + if (step_count == 1){ // first step, initialize rest + rest = 0; + } /* * accelerating */ @@ -212,8 +222,6 @@ long BasicStepperDriver::nextAction(void){ step_state = HIGH; } else { step_state = LOW; - steps_remaining--; - step_count++; calcStepPulse(); } digitalWrite(step_pin, step_state); From bb7eaa74f3eb69e4f43708277a598f408da6533a Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Wed, 14 Jun 2017 23:43:31 -0700 Subject: [PATCH 17/21] Handle old 1.0 code by calling begin() from setRPM() 1.0 lib did not have begin(), so pins were initialized in constructor (fixed in #14) --- src/BasicStepperDriver.cpp | 5 ++++- src/BasicStepperDriver.h | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index 87a1842..1c66591 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -40,8 +40,8 @@ void BasicStepperDriver::begin(short rpm, short microsteps){ digitalWrite(enable_pin, HIGH); // disable } + this->rpm = rpm; setMicrostep(microsteps); - setRPM(rpm); enable(); } @@ -50,6 +50,9 @@ void BasicStepperDriver::begin(short rpm, short microsteps){ * Set target motor RPM (1-200 is a reasonable range) */ void BasicStepperDriver::setRPM(short rpm){ + if (this->rpm == 0){ // begin() has not been called (old 1.0 code) + begin(rpm, microsteps); + } this->rpm = rpm; } diff --git a/src/BasicStepperDriver.h b/src/BasicStepperDriver.h index 97e3a71..87f8ed9 100644 --- a/src/BasicStepperDriver.h +++ b/src/BasicStepperDriver.h @@ -60,7 +60,7 @@ class BasicStepperDriver { // tWAKE wakeup time, nSLEEP inactive to STEP (us) static const int wakeup_time = 0; - short rpm = 60; + short rpm = 0; /* * Movement state From f4e4af85800fda9aa980334ec524edb64b8e37c1 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Thu, 15 Jun 2017 23:23:28 -0700 Subject: [PATCH 18/21] Fix slow move at high accel + high rpm (#20) - avoid overflow by leaving microsteps out of speed calculation - switch to short STEP pulse to allow larger time blocks to do tasks - subtract the time spent doing the math from next pulse interval (this was causing slower speeds than expected with slower CPUs) --- src/BasicStepperDriver.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/BasicStepperDriver.cpp b/src/BasicStepperDriver.cpp index 1c66591..d196ae7 100644 --- a/src/BasicStepperDriver.cpp +++ b/src/BasicStepperDriver.cpp @@ -119,9 +119,9 @@ void BasicStepperDriver::startMove(long steps){ switch (mode){ case LINEAR_SPEED: // speed is in [steps/s] - speed = rpm * motor_steps * microsteps / 60; + speed = rpm * motor_steps / 60; // how many steps from 0 to target rpm - steps_to_cruise = speed * speed / (2 * accel * microsteps); + steps_to_cruise = speed * speed * microsteps / (2 * accel); // how many steps from 0 til we need to begin slowing down steps_to_brake = steps_remaining * decel / (accel + decel); if (steps_to_cruise < steps_to_brake){ @@ -225,14 +225,18 @@ long BasicStepperDriver::nextAction(void){ step_state = HIGH; } else { step_state = LOW; - calcStepPulse(); } digitalWrite(step_pin, step_state); + unsigned m = micros(); + if (step_state == LOW){ + calcStepPulse(); + } + m = micros() - m; /* * We currently try to do a 50% duty cycle so it's easy to see. * Other option is step_high_min, pulse_duration-step_high_min. */ - return step_pulse/2; // FIXME: precision loss (1us) + return (step_state == LOW) ? step_pulse-step_high_min-m : step_high_min; } else { return 0; // end of move } From 1b1fca836974cc3fe18b12765ec958c5c5bde70d Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Thu, 15 Jun 2017 23:29:01 -0700 Subject: [PATCH 19/21] Fix: nextAction called on all motors even if not moving. Switch to signed time interval to indicate unused slot with -1 (also to sync with BasicStepperDriver definition) --- src/MultiDriver.cpp | 16 +++++++++------- src/MultiDriver.h | 4 ++-- src/SyncDriver.cpp | 4 ++-- src/SyncDriver.h | 2 +- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/MultiDriver.cpp b/src/MultiDriver.cpp index bf445d4..5fc8917 100644 --- a/src/MultiDriver.cpp +++ b/src/MultiDriver.cpp @@ -21,15 +21,17 @@ void MultiDriver::startMove(long steps1, long steps2, long steps3){ FOREACH_MOTOR( if (steps[i]){ motors[i]->startMove(steps[i]); - }; - event_timers[i] = 0; + event_timers[i] = 0; + } else { + event_timers[i] = -1; + } ); ready = false; } /* * Trigger next step action */ -unsigned long MultiDriver::nextAction(void){ +long MultiDriver::nextAction(void){ // Trigger all the motors that need it (event timer = 0) FOREACH_MOTOR( if (event_timers[i] == 0){ @@ -38,19 +40,19 @@ unsigned long MultiDriver::nextAction(void){ ); // Find the time when the next pulse needs to fire // this is the smallest non-zero timer value from all active motors - unsigned long next_event = ~0L; + long next_event = 0; ready = true; FOREACH_MOTOR( - if (event_timers[i]){ + if (event_timers[i] > 0){ ready = false; - if (event_timers[i] < next_event){ + if (event_timers[i] < next_event || next_event == 0){ next_event = event_timers[i]; } } ); // Reduce all event timers by the current left time so 0 marks next FOREACH_MOTOR( - if (event_timers[i]){ + if (event_timers[i] > 0){ event_timers[i] -= next_event; } ); diff --git a/src/MultiDriver.h b/src/MultiDriver.h index b041825..1dc9183 100644 --- a/src/MultiDriver.h +++ b/src/MultiDriver.h @@ -36,7 +36,7 @@ class MultiDriver { // ready to start a new move bool ready = true; // when next state change is due for each motor - unsigned long event_timers[MAX_MOTORS]; + long event_timers[MAX_MOTORS]; public: /* @@ -74,7 +74,7 @@ class MultiDriver { /* * Toggle step and return time until next change is needed (micros) */ - virtual unsigned long nextAction(void); + virtual long nextAction(void); /* * Set the same microstepping level on all motors diff --git a/src/SyncDriver.cpp b/src/SyncDriver.cpp index ca4e8ee..8a50305 100644 --- a/src/SyncDriver.cpp +++ b/src/SyncDriver.cpp @@ -53,8 +53,8 @@ void SyncDriver::startMove(long steps1, long steps2, long steps3){ ready = false; } -unsigned long SyncDriver::nextAction(void){ - unsigned long next_event = MultiDriver::nextAction(); +long SyncDriver::nextAction(void){ + long next_event = MultiDriver::nextAction(); if (!next_event){ /* * Restore original rpm settings diff --git a/src/SyncDriver.h b/src/SyncDriver.h index 8ae8097..c8421d3 100644 --- a/src/SyncDriver.h +++ b/src/SyncDriver.h @@ -40,6 +40,6 @@ class SyncDriver : public MultiDriver { {}; void startMove(long steps1, long steps2, long steps3=0) override; - unsigned long nextAction(void) override; + long nextAction(void) override; }; #endif // SYNC_DRIVER_H From 17b9445518f447085b2d9da67133c79a90cf4f80 Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 25 Jun 2017 22:02:12 -0700 Subject: [PATCH 20/21] Updated keywords --- keywords.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/keywords.txt b/keywords.txt index e5cf898..55b78f9 100644 --- a/keywords.txt +++ b/keywords.txt @@ -5,8 +5,20 @@ DRV8834 KEYWORD1 DRV8824 KEYWORD1 DRV8825 KEYWORD1 A4988 KEYWORD1 +MultiDriver KEYWORD1 +SyncDriver KEYWORD1 setMicrostep KEYWORD2 +setSpeedProfile KEYWORD2 move KEYWORD2 rotate KEYWORD2 setRPM KEYWORD2 +getRPM KEYWORD2 +enable KEYWORD2 +disable KEYWORD2 +startMove KEYWORD2 +startRotate KEYWORD2 +nextAction KEYWORD2 + +CONSTANT_SPEED LITERAL1 +LINEAR_SPEED LITERAL1 From 21959c70e0542beed3bb335f68ea752beecd934b Mon Sep 17 00:00:00 2001 From: Laurentiu Badea Date: Sun, 25 Jun 2017 22:11:09 -0700 Subject: [PATCH 21/21] version 1.1.0 --- library.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library.properties b/library.properties index 192cc04..a7145b1 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=StepperDriver -version=1.0.6 +version=1.1.0 author=Laurentiu Badea maintainer=Laurentiu Badea sentence=A4988, DRV8825 and generic two-pin stepper motor driver library. -paragraph=Control steppers via a driver board providing STEP+DIR. Microstepping is supported. Supported drivers are A4988, DRV8824, DRV8825, DRV8834. +paragraph=Control steppers via a driver board providing STEP+DIR. Microstepping is supported. Acceleration is supported. Supported drivers are A4988, DRV8824, DRV8825, DRV8834. category=Device Control url=https://github.com/laurb9/StepperDriver architectures=*