//
#include "Define.h"
//
#include "StepperDRV8825.h"
//
CStepperDRV8825::CStepperDRV8825(int pinstep, int pindirection,
                                 int pinm0, int pinm1, int pinm2,
                                 int pinenablen)
{
    FOpened = false;
    FPinStep = pinstep;
    FPinDirection = pindirection;
    FPinM0 = pinm0;
    FPinM1 = pinm1;
    FPinM2 = pinm2;
    FPinEnableN = pinenablen;
    FStepCount = 0L;
    FDistance = 0L;
    Disable();
    SetStateStep(ssIdle);
}
//
void CStepperDRV8825::SetStateStep(EStateStep statestep)
{
    FStateStep = statestep;
    switch (FStateStep)
    {
        case ssStepLow:
            digitalWrite(PIN_MOTOR_STEP, LOW);
            break;
        case ssStepHigh:
            digitalWrite(PIN_MOTOR_STEP, HIGH);
            if (sdPositive == GetStepDirection())
            {
                switch (GetStepResolution())
                {
                    case srThirtyTwo:
                        FDistance += 1L;
                        break;
                    case srSixteen:
                        FDistance += 2L;
                        break;
                    case srEight:
                        FDistance += 4L;
                        break;
                    case srQuarter:
                        FDistance += 8L;
                        break;
                    case srHalf:
                        FDistance += 16L;
                        break;
                    default:
                        FDistance += 32L;
                        break;
                }                    
            }
            else
            {
                switch (GetStepResolution())
                {
                    case srThirtyTwo:
                        FDistance -= 1L;
                        break;
                    case srSixteen:
                        FDistance -= 2L;
                        break;
                    case srEight:
                        FDistance -= 4L;
                        break;
                    case srQuarter:
                        FDistance -= 8L;
                        break;
                    case srHalf:
                        FDistance -= 16L;
                        break;
                    default:
                        FDistance -= 32L;
                        break;
                }
            }
            break;
        default: // ssIdle
            break;
    }
}
//
void CStepperDRV8825::SetStepDirection(EStepDirection stepdirection)
{
    switch (stepdirection)
    {
        case sdNegative:
            digitalWrite(PIN_MOTOR_DIRECTION, LOW);  
            break;
        default: // sdPositive
            digitalWrite(PIN_MOTOR_DIRECTION, HIGH);  
            break;
    }
}
EStepDirection CStepperDRV8825::GetStepDirection(void)
{
    if (HIGH == digitalRead(PIN_MOTOR_DIRECTION))
    {
        return sdPositive;
    }
    return sdNegative;
}
//
EStepResolution CStepperDRV8825::GetStepResolution(void)
{
    if (HIGH == digitalRead(PIN_MOTOR_PRECISIONM2))
    {
        if (HIGH == digitalRead(PIN_MOTOR_PRECISIONM1))
        {
            if (HIGH == digitalRead(PIN_MOTOR_PRECISIONM0))
            { // H
                return srThirtyTwo; // table
            } // L
            return srThirtyTwo; // table
        } 
        else // LOW == PIN_MOTOR_PRECISIONM1
        {
            if (HIGH == digitalRead(PIN_MOTOR_PRECISIONM0))
            { // H
                return srThirtyTwo; // table
            } // L
            return srSixteen; // table
        }
    }
    else // LOW == PIN_MOTOR_PRECISIONM2
    {
        if (HIGH == digitalRead(PIN_MOTOR_PRECISIONM1))
        {
            if (HIGH == digitalRead(PIN_MOTOR_PRECISIONM0))
            { // H
                return srEight; // table
            } // L
            return srQuarter; // table
        } 
        else // LOW == PIN_MOTOR_PRECISIONM1
        {
            if (HIGH == digitalRead(PIN_MOTOR_PRECISIONM0))
            { // H
                return srHalf; // table
            } // L 
            return srFull;  // table
        }
    }
    return srFull;
}
//
void CStepperDRV8825::SetStepResolution(EStepResolution stepresolution)
{    
    switch (stepresolution)
    {        
        case srHalf:
            digitalWrite(PIN_MOTOR_PRECISIONM2, LOW);
            digitalWrite(PIN_MOTOR_PRECISIONM1, LOW);
            digitalWrite(PIN_MOTOR_PRECISIONM0, HIGH);
            break;
        case srQuarter:
            digitalWrite(PIN_MOTOR_PRECISIONM2, LOW);
            digitalWrite(PIN_MOTOR_PRECISIONM1, HIGH);
            digitalWrite(PIN_MOTOR_PRECISIONM0, LOW);
            break;
        case srEight:
            digitalWrite(PIN_MOTOR_PRECISIONM2, LOW);
            digitalWrite(PIN_MOTOR_PRECISIONM1, HIGH);
            digitalWrite(PIN_MOTOR_PRECISIONM0, HIGH);
            break;
        case srSixteen:
            digitalWrite(PIN_MOTOR_PRECISIONM2, HIGH);
            digitalWrite(PIN_MOTOR_PRECISIONM1, LOW);
            digitalWrite(PIN_MOTOR_PRECISIONM0, LOW);
            break;
        case srThirtyTwo:
            digitalWrite(PIN_MOTOR_PRECISIONM2, HIGH);
            digitalWrite(PIN_MOTOR_PRECISIONM1, HIGH);
            digitalWrite(PIN_MOTOR_PRECISIONM0, HIGH);
            break;
        default: // srFull
            digitalWrite(PIN_MOTOR_PRECISIONM2, LOW);
            digitalWrite(PIN_MOTOR_PRECISIONM1, LOW);
            digitalWrite(PIN_MOTOR_PRECISIONM0, LOW);
            break;
    }
}
//
void CStepperDRV8825::Enable(void)
{
    digitalWrite(PIN_MOTOR_ENABLEN, LOW);
}
void CStepperDRV8825::Disable(void)
{
    digitalWrite(PIN_MOTOR_ENABLEN, HIGH);
}

