From 3dede9eabec96d00e48418b580f193ef245740d1 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 21 Jan 2021 23:06:47 +0000 Subject: [PATCH 1/8] Linear address cmd --- DCCEXParser.cpp | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 3ecdb61..d7f45e9 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -306,12 +306,33 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking) return; break; - case 'a': // ACCESSORY - if (p[2] != (p[2] & 1)) - return; - DCC::setAccessory(p[0], p[1], p[2] == 1); + case 'a': // ACCESSORY or + { + int address; + byte subaddress; + byte activep; + if (params==2) { // + address=(p[0] - 1) / 4 + 1; + subaddress=(p[0] - 1) % 4; + activep=1; + } + else if (params==3) { // + address=p[0]; + subaddress=p[1]; + activep=2; + } + else break; // invalid no of parameters + + if ( + ((address & 0x01FF) != address) // invalid address (limit 9 bits ) + || ((subaddress & 0x03) != subaddress) // invalid subaddress (limit 2 bits ) + || ((p[activep] & 0x01) != p[activep]) // invalid activate 0|1 + ) break; + + DCC::setAccessory(address, subaddress,p[activep]==1); + } return; - + case 'T': // TURNOUT if (parseT(stream, params, p)) return; From 9399aca63dde1ee7337b8f706f96ae263a084fad Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 21 Jan 2021 23:13:08 +0000 Subject: [PATCH 2/8] Allow lower case keywords --- DCCEXParser.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index d7f45e9..8fd0561 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -143,6 +143,7 @@ int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd) runningValue = 10 * runningValue + (hot - '0'); break; } + if (hot >= 'a' && hot <= 'z') hot=hot-'a'+'A'; // uppercase a..z if (hot >= 'A' && hot <= 'Z') { // Since JMRI got modified to send keywords in some rare cases, we need this From 032b36ab45092a346db985c0122146c3d4819808 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 23 Jan 2021 22:07:28 +0100 Subject: [PATCH 3/8] Add the F define to be on safe side if it is not present in the library core code --- FSH.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/FSH.h b/FSH.h index 089216a..fe11938 100644 --- a/FSH.h +++ b/FSH.h @@ -2,6 +2,10 @@ #define FSH_h #include #if defined(ARDUINO_ARCH_MEGAAVR) +#ifdef F +#undef F +#define F(str) (str) +#endif typedef char FSH; #define GETFLASH(addr) (*(const unsigned char *)(addr)) #define FLASH From 8a9feaef22e6d161c20c7ea57a1a11804502a47f Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 25 Jan 2021 15:26:39 +0000 Subject: [PATCH 4/8] Clean simple Timer interface Removes overkill files, puts all timer in a single small file. (DCCTimer) --- ATMEGA2560/Timer.h | 194 --------------------------------------- ATMEGA328/Timer.h | 208 ------------------------------------------ ArduinoTimers.h | 18 ---- CommandStation-EX.ino | 4 +- DCC.cpp | 5 +- DCC.h | 2 +- DCCTimer.cpp | 54 +++++++++++ DCCTimer.h | 13 +++ DCCWaveform.cpp | 37 ++------ DCCWaveform.h | 5 +- Timer.cpp | 52 ----------- VirtualTimer.h | 21 ----- 12 files changed, 79 insertions(+), 534 deletions(-) delete mode 100644 ATMEGA2560/Timer.h delete mode 100644 ATMEGA328/Timer.h delete mode 100644 ArduinoTimers.h create mode 100644 DCCTimer.cpp create mode 100644 DCCTimer.h delete mode 100644 Timer.cpp delete mode 100644 VirtualTimer.h 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 From cbb039c02f84f25f5b751a8e841f393badb83867 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 25 Jan 2021 20:20:41 +0000 Subject: [PATCH 5/8] Timer port --- DCCTimer.cpp | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/DCCTimer.cpp b/DCCTimer.cpp index 818937a..94fdfba 100644 --- a/DCCTimer.cpp +++ b/DCCTimer.cpp @@ -38,17 +38,36 @@ INTERRUPT_CALLBACK interruptHandler=0; void DCCTimer::begin(INTERRUPT_CALLBACK callback, bool slow) { interruptHandler=callback; // Initialise timer1 to trigger every 58us (DCC_SIGNAL_TIME) + long clockCycles=((F_CPU / 1000000) * (slow? DCC_SLOW_TIME : DCC_SIGNAL_TIME)) >>1; 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 + +#ifdef ARDUINO_ARCH_MEGAAVR + // Arduino unoWifi Rev2 and nanoEvery architectire + TCB0.CCMP = clockCycles; + TCB0.INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag + TCB0.INTCTRL = TCB_CAPT_bm; // Enable the interrupt + TCB0.CNT = 0; + TCB0.CTRLA |= TCB_ENABLE_bm; // start + #define ISR_NAME TCB2_INT_vect + +#else + // Arduino nano, uno, mega + TCCR1A = 0; + ICR1 = clockCycles; + TCNT1 = 0; + TCCR1B = _BV(WGM13) | _BV(CS10); // Mode 8, clock select 1 + TIMSK1 = _BV(TOIE1); // Enable Software interrupt + #define ISR_NAME TIMER1_OVF_vect +#endif + interrupts(); } -// Timer interrupt every 58uS -ISR(TIMER1_OVF_vect) +// ISR called by timer interrupt every 58uS +ISR(ISR_NAME) { +#ifdef ARDUINO_ARCH_MEGAAVR + TCB0.INTFLAGS = TCB_CAPT_bm; +#endif if (interruptHandler) interruptHandler(); } From a4b63013bafefcfc32ed92d581f6b9ade4b0d4d7 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 26 Jan 2021 09:04:09 +0000 Subject: [PATCH 6/8] Almost... --- DCCTimer.cpp | 12 +- EveryTimerB.cpp | 14 -- EveryTimerB.h | 390 ------------------------------------------------ MegaAvr20Mhz.h | 126 ---------------- 4 files changed, 8 insertions(+), 534 deletions(-) delete mode 100644 EveryTimerB.cpp delete mode 100644 EveryTimerB.h delete mode 100644 MegaAvr20Mhz.h diff --git a/DCCTimer.cpp b/DCCTimer.cpp index 94fdfba..15409ae 100644 --- a/DCCTimer.cpp +++ b/DCCTimer.cpp @@ -28,7 +28,7 @@ */ #include "DCCTimer.h" - +#include "DIAG.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 @@ -38,20 +38,24 @@ INTERRUPT_CALLBACK interruptHandler=0; void DCCTimer::begin(INTERRUPT_CALLBACK callback, bool slow) { interruptHandler=callback; // Initialise timer1 to trigger every 58us (DCC_SIGNAL_TIME) - long clockCycles=((F_CPU / 1000000) * (slow? DCC_SLOW_TIME : DCC_SIGNAL_TIME)) >>1; noInterrupts(); #ifdef ARDUINO_ARCH_MEGAAVR // Arduino unoWifi Rev2 and nanoEvery architectire + long clockCycles=slow? (14*512) : 14; // guesswork!!!! + DIAG(F("\nTimer unoWifi/nanoEvery F_CPU=%l c=%d"),F_CPU,clockCycles); TCB0.CCMP = clockCycles; TCB0.INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag TCB0.INTCTRL = TCB_CAPT_bm; // Enable the interrupt TCB0.CNT = 0; TCB0.CTRLA |= TCB_ENABLE_bm; // start - #define ISR_NAME TCB2_INT_vect + #define ISR_NAME TCB0_INT_vect #else - // Arduino nano, uno, mega + + // Arduino nano, uno, mega + long clockCycles=((F_CPU / 1000000) * (slow? DCC_SLOW_TIME : DCC_SIGNAL_TIME)) >>1; + DIAG(F("\nTimer nano/uno/mega F_CPU=%l c=%d"),F_CPU,clockCycles); TCCR1A = 0; ICR1 = clockCycles; TCNT1 = 0; diff --git a/EveryTimerB.cpp b/EveryTimerB.cpp deleted file mode 100644 index 388d090..0000000 --- a/EveryTimerB.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifdef ARDUINO_ARCH_MEGAAVR -#include "EveryTimerB.h" - -void EveryTimerB::isrDefaultUnused(void) {} - -// code timer B2. For B0 and B1 copy this code and change the '2' to '0' and '1' -EveryTimerB TimerB2; -ISR(TCB2_INT_vect) -{ - TimerB2.next_tick(); - TCB2.INTFLAGS = TCB_CAPT_bm; -} - -#endif // ARDUINO_ARCH_MEGAAVR diff --git a/EveryTimerB.h b/EveryTimerB.h deleted file mode 100644 index 4c371a9..0000000 --- a/EveryTimerB.h +++ /dev/null @@ -1,390 +0,0 @@ -// EveryTimerB library. -// by Kees van der Oord Kees.van.der.Oord@inter.nl.net - -// Timer library for the TCB timer of the AtMega4809 processor. -// tested on the Arduino Nano Every (AtMega4809) and the Arduino 1.8.12 IDE -// support for the Every is the 'Arduino MegaAVR' boards module (Tools | Board | Boards Manager) - -// usage: -/* -#ifdef ARDUINO_ARCH_MEGAAVR -#include "EveryTimerB.h" -#define Timer1 TimerB2 // use TimerB2 as a drop in replacement for Timer1 -#else // assume architecture supported by TimerOne library .... -#include "TimerOne.h" -#endif - -// code below will now work both on the MegaAVR and AVR processors - -void setup() { - Timer1.initialize(); - Timer1.attachInterrupt(myisr); - Timer1.setPeriod(1000000UL); // like the TimerOne library this will start the timer as well -} - -void myisr() { - // do something useful every second -} -*/ -// clock source options: -// The TCB clock source is specified in the initialize() function with default value EveryTimerB_CLOCMODE. -// define this macro before including this file to use a different default clock mode -// e.g.: -// #define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKTCA_gc // 250 kHz ~ 4 us -// #define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKDIV2_gc // 8 MHz ~ 0.125 us -// #define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKDIV_gc // 16 MHz ~ 0.0625 us - -// timer options -// The 4809 has one A timer (TCA) and four B timers (TCB). -// TCA and TCB3 are used by the arduino core to generate the clock used by millis() and micros(). -// TCB0 generates the PWM timing for pin D6, TCB1 for pin D3. -// By default Timer Control B2 is defined as TimerB2 in the EveryTimerB library. -// If you would like to use the TCB0 and TCB1 as well you have to copy the code -// from the EveryTimerB.cpp into your product file and adapt for B0 and B1 timers. -// -// for information on the 4809 TCA and TCB timers: -// http://ww1.microchip.com/downloads/en/AppNotes/TB3217-Getting-Started-with-TCA-90003217A.pdf -// http://ww1.microchip.com/downloads/en/Appnotes/TB3214-Getting-Started-with-TCB-90003214A.pdf -// %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5\cores\arduino\wiring.c -// %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5\variants\nona4809\variant.c -// %LOCALAPPDATA%\Arduino15\packages\arduino\tools\avr-gcc\7.3.0-atmel3.6.1-arduino5\avr\include\avr\iom4809.h - -// 20 MHz system clock -// to run the Every at 20 MHz, add the lines below to the nona4809 section of the boards.txt file -// in %LOCALAPPDATA%\Arduino15\packages\arduino\hardware\megaavr\1.8.5. -// they add the sub menu 'Tools | Clock' to choose between 16MHz and 20MHz. -/* -menu.clock=Clock -nona4809.menu.clock.16internal=16MHz -nona4809.menu.clock.16internal.build.f_cpu=16000000L -nona4809.menu.clock.16internal.bootloader.OSCCFG=0x01 -nona4809.menu.clock.20internal=20MHz -nona4809.menu.clock.20internal.build.f_cpu=20000000L -nona4809.menu.clock.20internal.bootloader.OSCCFG=0x02 -*/ -// On 20Mhz, the 1.8.12 IDE MegaAvr core library implementation -// of the millis() and micros() functions is not accurate. -// the file "MegaAvr20MHz.h" implements a quick hack to correct for this -// -// to do: -// there is no range check on the 'period' arguments of setPeriod ... -// check if it is necessary to set the CNT register to 0 in start() - -#ifndef EveryTimerB_h_ -#define EveryTimerB_h_ -#ifdef ARDUINO_ARCH_MEGAAVR - -#ifndef EveryTimerB_CLOCMODE -#define EveryTimerB_CLOCMODE TCB_CLKSEL_CLKTCA_gc -#endif - -#if defined(ARDUINO) && ARDUINO >= 100 -#include "Arduino.h" -#else -#include "WProgram.h" -#endif -#include "MegaAvr20MHz.h" -#include "pins_arduino.h" - -#define TCB_RESOLUTION 65536UL // TCB is 16 bit -// CLOCK F_CPU DIV TICK OVERFLOW OVERFLOW/s -// CLKTCA 16MHz 64 4000 ns 262144us 3.8 Hz -// CLKDIV2 16MHz 2 125 ns 8192us 122 Hz -// CLKDIV1 16MHz 1 62.5ns 4096us 244 Hz -// CLKTCA 20MHz 64 3200 ns 209716us 4.8 Hz -// CLKDIV2 20MHz 2 100 ns 6554us 153 Hz -// CLKDIV1 20MHz 1 50 ns 3277us 305 Hz - -class EveryTimerB -{ - public: - // The AtMega Timer Control B clock sources selection: - // TCB_CLKSEL_CLKTCA_gc, // Timer Controller A, Arduino framework sets TCA to F_CPU/64 = 250kHz (4us) @ 16MHz or 312.5kHz (3.2us) @ 20MHz - // TCB_CLKSEL_CLKDIV2_gc, // CLK_PER/2 Peripheral Clock / 2: 8MHz @ 16Mhz or 10MHz @ 20MHz - // TCB_CLKSEL_CLKDIV1_gc // CLK_PER Peripheral Clock: 16MHz @ 16Mhz or 20MHz @ 20MHz - - // intialize: sets the timer compare mode and the clock source - void initialize(TCB_t * timer_ = &TCB2, TCB_CLKSEL_t clockSource = EveryTimerB_CLOCMODE, unsigned long period = 1000000UL) __attribute__((always_inline)) { - timer = timer_; -#if defined(MegaAvr20MHzCorrected) - corrected20MHzInit(); // see commment in MegaAvr20MHz_h -#endif - stop(); - timer->CTRLB = TCB_CNTMODE_INT_gc & ~TCB_CCMPEN_bm; // timer compare mode with output disabled - if(clockSource) setClockSource(clockSource); - if(period) setPeriod(period); - } - - void setClockSource(TCB_CLKSEL_t clockSource) __attribute__((always_inline)) { - timer->CTRLA = clockSource; // this stops the clock as well ... - switch(clockSource) { -#if F_CPU == 20000000UL - case TCB_CLKSEL_CLKTCA_gc: maxTimeWithoutOverflow = 209715; break; // (TCB_RESOLUTION * 64) / 20 - case TCB_CLKSEL_CLKDIV2_gc: maxTimeWithoutOverflow = 6553; break; // (TCB_RESOLUTION * 2) / 20 - case TCB_CLKSEL_CLKDIV1_gc: maxTimeWithoutOverflow = 3276; break; // (TCB_RESOLUTION * 1) / 20 -#else - case TCB_CLKSEL_CLKTCA_gc: maxTimeWithoutOverflow = 262144; break; - case TCB_CLKSEL_CLKDIV2_gc: maxTimeWithoutOverflow = 8192; break; - case TCB_CLKSEL_CLKDIV1_gc: maxTimeWithoutOverflow = 4096; break; -#endif - } - } - - TCB_CLKSEL_t getClockSource() { - return (TCB_CLKSEL_t)(timer->CTRLA & (TCB_CLKSEL_CLKTCA_gc|TCB_CLKSEL_CLKDIV2_gc|TCB_CLKSEL_CLKDIV1_gc)); - } - - double getFrequencyOfClock(TCB_CLKSEL_t clock) { - switch(clock) { - // suppose nobody touched the default TCA configuration ... - case TCB_CLKSEL_CLKTCA_gc: return double(F_CPU/64); break; - case TCB_CLKSEL_CLKDIV2_gc: return double(F_CPU/2); break; - case TCB_CLKSEL_CLKDIV1_gc: return double(F_CPU); break; - } - return 0.0; - } - - double getClockFrequency() { - return getFrequencyOfClock(getClockSource()); - } - - // setPeriod: sets the period - // note: max and min values are different for each clock - // CLKTCA: conversion from us to ticks multiplies 'period' first with 10, so max value is MAX_ULONG/10 ~ 1 hr 11 minutes 34 seconds - // CLKDIV2: conversion from us to ticks is a *10 multiplication, so max value is 420M us (~ 7 minutes) - // CLKDIV1: conversion from us to ticks is a *20 multiplication, so max value is 210M us (~ 3.5 minutes) - void setPeriod(unsigned long period /* us */) __attribute__((always_inline)) { - timer->CTRLA &= ~TCB_ENABLE_bm; - // conversion from us to ticks depends on the clock - switch(timer->CTRLA & TCB_CLKSEL_gm) - { - case TCB_CLKSEL_CLKTCA_gc: -#if F_CPU == 20000000UL - period = (period * 10) / 32; // 20Mhz / 64x clock divider of TCA => 3.2 us / tick -#else // 16000000UL - period /= 4; // 16MHz / 64x clock divider of TCA => 4 us / tock -#endif - break; - case TCB_CLKSEL_CLKDIV2_gc: -#if F_CPU == 20000000UL - period *= 10; // 20MHz / 2x clock divider => 10 ticks / us -#else // 16000000UL - period *= 8; // 16MHz / 2x clock divider => 8 ticks / us -#endif - break; - case TCB_CLKSEL_CLKDIV1_gc: -#if F_CPU == 20000000UL - period *= 20; // 20MHz: 20 ticks / us -#else // 16000000UL - period *= 16; // 16MHz: 16 ticks / u3 -#endif - break; - } - - // to support longer than TCB_RESOLUTION ticks, - // this class supports first waiting for N 'overflowCounts' - // and next program the timer the remaining 'remainder' ticks: - countsPerOverflow = TCB_RESOLUTION; - overflowCounts = period / TCB_RESOLUTION; - remainder = period % TCB_RESOLUTION; - - // the timer period is always one tick longer than programmed, - // so a remainder of 1 is not possible. reduce the length of - // the 'overflow' cycles to get a remainder that is not 1 - if(overflowCounts) { - while(remainder == 1) { - --countsPerOverflow; - overflowCounts = period / countsPerOverflow; - remainder = period % countsPerOverflow; - } - } - - // the timer period is always one tick longer than programmed - --countsPerOverflow; - if(remainder) --remainder; - - // let's go - start(); - } - - void start() __attribute__((always_inline)) { - stop(); - overflowCounter = overflowCounts; - timer->CCMP = overflowCounts ? countsPerOverflow : remainder; - timer->CNT = 0; - timer->CTRLA |= TCB_ENABLE_bm; - } - - void stop() __attribute__((always_inline)) { - timer->CTRLA &= ~TCB_ENABLE_bm; - timer->INTFLAGS = TCB_CAPT_bm; // writing to the INTFLAGS register will clear the interrupt request flag - } - - bool isEnabled(void) __attribute__((always_inline)) { - return timer->CTRLA & TCB_ENABLE_bm ? true : false; - } - - void enable(void) __attribute__((always_inline)) { - timer->CTRLA |= TCB_ENABLE_bm; - } - - bool disable(void) __attribute__((always_inline)) { - timer->CTRLA &= ~TCB_ENABLE_bm; - } - - void attachInterrupt(void (*isr)()) __attribute__((always_inline)) { - isrCallback = isr; - timer->INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag - timer->INTCTRL = TCB_CAPT_bm; // Enable the interrupt - } - - void attachInterrupt(void (*isr)(), unsigned long microseconds) __attribute__((always_inline)) { - if(microseconds > 0) stop(); - attachInterrupt(isr); - if (microseconds > 0) setPeriod(microseconds); - } - - void detachInterrupt() __attribute__((always_inline)) { - timer->INTCTRL &= ~TCB_CAPT_bm; // Disable the interrupt - isrCallback = isrDefaultUnused; - } - - void enableInterrupt() __attribute__((always_inline)) { - timer->INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag - timer->INTCTRL = TCB_CAPT_bm; // Enable the interrupt - } - - void disableInterrupt() __attribute__((always_inline)) { - timer->INTCTRL &= ~TCB_CAPT_bm; // Enable the interrupt - } - - TCB_CNTMODE_enum getMode() __attribute__((always_inline)) { - return (TCB_CNTMODE_enum) (timer->CTRLB & 0x7); - } - - void setMode(TCB_CNTMODE_enum mode) __attribute__((always_inline)) { - timer->CTRLB = (timer->CTRLB & ~0x7) | mode; - } - - uint8_t isOutputEnabled() __attribute__((always_inline)) { - return timer->CTRLB & TCB_CCMPEN_bm; - } - - uint8_t enableOutput() __attribute__((always_inline)) { - timer->CTRLB |= TCB_CCMPEN_bm; - } - - uint8_t disableOutput() __attribute__((always_inline)) { - timer->CTRLB &= ~TCB_CCMPEN_bm; - } - - // this will start PWM on pin 6 (TCB0) or pin 3 (TCB1) - // set the pins to output with setMode(x,OUTPUT) before calling this function - // period determines the clock ticks in one cycle: - // 16MHz clock: slowest frequency at 255 = 62 kHz. - // 8MHz clock: slowest frequency at 255 = 31 kHz. - // 256kHz clock: slowest frequency at 255 = 1 kHz. - // compare determines the duty cycle. - // with a period of 255, set the compare to 128 to get 50% duty cycle. - void setPwmMode(byte period, byte compare) { - disableInterrupt(); - setMode(TCB_CNTMODE_PWM8_gc); - timer->CCMPL = period; - timer->CCMPH = compare; - enableOutput(); - enable(); - } - - void getPwmMode(byte & period, byte & compare) { - period = timer->CCMPL; - compare = timer->CCMPH; - } - - void setPwm(double frequency, double dutyCycle) { - TCB_CLKSEL_t clockSource = TCB_CLKSEL_CLKDIV1_gc; - double clockFrequency = getFrequencyOfClock(clockSource); - if(frequency < (clockFrequency/256.)) { - clockSource = TCB_CLKSEL_CLKDIV2_gc; - clockFrequency = getFrequencyOfClock(clockSource); - } - if(frequency < (clockFrequency/256.)) { - clockSource = TCB_CLKSEL_CLKTCA_gc; - clockFrequency = getFrequencyOfClock(clockSource); - } - double period = (clockFrequency / frequency) - 1.0 + 0.5; - if(period > 255.) period = 255.; - if(period < 0.) period = 0.0; - double compare = period * dutyCycle + 0.5; - if(compare < 0.0) compare = 0.0; - if(compare > period) compare = period; - setPwmMode((byte)(period),(byte)(compare)); - } - - void getPwm(double & frequency, double & dutyCycle) { - byte period, compare; - getPwmMode(period,compare); - frequency = getClockFrequency() / (((double)period) + 1); - dutyCycle = (double) compare / (((double)period) + 1); - } - - void setTimerMode() { - disable(); - disableOutput(); - setMode(TCB_CNTMODE_INT_gc); - if(isrCallback != isrDefaultUnused) { - enableInterrupt(); - } - } - - TCB_t * getTimer() { return timer; } - long getOverflowCounts() { return overflowCounts; } - long getRemainder() { return remainder; } - long getOverflowCounter() { return overflowCounter; } - long getOverflowTime() { return maxTimeWithoutOverflow; } - -//protected: - // the next_tick function is called by the interrupt service routine TCB0_INT_vect - //friend extern "C" void TCB0_INT_vect(void); - void next_tick() __attribute__((always_inline)) { - --overflowCounter; - if(overflowCounter > 0) { - return; - } - if(overflowCounter < 0) { - // finished waiting for remainder - if (overflowCounts) { - // restart with a max counter - overflowCounter = overflowCounts; - timer->CCMP = countsPerOverflow; - } - } else { - // overflowCounter == 0 - // the overflow series has finished: to the remainder if any - if(remainder) { - timer->CCMP = remainder; - if(timer->CNT < remainder) return; - // remainder is so short: already passed ! - timer->CCMP = countsPerOverflow; - } - // no remainder series: reset the overflow counter and do the callback - overflowCounter = overflowCounts; - } - (*isrCallback)(); - } - -private: - TCB_t * timer = &TCB0; - long overflowCounts = 0; - long remainder = 10; - long overflowCounter = 0; - unsigned long countsPerOverflow = TCB_RESOLUTION - 1; - void (*isrCallback)(); - static void isrDefaultUnused(); - unsigned long maxTimeWithoutOverflow; - -}; // EveryTimerB - -extern EveryTimerB TimerB2; - -#endif // ARDUINO_ARCH_MEGAAVR -#endif // EveryTimerB_h_ diff --git a/MegaAvr20Mhz.h b/MegaAvr20Mhz.h deleted file mode 100644 index ed9f807..0000000 --- a/MegaAvr20Mhz.h +++ /dev/null @@ -1,126 +0,0 @@ -#if !defined(MegaAvr20MHz_h_) -#define MegaAvr20MHz_h_ -#if defined(ARDUINO_ARCH_MEGAAVR) && (F_CPU == 20000000UL) && defined(MILLIS_USE_TIMERB3) -#define MegaAvr20MHzCorrected -// Quick hack to correct the millis() and micros() functions for 20MHz MegaAVR boards. -// by Kees van der Oord -// Remember to call the function corrected20MHzInit() from setup() or an class constructor ! - -// in the IDE 1.8.5 the implementation of millis() and micros() is not accurate -// for the MegaAvr achitecture board clocked at 20 MHz: -// 1) -// in ~\Arduino15\packages\arduino\hardware\megaavr\1.8.5\cores\arduino\wiring.c(386) -// microseconds_per_timer_overflow is initialized as: -// microseconds_per_timer_overflow = clockCyclesToMicroseconds(TIME_TRACKING_CYCLES_PER_OVF); -// this evaluates to (256 * 64) / (20000000/1000000)) = 819.2 which is rounded 819. -// the rounding causes millis() and micros() to report times that are 0.2/819.2 = 0.024 % too short -// 2) -// in ~\Arduino15\packages\arduino\hardware\megaavr\1.8.5\cores\arduino\wiring.c(387) -// microseconds_per_timer_tick is defined as: -// microseconds_per_timer_tick = microseconds_per_timer_overflow/TIME_TRACKING_TIMER_PERIOD; -// which evaluates to 819.2 / 255 = 3.21254901960784 which is rounded to 3 -// this is wrong in two ways: -// - the TIME_TRACKING_TIMER_PERIOD constant is wrong: this should be TIME_TRACKING_TICKS_PER_OVF -// so the correct value is 3.2 ns/tick -// - the rounding causes micros() to return times that are 0.2/3 = 6.25 % too short -// as a quick hack, initialize these variables with settings a factor 5 larger -// and redefine the millis() and micros() functions to return the corrected values - -// The code in this header file corrects for these problems by incrementing the counters -// with increments that are 5 times larger (the lowest factor that gives integer values). -// The millis() and micros() functions are redefined to return the counters / 5. -// The costs you pay is that the number of clock cycles of the new millis() and micros() -// functions is higher. This should be covered by the fact that the chip runs 25% faster -// at 20 MHz than at 16 MHz. - -// This header file redefines the millis() and micros() functions. The redefinition -// is only active for source files in which this header file is included. If you link -// to libraries with a .cpp file, you have to manually change the library .cpp file to -// include this header as well. In addition the corrected20MHzInit() method must be called -// from your sketch to re-initialize the variables used by the timer isr function. - -// for micros() -// from wiring.c: -extern volatile uint32_t timer_overflow_count; - -inline unsigned long corrected_micros() { - - static volatile unsigned long microseconds_offset = 0; - - unsigned long overflows, microseconds; - uint8_t ticks; - unsigned long offset; - - // Save current state and disable interrupts - uint8_t status = SREG; - cli(); - - // we need to prevent that the double calculation below exceeds MAX_ULONG - // this assumes that micros() is called at least once every 35mins) - while(timer_overflow_count > 500000UL) { - microseconds_offset += 409600000UL; // 500000 * 819.2 ~ almost 7 minutes - timer_overflow_count -= 500000UL; - } - - // Get current number of overflows and timer count - overflows = timer_overflow_count; - ticks = TCB3.CNTL; - offset = microseconds_offset; - - // If the timer overflow flag is raised, we just missed it, - // increment to account for it, & read new ticks - if(TCB3.INTFLAGS & TCB_CAPT_bm){ - overflows++; - ticks = TCB3.CNTL; - } - - // Restore state - SREG = status; - - // Return microseconds of up time (resets every ~70mins) - // float aritmic is faster than integer multiplication ? - return offset + (unsigned long)((overflows * 819.2) + (ticks * 3.2)); -} -#define micros corrected_micros - -// for millis() -// from wiring.c: -extern volatile uint32_t timer_millis; -extern uint16_t millis_inc; -extern uint16_t fract_inc; - -// call this method from your sketch setup() if you include this file ! -inline void corrected20MHzInit(void) { - fract_inc = 96; // (5 * 819.2) % 1000 - millis_inc = 4; // (5 * 819.2) / 1000 -} - -inline unsigned long corrected_millis() { - static volatile unsigned long last = 0; - static volatile unsigned long integer = 0; - static volatile unsigned long fraction = 0; - - unsigned long m; - - // disable interrupts while we read timer_millis or we might get an - // inconsistent value (e.g. in the middle of a write to timer_millis) - uint8_t status = SREG; - cli(); - - unsigned long elapsed = timer_millis - last; - last = timer_millis; - integer += elapsed / 5; - fraction += elapsed % 5; - if(fraction >= 5) { ++integer; fraction -= 5; } - - m = integer; - - SREG = status; - - return m; -} -#define millis corrected_millis - -#endif // defined(ARDUINO_ARCH_MEGAAVR) && (F_CPU == 20000000UL) && defined(MILLIS_USE_TIMERB3) - -#endif // !defined(MegaAvr20MHz_h_) From 13593ecf4fe4b1aca72ba9d62fb068d822c0df69 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 26 Jan 2021 10:55:46 +0000 Subject: [PATCH 7/8] Timer working And slow wave crap removed --- DCC.cpp | 4 +-- DCC.h | 2 +- DCCEXParser.cpp | 4 --- DCCTimer.cpp | 69 +++++++++++++++++++++++-------------------------- DCCTimer.h | 2 +- DCCWaveform.cpp | 5 ---- DCCWaveform.h | 1 - 7 files changed, 36 insertions(+), 51 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index b0973bc..3876e23 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -46,8 +46,8 @@ const byte FN_GROUP_5=0x10; FSH* DCC::shieldName=NULL; -void DCC::begin( FSH * motorShieldName, MotorDriver * mainDriver, MotorDriver* progDriver) { - shieldName=motorShieldName; +void DCC::begin(const FSH * motorShieldName, MotorDriver * mainDriver, MotorDriver* progDriver) { + shieldName=(FSH *)motorShieldName; DIAG(F("\n"), F(VERSION), F(ARDUINO_TYPE), shieldName, F(GITHUB_SHA)); // Load stuff from EEprom diff --git a/DCC.h b/DCC.h index 7099056..524c05c 100644 --- a/DCC.h +++ b/DCC.h @@ -64,7 +64,7 @@ const byte MAX_LOCOS = 50; class DCC { public: - static void begin(FSH * motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver); + static void begin(const FSH * motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver); static void loop(); // Public DCC API functions diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 04546a3..7fb6fa9 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -732,10 +732,6 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[]) Diag::WITHROTTLE = onOff; return true; - case HASH_KEYWORD_DCC: - DCCWaveform::setDiagnosticSlowWave(params >= 1 && p[1] == HASH_KEYWORD_SLOW); - return true; - case HASH_KEYWORD_PROGBOOST: DCC::setProgTrackBoost(true); return true; diff --git a/DCCTimer.cpp b/DCCTimer.cpp index 15409ae..bbec1c1 100644 --- a/DCCTimer.cpp +++ b/DCCTimer.cpp @@ -28,50 +28,45 @@ */ #include "DCCTimer.h" -#include "DIAG.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 +const long CLOCK_CYCLES=(F_CPU / 1000000 * DCC_SIGNAL_TIME) >>1; INTERRUPT_CALLBACK interruptHandler=0; - -void DCCTimer::begin(INTERRUPT_CALLBACK callback, bool slow) { - interruptHandler=callback; - // Initialise timer1 to trigger every 58us (DCC_SIGNAL_TIME) - noInterrupts(); - #ifdef ARDUINO_ARCH_MEGAAVR - // Arduino unoWifi Rev2 and nanoEvery architectire - long clockCycles=slow? (14*512) : 14; // guesswork!!!! - DIAG(F("\nTimer unoWifi/nanoEvery F_CPU=%l c=%d"),F_CPU,clockCycles); - TCB0.CCMP = clockCycles; - TCB0.INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag - TCB0.INTCTRL = TCB_CAPT_bm; // Enable the interrupt - TCB0.CNT = 0; - TCB0.CTRLA |= TCB_ENABLE_bm; // start - #define ISR_NAME TCB0_INT_vect + // Arduino unoWifi Rev2 and nanoEvery architectire + void DCCTimer::begin(INTERRUPT_CALLBACK callback) { + interruptHandler=callback; + noInterrupts(); + 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.INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag + TCB0.INTCTRL = TCB_CAPT_bm; // Enable the interrupt + TCB0.CNT = 0; + TCB0.CTRLA |= TCB_ENABLE_bm; // start + interrupts(); + } -#else - - // Arduino nano, uno, mega - long clockCycles=((F_CPU / 1000000) * (slow? DCC_SLOW_TIME : DCC_SIGNAL_TIME)) >>1; - DIAG(F("\nTimer nano/uno/mega F_CPU=%l c=%d"),F_CPU,clockCycles); - TCCR1A = 0; - ICR1 = clockCycles; - TCNT1 = 0; - TCCR1B = _BV(WGM13) | _BV(CS10); // Mode 8, clock select 1 - TIMSK1 = _BV(TOIE1); // Enable Software interrupt - #define ISR_NAME TIMER1_OVF_vect -#endif + // ISR called by timer interrupt every 58uS + ISR(TCB0_INT_vect){ + TCB0.INTFLAGS = TCB_CAPT_bm; + interruptHandler(); + } - interrupts(); -} +#else + // Arduino nano, uno, mega etc + void DCCTimer::begin(INTERRUPT_CALLBACK callback) { + interruptHandler=callback; + noInterrupts(); + TCCR1A = 0; + ICR1 = CLOCK_CYCLES; + TCNT1 = 0; + TCCR1B = _BV(WGM13) | _BV(CS10); // Mode 8, clock select 1 + TIMSK1 = _BV(TOIE1); // Enable Software interrupt + interrupts(); + } // ISR called by timer interrupt every 58uS -ISR(ISR_NAME) -{ -#ifdef ARDUINO_ARCH_MEGAAVR - TCB0.INTFLAGS = TCB_CAPT_bm; + ISR(TIMER1_OVF_vect){ interruptHandler(); } #endif - if (interruptHandler) interruptHandler(); -} diff --git a/DCCTimer.h b/DCCTimer.h index 7f99acc..a388349 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -6,7 +6,7 @@ typedef void (*INTERRUPT_CALLBACK)(); class DCCTimer { public: - static void begin(INTERRUPT_CALLBACK interrupt, bool slow=false); + static void begin(INTERRUPT_CALLBACK interrupt); private: }; diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 90db3ee..7d76e59 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -40,11 +40,6 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { DCCTimer::begin(DCCWaveform::interruptHandler); } -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")); -} - void DCCWaveform::loop() { mainTrack.checkPowerOverload(); progTrack.checkPowerOverload(); diff --git a/DCCWaveform.h b/DCCWaveform.h index e3c05fd..c193545 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -46,7 +46,6 @@ class DCCWaveform { public: DCCWaveform( byte preambleBits, bool isMain); static void begin(MotorDriver * mainDriver, MotorDriver * progDriver); - static void setDiagnosticSlowWave(bool slow); static void loop(); static DCCWaveform mainTrack; static DCCWaveform progTrack; From 7092f7de3392444402294937a5a7e953e749fe1e Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 26 Jan 2021 11:54:51 +0000 Subject: [PATCH 8/8] Correcting non-portables merged from master --- DCC.cpp | 4 ++-- DCCEXParser.cpp | 2 +- DCCEXParser.h | 3 ++- FSH.h | 17 +++++++++++++++-- MotorDrivers.h | 2 -- 5 files changed, 20 insertions(+), 8 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 3876e23..8e8aa8f 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -400,7 +400,7 @@ const ackOp FLASH LOCO_ID_PROG[] = { FAIL }; -const ackOp PROGMEM SHORT_LOCO_ID_PROG[] = { +const ackOp FLASH SHORT_LOCO_ID_PROG[] = { BASELINE, SETCV,(ackOp)19, SETBYTE, (ackOp)0, @@ -416,7 +416,7 @@ const ackOp PROGMEM SHORT_LOCO_ID_PROG[] = { FAIL }; -const ackOp PROGMEM LONG_LOCO_ID_PROG[] = { +const ackOp FLASH LONG_LOCO_ID_PROG[] = { BASELINE, // Clear consist CV 19 SETCV,(ackOp)19, diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 7fb6fa9..7c5f46f 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -237,7 +237,7 @@ void DCCEXParser::setAtCommandCallback(AT_COMMAND_CALLBACK callback) } // Parse an F() string -void DCCEXParser::parse(const __FlashStringHelper * cmd) { +void DCCEXParser::parse(const FSH * cmd) { int size=strlen_P((char *)cmd)+1; char buffer[size]; strcpy_P(buffer,(char *)cmd); diff --git a/DCCEXParser.h b/DCCEXParser.h index c9197de..f0546b2 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -19,6 +19,7 @@ #ifndef DCCEXParser_h #define DCCEXParser_h #include +#include "FSH.h" typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int p[]); typedef void (*AT_COMMAND_CALLBACK)(const byte * command); @@ -28,7 +29,7 @@ struct DCCEXParser DCCEXParser(); void loop(Stream & stream); void parse(Print * stream, byte * command, bool blocking); - void parse(const __FlashStringHelper * cmd); + void parse(const FSH * cmd); void flush(); static void setFilter(FILTER_CALLBACK filter); static void setRMFTFilter(FILTER_CALLBACK filter); diff --git a/FSH.h b/FSH.h index fe11938..c6d787e 100644 --- a/FSH.h +++ b/FSH.h @@ -1,11 +1,24 @@ #ifndef FSH_h #define FSH_h + +/* This is an architecture support file to manage the differences + * between the nano/uno.mega and the later nanoEvery, unoWifiRev2 etc + * + * IMPORTANT: + * To maintain portability the main code should NOT contain ANY references + * to the following: + * + * __FlashStringHelper Use FSH instead. + * PROGMEM use FLASH instead + * pgm_read_byte_near use GETFLASH instead. + * + */ #include #if defined(ARDUINO_ARCH_MEGAAVR) #ifdef F -#undef F -#define F(str) (str) + #undef F #endif +#define F(str) (str) typedef char FSH; #define GETFLASH(addr) (*(const unsigned char *)(addr)) #define FLASH diff --git a/MotorDrivers.h b/MotorDrivers.h index ef1e2cf..a277bbf 100644 --- a/MotorDrivers.h +++ b/MotorDrivers.h @@ -1,8 +1,6 @@ #ifndef MotorDrivers_h #define MotorDrivers_h -#if defined(ARDUINO_ARCH_MEGAAVR) #include -#endif // *** PLEASE NOTE *** THIS FILE IS **NOT** INTENDED TO BE EDITED WHEN CONFIGURING A SYSTEM. // It will be overwritten if the library is updated.