Provides an API software interface to TIMER2 to control upto four stepper motors.

Dependents:   Steppermotor

inc/SimpleSteppers.h

Committer:
AjK
Date:
2011-05-20
Revision:
4:7b7940df7865
Parent:
3:71ab7209adb3

File content as of revision 4:7b7940df7865:

/*
    Copyright (c) 2011 Andy Kirkham
 
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
 
    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.
 
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
*/

#ifndef AJK_SIMPLESTEPPERS_H
#define AJK_SIMPLESTEPPERS_H

#define SIMPLESTEPPER_F 100000
#include "LPC17xx.h"
#include "core_cm3.h"
#include <stdint.h>

#ifndef __ARMCC_VERSION
#include "SimpleStepperMbed.h"
#else
#include "mbed.h"
#endif

#include "SimpleStepperOutput.h"

namespace AjK {

// Forward reference the main controller.
class SimpleStepperController;

/** SimpleStepper
 *
 * SimpleStepper is a class designed to handle the pulse and direction
 * signals for a stepper motor. A simple API is exposed to allow the
 * setting of the pulse width and the number of pulses per second.
 *
 * Note, SimpleStepper uses the SimpleStepperController class to manage
 * the TIMER2. SimpleStepperController and SimpleStepper take total
 * control of the LPC17xx's TIMER2. Your application or other library that
 * your application uses, should <b>not</b>use TIMER2.
 *
 * This library is a software interface to the TIMER2 MATCH system. It does 
 * <b>not</b> provide position and/or acceleration, PID control, etc, of
 * the motors. Your application or other library should provide the control
 * function needed to position/run your stepper motor(s).
 *
 * @code
 * #include "mbed.h"
 * #include "SimpleSteppers.h"
 * 
 * SimpleStepperOutput led1(LED1);
 * 
 * // SimpleStepperOutput is basically the same as
 * // Mbed's DigitalOut class. However, it's more
 * // portable to other platforms without having
 * // to also port the entire Mbed library.
 * SimpleStepperOutput sdir0(p17);
 * SimpleStepperOutput sdir1(p18);
 * SimpleStepperOutput sdir2(p19);
 * SimpleStepperOutput sdir3(p20);
 * 
 * // Create four steppers.
 * // Stepper0 has the pulse output on p8 and dir on p17
 * SimpleStepper stepper0(p8, &sdir0);
 * // Stepper1 has the pulse output on p7 and dir on p18
 * SimpleStepper stepper1(p7, &sdir1);
 * // Stepper2 has the pulse output on p7 and dir on p19
 * SimpleStepper stepper2(p6, &sdir2);
 * // Stepper3 has the pulse output on p7 and dir on p20
 * SimpleStepper stepper3(p5, &sdir3);
 * 
 * int main() {
 *     
 *    // We do not need to maintain the stepper position
 *    // for this simple example. This reduces the amount
 *    // of work the ISR has to do.
 *    stepper0.setMaintainPositionData(false);
 *    stepper1.setMaintainPositionData(false);
 *    stepper2.setMaintainPositionData(false);
 *    stepper3.setMaintainPositionData(false);
 *    
 *    // Set all steppers to top speed of 5000 pulses/second.
 *    stepper0.setSpeed(5000);
 *    stepper1.setSpeed(5000);
 *    stepper2.setSpeed(5000);
 *    stepper3.setSpeed(5000);
 *
 *    while(1) {
 *        led1 = !led1;
 *        wait(0.2);        
 *    }
 * }
 * @endcode
 * @see example1.h
 */
class SimpleStepper {

public:
    enum MAT           { MAT2_0, MAT2_1, MAT2_2, MAT2_3 };
    enum MATCH_OP      { NothingOnMatch, ClrOnMatch, SetOnMatch, ToggleOnMatch };
    enum PULSE_STATE   { PulseIdle, PulseAssert, PulseDeassert };
    enum STEPPER_STATE { Stopped, ConstantSpeed };
        
protected:    
    SimpleStepperController *_simpleStepperController;    
    SimpleStepperOutput *_direction;
    
    PinName     _pulse;
    MAT         _pulseMAT;
    uint32_t    _operShift;
    uint32_t    _pMR;
    uint32_t    _match_shift;
    uint32_t    _EMmask;
    uint32_t    _EMCshift;
    int         _pulseLen;
    int         _pulseSense;
    int         _directionSense;
    int         _commandSpeed;
    int64_t     _pulseCounter;
    int64_t     _pulseWrapPos;
    int64_t     _pulseWrapNeg;    
    uint32_t    _currentMatch;
    
    volatile int _steps_per_second;
    
    bool        _maintainPositionData;
    
    PULSE_STATE _pulseState;
    
    STEPPER_STATE _stepperState;

    void init(void);
    
    void isr(uint32_t);
    
    void next_step(void);
    
public:
    friend class SimpleStepperController;
    
    /** Constructor
     * 
     * PinName pulse can be p5, p6, p7 or p8
     * SimpleStepperOutput *direction is a pointer to an output.
     *
     * @param PinName The output for the pulse output.
     * @param SimpleStepperOutput *direction The output pin for direction control
     */
    SimpleStepper(PinName pulse, SimpleStepperOutput *direction = 0);
    
