1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-01-26 20:28:52 +01:00

Clean simple Timer interface

Removes overkill files, puts all timer in a single small file. (DCCTimer)
This commit is contained in:
Asbelos 2021-01-25 15:26:39 +00:00
parent f646f12c65
commit 8a9feaef22
12 changed files with 79 additions and 534 deletions

View File

@ -1,194 +0,0 @@
#ifndef ATMEGA2560Timer_h
#define ATMEGA2560Timer_h
#include "../VirtualTimer.h"
#include <Arduino.h>
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

View File

@ -1,208 +0,0 @@
#ifndef ATMEGA328Timer_h
#define ATMEGA328Timer_h
#include "../VirtualTimer.h"
#include <Arduino.h>
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

View File

@ -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

View File

@ -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)

View File

@ -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("<iDCC-EX V-%S / %S / %S G-%S>\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);
}

2
DCC.h
View File

@ -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

54
DCCTimer.cpp Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
/* 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 <D DCC SLOW> 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();
}

13
DCCTimer.h Normal file
View File

@ -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

View File

@ -20,10 +20,9 @@
#include <Arduino.h>
#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() {

View File

@ -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();

View File

@ -1,52 +0,0 @@
// This file is copied from https://github.com/davidcutting42/ArduinoTimers
// All Credit to David Cutting
#include <Arduino.h>
#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

View File

@ -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