Slightly altered version of Arduino OneStep

This library is specific to the ST L6474 stepper driver.

OneStep.cpp

Committer:
jonebuckman
Date:
2015-09-02
Revision:
0:92e1b5622620

File content as of revision 0:92e1b5622620:

/**
 * @file OneStep.cpp
 *
 * @author Jon Buckman
 * 
 * @section LICENSE
 *
 * Copyright (c) 2014 Jon Buckman
 *
 * Copyright (C) 2009-2013 Mike McCauley
 * $Id: AccelStepper.cpp,v 1.19 2014/10/31 06:05:27 mikem Exp mikem $
 *
 *    This program is free software: you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation, either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * @section DESCRIPTION
 *
 * OneStep stepper motor accelerate and manipulate.
 *
 * Datasheet:
 *
 *
 */
 
/**
 * Includes
 */
#include "OneStep.h"

/**
 * Methods
 */
OneStep::OneStep(PinName mosi, 
                PinName miso, 
                PinName sck, 
                PinName csn,
                PinName step,
                PinName dir,
                PinName reset,
                PinName flag) : spi_(mosi, miso, sck), nCS_(csn), step_(step), dir_(dir), reset_(reset), flag_(flag)   // step, direction, reset, flag
{
    //5MHz, see page 13 of datasheet for max clock.  May need to be reduces if using long wires
    spi_.frequency(5000000);
    spi_.format(8,3);

    nCS_ = 1;

    wait_us(500);

    _currentPos = 0;
    _targetPos = 0;
    _speed = 0.0;
    _maxSpeed = 1.0;
    _acceleration = 0.0;
    _sqrt_twoa = 1.0;
    _stepInterval = 0;
    _minPulseWidth = 1;
    _lastStepTime = 0;

    // NEW
    _n = 0;
    _c0 = 0.0;
    _cn = 0.0;
    _cmin = 1.0;
    _direction = dir_ = DIRECTION_CCW;
    reset_ = 0;
    t_.start();
}

// No operation.
void OneStep::nop(void) {
    wait_us(1);
    nCS_ = 0;
    spi_.write(0x00);
    nCS_ = 1;
}

// Enable driver.
void OneStep::enable(void) {
    wait_us(1);
    nCS_ = 0;
    spi_.write(ENBL);
    nCS_ = 1;
}

// Disable driver.
void OneStep::disable(void) {
    wait_us(1);
    nCS_ = 0;
    spi_.write(DSBL);
    nCS_ = 1;
}

// Set parameter value;
void OneStep::set_param(char parameter, int length, int value) {
    char val[3];
    switch (length) {
        case 1:
            val[0] = (value & 0x0000ffUL)      ;
            break;
        case 2:
            val[0] = (value & 0x00ff00UL) >>  8;
            val[1] = (value & 0x0000ffUL)      ;
            break;
        case 3:
            val[0] = (value & 0xff0000UL) >> 16;
            val[1] = (value & 0x00ff00UL) >>  8;
            val[2] = (value & 0x0000ffUL)      ;
            break;
    }
    wait_us(1);
    nCS_ = 0;
    spi_.write(parameter);
    nCS_ = 1;
    for (int i = 0; i < length; i++) {
        wait_us(1);
        nCS_ = 0;
        spi_.write(val[i]);
        nCS_ = 1;
    }
}

// Get parameter value.
int OneStep::get_param(char parameter, int length) {
    char output = 0x20 | parameter;
    char buf[3] = {0, 0, 0};
    wait_us(1);
    nCS_ = 0;
    spi_.write(output);
    nCS_ = 1;
    for (int i = 0; i < length; i++) {
        wait_us(1);
        nCS_ = 0;
        buf[i] = spi_.write(0x00);
        nCS_ = 1;
    }
    switch (length) {
        case 1:
            return buf[0];
        case 2:
            return buf[0] << 8 | buf[1];
        case 3:
            return buf[0] << 16 | buf[1] << 8 | buf[2];
    }
    return 0;
}

// Get status.
int OneStep::get_status() {
    int ret_bytes[2];
    wait_us(1);
    nCS_ = 0;
    spi_.write(0xD0); //write request for status return
    nCS_ = 1;
    wait_us(1);
    nCS_ = 0;
    ret_bytes[0] = spi_.write(0x00);  //read first byte
    nCS_ = 1;
    wait_us(1);
    nCS_ = 0;
    ret_bytes[1] = spi_.write(0x00);  //read second byte
    nCS_ = 1;
    return ret_bytes[0] << 8 | ret_bytes[1];  //return i16 response
}

void OneStep::moveTo(long absolute)
{
    if (_targetPos != absolute)
    {
    _targetPos = absolute;
    computeNewSpeed();
    }
}