    /** Constructor
     * 
     * PinName pulse can be p5, p6, p7 or p8
     * SimpleStepperOutput *direction is a pointer to an output.
     *
     * @param SimpleStepperController *con A pointer to a base controller.
     * @param PinName The output for the pulse output.
     * @param SimpleStepperOutput *direction The output pin for direction control
     */
    SimpleStepper(SimpleStepperController *con, PinName pulse, SimpleStepperOutput *direction);
   
    /** setPulseSense
     *
     * Set's the logic value that pulse asserted assumes. Default is 1.
     *
     * 1 means pulse goes from 0 to 1 and remains 1 for pulseLen time then goes to 0.
     * 0 means pulse goes from 1 to 0 and remains 0 for pulseLen time then goes to 1.
     *
     * @param int What is the logic sense for the pulse output.
     */
    void setPulseSense(int i = 1) { _pulseSense = i; }
    
    /** setDirectionSense
     *
     * Set's the logic value that direction forward assumes. Default is 1.
     *
     * 1 means that the direction output is 1 for forward and 0 for reverse.
     * 0 means that the direction output is 0 for forward and 1 for reverse.
     *
     * Additionally, for position:-
     * 1 means forward pulses are counted upwards, reverse means counted downwards.
     * 0 means forward pulses are counted downwards, reverse means counted upwards.
     *
     * @param int What is the logic sense for the direction output.
     */
    void setDirectionSense(int i = 1) { _directionSense = i; }
    
    /** setPulseLen
     *
     * Used to set the pulse length. Default pulse length is 50us.
     * If no arg supplied returns the current pulse length.
     * <b>Note,</b> the length is specified as 10us increments.
     * The default is 5 which is 50us
     * @param int pulse length
     * @return int the value of the pulse length.
     */
    int setPulseLen(int i = 0) { if (i) _pulseLen = i; return _pulseLen; }
    
    /** setSpeed
     * 
     * Set the motor speed in pulses per second. Zero stops all motor pulses.
     * 
     * With the default pulseLen of 50us (5 timer "ticks") the maximum speed
     * that can be set is 10000 pulses per second (pps) which will produce a square
     * wave with 50% duty cycle. Pushing it beyond will give a square wave a duty 
     * cycle shifts so that the off time between pulses becomes shorter than the on 
     * pulse length.
     *
     * The pulseLen can be adjusted using setPulseLen() down even more to eek out 
     * some extra bandwidth. However, you really should be checking both the datasheet
     * for your stepper motor amplifier and stepper motor. Running a stepper motor at
     * such high speed may have electrical and mechanical issues.
     *
     * The arg raw is used when you want to "sync" channels. Suppose for example you
     * have the following code to set the speed to two front wheels:-
     * @code
     *      stepper0.setSpeed(100);
     *      stepper1.setSpeed(100);
     * @endcode
     *
     * The problem here is they will be a few "clock ticks" out from each other as the
     * TIMER2 TC increments between the two calls. If you wanted to make two motors sync
     * together you can pass in the raw value of TIMER2 TC to make both functions calculate
     * the same stepp values and so sync together:-
     * @code
     *      uint32_t i = LPC_TIM2->TC; // Capture TC.
     *      stepper0.setSpeed(100, i);
     *      stepper1.setSpeed(100, i);
     * @endcode     
     *
     * The above code forces setSpeed() to calculate both axis in sync with respect to 
     * a common TC value.
     *
     * @param int steps_per_second Number of pulses per second required.     
     */
    void setSpeed(int steps_per_second = 0, uint32_t raw = 0); 
    
    /** setSpeed
     * 
     * Set the motor speed in pulses per second. Zero stops all motor pulses.
     * @param double steps_per_second Number of pulses per second required.     
     */
    void setSpeed(double steps_per_second = 0, uint32_t raw = 0); 
    
    /** getSpeed
     * 
     * Get the demanded motor speed in pulses per second. Zero stops all motor pulses.
     * @return int steps_per_second Number of pulses per second demanded.     
     */
    int getSpeed(void) { return _steps_per_second; }
    
    /** setInterval
     * 
     * Set the motor speed pulse interval. Zero stops all motor pulses.
     * @param int interval 
     */
    void setInterval(int i = 0, uint32_t raw = 0); 
    
    /** getPosition
     * 
     * Get the current position (the pulse counter).
     *
     * @return int64_t The current position as maintained by the interrupts.
     */
    int64_t getPosition(void) { return _pulseCounter; }

    /** setWrapPos
     *     
     * Set the value that we should wrap the pulse position counter.
     * Zero (default) means no wrap occurs.
     *
     * @param int64_t The value to wrap to NEG at.
     */
    void setWrapPos(int64_t i = 0) { _pulseWrapPos = i; }
    
    /** setWrapNeg
     *     
     * Set the value that we should wrap the pulse position counter.
     * Zero (default) means no wrap occurs.
     *
     * @param int64_t The value to wrap to POS at.
     */
    void setWrapNeg(int64_t i = 0) { _pulseWrapNeg = i; }
    
    /** setMaintainPositionData
     *
     * Setting this false removes the maintainence of positional data.
     * This allows the interrupt service routine to not perform these
     * steps (if not required) thus making the ISR run slightly fatser.
     * You might want to do this if positional data isn't required.
     */
    void setMaintainPositionData(bool b) { _maintainPositionData = b; }
    
};

class SimpleStepperController {
protected:
    SimpleStepper   *_stepper[4];

public:
    friend class SimpleStepper;
    
    SimpleStepperController();    
    ~SimpleStepperController();    
    void isr(void);
};

}; // namespace AjK ends

#endif