diff --git a/DCCTimer.cpp b/DCCTimer.cpp index f1f3ba2..46cd2ea 100644 --- a/DCCTimer.cpp +++ b/DCCTimer.cpp @@ -25,6 +25,21 @@ * DCCEX works on a single timer interrupt at a regular 58uS interval. * The DCCWaveform class generates the signals to the motor shield * based on this timer. + * + * If the motor drivers are BOTH configured to use the correct 2 pins for the architecture, + * (see isPWMPin() function. ) + * then this allows us to use a hardware driven pin switching arrangement which is + * achieved by setting the duty cycle of the NEXT clock interrupt to 0% or 100% depending on + * the required pin state. (see setPWM()) + * This is more accurate than the software interrupt but at the expense of + * limiting the choice of available pins. + * Fortunately, a standard motor shield on a Mega uses pins that qualify for PWM... + * Other shields may be jumpered to PWM pins or run directly using the software interrupt. + * + * Because the PWM-based waveform is effectively set half a cycle after the software version, + * it is not acceptable to drive the two tracks on different methiods or it would cause + * problems for <1 JOIN> etc. + * */ #include "DCCTimer.h" @@ -35,13 +50,14 @@ INTERRUPT_CALLBACK interruptHandler=0; #ifdef ARDUINO_ARCH_MEGAAVR // Arduino unoWifi Rev2 and nanoEvery architectire + void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interruptHandler=callback; noInterrupts(); ADC0.CTRLC = (ADC0.CTRLC & 0b00110000) | 0b01000011; // speed up analogRead sample time TCB0.CTRLB = TCB_CNTMODE_INT_gc & ~TCB_CCMPEN_bm; // timer compare mode with output disabled TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc; // 8 MHz ~ 0.125 us - TCB0.CCMP = CLOCK_CYCLES -1; // 1 tick less for timer reset + TCB0.CCMP = CLOCK_CYCLES -1; // 1 tick less for timer reset TCB0.INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag TCB0.INTCTRL = TCB_CAPT_bm; // Enable the interrupt TCB0.CNT = 0; @@ -55,12 +71,29 @@ INTERRUPT_CALLBACK interruptHandler=0; interruptHandler(); } + bool DCCTimer::isPWMPin(byte pin) { + return false; // TODO what are the relevant pins? + } + + void DCCTimer::setPWM(byte pin, bool high) { + // TODO what are the relevant pins? + } + void DCCTimer::getSimulatedMacAddress(byte mac[6]) { - memcpy(mac,&SIGROW.SERNUM0,6); // serial number + memcpy(mac,(void *) &SIGROW.SERNUM0,6); // serial number } #else // Arduino nano, uno, mega etc +#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) + #define TIMER1_A_PIN 11 + #define TIMER1_B_PIN 12 + #define TIMER1_C_PIN 13 +#else + #define TIMER1_A_PIN 9 + #define TIMER1_B_PIN 10 +#endif + void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interruptHandler=callback; noInterrupts(); @@ -76,6 +109,34 @@ INTERRUPT_CALLBACK interruptHandler=0; // ISR called by timer interrupt every 58uS ISR(TIMER1_OVF_vect){ interruptHandler(); } +// Alternative pin manipulation via PWM control. + bool DCCTimer::isPWMPin(byte pin) { + return pin==TIMER1_A_PIN + || pin==TIMER1_B_PIN + #ifdef TIMER1_C_PIN + || pin==TIMER1_C_PIN + #endif + ; + } + + void DCCTimer::setPWM(byte pin, bool high) { + if (pin==TIMER1_A_PIN) { + TCCR1A |= _BV(COM1A1); + OCR1A= high?1024:0; + } + else if (pin==TIMER1_B_PIN) { + TCCR1A |= _BV(COM1B1); + OCR1B= high?1024:0; + } + #ifdef TIMER1_C_PIN + else if (pin==TIMER1_C_PIN) { + TCCR1A |= _BV(COM1C1); + OCR1C= high?1024:0; + } + #endif + } + + #include void DCCTimer::getSimulatedMacAddress(byte mac[6]) { for (byte i=0; i<6; i++) mac[i]=boot_signature_byte_get(0x0E + i); diff --git a/DCCTimer.h b/DCCTimer.h index 1d17789..dfe9ef2 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -8,6 +8,8 @@ class DCCTimer { public: static void begin(INTERRUPT_CALLBACK interrupt); static void getSimulatedMacAddress(byte mac[6]); + static bool isPWMPin(byte pin); + static void setPWM(byte pin, bool high); private: }; diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index e585b13..439717e 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -39,6 +39,9 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static mainTrack.setPowerMode(POWERMODE::OFF); progTrack.setPowerMode(POWERMODE::OFF); + MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable(); + if (MotorDriver::usePWM) DIAG(F("\nWaveform using PWM pins for accuracy.")); + else DIAG(F("\nWaveform accuracy limited by signal pin configuration.")); DCCTimer::begin(DCCWaveform::interruptHandler); } diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 96f43f3..6cc1d10 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -18,6 +18,7 @@ */ #include #include "MotorDriver.h" +#include "DCCTimer.h" #include "DIAG.h" #define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH @@ -25,6 +26,7 @@ #define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH) #define isLOW(fastpin) (!isHIGH(fastpin)) +bool MotorDriver::usePWM=false; MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin) { @@ -68,6 +70,11 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8 rawCurrentTripValue=(int)(trip_milliamps / sense_factor); } +bool MotorDriver::isPWMCapable() { + return (!dualSignal) && DCCTimer::isPWMPin(signalPin); +} + + void MotorDriver::setPower(bool on) { if (on) { // toggle brake before turning power on - resets overcurrent error @@ -94,17 +101,21 @@ void MotorDriver::setBrake(bool on) { } void MotorDriver::setSignal( bool high) { - if (high) { - setHIGH(fastSignalPin); - if (dualSignal) setLOW(fastSignalPin2); + if (usePWM) { + DCCTimer::setPWM(signalPin,high); } else { - setLOW(fastSignalPin); - if (dualSignal) setHIGH(fastSignalPin2); + if (high) { + setHIGH(fastSignalPin); + if (dualSignal) setLOW(fastSignalPin2); + } + else { + setLOW(fastSignalPin); + if (dualSignal) setHIGH(fastSignalPin2); + } } } - int MotorDriver::getCurrentRaw() { int current = analogRead(currentPin); if (faultPin != UNUSED_PIN && isLOW(fastFaultPin) && isHIGH(fastPowerPin)) diff --git a/MotorDriver.h b/MotorDriver.h index b8bef7d..2c6b230 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -44,7 +44,9 @@ class MotorDriver { inline int getRawCurrentTripValue() { return rawCurrentTripValue; } - + bool isPWMCapable(); + static bool usePWM; + private: void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result); void getFastPin(const FSH* type,int pin, FASTPIN & result) {