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

Dependents:   Steppermotor

src/SimpleSteppers.cpp

Committer:
AjK
Date:
2011-05-20
Revision:
4:7b7940df7865
Parent:
0:7393c52297ee

File content as of revision 4:7b7940df7865:

/*
    Copyright (c) 2010 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.
*/

#include <math.h>
#include "SimpleSteppers.h"
#include "IOmacros.h"

#define _PMR    *((uint32_t *)_pMR)

namespace AjK {

extern SimpleStepperController __simpleStepperController;

void TIMER2_IRQHandler(void);

SimpleStepper::SimpleStepper(PinName pulse, SimpleStepperOutput *direction)
{
    _simpleStepperController = &__simpleStepperController;
    _pulse     = pulse;
    _direction = direction;
    init();
}

SimpleStepper::SimpleStepper(SimpleStepperController *con, PinName pulse, SimpleStepperOutput *direction)
{
    _simpleStepperController = con;
    _pulse     = pulse;
    _direction = direction;
    init();
}

void
SimpleStepper::init(void)
{
    _pulseCounter = 0;
    _pulseWrapPos = 0;
    _pulseWrapNeg = 0;
    
    _maintainPositionData = true; 
    
    _steps_per_second = 0;
    
    setPulseSense(1);
    setDirectionSense(1);
    
    switch (_pulse) {
        case P0_6: // Mbed p8
            _pulseMAT = MAT2_0;
            _operShift = 4;
            _pMR = 0x40090018; // T2MR0
            _match_shift = 0;
            _EMmask = 1;
            _EMCshift = 4;
            _simpleStepperController->_stepper[0] = this;
            if (_pulseSense) LPC_TIM2->EMR &= ~_EMmask; else LPC_TIM2->EMR |= _EMmask; 
            LPC_PINCON->PINSEL0 |= (3UL << 12);
            break;
        case P0_7: // Mbed p7
            _pulseMAT = MAT2_1;
            _operShift = 6;
            _pMR = 0x4009001C; // T2MR1
            _match_shift = 3;
            _EMmask = 2;
            _EMCshift = 6;
            _simpleStepperController->_stepper[1] = this;
            if (_pulseSense) LPC_TIM2->EMR &= ~_EMmask; else LPC_TIM2->EMR |= _EMmask; 
            LPC_PINCON->PINSEL0 |= (3UL << 14);
            break;        
        case P0_8: // Mbed p6
            _pulseMAT = MAT2_2;
            _operShift = 8;
            _pMR = 0x40090020; // T2MR2
            _match_shift = 6;
            _EMmask = 4;
            _EMCshift = 8;
            _simpleStepperController->_stepper[2] = this;
            if (_pulseSense) LPC_TIM2->EMR &= ~_EMmask; else LPC_TIM2->EMR |= _EMmask; 
            LPC_PINCON->PINSEL0 |= (3UL << 16);
            break;        
        case P0_9: // Mbed p5
            _pulseMAT = MAT2_3;
            _operShift = 10;
            _pMR = 0x40090024; // T2MR3
            _match_shift = 9;
            _EMmask = 8;
            _EMCshift = 10;
            _simpleStepperController->_stepper[3] = this;
            if (_pulseSense) LPC_TIM2->EMR &= ~_EMmask; else LPC_TIM2->EMR |= _EMmask; 
            LPC_PINCON->PINSEL0 |= (3UL << 18);
            break;
        default:
            while(1); /* ToDo, proper error reporting. */
    } 
    
    
    // The default pulse length. 5 * (1/SIMPLESTEPPER_F) = 100us.
    _pulseLen = 5;
    
    // Initial state machine conditions.
    _pulseState = PulseIdle;    
    _stepperState = Stopped;       
}

void
SimpleStepper::setInterval(int interval, uint32_t raw)
{    
    
    if (interval == 0) { setSpeed((int)0); }
    else {
        int n = interval;
        if (n < 0) n *= -1;
        if (_direction) {
            if (_directionSense) {
                if (interval >= 0.0) _direction->write(1);
                if (interval <  0.0) _direction->write(0);
            }
            else {
                if (interval >= 0.0) _direction->write(0);
                if (interval <  0.0) _direction->write(1);        
            }
        }        
        _commandSpeed = n - _pulseLen; 
        _pulseState = PulseAssert;
        _stepperState = ConstantSpeed;
        if (raw) {
            _PMR = raw + _commandSpeed;
        }
        else {
            _PMR = LPC_TIM2->TC + _commandSpeed;
        }                
        if (_pulseSense) LPC_TIM2->EMR &= ~_EMmask; else LPC_TIM2->EMR |= _EMmask;
        LPC_TIM2->MCR &= ~(7UL << _match_shift);
        LPC_TIM2->MCR |=  (1UL << _match_shift);
    }
}

void
SimpleStepper::setSpeed(double steps_per_second, uint32_t raw)
{    
    int i = 0;
    
    if (steps_per_second == 0.0) { setSpeed(i); }
    else {
        double fractpart, intpart;
        fractpart = modf (steps_per_second , &intpart);
        if (fractpart >= 0.50) intpart++;
        double n = intpart;
        if (n < 0.0) n *= -1.0;
        if (_direction) {
            if (_directionSense) {
                if (steps_per_second >= 0.0) _direction->write(1);
                if (steps_per_second <  0.0) _direction->write(0);
            }
            else {
                if (steps_per_second >= 0.0) _direction->write(0);
                if (steps_per_second <  0.0) _direction->write(1);        
            }
        }        
        _commandSpeed = (int)(((double)SIMPLESTEPPER_F / n)) - _pulseLen; 
        _pulseState = PulseAssert;
        _stepperState = ConstantSpeed;
        if (raw) {
            _PMR = raw + _commandSpeed;
        }
        else {
            _PMR = LPC_TIM2->TC + _commandSpeed;
        }                
        if (_pulseSense) LPC_TIM2->EMR &= ~_EMmask; else LPC_TIM2->EMR |= _EMmask;
        LPC_TIM2->MCR &= ~(7UL << _match_shift);
        LPC_TIM2->MCR |=  (1UL << _match_shift);
    }
}

void
SimpleStepper::setSpeed(int steps_per_second, uint32_t raw)
{
    if (steps_per_second == 0) {
        LPC_TIM2->EMR |=  (NothingOnMatch << _EMCshift);
        LPC_TIM2->MCR &= ~(7UL << _match_shift);
        if (_pulseSense) LPC_TIM2->EMR |= _EMmask; else LPC_TIM2->EMR &= ~_EMmask; 
        _stepperState = Stopped;
        _pulseState = PulseIdle;
        _commandSpeed = 0;
        _steps_per_second = 0;
    }
    else {
        _steps_per_second = steps_per_second;
        int n = steps_per_second;
        if (n < 0) n *= -1;
        if (_direction) {
            if (_directionSense) {
                if (steps_per_second >= 0) _direction->write(1);
                if (steps_per_second <  0) _direction->write(0);
            }
            else {
                if (steps_per_second >= 0) _direction->write(0);
                if (steps_per_second <  0) _direction->write(1);        
            }
        }        
        _commandSpeed = (SIMPLESTEPPER_F / n) - _pulseLen;        
        _pulseState = PulseAssert;
        _stepperState = ConstantSpeed;        
        if (raw) {
            _PMR = raw + _commandSpeed;
        }
        else {
            _PMR = LPC_TIM2->TC + _commandSpeed;
        }
        if (_pulseSense) LPC_TIM2->EMR &= ~_EMmask; else LPC_TIM2->EMR |= _EMmask;
        LPC_TIM2->MCR &= ~(7UL << _match_shift);
        LPC_TIM2->MCR |=  (1UL << _match_shift);
    }
}

void 
SimpleStepper::next_step(void) 
{    
    _PMR += _commandSpeed;
        
    switch(_pulseSense) {
        case 1:
            LPC_TIM2->EMR &= ~(3UL << _EMCshift); 
            LPC_TIM2->EMR |=  (SetOnMatch << _EMCshift); 
            break;
        case 0:
            LPC_TIM2->EMR &= ~(3UL << _EMCshift); 
            LPC_TIM2->EMR |=  (ClrOnMatch << _EMCshift); 
            break;
    }
    
    // Next IRQ will automatically assert the pulse.
    _pulseState = PulseAssert;
}

void 
SimpleStepper::isr(uint32_t ir) 
{
    // Test to see if this interrupt is for us.
    if (ir & _EMmask) { 
    
        if (_stepperState == Stopped) {
            if (_pulseSense) LPC_TIM2->EMR &= ~_EMmask; else LPC_TIM2->EMR |= _EMmask; 
            return;
        }
       
        switch(_pulseState) {
            case PulseIdle:
                if (_pulseSense) LPC_TIM2->EMR &= ~_EMmask; else LPC_TIM2->EMR |= _EMmask;
                _currentMatch = _PMR; // Store the current match value for profile().
                next_step();
                break;
                
            case PulseAssert: // Pulse has just been asserted on MAT2_x. 
                _PMR += _pulseLen; // We now program for the deassert IRQ to occur at the required time.            
                LPC_TIM2->EMR &= ~(ToggleOnMatch << _EMCshift); // At next IRQ we want to switch the pulse off...
                LPC_TIM2->EMR |=  (ToggleOnMatch << _EMCshift); // ... we do this by toggling it.
                _pulseState = PulseDeassert; // Set state for next interrupt.
                
                if (_maintainPositionData) {
                    if (_directionSense) {
                        if (_commandSpeed > 0) {
                            _pulseCounter++;
                            if (_pulseWrapPos && _pulseCounter >= _pulseWrapPos) {
                                _pulseCounter = _pulseWrapNeg + 1;
                            }
                        }                        
                        if (_commandSpeed < 0) {
                            _pulseCounter--;
                            if (_pulseWrapNeg && _pulseCounter <= _pulseWrapNeg) {
                                _pulseCounter = _pulseWrapPos - 1;
                            }
                        }
                    }
                    else {
                        if (_commandSpeed > 0) {
                            _pulseCounter--;
                            if (_pulseWrapNeg && _pulseCounter <= _pulseWrapNeg) {
                                _pulseCounter = _pulseWrapPos - 1;
                            }
                        }                        
                        if (_commandSpeed < 0) {
                            _pulseCounter++;
                            if (_pulseWrapPos && _pulseCounter >= _pulseWrapPos) {
                                _pulseCounter = _pulseWrapNeg + 1;
                            }
                        }
                    }
                }
                break;
                
            case PulseDeassert: // Pulse has just been deasserted on MAT2_x.
                LPC_TIM2->EMR &= ~(NothingOnMatch << _EMCshift); // No further action...
                LPC_TIM2->EMR |=  (NothingOnMatch << _EMCshift); // ... just in case.
                _pulseState = PulseIdle; // Set state for next interrupt (profile() may change this).
                _currentMatch = _PMR; // Store the current match value for profile().
                next_step();
                break;
        }    
    }
}

// **************************************************
// * Controller class SimpleStepperController code. *
// **************************************************

SimpleStepperController::SimpleStepperController()
{
    for (int i = 0; i < 4; i++) _stepper[i] = 0;
    
    uint32_t pr = (uint32_t)(SystemCoreClock / 8 / SIMPLESTEPPER_F);
    
    LPC_SC->PCONP    |= (1UL << 22); // TIM2 On
    LPC_SC->PCLKSEL1 |= (3UL << 12); // CCLK/8 = 12MHz
    LPC_TIM2->PR      = pr - 1;      // TC clocks at SIMPLESTEPPER_F hz.
    LPC_TIM2->MR0     = 0;
    LPC_TIM2->MR1     = 0;
    LPC_TIM2->MR2     = 0;
    LPC_TIM2->MR3     = 0;
    LPC_TIM2->MCR     = 0;          
    
    // Enable interrupts
    NVIC_EnableIRQ(TIMER2_IRQn);  
    
    // Start timer operations.
    LPC_TIM2->TCR = 2; // reset 
    LPC_TIM2->TCR = 1; // enable
}

SimpleStepperController::~SimpleStepperController()
{
    NVIC_DisableIRQ(TIMER2_IRQn);    
    LPC_SC->PCONP &= ~(1UL << 22); // TIM2 Off.
}

void
SimpleStepperController::isr(void)
{
    uint32_t ir = LPC_TIM2->IR & 0xF;
    if (_stepper[0] && ir & 1) _stepper[0]->isr(ir);
    if (_stepper[1] && ir & 2) _stepper[1]->isr(ir);
    if (_stepper[2] && ir & 4) _stepper[2]->isr(ir);
    if (_stepper[3] && ir & 8) _stepper[3]->isr(ir);
}

}; // namespace AjK ends.