1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-12-24 21:21:24 +01:00

ACK diagnostics

Type <D 1> to enable
This commit is contained in:
Asbelos 2020-07-01 10:27:53 +01:00
parent 5269177f2e
commit bec57345f1
3 changed files with 64 additions and 28 deletions

72
DCC.cpp
View File

@ -24,6 +24,7 @@ const byte FN_GROUP_5=0x10;
void DCC::begin() { void DCC::begin() {
debugMode=false;
DCCWaveform::begin(); DCCWaveform::begin();
} }
@ -50,7 +51,7 @@ void DCC::setThrottle2( uint16_t cab, byte speedCode) {
} }
void DCC::setFunctionInternal(int cab, byte byte1, byte byte2) { void DCC::setFunctionInternal(int cab, byte byte1, byte byte2) {
//DIAG(F("\nsetFunctionInternal %d %x %x"),cab,byte1,byte2); // DIAG(F("\nsetFunctionInternal %d %x %x"),cab,byte1,byte2);
byte b[4]; byte b[4];
byte nB = 0; byte nB = 0;
@ -274,6 +275,10 @@ void DCC::forgetAllLocos() { // removes all speed reminders
for (int i=0;i<MAX_LOCOS;i++) speedTable[i].loco=0; for (int i=0;i<MAX_LOCOS;i++) speedTable[i].loco=0;
} }
void DCC::setDebug(bool on) {
debugMode=on;
}
byte DCC::loopStatus=0; byte DCC::loopStatus=0;
void DCC::loop() { void DCC::loop() {
@ -399,6 +404,9 @@ byte DCC::ackManagerBitNum;
bool DCC::ackReceived; bool DCC::ackReceived;
int DCC::ackTriggerMilliamps; int DCC::ackTriggerMilliamps;
unsigned long DCC::ackPulseStart; unsigned long DCC::ackPulseStart;
int DCC::ackMaxCurrent;
int DCC::ackPollCount;
bool DCC::debugMode=false;
ACK_CALLBACK DCC::ackManagerCallback; ACK_CALLBACK DCC::ackManagerCallback;
@ -408,7 +416,8 @@ void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[]
ackManagerByte = byteValueOrBitnum; ackManagerByte = byteValueOrBitnum;
ackManagerBitNum=byteValueOrBitnum; ackManagerBitNum=byteValueOrBitnum;
ackManagerCallback = callback; ackManagerCallback = callback;
ackMaxCurrent=0;
ackPollCount=0;
} }
const byte RESET_MIN=8; // tuning of reset counter before sending message const byte RESET_MIN=8; // tuning of reset counter before sending message
@ -419,15 +428,14 @@ void DCC::ackManagerLoop() {
// breaks from this switch will step to next prog entry // breaks from this switch will step to next prog entry
// returns from this switch will stay on same entry (typically WACK waiting and when all finished.) // returns from this switch will stay on same entry (typically WACK waiting and when all finished.)
byte opcode=pgm_read_byte_near(ackManagerProg); byte opcode=pgm_read_byte_near(ackManagerProg);
// DIAG(F("apAck %d\n"),opcode);
int resets=DCCWaveform::progTrack.sentResetsSincePacket; int resets=DCCWaveform::progTrack.sentResetsSincePacket;
int current;
switch (opcode) { switch (opcode) {
case BASELINE: case BASELINE:
if (resets<RESET_MIN) return; // try later if (resets<RESET_MIN) return; // try later
ackTriggerMilliamps=Hardware::getCurrentMilliamps(false) + ACK_MIN_PULSE; ackTriggerMilliamps=Hardware::getCurrentMilliamps(false) + ACK_MIN_PULSE;
// DIAG(F("\nBASELINE trigger mA=%d\n"),ackTriggerMilliamps); if (debugMode) DIAG(F("\nACK_BASELINE trigger mA=%d\n"),ackTriggerMilliamps);
break; break;
case W0: // write 0 bit case W0: // write 0 bit
@ -438,6 +446,8 @@ void DCC::ackManagerLoop() {
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction }; byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6); DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6);
ackPulseStart=0; ackPulseStart=0;
ackMaxCurrent=0;
ackPollCount=0;
} }
break; break;
@ -447,16 +457,20 @@ void DCC::ackManagerLoop() {
byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte}; byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6); DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6);
ackPulseStart=0; ackPulseStart=0;
ackMaxCurrent=0;
ackPollCount=0;
} }
break; break;
case VB: // Issue validate Byte packet case VB: // Issue validate Byte packet
{ {
if (resets<RESET_MIN) return; // try later if (resets<RESET_MIN) return; // try later
// DIAG(F("\nVB %d %d"),ackManagerCv,ackManagerByte); if (debugMode) DIAG(F("\nVB %d %d"),ackManagerCv,ackManagerByte);
byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte}; byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte};
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5); DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5);
ackPulseStart=0; ackPulseStart=0;
ackMaxCurrent=0;
ackPollCount=0;
} }
break; break;
@ -464,48 +478,57 @@ void DCC::ackManagerLoop() {
case V1: // Issue validate bit=0 or bit=1 packet case V1: // Issue validate bit=0 or bit=1 packet
{ {
if (resets<RESET_MIN) return; // try later if (resets<RESET_MIN) return; // try later
// DIAG(F("V%d cv=%d bit=%d"),opcode==V1, ackManagerCv,ackManagerBitNum); if (debugMode) DIAG(F("\nV%d cv=%d bit=%d"),opcode==V1, ackManagerCv,ackManagerBitNum);
byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum; byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum;
byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction }; byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction };
DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5); DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5);
ackPulseStart=0; ackPulseStart=0;
ackMaxCurrent=0;
ackPollCount=0;
} }
break; break;
case WACK: // wait for ack (or absence of ack) case WACK: // wait for ack (or absence of ack)
{
if (resets > 6) { //ACK timeout if (resets > 6) { //ACK timeout
// DIAG(F("\nWACK fail %d\n"), resets); if (debugMode) DIAG(F("\nWACK fail polls=%d, resets=%d, max=%dmA"), ackPollCount, resets, ackMaxCurrent);
ackReceived = false; ackReceived = false;
break; // move on to next prog step break; // move on to next prog step
} }
current=Hardware::getCurrentMilliamps(false); int current=Hardware::getCurrentMilliamps(false);
if (current > ackMaxCurrent) ackMaxCurrent=current;
ackPollCount++;
// An ACK is a pulse lasting between 4.5 and 8.5 mSecs (refer @haba) // An ACK is a pulse lasting between 4.5 and 8.5 mSecs (refer @haba)
if (current>ackTriggerMilliamps) { if (current>ackTriggerMilliamps) {
if (ackPulseStart==0)ackPulseStart=micros(); // leading edge of pulse detected if (ackPulseStart==0) ackPulseStart=micros(); // leading edge of pulse detected
return; return;
} }
// not in pulse // not in pulse
if (ackPulseStart==0) return; // keep waiting for leading edge if (ackPulseStart==0) return; // keep waiting for leading edge
{ // detected trailing edge of pulse
// detected trailing edge of pulse
long pulseDuration=micros()-ackPulseStart; long pulseDuration=micros()-ackPulseStart;
if (pulseDuration>4500 && pulseDuration<8000) {
if (pulseDuration>4000 && pulseDuration<9000) {
if (debugMode) DIAG(F("\nWACK-OK polls=%d, max=%dmA, pulse=%duS"),ackPollCount, ackMaxCurrent, pulseDuration);
ackReceived=true; ackReceived=true;
DCCWaveform::progTrack.killRemainingRepeats(); // probably no need after 8.5ms!! DCCWaveform::progTrack.killRemainingRepeats(); // probably no need after 8.5ms!!
break; // we have a genuine ACK result break; // we have a genuine ACK result
} }
} if (debugMode) DIAG(F("\nWACK-bad pulse polls=%d, max=%dmA, pulse=%duS"), ackPollCount, ackMaxCurrent, pulseDuration);
ackPulseStart=0; // We have detected a too-short or too-long pulse so ignore and wait for next leading edge ackPulseStart=0; // We have detected a too-short or too-long pulse so ignore and wait for next leading edge
return; // keep waiting return; // keep waiting
}
case ITC0: case ITC0:
case ITC1: // If True Callback(0 or 1) (if prevous WACK got an ACK) case ITC1: // If True Callback(0 or 1) (if prevous WACK got an ACK)
if (ackReceived) { if (ackReceived) {
ackManagerProg = NULL; // all done now ackManagerProg = NULL; // all done now
(ackManagerCallback)(opcode==ITC0?0:1); callback(opcode==ITC0?0:1);
return; return;
} }
break; break;
@ -513,7 +536,7 @@ void DCC::ackManagerLoop() {
case ITCB: // If True callback(byte) case ITCB: // If True callback(byte)
if (ackReceived) { if (ackReceived) {
ackManagerProg = NULL; // all done now ackManagerProg = NULL; // all done now
(ackManagerCallback)(ackManagerByte); callback(ackManagerByte);
return; return;
} }
break; break;
@ -521,14 +544,14 @@ void DCC::ackManagerLoop() {
case NAKFAIL: // If nack callback(-1) case NAKFAIL: // If nack callback(-1)
if (!ackReceived) { if (!ackReceived) {
ackManagerProg = NULL; // all done now ackManagerProg = NULL; // all done now
(ackManagerCallback)(-1); callback(-1);
return; return;
} }
break; break;
case FAIL: // callback(-1) case FAIL: // callback(-1)
ackManagerProg = NULL; ackManagerProg = NULL;
(ackManagerCallback)(-1); callback(-1);
return; return;
case STARTMERGE: case STARTMERGE:
@ -560,7 +583,7 @@ void DCC::ackManagerLoop() {
case COMBINELOCOID: case COMBINELOCOID:
// ackManagerStash is cv17, ackManagerByte is CV 18 // ackManagerStash is cv17, ackManagerByte is CV 18
ackManagerProg=NULL; ackManagerProg=NULL;
(ackManagerCallback)( ackManagerByte + ((ackManagerStash - 192) << 8)); callback( ackManagerByte + ((ackManagerStash - 192) << 8));
return; return;
case ITSKIP: case ITSKIP:
@ -570,17 +593,20 @@ void DCC::ackManagerLoop() {
ackManagerProg++; ackManagerProg++;
opcode=pgm_read_byte_near(ackManagerProg); opcode=pgm_read_byte_near(ackManagerProg);
} }
// DIAG(F("\nSKIPTARGET located\n"));
break; break;
case SKIPTARGET: case SKIPTARGET:
break; break;
default: default:
// DIAG(F("!! ackOp %d FAULT!!"),opcode); DIAG(F("!! ackOp %d FAULT!!"),opcode);
ackManagerProg=NULL; ackManagerProg=NULL;
(ackManagerCallback)( -1); callback( -1);
return; return;
} // end of switch } // end of switch
ackManagerProg++; ackManagerProg++;
} }
} }
void DCC::callback(int value) {
if (debugMode) DIAG(F("\nCallback(%d)\n"),value);
(ackManagerCallback)( value);
}

