diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 20c5205..aba716b 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -20,19 +20,26 @@ #include "Hardware.h" #include "DCCWaveform.h" #include "DIAG.h" +#include // use IDE menu Tools..Manage Libraries to locate and install TimerOne -DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true, (int)(MAIN_MAX_MILLIAMPS / MAIN_SENSE_FACTOR)); -DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false, (int)(PROG_MAX_MILLIAMPS / PROG_SENSE_FACTOR)); + +DCCWaveform DCCWaveform::mainTrack=NULL; +DCCWaveform DCCWaveform::progTrack=NULL; const int ACK_MIN_PULSE_RAW=65 / PROG_SENSE_FACTOR; bool DCCWaveform::progTrackSyncMain=false; -void DCCWaveform::begin() { - Hardware::init(); - Hardware::setCallback(58, interruptHandler); - mainTrack.beginTrack(); - progTrack.beginTrack(); +void DCCWaveform::begin(MotorDriver mainDriver, MotorDriver progDriver) { + + mainTrack=new DCCWaveform(PREAMBLE_BITS_MAIN, true, mainDriver); + progTrack=new DCCWaveform(PREAMBLE_BITS_PROG, false, progDriver); + progTrack.beginTrack(progDriver); + TimerA.initialize(); + TimerA.setPeriod(58); + TimerA.attachInterrupt(interruptHandler); + TimerA.start(); + } void DCCWaveform::loop() { @@ -66,8 +73,9 @@ void DCCWaveform::interruptHandler() { const byte bitMask[] = {0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; -DCCWaveform::DCCWaveform( byte preambleBits, bool isMain, int rawCurrentTrip) { +DCCWaveform::DCCWaveform( byte preambleBits, bool isMain, MotorDriver driver) { // establish appropriate pins + motorDriver=driver; rawCurrentTripValue=rawCurrentTrip; isMainTrack = isMain; packetPending = false; @@ -80,11 +88,8 @@ DCCWaveform::DCCWaveform( byte preambleBits, bool isMain, int rawCurrentTrip) { bits_sent = 0; sampleDelay = 0; lastSampleTaken = millis(); - ackPending=false; -} -void DCCWaveform::beginTrack() { - setPowerMode(POWERMODE::ON); - + ackPending=false; + setPowerMode(POWERMODE::ON); } POWERMODE DCCWaveform::getPowerMode() { @@ -94,8 +99,7 @@ POWERMODE DCCWaveform::getPowerMode() { void DCCWaveform::setPowerMode(POWERMODE mode) { powerMode = mode; bool ison = (mode == POWERMODE::ON); - Hardware::setPower(isMainTrack, ison); - Hardware::setBrake(isMainTrack, !ison); + driver.setPower( ison); if (mode == POWERMODE::ON) delay(200); } @@ -104,7 +108,7 @@ void DCCWaveform::checkPowerOverload() { if (millis() - lastSampleTaken < sampleDelay) return; lastSampleTaken = millis(); - int tripValue= rawCurrentTripValue; + int tripValue= driver.rawCurrentTripValue; if (!isMainTrack && (ackPending || progTrackSyncMain)) tripValue=ACK_CURRENT_TRIP; switch (powerMode) { @@ -113,7 +117,7 @@ void DCCWaveform::checkPowerOverload() { break; case POWERMODE::ON: // Check current - lastCurrent = Hardware::getCurrentRaw(isMainTrack); + lastCurrent = driver.getCurrentRaw(); if (lastCurrent <= tripValue) { sampleDelay = POWER_SAMPLE_ON_WAIT; if(power_good_counter<100) @@ -122,8 +126,8 @@ void DCCWaveform::checkPowerOverload() { if (power_sample_overload_wait>POWER_SAMPLE_OVERLOAD_WAIT) power_sample_overload_wait=POWER_SAMPLE_OVERLOAD_WAIT; } else { setPowerMode(POWERMODE::OVERLOAD); - unsigned int mA=Hardware::getCurrentMilliamps(isMainTrack,lastCurrent); - unsigned int maxmA=Hardware::getCurrentMilliamps(isMainTrack,tripValue); + unsigned int mA=driver.convertRawToMilliamps(lastCurrent); + unsigned int maxmA=driver.convertRawToMilliamps(tripValue); DIAG(F("\n*** %S TRACK POWER OVERLOAD current=%d max=%d offtime=%l ***\n"), isMainTrack ? F("MAIN") : F("PROG"), mA, maxmA, power_sample_overload_wait); power_good_counter=0; sampleDelay = power_sample_overload_wait; @@ -183,10 +187,11 @@ void DCCWaveform::setSignal(bool high) { if (progTrackSyncMain) { if (!isMainTrack) return; // ignore PROG track waveform while in sync // set both tracks to same signal - Hardware::setSyncSignal(high); + driver.setSyncSignal(high); + progTrack.driver.setSignal(high); return; } - Hardware::setSignal(isMainTrack,high); + driver.setSignal(high); } void DCCWaveform::interrupt2() { @@ -264,8 +269,8 @@ int DCCWaveform::getLastCurrent() { void DCCWaveform::setAckBaseline(bool debug) { if (isMainTrack) return; - ackThreshold=Hardware::getCurrentRaw(false) + ACK_MIN_PULSE_RAW; - if (debug) DIAG(F("\nACK-BASELINE %d/%dmA"),ackThreshold,Hardware::getCurrentMilliamps(false,ackThreshold)); + ackThreshold=driver.getCurrentRaw() + ACK_MIN_PULSE_RAW; + if (debug) DIAG(F("\nACK-BASELINE %d/%dmA"),ackThreshold,driver.convertRawToMilliamps(ackThreshold)); } void DCCWaveform::setAckPending(bool debug) { diff --git a/MotorDriver.cpp b/MotorDriver.cpp new file mode 100644 index 0000000..d521248 --- /dev/null +++ b/MotorDriver.cpp @@ -0,0 +1,77 @@ +/* + * © 2020, Chris Harlow. All rights reserved. + * + * This file is part of Asbelos DCC API + * + * This 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. + * + * It 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 CommandStation. If not, see . + */ +#include +#include "MotorDriver.h" +#include "AnalogReadFast.h" +#include "DIAG.h" + + +#if defined(ARDUINO_ARCH_AVR) + #include // use IDE menu Tools..Manage Libraries to locate and install DIO2 + #define WritePin digitalWrite2 + #define ReadPin digitalRead2 +#else + #define WritePin digitalWrite + #define ReadPin digitalRead +#endif + +MotorDriver::MotorDriver(byte power_pin, int signal_pin, int signal_pin2, int brake_pin, int current_pin, float sense_factor, int fault_pin) { + powerPin=power_pin; + signalPin=signal_pin; + signalPin2=signal_pin2; + brakePin=brake_pin; + currentPin=current_pin; + senseFactor=sense_factor; + faultPin=fault_pin; + I32=(int) (32000 / sensefactor); + + pinMode(powerPin, OUTPUT); + pinMode(brakePin, OUTPUT); + pinMode(signalPin, OUTPUT); + if (signalPin2 != UNUSED_PIN) pinMode(signalPin2, OUTPUT); + pinMode(currentPin, INPUT); + if (faultPin != UNUSED_PIN) pinMode(faultPin, INPUT); +} + +void MotorDriver::setPower(bool on) { + WritePin(powerPin, on ? HIGH : LOW); +} +void MotorDriver::setBrake( bool on) { + WritePin(brakePin, on ? HIGH : LOW); +} + +void MotorDriver::setSignal( bool high) { + WritePin(signalPin, high ? HIGH : LOW); + if (signalPin2 != UNUSED_PIN) WritePin(signalPin2, high ? LOW : HIGH); +} + + +int MotorDriver::getCurrentRaw() { + if (faultPin != UNUSED_PIN && ReadPin(faultPin) == LOW && ReadPin(powerPin) == HIGH) + return I32: + + // IMPORTANT: This function can be called in Interrupt() time within the 56uS timer + // The default analogRead takes ~100uS which is catastrphic + // so analogReadFast is used here. (-2uS) + return analogReadFast(sensePin); +} + +unsigned int MortorDriver::convertRawToMilliamps( int raw) { + return (unsigned int)(raw * senseFactor); +} diff --git a/MotorDriver.h b/MotorDriver.h new file mode 100644 index 0000000..6fc6261 --- /dev/null +++ b/MotorDriver.h @@ -0,0 +1,35 @@ +/* + * © 2020, Chris Harlow. All rights reserved. + * + * This file is part of Asbelos DCC API + * + * This 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. + * + * It 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 CommandStation. If not, see . + */ +#ifndef MotorDriver_h +#define MotorDriver_h +// Virtualised Motor shield 1-track hardware Interface +class MotorDriver { + public: + MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte brake_pin, byte current_pin, float senseFactor, byte faultPin); + void setPower( bool on); + void setSignal( bool high); + void setBrake( bool on); + int getCurrentRaw(); + unsigned int convertToMilliamps( int rawValue); + private: + byte powerPin, signalPin, signalPin2, brakePin,currentPin,faultPin; + float senseFactor; + +}; +#endif