mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-03-15 10:33:07 +01:00
Compare commits
No commits in common. "9ccf1fe41af4834617455f16d967d9681234df01" and "fae713eae7967028943dc8649edd0c1201541144" have entirely different histories.
9ccf1fe41a
...
fae713eae7
36
DCC.cpp
36
DCC.cpp
@ -897,42 +897,6 @@ DCC::LOCO DCC::speedTable[MAX_LOCOS];
|
|||||||
int DCC::lastLocoReminder = 0;
|
int DCC::lastLocoReminder = 0;
|
||||||
int DCC::highestUsedReg = 0;
|
int DCC::highestUsedReg = 0;
|
||||||
|
|
||||||
void DCC::setLocoInBlock(int loco, uint16_t blockid, bool exclusive) {
|
|
||||||
// update block loco is in, tell exrail leaving old block, and entering new.
|
|
||||||
|
|
||||||
// NOTE: The loco table scanning is really inefficient and needs rewriting
|
|
||||||
// This was done once in the momentum poc.
|
|
||||||
#ifdef EXRAIL_ACTIVE
|
|
||||||
int reg=lookupSpeedTable(loco,true);
|
|
||||||
if (reg<0) return;
|
|
||||||
auto oldBlock=speedTable[reg].blockOccupied;
|
|
||||||
if (oldBlock==blockid) return;
|
|
||||||
if (oldBlock) RMFT2::blockEvent(oldBlock,loco,false);
|
|
||||||
speedTable[reg].blockOccupied=blockid;
|
|
||||||
if (blockid) RMFT2::blockEvent(blockid,loco,true);
|
|
||||||
|
|
||||||
if (exclusive) {
|
|
||||||
for (int reg = 0; reg <= highestUsedReg; reg++) {
|
|
||||||
if (speedTable[reg].loco!=loco && speedTable[reg].blockOccupied==blockid) {
|
|
||||||
RMFT2::blockEvent(blockid,speedTable[reg].loco,false);
|
|
||||||
speedTable[reg].blockOccupied=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCC::clearBlock(uint16_t blockid) {
|
|
||||||
// Railcom reports block empty... tell Exrail about all leavers
|
|
||||||
#ifdef EXRAIL_ACTIVE
|
|
||||||
for (int reg = 0; reg <= highestUsedReg; reg++) {
|
|
||||||
if (speedTable[reg].blockOccupied==blockid) {
|
|
||||||
RMFT2::blockEvent(blockid,speedTable[reg].loco,false);
|
|
||||||
speedTable[reg].blockOccupied=0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCC::displayCabList(Print * stream) {
|
void DCC::displayCabList(Print * stream) {
|
||||||
|
|
||||||
|
4
DCC.h
4
DCC.h
@ -102,14 +102,12 @@ public:
|
|||||||
byte speedCode;
|
byte speedCode;
|
||||||
byte groupFlags;
|
byte groupFlags;
|
||||||
uint32_t functions;
|
uint32_t functions;
|
||||||
uint16_t blockOccupied; // railcom detected block
|
|
||||||
};
|
};
|
||||||
static LOCO speedTable[MAX_LOCOS];
|
static LOCO speedTable[MAX_LOCOS];
|
||||||
static int lookupSpeedTable(int locoId, bool autoCreate=true);
|
static int lookupSpeedTable(int locoId, bool autoCreate=true);
|
||||||
static byte cv1(byte opcode, int cv);
|
static byte cv1(byte opcode, int cv);
|
||||||
static byte cv2(int cv);
|
static byte cv2(int cv);
|
||||||
static void setLocoInBlock(int locoid, uint16_t blockid, bool exclusive);
|
|
||||||
static void clearBlock(uint16_t blockid);
|
|
||||||
private:
|
private:
|
||||||
static byte loopStatus;
|
static byte loopStatus;
|
||||||
static void setThrottle2(uint16_t cab, uint8_t speedCode);
|
static void setThrottle2(uint16_t cab, uint8_t speedCode);
|
||||||
|
@ -1205,10 +1205,6 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
|
|||||||
Diag::CMD = onOff;
|
Diag::CMD = onOff;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
case "RAILCOM"_hk: // <D RAILCOM ON/OFF>
|
|
||||||
Diag::RAILCOM = onOff;
|
|
||||||
return true;
|
|
||||||
|
|
||||||
#ifdef HAS_ENOUGH_MEMORY
|
#ifdef HAS_ENOUGH_MEMORY
|
||||||
case "WIFI"_hk: // <D WIFI ON/OFF>
|
case "WIFI"_hk: // <D WIFI ON/OFF>
|
||||||
Diag::WIFI = onOff;
|
Diag::WIFI = onOff;
|
||||||
|
@ -127,11 +127,10 @@ volatile bool DCCWaveform::railcomActive=false; // switched on by user
|
|||||||
volatile bool DCCWaveform::railcomDebug=false; // switched on by user
|
volatile bool DCCWaveform::railcomDebug=false; // switched on by user
|
||||||
volatile bool DCCWaveform::railcomSampleWindow=false; // true during packet transmit
|
volatile bool DCCWaveform::railcomSampleWindow=false; // true during packet transmit
|
||||||
volatile byte DCCWaveform::railcomCutoutCounter=0; // cyclic cutout
|
volatile byte DCCWaveform::railcomCutoutCounter=0; // cyclic cutout
|
||||||
volatile byte DCCWaveform::railcomLastAddressHigh=0;
|
|
||||||
volatile byte DCCWaveform::railcomLastAddressLow=0;
|
|
||||||
|
|
||||||
bool DCCWaveform::setRailcom(bool on, bool debug) {
|
bool DCCWaveform::setRailcom(bool on, bool debug) {
|
||||||
if (on && railcomPossible) {
|
if (on && railcomPossible) {
|
||||||
|
// TODO check possible
|
||||||
railcomActive=true;
|
railcomActive=true;
|
||||||
railcomDebug=debug;
|
railcomDebug=debug;
|
||||||
}
|
}
|
||||||
@ -234,12 +233,6 @@ void DCCWaveform::promotePendingPacket() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remember address bytes of last sent packet so that Railcom can
|
|
||||||
// work out where the channel2 data came from.
|
|
||||||
railcomLastAddressHigh=transmitPacket[0];
|
|
||||||
railcomLastAddressLow =transmitPacket[1];
|
|
||||||
|
|
||||||
|
|
||||||
if (packetPending) {
|
if (packetPending) {
|
||||||
// Copy pending packet to transmit packet
|
// Copy pending packet to transmit packet
|
||||||
// a fixed length memcpy is faster than a variable length loop for these small lengths
|
// a fixed length memcpy is faster than a variable length loop for these small lengths
|
||||||
|
@ -100,14 +100,6 @@ class DCCWaveform {
|
|||||||
railcomPossible=yes;
|
railcomPossible=yes;
|
||||||
if (!yes) setRailcom(false,false);
|
if (!yes) setRailcom(false,false);
|
||||||
};
|
};
|
||||||
inline static uint16_t getRailcomLastLocoAddress() {
|
|
||||||
// first 2 bits 00=short loco, 11=long loco , 01/10 = accessory
|
|
||||||
byte addressType=railcomLastAddressHigh & 0xC0;
|
|
||||||
if (addressType==0xC0) return ((railcomLastAddressHigh & 0x3f)<<8) | railcomLastAddressLow;
|
|
||||||
if (addressType==0x00) return railcomLastAddressHigh & 0x3F;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifndef ARDUINO_ARCH_ESP32
|
#ifndef ARDUINO_ARCH_ESP32
|
||||||
volatile bool packetPending;
|
volatile bool packetPending;
|
||||||
@ -137,7 +129,6 @@ class DCCWaveform {
|
|||||||
static volatile bool railcomDebug; // switched on by user
|
static volatile bool railcomDebug; // switched on by user
|
||||||
static volatile bool railcomSampleWindow; // when safe to sample
|
static volatile bool railcomSampleWindow; // when safe to sample
|
||||||
static volatile byte railcomCutoutCounter; // incremented for each cutout
|
static volatile byte railcomCutoutCounter; // incremented for each cutout
|
||||||
static volatile byte railcomLastAddressHigh,railcomLastAddressLow;
|
|
||||||
static bool cutoutNextTime; // railcom
|
static bool cutoutNextTime; // railcom
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
static RMTChannel *rmtMainChannel;
|
static RMTChannel *rmtMainChannel;
|
||||||
|
@ -58,7 +58,7 @@ class I2CRailcom : public IODevice {
|
|||||||
private:
|
private:
|
||||||
// SC16IS752 defines
|
// SC16IS752 defines
|
||||||
uint8_t _UART_CH=0x00; // channel 0 or 1 flips each loop if npins>1
|
uint8_t _UART_CH=0x00; // channel 0 or 1 flips each loop if npins>1
|
||||||
byte _inbuf[12];
|
byte _inbuf[65];
|
||||||
byte _outbuf[2];
|
byte _outbuf[2];
|
||||||
byte cutoutCounter[2];
|
byte cutoutCounter[2];
|
||||||
Railcom * _channelMonitors[2];
|
Railcom * _channelMonitors[2];
|
||||||
@ -118,7 +118,7 @@ public:
|
|||||||
// Read incoming raw Railcom data, and process accordingly
|
// Read incoming raw Railcom data, and process accordingly
|
||||||
|
|
||||||
auto inlength = UART_ReadRegister(REG_RXLV);
|
auto inlength = UART_ReadRegister(REG_RXLV);
|
||||||
|
if (inlength==0) return;
|
||||||
if (inlength> sizeof(_inbuf)) inlength=sizeof(_inbuf);
|
if (inlength> sizeof(_inbuf)) inlength=sizeof(_inbuf);
|
||||||
_inbuf[0]=0;
|
_inbuf[0]=0;
|
||||||
if (inlength>0) {
|
if (inlength>0) {
|
||||||
|
98
Railcom.cpp
98
Railcom.cpp
@ -51,9 +51,10 @@
|
|||||||
#include "Railcom.h"
|
#include "Railcom.h"
|
||||||
#include "defines.h"
|
#include "defines.h"
|
||||||
#include "FSH.h"
|
#include "FSH.h"
|
||||||
#include "DCC.h"
|
#include "EXRAIL3.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
|
||||||
|
//#define DIAG_I2CRailcom_data
|
||||||
|
|
||||||
|
|
||||||
/** Table for 8-to-6 decoding of railcom data. This table can be indexed by the
|
/** Table for 8-to-6 decoding of railcom data. This table can be indexed by the
|
||||||
@ -134,49 +135,31 @@ const uint8_t HIGHFLASH decode[256] =
|
|||||||
RMOB_LOGON_ENABLE_FEEDBACK = 15,
|
RMOB_LOGON_ENABLE_FEEDBACK = 15,
|
||||||
};
|
};
|
||||||
|
|
||||||
// each railcom block is represented by an instance of this class.
|
|
||||||
// The blockvpin is the vpin associated with this block for the purposes of
|
|
||||||
// a HAL driver for the railcom detection and the EXRAIL ONBLOCKENTER/ONBLOCKEXIT
|
|
||||||
|
|
||||||
Railcom::Railcom(uint16_t blockvpin) {
|
Railcom::Railcom(uint16_t blockvpin) {
|
||||||
haveHigh=false;
|
haveHigh=false;
|
||||||
haveLow=false;
|
haveLow=false;
|
||||||
packetsWithNoData=0;
|
packetsWithNoData=0;
|
||||||
lastChannel1Loco=0;
|
locoOnTrack=0;
|
||||||
vpin=blockvpin;
|
vpin=blockvpin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* returns -1: Call again next packet
|
||||||
// Process is called by a raw data collector.
|
0: No loco on track
|
||||||
|
>0: loco id
|
||||||
|
*/
|
||||||
void Railcom::process(uint8_t * inbound, uint8_t length) {
|
void Railcom::process(uint8_t * inbound, uint8_t length) {
|
||||||
|
#ifdef DIAG_I2CRailcom_data
|
||||||
|
DIAG(F("Railcom %d RX FIFO Data, %d"), vpin,length);
|
||||||
if (length<2 || (inbound[0]==0 && inbound[1]==0)) {
|
for (int i = 0; i < 2; i++){
|
||||||
noData();
|
if (inbound[i]) DIAG(F("[0x%x]: 0x%x"), i, inbound[i]);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (Diag::RAILCOM) {
|
|
||||||
static const char hexchars[]="0123456789ABCDEF";
|
|
||||||
if (length>2) {
|
|
||||||
USB_SERIAL.print(F("<*R "));
|
|
||||||
for (byte i=0;i<length;i++) {
|
|
||||||
if (i==2) Serial.write(' ');
|
|
||||||
USB_SERIAL.write(hexchars[inbound[i]>>4]);
|
|
||||||
USB_SERIAL.write(hexchars[inbound[i]& 0x0F ]);
|
|
||||||
}
|
|
||||||
USB_SERIAL.print(F(" *>\n"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
auto v1=GETHIGHFLASH(decode,inbound[0]);
|
auto v1=GETHIGHFLASH(decode,inbound[0]);
|
||||||
auto v2=(length>1) ? GETHIGHFLASH(decode,inbound[1]):INV;
|
auto v2=(length>2) ? GETHIGHFLASH(decode,inbound[1]):0x0;
|
||||||
uint16_t packet=(v1<<6) | (v2 & 0x3f);
|
uint16_t packet=(v1<<6) | (v2 & 0x3f);
|
||||||
// packet is 12 bits TTTTDDDDDDDD
|
// packet is 12 bits TTTTDDDDDDDD
|
||||||
byte type=(packet>>8) & 0x0F;
|
auto type=packet>>8;
|
||||||
byte data= packet & 0xFF;
|
auto data= packet & 0xFF;
|
||||||
if (type==RMOB_ADRHIGH) {
|
if (type==RMOB_ADRHIGH) {
|
||||||
holdoverHigh=data;
|
holdoverHigh=data;
|
||||||
haveHigh=true;
|
haveHigh=true;
|
||||||
@ -191,38 +174,31 @@ void Railcom::process(uint8_t * inbound, uint8_t length) {
|
|||||||
return; /* ignore*/
|
return; /* ignore*/
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// channel1 is unreadable so maybe multiple locos in block
|
if (packetsWithNoData>MAX_WAIT_FOR_GLITCH) {
|
||||||
if (length>2 && GETHIGHFLASH(decode,inbound[0])!=INV) {
|
|
||||||
// it looks like we have channel2 data
|
|
||||||
auto thisLoco=DCCWaveform::getRailcomLastLocoAddress();
|
|
||||||
if (Diag::RAILCOM) DIAG(F("c2=%d"),thisLoco);
|
|
||||||
if (thisLoco) DCC::setLocoInBlock(thisLoco,vpin,false); // this loco is in block, but not exclusive
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// channel1 no good and no channel2
|
|
||||||
noData();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (haveHigh && haveLow) {
|
|
||||||
uint16_t thisLoco=((holdoverHigh<<8)| holdoverLow);
|
|
||||||
if (thisLoco!=lastChannel1Loco) {
|
|
||||||
// the exclusive DCC call is quite expensive, we dont want to call it every packet
|
|
||||||
DCC::setLocoInBlock(thisLoco,vpin,true); // only this loco is in block
|
|
||||||
lastChannel1Loco=thisLoco;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Railcom::noData() {
|
|
||||||
if (packetsWithNoData>MAX_WAIT_FOR_GLITCH) return;
|
|
||||||
if (packetsWithNoData==MAX_WAIT_FOR_GLITCH) {
|
|
||||||
// treat as no loco
|
// treat as no loco
|
||||||
haveHigh=false;
|
haveHigh=false;
|
||||||
haveLow=false;
|
haveLow=false;
|
||||||
lastChannel1Loco=0;
|
// Previous loco (if any) is exiting block
|
||||||
// Previous locos (if any) is exiting block
|
blockEvent(false);
|
||||||
DCC::clearBlock(vpin);
|
locoOnTrack=0;
|
||||||
|
return ;
|
||||||
}
|
}
|
||||||
packetsWithNoData++;
|
packetsWithNoData++;
|
||||||
|
return; // need more data
|
||||||
|
}
|
||||||
|
if (haveHigh && haveLow) {
|
||||||
|
uint16_t thisLoco=((holdoverHigh<<8)| holdoverLow);
|
||||||
|
if (locoOnTrack!=thisLoco) {
|
||||||
|
// Previous loco (if any) is exiting block
|
||||||
|
blockEvent(false);
|
||||||
|
locoOnTrack=thisLoco;
|
||||||
|
blockEvent(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Railcom::blockEvent(bool entering) {
|
||||||
|
#ifdef EXRAIL_ACTIVE
|
||||||
|
if (locoOnTrack) RMFT3::blockEvent(vpin,locoOnTrack,entering);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
@ -33,12 +33,12 @@ class Railcom {
|
|||||||
void process(uint8_t * inbound,uint8_t length);
|
void process(uint8_t * inbound,uint8_t length);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void noData();
|
void blockEvent(bool entering);
|
||||||
|
uint16_t locoOnTrack;
|
||||||
uint16_t vpin;
|
uint16_t vpin;
|
||||||
uint8_t holdoverHigh,holdoverLow;
|
uint8_t holdoverHigh,holdoverLow;
|
||||||
bool haveHigh,haveLow;
|
bool haveHigh,haveLow;
|
||||||
uint8_t packetsWithNoData;
|
uint8_t packetsWithNoData;
|
||||||
uint16_t lastChannel1Loco;
|
|
||||||
static const byte MAX_WAIT_FOR_GLITCH=20; // number of dead or empty packets before assuming loco=0
|
static const byte MAX_WAIT_FOR_GLITCH=20; // number of dead or empty packets before assuming loco=0
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,8 +27,6 @@ bool Diag::WIFI=false;
|
|||||||
bool Diag::WITHROTTLE=false;
|
bool Diag::WITHROTTLE=false;
|
||||||
bool Diag::ETHERNET=false;
|
bool Diag::ETHERNET=false;
|
||||||
bool Diag::LCN=false;
|
bool Diag::LCN=false;
|
||||||
bool Diag::RAILCOM=false;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void StringFormatter::diag( const FSH* input...) {
|
void StringFormatter::diag( const FSH* input...) {
|
||||||
|
@ -30,7 +30,6 @@ class Diag {
|
|||||||
static bool WITHROTTLE;
|
static bool WITHROTTLE;
|
||||||
static bool ETHERNET;
|
static bool ETHERNET;
|
||||||
static bool LCN;
|
static bool LCN;
|
||||||
static bool RAILCOM;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user