5
DCC.h
View File

@ -44,6 +44,7 @@ class DCC {
static void setFn( int cab, byte functionNumber, bool on); static void setFn( int cab, byte functionNumber, bool on);
static void setAccessory(int aAdd, byte aNum, bool activate) ; static void setAccessory(int aAdd, byte aNum, bool activate) ;
static bool writeTextPacket( byte *b, int nBytes); static bool writeTextPacket( byte *b, int nBytes);
static void setDebug(bool on);
// ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1 // ACKable progtrack calls bitresults callback 0,0 or -1, cv returns value or -1
static void readCV(int cv, ACK_CALLBACK callback); static void readCV(int cv, ACK_CALLBACK callback);
@ -75,6 +76,8 @@ private:
static byte cv2(int cv); static byte cv2(int cv);
static int lookupSpeedTable(int locoId); static int lookupSpeedTable(int locoId);
static void issueReminders(); static void issueReminders();
static void callback(int value);
static bool debugMode;
// ACK MANAGER // ACK MANAGER
static ackOp const * ackManagerProg; static ackOp const * ackManagerProg;
@ -84,6 +87,8 @@ private:
static byte ackManagerStash; static byte ackManagerStash;
static bool ackReceived; static bool ackReceived;
static int ackTriggerMilliamps; static int ackTriggerMilliamps;
static int ackMaxCurrent;
static int ackPollCount;
static unsigned long ackPulseStart; static unsigned long ackPulseStart;
static ACK_CALLBACK ackManagerCallback; static ACK_CALLBACK ackManagerCallback;
static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback); static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback);

View File

@ -227,6 +227,11 @@ void DCCEXParser::parse(Print & stream, const byte *com) {
StringFormatter::send(stream,F("\n")); StringFormatter::send(stream,F("\n"));
return; return;
case 'D': // < >
DCC::setDebug(p[0]==1);
DIAG(F("\nDCC DEBUG MODE %d"),p[0]==1);
return;
default: //anything else will drop out to <X> default: //anything else will drop out to <X>
break; break;