diff --git a/ATMEGA2560/Timer.h b/ATMEGA2560/Timer.h deleted file mode 100644 index 2568c9e..0000000 --- a/ATMEGA2560/Timer.h +++ /dev/null @@ -1,194 +0,0 @@ -#ifndef ATMEGA2560Timer_h -#define ATMEGA2560Timer_h - -#include "../VirtualTimer.h" -#include - -class Timer : public VirtualTimer { -private: - int pwmPeriod; - unsigned long timer_resolution; - unsigned char clockSelectBits; - int timer_num; - unsigned long lastMicroseconds; -public: -void (*isrCallback)(); - Timer(int timer_num) { - switch (timer_num) - { - case 1: - case 3: - case 4: - case 5: - timer_resolution = 65536; - break; - } - this->timer_num = timer_num; - lastMicroseconds = 0; - } - - void initialize() { - switch (timer_num) - { - case 1: - TCCR1B = _BV(WGM13) | _BV(WGM12); - TCCR1A = _BV(WGM11); - break; - case 3: - TCCR3B = _BV(WGM33) | _BV(WGM32); - TCCR3A = _BV(WGM31); - break; - case 4: - TCCR4B = _BV(WGM43) | _BV(WGM42); - TCCR4A = _BV(WGM41); - break; - case 5: - TCCR5B = _BV(WGM53) | _BV(WGM52); - TCCR5A = _BV(WGM51); - break; - } - } - - void setPeriod(unsigned long microseconds) { - if(microseconds == lastMicroseconds) - return; - lastMicroseconds = microseconds; - const unsigned long cycles = (F_CPU / 1000000) * microseconds; - if (cycles < timer_resolution) { - clockSelectBits = 1 << 0; - pwmPeriod = cycles; - } else - if (cycles < timer_resolution * 8) { - clockSelectBits = 1 << 1; - pwmPeriod = cycles / 8; - } else - if (cycles < timer_resolution * 64) { - clockSelectBits = (1 << 0) | (1 << 1); - pwmPeriod = cycles / 64; - } else - if (cycles < timer_resolution * 256) { - clockSelectBits = 1 << 2; - pwmPeriod = cycles / 256; - } else - if (cycles < timer_resolution * 1024) { - clockSelectBits = (1 << 2) | (1 << 0); - pwmPeriod = cycles / 1024; - } else { - clockSelectBits = (1 << 2) | (1 << 0); - pwmPeriod = timer_resolution - 1; - } - - switch (timer_num) - { - case 1: - ICR1 = pwmPeriod; - TCCR1B = _BV(WGM13) | _BV(WGM12) | clockSelectBits; - break; - case 3: - ICR3 = pwmPeriod; - TCCR3B = _BV(WGM33) | _BV(WGM32) | clockSelectBits; - break; - case 4: - ICR4 = pwmPeriod; - TCCR4B = _BV(WGM43) | _BV(WGM42) | clockSelectBits; - break; - case 5: - ICR5 = pwmPeriod; - TCCR5B = _BV(WGM53) | _BV(WGM52) | clockSelectBits; - break; - } - - } - void start() { - switch (timer_num) - { - case 1: - TCCR1B = 0; - TCNT1 = 0; // TODO: does this cause an undesired interrupt? - TCCR1B = _BV(WGM13) | _BV(WGM12) | clockSelectBits; - break; - case 3: - TCCR3B = 0; - TCNT3 = 0; // TODO: does this cause an undesired interrupt? - TCCR3B = _BV(WGM33) | _BV(WGM32) | clockSelectBits; - break; - case 4: - TCCR4B = 0; - TCNT4 = 0; // TODO: does this cause an undesired interrupt? - TCCR4B = _BV(WGM43) | _BV(WGM42) | clockSelectBits; - break; - case 5: - TCCR5B = 0; - TCNT5 = 0; // TODO: does this cause an undesired interrupt? - TCCR5B = _BV(WGM53) | _BV(WGM52) | clockSelectBits; - break; - } - - - } - void stop() { - switch (timer_num) - { - case 1: - TCCR1B = _BV(WGM13) | _BV(WGM12); - break; - case 3: - TCCR3B = _BV(WGM33) | _BV(WGM32); - break; - case 4: - TCCR4B = _BV(WGM43) | _BV(WGM42); - break; - case 5: - TCCR5B = _BV(WGM53) | _BV(WGM52); - break; - } - } - - void attachInterrupt(void (*isr)()) { - isrCallback = isr; - - switch (timer_num) - { - case 1: - TIMSK1 = _BV(TOIE1); - break; - case 3: - TIMSK3 = _BV(TOIE3); - break; - case 4: - TIMSK4 = _BV(TOIE4); - break; - case 5: - TIMSK5 = _BV(TOIE5); - break; - } - } - - void detachInterrupt() { - switch (timer_num) - { - case 1: - TIMSK1 = 0; - break; - case 3: - TIMSK3 = 0; - break; - case 4: - TIMSK4 = 0; - break; - case 5: - TIMSK5 = 0; - break; - } - } - -}; - -extern Timer TimerA; -extern Timer TimerB; -extern Timer TimerC; -extern Timer TimerD; - - - -#endif \ No newline at end of file diff --git a/ATMEGA328/Timer.h b/ATMEGA328/Timer.h deleted file mode 100644 index 6204953..0000000 --- a/ATMEGA328/Timer.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef ATMEGA328Timer_h -#define ATMEGA328Timer_h - -#include "../VirtualTimer.h" -#include - -class Timer : public VirtualTimer { -private: - int pwmPeriod; - unsigned long timer_resolution; - unsigned char clockSelectBits; - int timer_num; - unsigned long lastMicroseconds; -public: -void (*isrCallback)(); - Timer(int timer_num) { - switch (timer_num) - { - //case 0: - case 2: - timer_resolution = 256; - break; - case 1: - timer_resolution = 65536; - break; - } - this->timer_num = timer_num; - lastMicroseconds = 0; - } - - void initialize() { - switch (timer_num) - { - // case 0: - // TCCR0B = _BV(WGM02); - // TCCR0A = _BV(WGM00) | _BV(WGM01); - // break; - case 1: - TCCR1B = _BV(WGM13) | _BV(WGM12); - TCCR1A = _BV(WGM11); - break; - case 2: - TCCR2B = _BV(WGM22); - TCCR2A = _BV(WGM20) | _BV(WGM21); - break; - } - } - - void setPeriod(unsigned long microseconds) { - if(microseconds == lastMicroseconds) - return; - lastMicroseconds = microseconds; - const unsigned long cycles = (F_CPU / 1000000) * microseconds; - - switch(timer_num) { - case 2: - if (cycles < timer_resolution) { - clockSelectBits = 1 << 0; - pwmPeriod = cycles; - } else - if (cycles < timer_resolution * 8) { - clockSelectBits = 1 << 1; - pwmPeriod = cycles / 8; - } else - if (cycles < timer_resolution * 32) { - clockSelectBits = 1 << 0 | 1 << 1; - pwmPeriod = cycles / 32; - } else - if (cycles < timer_resolution * 64) { - clockSelectBits = 1 << 2; - pwmPeriod = cycles / 64; - } else - if (cycles < timer_resolution * 128) { - clockSelectBits = 1 << 2 | 1 << 0; - pwmPeriod = cycles / 128; - } else - if (cycles < timer_resolution * 256) { - clockSelectBits = 1 << 2 | 1 << 1; - pwmPeriod = cycles / 256; - } else - if (cycles < timer_resolution * 1024) { - clockSelectBits = 1 << 2 | 1 << 1 | 1 << 0; - pwmPeriod = cycles / 1024; - } else { - clockSelectBits = 1 << 2 | 1 << 1 | 1 << 0; - pwmPeriod = timer_resolution - 1; - } - break; - //case 0: - case 1: - if (cycles < timer_resolution) { - clockSelectBits = 1 << 0; - pwmPeriod = cycles; - } else - if (cycles < timer_resolution * 8) { - clockSelectBits = 1 << 1; - pwmPeriod = cycles / 8; - } else - if (cycles < timer_resolution * 64) { - clockSelectBits = (1 << 0) | (1 << 1); - pwmPeriod = cycles / 64; - } else - if (cycles < timer_resolution * 256) { - clockSelectBits = 1 << 2; - pwmPeriod = cycles / 256; - } else - if (cycles < timer_resolution * 1024) { - clockSelectBits = (1 << 2) | (1 << 0); - pwmPeriod = cycles / 1024; - } else { - clockSelectBits = (1 << 2) | (1 << 0); - pwmPeriod = timer_resolution - 1; - } - break; - } - - switch (timer_num) - { - // case 0: - // OCR0A = pwmPeriod; - // TCCR0B = _BV(WGM02) | clockSelectBits; - // break; - case 1: - ICR1 = pwmPeriod; - TCCR1B = _BV(WGM13) | _BV(WGM12) | clockSelectBits; - break; - case 2: - OCR2A = pwmPeriod; - TCCR2B = _BV(WGM22) | clockSelectBits; - break; - } - - } - void start() { - switch (timer_num) - { - // case 0: - // TCCR0B = 0; - // TCNT0 = 0; // TODO: does this cause an undesired interrupt? - // TCCR0B = _BV(WGM02) | clockSelectBits; - // break; - case 1: - TCCR1B = 0; - TCNT1 = 0; // TODO: does this cause an undesired interrupt? - TCCR1B = _BV(WGM13) | _BV(WGM12) | clockSelectBits; - break; - case 2: - TCCR2B = 0; - TCNT2 = 0; // TODO: does this cause an undesired interrupt? - TCCR2B = _BV(WGM22) | clockSelectBits; - break; - } - - - } - void stop() { - switch (timer_num) - { - // case 0: - // TCCR0B = _BV(WGM02); - // break; - case 1: - TCCR1B = _BV(WGM13) | _BV(WGM12); - break; - case 2: - TCCR2B = _BV(WGM22); - break; - } - } - - void attachInterrupt(void (*isr)()) { - isrCallback = isr; - - switch (timer_num) - { - // case 0: - // TIMSK0 = _BV(TOIE0); - // break; - case 1: - TIMSK1 = _BV(TOIE1); - break; - case 2: - TIMSK2 = _BV(TOIE2); - break; - } - } - - void detachInterrupt() { - switch (timer_num) - { - // case 0: - // TIMSK0 = 0; - // break; - case 1: - TIMSK1 = 0; - break; - case 2: - TIMSK2 = 0; - break; - } - } - -}; - -extern Timer TimerA; -extern Timer TimerB; - -#endif \ No newline at end of file diff --git a/ArduinoTimers.h b/ArduinoTimers.h deleted file mode 100644 index c0acd30..0000000 --- a/ArduinoTimers.h +++ /dev/null @@ -1,18 +0,0 @@ -// This file is copied from https://github.com/davidcutting42/ArduinoTimers -// All Credit and copyright David Cutting -// The files included below come from the same source. -// This library had been included with the DCC code to avoid issues with -// library management for inexperienced users. "It just works (TM)" - -#ifndef ArduinoTimers_h -#define ArduinoTimers_h - -#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) - #include "ATMEGA2560/Timer.h" -#elif defined(ARDUINO_AVR_UNO) - #include "ATMEGA328/Timer.h" -#else - #error "Cannot compile - ArduinoTimers library does not support your board, or you are missing compatible build flags." -#endif - -#endif diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index c0c3a1d..4906498 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -49,9 +49,7 @@ void setup() // STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h - // Optionally a Timer number (1..4) may be passed to DCC::begin to override the default Timer1 used for the - // waveform generation. e.g. DCC::begin(STANDARD_MOTOR_SHIELD,2); to use timer 2 - + DCC::begin(MOTOR_SHIELD_TYPE); #if defined(RMFT_ACTIVE) diff --git a/DCC.cpp b/DCC.cpp index f192ef5..f426bb0 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -45,7 +45,7 @@ const byte FN_GROUP_5=0x10; __FlashStringHelper* DCC::shieldName=NULL; -void DCC::begin(const __FlashStringHelper* motorShieldName, MotorDriver * mainDriver, MotorDriver* progDriver, byte timerNumber) { +void DCC::begin(const __FlashStringHelper* motorShieldName, MotorDriver * mainDriver, MotorDriver* progDriver) { shieldName=(__FlashStringHelper*)motorShieldName; DIAG(F("\n"), F(VERSION), F(ARDUINO_TYPE), shieldName, F(GITHUB_SHA)); @@ -53,7 +53,7 @@ void DCC::begin(const __FlashStringHelper* motorShieldName, MotorDriver * mainDr (void)EEPROM; // tell compiler not to warn this is unused EEStore::init(); - DCCWaveform::begin(mainDriver,progDriver, timerNumber); + DCCWaveform::begin(mainDriver,progDriver); } void DCC::setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection) { @@ -481,7 +481,6 @@ void DCC::getLocoId(ACK_CALLBACK callback, bool blocking) { void DCC::setLocoId(int id,ACK_CALLBACK callback, bool blocking) { if (id<=0 || id>9999) callback(-1); - int wordval; if (id<=127) ackManagerSetup(id,SHORT_LOCO_ID_PROG, callback, blocking); else ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking); } diff --git a/DCC.h b/DCC.h index d8a1b6b..162cbfe 100644 --- a/DCC.h +++ b/DCC.h @@ -64,7 +64,7 @@ const byte MAX_LOCOS = 50; class DCC { public: - static void begin(const __FlashStringHelper *motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver, byte timerNumber = 1); + static void begin(const __FlashStringHelper *motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver); static void loop(); // Public DCC API functions diff --git a/DCCTimer.cpp b/DCCTimer.cpp new file mode 100644 index 0000000..818937a --- /dev/null +++ b/DCCTimer.cpp @@ -0,0 +1,54 @@ +/* + * © 2021, Chris Harlow & David Cutting. 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 . + */ + + +/* This timer class is used to manage the single timer required to handle the DCC waveform. + * All timer access comes through this class so that it can be compiled for + * various hardware CPU types. + * + * 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. + */ + +#include "DCCTimer.h" + +const int DCC_SIGNAL_TIME=58; // this is the 58uS DCC 1-bit waveform half-cycle +const int DCC_SLOW_TIME=58*512; // for command diagnostics + +INTERRUPT_CALLBACK interruptHandler=0; + + +void DCCTimer::begin(INTERRUPT_CALLBACK callback, bool slow) { + interruptHandler=callback; + // Initialise timer1 to trigger every 58us (DCC_SIGNAL_TIME) + noInterrupts(); + TCCR1A = 0; + ICR1 = ((F_CPU / 1000000) * (slow? DCC_SLOW_TIME : DCC_SIGNAL_TIME)) >>1; + TCNT1 = 0; + TCCR1B = _BV(WGM13) | _BV(CS10); // Mode 8, clock select 1 + TIMSK1 = _BV(TOIE1); // Enable Software interrupt + interrupts(); +} + +// Timer interrupt every 58uS +ISR(TIMER1_OVF_vect) +{ + if (interruptHandler) interruptHandler(); +} diff --git a/DCCTimer.h b/DCCTimer.h new file mode 100644 index 0000000..7f99acc --- /dev/null +++ b/DCCTimer.h @@ -0,0 +1,13 @@ +#ifndef DCCTimer_h +#define DCCTimer_h +#include "Arduino.h" + +typedef void (*INTERRUPT_CALLBACK)(); + +class DCCTimer { + public: + static void begin(INTERRUPT_CALLBACK interrupt, bool slow=false); + private: +}; + +#endif diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 9eb7022..90db3ee 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -20,10 +20,9 @@ #include #include "DCCWaveform.h" +#include "DCCTimer.h" #include "DIAG.h" -const int NORMAL_SIGNAL_TIME=58; // this is the 58uS DCC 1-bit waveform half-cycle -const int SLOW_SIGNAL_TIME=NORMAL_SIGNAL_TIME*512; DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true); DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false); @@ -31,32 +30,18 @@ DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false); bool DCCWaveform::progTrackSyncMain=false; bool DCCWaveform::progTrackBoosted=false; -VirtualTimer * DCCWaveform::interruptTimer=NULL; -void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte timerNumber) { +void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { mainTrack.motorDriver=mainDriver; progTrack.motorDriver=progDriver; mainTrack.setPowerMode(POWERMODE::OFF); progTrack.setPowerMode(POWERMODE::OFF); - switch (timerNumber) { - case 1: interruptTimer= &TimerA; break; - case 2: interruptTimer= &TimerB; break; -#ifndef ARDUINO_AVR_UNO - case 3: interruptTimer= &TimerC; break; -#endif - default: - DIAG(F("\n\n *** Invalid Timer number %d requested. Only 1..3 valid. DCC will not work.*** \n\n"), timerNumber); - return; - } - interruptTimer->initialize(); - interruptTimer->setPeriod(NORMAL_SIGNAL_TIME); // this is the 58uS DCC 1-bit waveform half-cycle - interruptTimer->attachInterrupt(interruptHandler); - interruptTimer->start(); + DCCTimer::begin(DCCWaveform::interruptHandler); } -void DCCWaveform::setDiagnosticSlowWave(bool slow) { - interruptTimer->setPeriod(slow? SLOW_SIGNAL_TIME : NORMAL_SIGNAL_TIME); - interruptTimer->start(); + +void DCCWaveform::setDiagnosticSlowWave(bool slow) { + DCCTimer::begin(DCCWaveform::interruptHandler, slow); DIAG(F("\nDCC SLOW WAVE %S\n"),slow?F("SET. DO NOT ADD LOCOS TO TRACK"):F("RESET")); } @@ -65,8 +50,6 @@ void DCCWaveform::loop() { progTrack.checkPowerOverload(); } - -// static // void DCCWaveform::interruptHandler() { // call the timer edge sensitive actions for progtrack and maintrack bool mainCall2 = mainTrack.interrupt1(); @@ -112,10 +95,6 @@ POWERMODE DCCWaveform::getPowerMode() { } void DCCWaveform::setPowerMode(POWERMODE mode) { - - // Prevent power switch on with no timer... Otheruise track will get full power DC and locos will run away. - if (!interruptTimer) return; - powerMode = mode; bool ison = (mode == POWERMODE::ON); motorDriver->setPower( ison); @@ -168,10 +147,6 @@ void DCCWaveform::checkPowerOverload() { } } - - - - // process time-edge sensitive part of interrupt // return true if second level required bool DCCWaveform::interrupt1() { diff --git a/DCCWaveform.h b/DCCWaveform.h index 7908e02..e3c05fd 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -20,7 +20,6 @@ #ifndef DCCWaveform_h #define DCCWaveform_h #include "MotorDriver.h" -#include "ArduinoTimers.h" // Wait times for power management. Unit: milliseconds const int POWER_SAMPLE_ON_WAIT = 100; @@ -46,7 +45,7 @@ const byte resetPacket[] = {0x00, 0x00, 0x00}; class DCCWaveform { public: DCCWaveform( byte preambleBits, bool isMain); - static void begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte timerNumber); + static void begin(MotorDriver * mainDriver, MotorDriver * progDriver); static void setDiagnosticSlowWave(bool slow); static void loop(); static DCCWaveform mainTrack; @@ -105,7 +104,7 @@ class DCCWaveform { } private: - static VirtualTimer * interruptTimer; + static void interruptHandler(); bool interrupt1(); void interrupt2(); diff --git a/Timer.cpp b/Timer.cpp deleted file mode 100644 index 10673ec..0000000 --- a/Timer.cpp +++ /dev/null @@ -1,52 +0,0 @@ -// This file is copied from https://github.com/davidcutting42/ArduinoTimers -// All Credit to David Cutting - -#include - -#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) - -#include "ATMEGA2560/Timer.h" - -Timer TimerA(1); -Timer TimerB(3); -Timer TimerC(4); -Timer TimerD(5); - -ISR(TIMER1_OVF_vect) -{ - TimerA.isrCallback(); -} - -ISR(TIMER3_OVF_vect) -{ - TimerB.isrCallback(); -} - -ISR(TIMER4_OVF_vect) -{ - TimerC.isrCallback(); -} - -ISR(TIMER5_OVF_vect) -{ - TimerD.isrCallback(); -} - -#elif defined(ARDUINO_AVR_UNO) // Todo: add other 328 boards for compatibility - -#include "ATMEGA328/Timer.h" - -Timer TimerA(1); -Timer TimerB(2); - -ISR(TIMER1_OVF_vect) -{ - TimerA.isrCallback(); -} - -ISR(TIMER2_OVF_vect) -{ - TimerB.isrCallback(); -} - -#endif diff --git a/VirtualTimer.h b/VirtualTimer.h deleted file mode 100644 index 5e3832f..0000000 --- a/VirtualTimer.h +++ /dev/null @@ -1,21 +0,0 @@ -// This file is copied from https://github.com/davidcutting42/ArduinoTimers -// All Credit to David Cutting - -#ifndef VirtualTimer_h -#define VirtualTimer_h - -class VirtualTimer -{ -public: - virtual void initialize() = 0; - virtual void setPeriod(unsigned long microseconds) = 0; - virtual void start() = 0; - virtual void stop() = 0; - - virtual void attachInterrupt(void (*isr)()) = 0; - virtual void detachInterrupt() = 0; -private: - -}; - -#endif