void OneStep::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.
bool OneStep::runSpeed()
{
    // Dont do anything unless we actually have a step interval
    if (!_stepInterval)
        return false;

    unsigned long time = t_.read_us();
    // Gymnastics to detect wrapping of either the nextStepTime and/or the current time
    unsigned long nextStepTime = _lastStepTime + _stepInterval;
    if (   ((nextStepTime >= _lastStepTime) && ((time >= nextStepTime) || (time < _lastStepTime)))
        || ((nextStepTime < _lastStepTime) && ((time >= nextStepTime) && (time < _lastStepTime))))

    {
        if (_direction == DIRECTION_CW)
        {
            // Clockwise
            _currentPos += 1;
        }
        else
        {
            // Anticlockwise  
            _currentPos -= 1;
        }
        step();

        _lastStepTime = time;
        return true;
    }
    else
    {
        return false;
    }
}

long OneStep::distanceToGo()
{
    return _targetPos - _currentPos;
}

long OneStep::targetPosition()
{
    return _targetPos;
}

long OneStep::currentPosition()
{
    return _currentPos;
}

// Useful during initialisations or after initial positioning
// Sets speed to 0
void OneStep::setCurrentPosition(long position)
{
    _targetPos = _currentPos = position;
    _n = 0;
    _stepInterval = 0;
}

void OneStep::computeNewSpeed()
{
    long distanceTo = distanceToGo(); // +ve is clockwise from curent location

    long stepsToStop = (long)((_speed * _speed) / (2.0f * _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 = dir_ = (distanceTo > 0) ? DIRECTION_CW : DIRECTION_CCW;
    }
    else
    {
    // Subsequent step. Works for accel (n is +_ve) and decel (n is -ve).
    _cn = _cn - ((2.0f * _cn) / ((4.0f * _n) + 1)); // Equation 13
    _cn = max(_cn, _cmin); 
    }
    _n++;
    _stepInterval = _cn;
    _speed = 1000000.0f / _cn;
    if (_direction == DIRECTION_CCW)
    _speed = -_speed;
}

// 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.
bool OneStep::run()
{
    if (runSpeed())
    computeNewSpeed();
    return _speed != 0.0f || distanceToGo() != 0;
}

// Step once.
void OneStep::step()
{
    step_ = 1;
    wait_us(5);
    step_ = 0;
}

// Set the max speed.
void OneStep::setMaxSpeed(float speed)
{
    if (_maxSpeed != speed)
    {
        _maxSpeed = speed;
        _cmin = 1000000.0f / speed;
        // Recompute _n from current speed and adjust speed if accelerating or cruising
        if (_n > 0)
        {
            _n = (long)((_speed * _speed) / (2.0f * _acceleration)); // Equation 16
            computeNewSpeed();
        }
    }
}

// Set the acceleration.
void OneStep::setAcceleration(float acceleration)
{
    if (acceleration == 0.0f)
        return;
    if (_acceleration != acceleration)
    {
        // Recompute _n per Equation 17
        _n = _n * (_acceleration / acceleration);
        // New c0 per Equation 7
        //_c0 = sqrt(2.0 / acceleration) * 1000000.0; // Accelerates at half the expected rate. Why?
        _c0 = sqrt(1.0f/acceleration) * 1000000.0f;
        _acceleration = acceleration;
        computeNewSpeed();
    }
}

// Set the speed.
void OneStep::setSpeed(float speed)
{
    if (speed == _speed)
        return;
    speed = constrain(speed, -_maxSpeed, _maxSpeed);
    if (speed == 0.0f)
        _stepInterval = 0;
    else
    {
        _stepInterval = fabs(1000000.0f / speed);
        _direction = dir_ = (speed > 0.0f) ? DIRECTION_CW : DIRECTION_CCW;
    }
    _speed = speed;
}

// Get current speed.
float OneStep::speed()
{
    return _speed;
}

// Set the minimum pulse width.
void OneStep::setMinPulseWidth(unsigned int minWidth)
{
    _minPulseWidth = minWidth;
}

// System reset enable.
void OneStep::enableReset()
{
    reset_ = 0;
}

// System reset disable.
void OneStep::disableReset()
{
    reset_ = 1;
}

// Blocks until the target position is reached and stopped.
void OneStep::runToPosition()
{
    while (run())
    ;
}

// Run at speed to a position.
bool OneStep::runSpeedToPosition()
{
    if (_targetPos == _currentPos)
    return false;
    if (_targetPos >_currentPos)
    _direction = dir_ = DIRECTION_CW;
    else
    _direction = dir_ = DIRECTION_CCW;
    return runSpeed();
}

// Blocks until the new target position is reached.
void OneStep::runToNewPosition(long position)
{
    moveTo(position);
    runToPosition();
}

void OneStep::stop()
{
    if (_speed != 0.0f)
    {    
    long stepsToStop = (long)((_speed * _speed) / (2.0f * _acceleration)) + 1; // Equation 16 (+integer rounding)
    if (_speed > 0)
        move(stepsToStop);
    else
        move(-stepsToStop);
    }
}

// Get the alarm flag state.
int OneStep::getAlarm()
{
    return(flag_);
}