mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 08:06:13 +01:00
Merge of new Nano Every start of UnoWifiInterface
This commit is contained in:
commit
6c27330fa5
|
@ -1,20 +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"
|
|
||||||
#elif defined(ARDUINO_ARCH_MEGAAVR)
|
|
||||||
#include "ATMEGA4809/EveryTimerB.h"
|
|
||||||
#else
|
|
||||||
#error "Cannot compile - ArduinoTimers library does not support your board, or you are missing compatible build flags."
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -49,8 +49,6 @@ void setup()
|
||||||
|
|
||||||
// STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h
|
// 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);
|
DCC::begin(MOTOR_SHIELD_TYPE);
|
||||||
|
|
||||||
|
|
14
DCC.cpp
14
DCC.cpp
|
@ -23,6 +23,7 @@
|
||||||
#include "EEStore.h"
|
#include "EEStore.h"
|
||||||
#include "GITHUB_SHA.h"
|
#include "GITHUB_SHA.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
#include "FSH.h"
|
||||||
|
|
||||||
// This module is responsible for converting API calls into
|
// This module is responsible for converting API calls into
|
||||||
// messages to be sent to the waveform generator.
|
// messages to be sent to the waveform generator.
|
||||||
|
@ -45,15 +46,15 @@ const byte FN_GROUP_5=0x10;
|
||||||
|
|
||||||
FSH* DCC::shieldName=NULL;
|
FSH* DCC::shieldName=NULL;
|
||||||
|
|
||||||
void DCC::begin(const FSH* motorShieldName, MotorDriver * mainDriver, MotorDriver* progDriver, byte timerNumber) {
|
void DCC::begin(const FSH * motorShieldName, MotorDriver * mainDriver, MotorDriver* progDriver) {
|
||||||
shieldName=(FSH*)motorShieldName;
|
shieldName=(FSH *)motorShieldName;
|
||||||
DIAG(F("<iDCC-EX V-%S / %S / %S G-%S>\n"), F(VERSION), F(ARDUINO_TYPE), shieldName, F(GITHUB_SHA));
|
DIAG(F("<iDCC-EX V-%S / %S / %S G-%S>\n"), F(VERSION), F(ARDUINO_TYPE), shieldName, F(GITHUB_SHA));
|
||||||
|
|
||||||
// Load stuff from EEprom
|
// Load stuff from EEprom
|
||||||
(void)EEPROM; // tell compiler not to warn this is unused
|
(void)EEPROM; // tell compiler not to warn this is unused
|
||||||
EEStore::init();
|
EEStore::init();
|
||||||
|
|
||||||
DCCWaveform::begin(mainDriver,progDriver, timerNumber);
|
DCCWaveform::begin(mainDriver,progDriver);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCC::setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection) {
|
void DCC::setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection) {
|
||||||
|
@ -399,7 +400,7 @@ const ackOp FLASH LOCO_ID_PROG[] = {
|
||||||
FAIL
|
FAIL
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp PROGMEM SHORT_LOCO_ID_PROG[] = {
|
const ackOp FLASH SHORT_LOCO_ID_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
SETCV,(ackOp)19,
|
SETCV,(ackOp)19,
|
||||||
SETBYTE, (ackOp)0,
|
SETBYTE, (ackOp)0,
|
||||||
|
@ -415,7 +416,7 @@ const ackOp PROGMEM SHORT_LOCO_ID_PROG[] = {
|
||||||
FAIL
|
FAIL
|
||||||
};
|
};
|
||||||
|
|
||||||
const ackOp PROGMEM LONG_LOCO_ID_PROG[] = {
|
const ackOp FLASH LONG_LOCO_ID_PROG[] = {
|
||||||
BASELINE,
|
BASELINE,
|
||||||
// Clear consist CV 19
|
// Clear consist CV 19
|
||||||
SETCV,(ackOp)19,
|
SETCV,(ackOp)19,
|
||||||
|
@ -481,7 +482,6 @@ void DCC::getLocoId(ACK_CALLBACK callback, bool blocking) {
|
||||||
|
|
||||||
void DCC::setLocoId(int id,ACK_CALLBACK callback, bool blocking) {
|
void DCC::setLocoId(int id,ACK_CALLBACK callback, bool blocking) {
|
||||||
if (id<=0 || id>9999) callback(-1);
|
if (id<=0 || id>9999) callback(-1);
|
||||||
int wordval;
|
|
||||||
if (id<=127) ackManagerSetup(id,SHORT_LOCO_ID_PROG, callback, blocking);
|
if (id<=127) ackManagerSetup(id,SHORT_LOCO_ID_PROG, callback, blocking);
|
||||||
else ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking);
|
else ackManagerSetup(id | 0xc000,LONG_LOCO_ID_PROG, callback, blocking);
|
||||||
}
|
}
|
||||||
|
@ -792,7 +792,7 @@ void DCC::ackManagerLoop(bool blocking) {
|
||||||
|
|
||||||
case SETBYTE:
|
case SETBYTE:
|
||||||
ackManagerProg++;
|
ackManagerProg++;
|
||||||
ackManagerByte=pgm_read_byte_near(ackManagerProg);
|
ackManagerByte=GETFLASH(ackManagerProg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SETBYTEH:
|
case SETBYTEH:
|
||||||
|
|
2
DCC.h
2
DCC.h
|
@ -64,7 +64,7 @@ const byte MAX_LOCOS = 50;
|
||||||
class DCC
|
class DCC
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void begin(const FSH *motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver, byte timerNumber = 1);
|
static void begin(const FSH * motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver);
|
||||||
static void loop();
|
static void loop();
|
||||||
|
|
||||||
// Public DCC API functions
|
// Public DCC API functions
|
||||||
|
|
|
@ -143,6 +143,7 @@ int DCCEXParser::splitValues(int result[MAX_PARAMS], const byte *cmd)
|
||||||
runningValue = 10 * runningValue + (hot - '0');
|
runningValue = 10 * runningValue + (hot - '0');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (hot >= 'a' && hot <= 'z') hot=hot-'a'+'A'; // uppercase a..z
|
||||||
if (hot >= 'A' && hot <= 'Z')
|
if (hot >= 'A' && hot <= 'Z')
|
||||||
{
|
{
|
||||||
// Since JMRI got modified to send keywords in some rare cases, we need this
|
// Since JMRI got modified to send keywords in some rare cases, we need this
|
||||||
|
@ -237,7 +238,7 @@ void DCCEXParser::setAtCommandCallback(AT_COMMAND_CALLBACK callback)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse an F() string
|
// Parse an F() string
|
||||||
void DCCEXParser::parse(const __FlashStringHelper * cmd) {
|
void DCCEXParser::parse(const FSH * cmd) {
|
||||||
int size=strlen_P((char *)cmd)+1;
|
int size=strlen_P((char *)cmd)+1;
|
||||||
char buffer[size];
|
char buffer[size];
|
||||||
strcpy_P(buffer,(char *)cmd);
|
strcpy_P(buffer,(char *)cmd);
|
||||||
|
@ -314,10 +315,31 @@ void DCCEXParser::parse(Print *stream, byte *com, bool blocking)
|
||||||
return;
|
return;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'a': // ACCESSORY <a ADDRESS SUBADDRESS ACTIVATE>
|
case 'a': // ACCESSORY <a ADDRESS SUBADDRESS ACTIVATE> or <a LINEARADDRESS ACTIVATE>
|
||||||
if (p[2] != (p[2] & 1))
|
{
|
||||||
return;
|
int address;
|
||||||
DCC::setAccessory(p[0], p[1], p[2] == 1);
|
byte subaddress;
|
||||||
|
byte activep;
|
||||||
|
if (params==2) { // <a LINEARADDRESS ACTIVATE>
|
||||||
|
address=(p[0] - 1) / 4 + 1;
|
||||||
|
subaddress=(p[0] - 1) % 4;
|
||||||
|
activep=1;
|
||||||
|
}
|
||||||
|
else if (params==3) { // <a ADDRESS SUBADDRESS ACTIVATE>
|
||||||
|
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;
|
return;
|
||||||
|
|
||||||
case 'T': // TURNOUT <T ...>
|
case 'T': // TURNOUT <T ...>
|
||||||
|
@ -732,10 +754,6 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[])
|
||||||
Diag::WITHROTTLE = onOff;
|
Diag::WITHROTTLE = onOff;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case HASH_KEYWORD_DCC:
|
|
||||||
DCCWaveform::setDiagnosticSlowWave(params >= 1 && p[1] == HASH_KEYWORD_SLOW);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case HASH_KEYWORD_PROGBOOST:
|
case HASH_KEYWORD_PROGBOOST:
|
||||||
DCC::setProgTrackBoost(true);
|
DCC::setProgTrackBoost(true);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#ifndef DCCEXParser_h
|
#ifndef DCCEXParser_h
|
||||||
#define DCCEXParser_h
|
#define DCCEXParser_h
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "FSH.h"
|
||||||
|
|
||||||
typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int p[]);
|
typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int p[]);
|
||||||
typedef void (*AT_COMMAND_CALLBACK)(const byte * command);
|
typedef void (*AT_COMMAND_CALLBACK)(const byte * command);
|
||||||
|
@ -28,7 +29,7 @@ struct DCCEXParser
|
||||||
DCCEXParser();
|
DCCEXParser();
|
||||||
void loop(Stream & stream);
|
void loop(Stream & stream);
|
||||||
void parse(Print * stream, byte * command, bool blocking);
|
void parse(Print * stream, byte * command, bool blocking);
|
||||||
void parse(const __FlashStringHelper * cmd);
|
void parse(const FSH * cmd);
|
||||||
void flush();
|
void flush();
|
||||||
static void setFilter(FILTER_CALLBACK filter);
|
static void setFilter(FILTER_CALLBACK filter);
|
||||||
static void setRMFTFilter(FILTER_CALLBACK filter);
|
static void setRMFTFilter(FILTER_CALLBACK filter);
|
||||||
|
|
72
DCCTimer.cpp
Normal file
72
DCCTimer.cpp
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* © 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 long CLOCK_CYCLES=(F_CPU / 1000000 * DCC_SIGNAL_TIME) >>1;
|
||||||
|
|
||||||
|
INTERRUPT_CALLBACK interruptHandler=0;
|
||||||
|
|
||||||
|
#ifdef ARDUINO_ARCH_MEGAAVR
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISR called by timer interrupt every 58uS
|
||||||
|
ISR(TCB0_INT_vect){
|
||||||
|
TCB0.INTFLAGS = TCB_CAPT_bm;
|
||||||
|
interruptHandler();
|
||||||
|
}
|
||||||
|
|
||||||
|
#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(TIMER1_OVF_vect){ interruptHandler(); }
|
||||||
|
#endif
|
13
DCCTimer.h
Normal file
13
DCCTimer.h
Normal 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);
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -20,17 +20,9 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
#include "DCCWaveform.h"
|
#include "DCCWaveform.h"
|
||||||
|
#include "DCCTimer.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
|
||||||
#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 ....
|
|
||||||
#include "TimerOne.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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::mainTrack(PREAMBLE_BITS_MAIN, true);
|
||||||
DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
|
DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
|
||||||
|
@ -39,22 +31,13 @@ DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
|
||||||
bool DCCWaveform::progTrackSyncMain=false;
|
bool DCCWaveform::progTrackSyncMain=false;
|
||||||
bool DCCWaveform::progTrackBoosted=false;
|
bool DCCWaveform::progTrackBoosted=false;
|
||||||
|
|
||||||
void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver, byte timerNumber) {
|
void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) {
|
||||||
mainTrack.motorDriver=mainDriver;
|
mainTrack.motorDriver=mainDriver;
|
||||||
progTrack.motorDriver=progDriver;
|
progTrack.motorDriver=progDriver;
|
||||||
|
|
||||||
mainTrack.setPowerMode(POWERMODE::OFF);
|
mainTrack.setPowerMode(POWERMODE::OFF);
|
||||||
progTrack.setPowerMode(POWERMODE::OFF);
|
progTrack.setPowerMode(POWERMODE::OFF);
|
||||||
|
DCCTimer::begin(DCCWaveform::interruptHandler);
|
||||||
Timer1.initialize();
|
|
||||||
Timer1.setPeriod(NORMAL_SIGNAL_TIME); // this is the 58uS DCC 1-bit waveform half-cycle
|
|
||||||
Timer1.attachInterrupt(interruptHandler);
|
|
||||||
Timer1.start();
|
|
||||||
}
|
|
||||||
void DCCWaveform::setDiagnosticSlowWave(bool slow) {
|
|
||||||
Timer1.setPeriod(slow? SLOW_SIGNAL_TIME : NORMAL_SIGNAL_TIME);
|
|
||||||
Timer1.start();
|
|
||||||
DIAG(F("\nDCC SLOW WAVE %S\n"),slow?F("SET. DO NOT ADD LOCOS TO TRACK"):F("RESET"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCCWaveform::loop() {
|
void DCCWaveform::loop() {
|
||||||
|
@ -62,8 +45,6 @@ void DCCWaveform::loop() {
|
||||||
progTrack.checkPowerOverload();
|
progTrack.checkPowerOverload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// static //
|
|
||||||
void DCCWaveform::interruptHandler() {
|
void DCCWaveform::interruptHandler() {
|
||||||
// call the timer edge sensitive actions for progtrack and maintrack
|
// call the timer edge sensitive actions for progtrack and maintrack
|
||||||
bool mainCall2 = mainTrack.interrupt1();
|
bool mainCall2 = mainTrack.interrupt1();
|
||||||
|
@ -109,7 +90,6 @@ POWERMODE DCCWaveform::getPowerMode() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCCWaveform::setPowerMode(POWERMODE mode) {
|
void DCCWaveform::setPowerMode(POWERMODE mode) {
|
||||||
|
|
||||||
powerMode = mode;
|
powerMode = mode;
|
||||||
bool ison = (mode == POWERMODE::ON);
|
bool ison = (mode == POWERMODE::ON);
|
||||||
motorDriver->setPower( ison);
|
motorDriver->setPower( ison);
|
||||||
|
@ -162,10 +142,6 @@ void DCCWaveform::checkPowerOverload() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// process time-edge sensitive part of interrupt
|
// process time-edge sensitive part of interrupt
|
||||||
// return true if second level required
|
// return true if second level required
|
||||||
bool DCCWaveform::interrupt1() {
|
bool DCCWaveform::interrupt1() {
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
#define DCCWaveform_h
|
#define DCCWaveform_h
|
||||||
#include "MotorDriver.h"
|
#include "MotorDriver.h"
|
||||||
|
|
||||||
|
|
||||||
// Wait times for power management. Unit: milliseconds
|
// Wait times for power management. Unit: milliseconds
|
||||||
const int POWER_SAMPLE_ON_WAIT = 100;
|
const int POWER_SAMPLE_ON_WAIT = 100;
|
||||||
const int POWER_SAMPLE_OFF_WAIT = 1000;
|
const int POWER_SAMPLE_OFF_WAIT = 1000;
|
||||||
|
@ -46,8 +45,7 @@ const byte resetPacket[] = {0x00, 0x00, 0x00};
|
||||||
class DCCWaveform {
|
class DCCWaveform {
|
||||||
public:
|
public:
|
||||||
DCCWaveform( byte preambleBits, bool isMain);
|
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 void loop();
|
||||||
static DCCWaveform mainTrack;
|
static DCCWaveform mainTrack;
|
||||||
static DCCWaveform progTrack;
|
static DCCWaveform progTrack;
|
||||||
|
@ -105,6 +103,7 @@ class DCCWaveform {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
static void interruptHandler();
|
static void interruptHandler();
|
||||||
bool interrupt1();
|
bool interrupt1();
|
||||||
void interrupt2();
|
void interrupt2();
|
||||||
|
|
|
@ -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
|
|
390
EveryTimerB.h
390
EveryTimerB.h
|
@ -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_
|
|
19
FSH.h
19
FSH.h
|
@ -1,7 +1,24 @@
|
||||||
#ifndef FSH_h
|
#ifndef FSH_h
|
||||||
#define 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 <Arduino.h>
|
#include <Arduino.h>
|
||||||
#ifdef ARDUINO_ARCH_MEGAAVR
|
#if defined(ARDUINO_ARCH_MEGAAVR)
|
||||||
|
#ifdef F
|
||||||
|
#undef F
|
||||||
|
#endif
|
||||||
|
#define F(str) (str)
|
||||||
typedef char FSH;
|
typedef char FSH;
|
||||||
#define GETFLASH(addr) (*(const unsigned char *)(addr))
|
#define GETFLASH(addr) (*(const unsigned char *)(addr))
|
||||||
#define FLASH
|
#define FLASH
|
||||||
|
|
126
MegaAvr20Mhz.h
126
MegaAvr20Mhz.h
|
@ -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 <Kees.van.der.Oord@inter.nl.net>
|
|
||||||
// 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_)
|
|
|
@ -1,8 +1,6 @@
|
||||||
#ifndef MotorDrivers_h
|
#ifndef MotorDrivers_h
|
||||||
#define MotorDrivers_h
|
#define MotorDrivers_h
|
||||||
#if defined(ARDUINO_ARCH_MEGAAVR)
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#endif
|
|
||||||
|
|
||||||
// *** PLEASE NOTE *** THIS FILE IS **NOT** INTENDED TO BE EDITED WHEN CONFIGURING A SYSTEM.
|
// *** PLEASE NOTE *** THIS FILE IS **NOT** INTENDED TO BE EDITED WHEN CONFIGURING A SYSTEM.
|
||||||
// It will be overwritten if the library is updated.
|
// It will be overwritten if the library is updated.
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
#ifndef version_h
|
#ifndef version_h
|
||||||
#define version_h
|
#define version_h
|
||||||
|
|
||||||
#include "StringFormatter.h"
|
|
||||||
|
|
||||||
// const char VERSION[] PROGMEM ="0.2.0";
|
|
||||||
#define VERSION "3.0.3"
|
#define VERSION "3.0.3"
|
||||||
// 3.0.3 Includes:
|
// 3.0.3 Includes:
|
||||||
// <W addr> command to write loco address and clear consist
|
// <W addr> command to write loco address and clear consist
|
||||||
|
|
Loading…
Reference in New Issue
Block a user