123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653 |
- // AccelStepper.cpp
- //
- // Copyright (C) 2009-2013 Mike McCauley
- // $Id: AccelStepper.cpp,v 1.23 2016/08/09 00:39:10 mikem Exp $
- #include "AccelStepper.h"
- #if 0
- // Some debugging assistance
- void dump(uint8_t* p, int l)
- {
- int i;
- for (i = 0; i < l; i++)
- {
- Serial.print(p[i], HEX);
- Serial.print(" ");
- }
- Serial.println("");
- }
- #endif
- void AccelStepper::moveTo(long absolute)
- {
- if (_targetPos != absolute)
- {
- _targetPos = absolute;
- computeNewSpeed();
- // compute new n?
- }
- }
- void AccelStepper::move(long relative)
- {
- moveTo(_currentPos + relative);
- }
- // Implements steps according to the current step interval
- // You must call this at least once per step
- // returns true if a step occurred
- boolean AccelStepper::runSpeed()
- {
- // Dont do anything unless we actually have a step interval
- if (!_stepInterval)
- return false;
- unsigned long time = micros();
- if (time - _lastStepTime >= _stepInterval)
- {
- if (_direction == DIRECTION_CW)
- {
- // Clockwise
- _currentPos += 1;
- }
- else
- {
- // Anticlockwise
- _currentPos -= 1;
- }
- step(_currentPos);
- _lastStepTime = time; // Caution: does not account for costs in step()
- return true;
- }
- else
- {
- return false;
- }
- }
- long AccelStepper::distanceToGo()
- {
- return _targetPos - _currentPos;
- }
- long AccelStepper::targetPosition()
- {
- return _targetPos;
- }
- long AccelStepper::currentPosition()
- {
- return _currentPos;
- }
- // Useful during initialisations or after initial positioning
- // Sets speed to 0
- void AccelStepper::setCurrentPosition(long position)
- {
- _targetPos = _currentPos = position;
- _n = 0;
- _stepInterval = 0;
- _speed = 0.0;
- }
- void AccelStepper::computeNewSpeed()
- {
- long distanceTo = distanceToGo(); // +ve is clockwise from curent location
- long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16
- if (distanceTo == 0 && stepsToStop <= 1)
- {
- // We are at the target and its time to stop
- _stepInterval = 0;
- _speed = 0.0;
- _n = 0;
- return;
- }
- if (distanceTo > 0)
- {
- // We are anticlockwise from the target
- // Need to go clockwise from here, maybe decelerate now
- if (_n > 0)
- {
- // Currently accelerating, need to decel now? Or maybe going the wrong way?
- if ((stepsToStop >= distanceTo) || _direction == DIRECTION_CCW)
- _n = -stepsToStop; // Start deceleration
- }
- else if (_n < 0)
- {
- // Currently decelerating, need to accel again?
- if ((stepsToStop < distanceTo) && _direction == DIRECTION_CW)
- _n = -_n; // Start accceleration
- }
- }
- else if (distanceTo < 0)
- {
- // We are clockwise from the target
- // Need to go anticlockwise from here, maybe decelerate
- if (_n > 0)
- {
- // Currently accelerating, need to decel now? Or maybe going the wrong way?
- if ((stepsToStop >= -distanceTo) || _direction == DIRECTION_CW)
- _n = -stepsToStop; // Start deceleration
- }
- else if (_n < 0)
- {
- // Currently decelerating, need to accel again?
- if ((stepsToStop < -distanceTo) && _direction == DIRECTION_CCW)
- _n = -_n; // Start accceleration
- }
- }
- // Need to accelerate or decelerate
- if (_n == 0)
- {
- // First step from stopped
- _cn = _c0;
- _direction = (distanceTo > 0) ? DIRECTION_CW : DIRECTION_CCW;
- }
- else
- {
- // Subsequent step. Works for accel (n is +_ve) and decel (n is -ve).
- _cn = _cn - ((2.0 * _cn) / ((4.0 * _n) + 1)); // Equation 13
- _cn = max(_cn, _cmin);
- }
- _n++;
- _stepInterval = _cn;
- _speed = 1000000.0 / _cn;
- if (_direction == DIRECTION_CCW)
- _speed = -_speed;
- #if 0
- Serial.println(_speed);
- Serial.println(_acceleration);
- Serial.println(_cn);
- Serial.println(_c0);
- Serial.println(_n);
- Serial.println(_stepInterval);
- Serial.println(distanceTo);
- Serial.println(stepsToStop);
- Serial.println("-----");
- #endif
- }
- // Run the motor to implement speed and acceleration in order to proceed to the target position
- // You must call this at least once per step, preferably in your main loop
- // If the motor is in the desired position, the cost is very small
- // returns true if the motor is still running to the target position.
- boolean AccelStepper::run()
- {
- if (runSpeed())
- computeNewSpeed();
- return _speed != 0.0 || distanceToGo() != 0;
- }
- AccelStepper::AccelStepper(uint8_t interface, uint8_t pin1, uint8_t pin2, uint8_t pin3, uint8_t pin4, bool enable)
- {
- _interface = interface;
- _currentPos = 0;
- _targetPos = 0;
- _speed = 0.0;
- _maxSpeed = 1.0;
- _acceleration = 0.0;
- _sqrt_twoa = 1.0;
- _stepInterval = 0;
- _minPulseWidth = 1;
- _enablePin = 0xff;
- _lastStepTime = 0;
- _pin[0] = pin1;
- _pin[1] = pin2;
- _pin[2] = pin3;
- _pin[3] = pin4;
- _enableInverted = false;
-
- // NEW
- _n = 0;
- _c0 = 0.0;
- _cn = 0.0;
- _cmin = 1.0;
- _direction = DIRECTION_CCW;
- int i;
- for (i = 0; i < 4; i++)
- _pinInverted[i] = 0;
- if (enable)
- enableOutputs();
- // Some reasonable default
- setAcceleration(1);
- }
- AccelStepper::AccelStepper(void (*forward)(), void (*backward)())
- {
- _interface = 0;
- _currentPos = 0;
- _targetPos = 0;
- _speed = 0.0;
- _maxSpeed = 1.0;
- _acceleration = 0.0;
- _sqrt_twoa = 1.0;
- _stepInterval = 0;
- _minPulseWidth = 1;
- _enablePin = 0xff;
- _lastStepTime = 0;
- _pin[0] = 0;
- _pin[1] = 0;
- _pin[2] = 0;
- _pin[3] = 0;
- _forward = forward;
- _backward = backward;
- // NEW
- _n = 0;
- _c0 = 0.0;
- _cn = 0.0;
- _cmin = 1.0;
- _direction = DIRECTION_CCW;
- int i;
- for (i = 0; i < 4; i++)
- _pinInverted[i] = 0;
- // Some reasonable default
- setAcceleration(1);
- }
- void AccelStepper::setMaxSpeed(float speed)
- {
- if (speed < 0.0)
- speed = -speed;
- if (_maxSpeed != speed)
- {
- _maxSpeed = speed;
- _cmin = 1000000.0 / speed;
- // Recompute _n from current speed and adjust speed if accelerating or cruising
- if (_n > 0)
- {
- _n = (long)((_speed * _speed) / (2.0 * _acceleration)); // Equation 16
- computeNewSpeed();
- }
- }
- }
- float AccelStepper::maxSpeed()
- {
- return _maxSpeed;
- }
- void AccelStepper::setAcceleration(float acceleration)
- {
- if (acceleration == 0.0)
- return;
- if (acceleration < 0.0)
- acceleration = -acceleration;
- if (_acceleration != acceleration)
- {
- // Recompute _n per Equation 17
- _n = _n * (_acceleration / acceleration);
- // New c0 per Equation 7, with correction per Equation 15
- _c0 = 0.676 * sqrt(2.0 / acceleration) * 1000000.0; // Equation 15
- _acceleration = acceleration;
- computeNewSpeed();
- }
- }
- void AccelStepper::setSpeed(float speed)
- {
- if (speed == _speed)
- return;
- speed = constrain(speed, -_maxSpeed, _maxSpeed);
- if (speed == 0.0)
- _stepInterval = 0;
- else
- {
- _stepInterval = fabs(1000000.0 / speed);
- _direction = (speed > 0.0) ? DIRECTION_CW : DIRECTION_CCW;
- }
- _speed = speed;
- }
- float AccelStepper::speed()
- {
- return _speed;
- }
- // Subclasses can override
- void AccelStepper::step(long step)
- {
- switch (_interface)
- {
- case FUNCTION:
- step0(step);
- break;
- case DRIVER:
- step1(step);
- break;
-
- case FULL2WIRE:
- step2(step);
- break;
-
- case FULL3WIRE:
- step3(step);
- break;
- case FULL4WIRE:
- step4(step);
- break;
- case HALF3WIRE:
- step6(step);
- break;
-
- case HALF4WIRE:
- step8(step);
- break;
- }
- }
- // You might want to override this to implement eg serial output
- // bit 0 of the mask corresponds to _pin[0]
- // bit 1 of the mask corresponds to _pin[1]
- // ....
- void AccelStepper::setOutputPins(uint8_t mask)
- {
- uint8_t numpins = 2;
- if (_interface == FULL4WIRE || _interface == HALF4WIRE)
- numpins = 4;
- else if (_interface == FULL3WIRE || _interface == HALF3WIRE)
- numpins = 3;
- uint8_t i;
- for (i = 0; i < numpins; i++)
- digitalWrite(_pin[i], (mask & (1 << i)) ? (HIGH ^ _pinInverted[i]) : (LOW ^ _pinInverted[i]));
- }
- // 0 pin step function (ie for functional usage)
- void AccelStepper::step0(long step)
- {
- (void)(step); // Unused
- if (_speed > 0)
- _forward();
- else
- _backward();
- }
- // 1 pin step function (ie for stepper drivers)
- // This is passed the current step number (0 to 7)
- // Subclasses can override
- void AccelStepper::step1(long step)
- {
- (void)(step); // Unused
- // _pin[0] is step, _pin[1] is direction
- setOutputPins(_direction ? 0b10 : 0b00); // Set direction first else get rogue pulses
- setOutputPins(_direction ? 0b11 : 0b01); // step HIGH
- // Caution 200ns setup time
- // Delay the minimum allowed pulse width
- delayMicroseconds(_minPulseWidth);
- setOutputPins(_direction ? 0b10 : 0b00); // step LOW
- }
- // 2 pin step function
- // This is passed the current step number (0 to 7)
- // Subclasses can override
- void AccelStepper::step2(long step)
- {
- switch (step & 0x3)
- {
- case 0: /* 01 */
- setOutputPins(0b10);
- break;
- case 1: /* 11 */
- setOutputPins(0b11);
- break;
- case 2: /* 10 */
- setOutputPins(0b01);
- break;
- case 3: /* 00 */
- setOutputPins(0b00);
- break;
- }
- }
- // 3 pin step function
- // This is passed the current step number (0 to 7)
- // Subclasses can override
- void AccelStepper::step3(long step)
- {
- switch (step % 3)
- {
- case 0: // 100
- setOutputPins(0b100);
- break;
- case 1: // 001
- setOutputPins(0b001);
- break;
- case 2: //010
- setOutputPins(0b010);
- break;
-
- }
- }
- // 4 pin step function for half stepper
- // This is passed the current step number (0 to 7)
- // Subclasses can override
- void AccelStepper::step4(long step)
- {
- switch (step & 0x3)
- {
- case 0: // 1010
- setOutputPins(0b0101);
- break;
- case 1: // 0110
- setOutputPins(0b0110);
- break;
- case 2: //0101
- setOutputPins(0b1010);
- break;
- case 3: //1001
- setOutputPins(0b1001);
- break;
- }
- }
- // 3 pin half step function
- // This is passed the current step number (0 to 7)
- // Subclasses can override
- void AccelStepper::step6(long step)
- {
- switch (step % 6)
- {
- case 0: // 100
- setOutputPins(0b100);
- break;
-
- case 1: // 101
- setOutputPins(0b101);
- break;
-
- case 2: // 001
- setOutputPins(0b001);
- break;
-
- case 3: // 011
- setOutputPins(0b011);
- break;
-
- case 4: // 010
- setOutputPins(0b010);
- break;
-
- case 5: // 011
- setOutputPins(0b110);
- break;
-
- }
- }
- // 4 pin half step function
- // This is passed the current step number (0 to 7)
- // Subclasses can override
- void AccelStepper::step8(long step)
- {
- switch (step & 0x7)
- {
- case 0: // 1000
- setOutputPins(0b0001);
- break;
-
- case 1: // 1010
- setOutputPins(0b0101);
- break;
-
- case 2: // 0010
- setOutputPins(0b0100);
- break;
-
- case 3: // 0110
- setOutputPins(0b0110);
- break;
-
- case 4: // 0100
- setOutputPins(0b0010);
- break;
-
- case 5: //0101
- setOutputPins(0b1010);
- break;
-
- case 6: // 0001
- setOutputPins(0b1000);
- break;
-
- case 7: //1001
- setOutputPins(0b1001);
- break;
- }
- }
-
- // Prevents power consumption on the outputs
- void AccelStepper::disableOutputs()
- {
- if (! _interface) return;
- setOutputPins(0); // Handles inversion automatically
- if (_enablePin != 0xff)
- {
- pinMode(_enablePin, OUTPUT);
- digitalWrite(_enablePin, LOW ^ _enableInverted);
- }
- }
- void AccelStepper::enableOutputs()
- {
- if (! _interface)
- return;
- pinMode(_pin[0], OUTPUT);
- pinMode(_pin[1], OUTPUT);
- if (_interface == FULL4WIRE || _interface == HALF4WIRE)
- {
- pinMode(_pin[2], OUTPUT);
- pinMode(_pin[3], OUTPUT);
- }
- else if (_interface == FULL3WIRE || _interface == HALF3WIRE)
- {
- pinMode(_pin[2], OUTPUT);
- }
- if (_enablePin != 0xff)
- {
- pinMode(_enablePin, OUTPUT);
- digitalWrite(_enablePin, HIGH ^ _enableInverted);
- }
- }
- void AccelStepper::setMinPulseWidth(unsigned int minWidth)
- {
- _minPulseWidth = minWidth;
- }
- void AccelStepper::setEnablePin(uint8_t enablePin)
- {
- _enablePin = enablePin;
- // This happens after construction, so init pin now.
- if (_enablePin != 0xff)
- {
- pinMode(_enablePin, OUTPUT);
- digitalWrite(_enablePin, HIGH ^ _enableInverted);
- }
- }
- void AccelStepper::setPinsInverted(bool directionInvert, bool stepInvert, bool enableInvert)
- {
- _pinInverted[0] = stepInvert;
- _pinInverted[1] = directionInvert;
- _enableInverted = enableInvert;
- }
- void AccelStepper::setPinsInverted(bool pin1Invert, bool pin2Invert, bool pin3Invert, bool pin4Invert, bool enableInvert)
- {
- _pinInverted[0] = pin1Invert;
- _pinInverted[1] = pin2Invert;
- _pinInverted[2] = pin3Invert;
- _pinInverted[3] = pin4Invert;
- _enableInverted = enableInvert;
- }
- // Blocks until the target position is reached and stopped
- void AccelStepper::runToPosition()
- {
- while (run())
- ;
- }
- boolean AccelStepper::runSpeedToPosition()
- {
- if (_targetPos == _currentPos)
- return false;
- if (_targetPos >_currentPos)
- _direction = DIRECTION_CW;
- else
- _direction = DIRECTION_CCW;
- return runSpeed();
- }
- // Blocks until the new target position is reached
- void AccelStepper::runToNewPosition(long position)
- {
- moveTo(position);
- runToPosition();
- }
- void AccelStepper::stop()
- {
- if (_speed != 0.0)
- {
- long stepsToStop = (long)((_speed * _speed) / (2.0 * _acceleration)) + 1; // Equation 16 (+integer rounding)
- if (_speed > 0)
- move(stepsToStop);
- else
- move(-stepsToStop);
- }
- }
- bool AccelStepper::isRunning()
- {
- return !(_speed == 0.0 && _targetPos == _currentPos);
- }
|