void CStepperDRV8825::SetStepLow(void)
{
    digitalWrite(PIN_MOTOR_STEP, LOW);
}
void CStepperDRV8825::SetStepHigh(void)
{
    digitalWrite(PIN_MOTOR_STEP, HIGH);
}
//
bool CStepperDRV8825::Open(void)
{
    pinMode(PIN_MOTOR_STEP, OUTPUT);
    pinMode(PIN_MOTOR_DIRECTION, OUTPUT);
    pinMode(PIN_MOTOR_PRECISIONM0, OUTPUT);
    pinMode(PIN_MOTOR_PRECISIONM1, OUTPUT);
    pinMode(PIN_MOTOR_PRECISIONM2, OUTPUT);
    pinMode(PIN_MOTOR_ENABLEN, OUTPUT);
    FStepCount = 0L;
    SetStateStep(ssIdle);
    SetStepResolution(srFull);
    SetStepDirection(sdPositive);
    Enable();
    FOpened = true;
    return true;
}
bool CStepperDRV8825::Close(void)
{
    FOpened = false;    
    FStepCount = 0L;
    SetStateStep(ssIdle);
    SetStepResolution(srFull);
    SetStepDirection(sdPositive);
    pinMode(PIN_MOTOR_STEP, INPUT);
    pinMode(PIN_MOTOR_DIRECTION, INPUT);
    pinMode(PIN_MOTOR_PRECISIONM0, INPUT);
    pinMode(PIN_MOTOR_PRECISIONM1, INPUT);
    pinMode(PIN_MOTOR_PRECISIONM2, INPUT);
    pinMode(PIN_MOTOR_ENABLEN, INPUT);
    return true;
}
//
bool CStepperDRV8825::Move(EStepDirection stepdirection,
                           EStepResolution stepresolution,
                           long unsigned stepperiod,
                           long unsigned stepcount)
{
    SetStepResolution(stepresolution);
    SetStepDirection(stepdirection);
    FMicrosPeriod = stepperiod;
    FStepCount = stepcount;
    if (0 < FStepCount)
    {
        FStepCount--;
        SetStateStep(ssStepHigh);
        FMicrosPreset = micros();
    }
    return true;
}
bool CStepperDRV8825::Halt(void)
{
    SetStateStep(ssIdle);
    return true;
}    
//
void CStepperDRV8825::Execute(void)
{
    switch (FStateStep)
    {
        case ssStepHigh:
            if ((FMicrosPeriod / 2) <= (micros() - FMicrosPreset))
            {
                FMicrosPreset = micros();
                SetStateStep(ssStepLow);
            }
            break;
        case ssStepLow:
            if ((FMicrosPeriod / 2) <= (micros() - FMicrosPreset))
            {
                if (0 < FStepCount)
                {
                    FStepCount--;
                    FMicrosPreset = micros();
                    SetStateStep(ssStepHigh);
                }
                else
                {
                    SetStateStep(ssIdle);
                }
            }
            break;
        default: // sdsIdle
          // nothing
          SetStateStep(ssIdle);
          break;
    }
}
//