diff --git a/DCC.cpp b/DCC.cpp index bd9e6df..679f771 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -247,9 +247,14 @@ const ackOp PROGMEM READ_CV_PROG[] = { // each bit is validated against 0 and the result inverted in MERGE // this is because there tend to be more zeros in cv values than ones. // There is no need for one validation as entire byte is validated at the end - V0, WACK, MERGE, // read and merge bit 0 - V0, WACK, MERGE, // read and merge bit 1 etc - V0, WACK, MERGE, + V0, WACK, MERGE, // read and merge first tested bit (7) + ITSKIP, // do small excursion if there was no ack + SETBIT,(ackOp)7, + V1, WACK, NAKFAIL, // test if there is an ack on the inverse of this bit (7) + SETBIT,(ackOp)6, // and abort whole test if not else continue with bit (6) + SKIPTARGET, + V0, WACK, MERGE, // read and merge second tested bit (6) + V0, WACK, MERGE, // read and merge third tested bit (5) ... V0, WACK, MERGE, V0, WACK, MERGE, V0, WACK, MERGE, @@ -494,13 +499,13 @@ void DCC::ackManagerSetup(int cv, byte byteValueOrBitnum, ackOp const program[] const byte RESET_MIN=8; // tuning of reset counter before sending message // checkRessets return true if the caller should yield back to loop and try later. -bool DCC::checkResets(bool blocking) { +bool DCC::checkResets(bool blocking, uint8_t numResets) { if (blocking) { // must block waiting for restest to be issued - while(DCCWaveform::progTrack.sentResetsSincePacket < RESET_MIN); + while(DCCWaveform::progTrack.sentResetsSincePacket < numResets); return false; // caller need not yield } - return DCCWaveform::progTrack.sentResetsSincePacket < RESET_MIN; + return DCCWaveform::progTrack.sentResetsSincePacket < numResets; } void DCC::ackManagerLoop(bool blocking) { @@ -513,13 +518,19 @@ void DCC::ackManagerLoop(bool blocking) { // if blocking then we must ONLY return AFTER callback issued switch (opcode) { case BASELINE: - if (checkResets(blocking)) return; + if (DCCWaveform::progTrack.getPowerMode() == POWERMODE::OFF) { + DCCWaveform::progTrack.setPowerMode(POWERMODE::ON); + DCCWaveform::progTrack.sentResetsSincePacket = 0; + DCCWaveform::progTrack.autoPowerOff=true; + return; + } + if (checkResets(blocking, DCCWaveform::progTrack.autoPowerOff ? 20 : 3)) return; DCCWaveform::progTrack.setAckBaseline(debugMode); break; case W0: // write 0 bit case W1: // write 1 bit { - if (checkResets(blocking)) return; + if (checkResets(blocking, RESET_MIN)) return; if (debugMode) DIAG(F("\nW%d cv=%d bit=%d"),opcode==W1, ackManagerCv,ackManagerBitNum); byte instruction = WRITE_BIT | (opcode==W1 ? BIT_ON : BIT_OFF) | ackManagerBitNum; byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction }; @@ -530,7 +541,7 @@ void DCC::ackManagerLoop(bool blocking) { case WB: // write byte { - if (checkResets(blocking)) return; + if (checkResets(blocking, RESET_MIN)) return; if (debugMode) DIAG(F("\nWB cv=%d value=%d"),ackManagerCv,ackManagerByte); byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte}; DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); @@ -540,7 +551,7 @@ void DCC::ackManagerLoop(bool blocking) { case VB: // Issue validate Byte packet { - if (checkResets(blocking)) return; + if (checkResets(blocking, RESET_MIN)) return; if (debugMode) DIAG(F("\nVB cv=%d value=%d"),ackManagerCv,ackManagerByte); byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte}; DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); @@ -551,7 +562,7 @@ void DCC::ackManagerLoop(bool blocking) { case V0: case V1: // Issue validate bit=0 or bit=1 packet { - if (checkResets(blocking)) return; + if (checkResets(blocking, RESET_MIN)) return; 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 message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction }; @@ -577,6 +588,7 @@ void DCC::ackManagerLoop(bool blocking) { case ITC1: // If True Callback(0 or 1) (if prevous WACK got an ACK) if (ackReceived) { ackManagerProg = NULL; // all done now + DCCWaveform::progTrack.doAutoPowerOff(); callback(opcode==ITC0?0:1); return; } @@ -585,6 +597,7 @@ void DCC::ackManagerLoop(bool blocking) { case ITCB: // If True callback(byte) if (ackReceived) { ackManagerProg = NULL; // all done now + DCCWaveform::progTrack.doAutoPowerOff(); callback(ackManagerByte); return; } @@ -593,6 +606,7 @@ void DCC::ackManagerLoop(bool blocking) { case NAKFAIL: // If nack callback(-1) if (!ackReceived) { ackManagerProg = NULL; // all done now + DCCWaveform::progTrack.doAutoPowerOff(); callback(-1); return; } @@ -600,7 +614,8 @@ void DCC::ackManagerLoop(bool blocking) { case FAIL: // callback(-1) ackManagerProg = NULL; - callback(-1); + DCCWaveform::progTrack.doAutoPowerOff(); + callback(-1); return; case STARTMERGE: @@ -632,6 +647,7 @@ void DCC::ackManagerLoop(bool blocking) { case COMBINELOCOID: // ackManagerStash is cv17, ackManagerByte is CV 18 ackManagerProg=NULL; + DCCWaveform::progTrack.doAutoPowerOff(); callback( ackManagerByte + ((ackManagerStash - 192) << 8)); return; diff --git a/DCC.h b/DCC.h index 195a918..ea14cbf 100644 --- a/DCC.h +++ b/DCC.h @@ -120,7 +120,7 @@ private: static ACK_CALLBACK ackManagerCallback; static void ackManagerSetup(int cv, byte bitNumOrbyteValue, ackOp const program[], ACK_CALLBACK callback, bool blocking); static void ackManagerLoop(bool blocking); - static bool checkResets(bool blocking); + static bool checkResets(bool blocking, uint8_t numResets); static const int PROG_REPEATS=8; // repeats of programming commands (some decoders need at least 8 to be reliable) diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 272eb78..53c5432 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -1,5 +1,6 @@ /* * © 2020, Chris Harlow. All rights reserved. + * © 2020, Harald Barth. * * This file is part of Asbelos DCC API * diff --git a/DCCWaveform.h b/DCCWaveform.h index 6537611..489dc89 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -1,5 +1,6 @@ /* * © 2020, Chris Harlow. All rights reserved. + * © 2020, Harald Barth. * * This file is part of Asbelos DCC API * @@ -60,10 +61,17 @@ class DCCWaveform { void schedulePacket(const byte buffer[], byte byteCount, byte repeats); volatile bool packetPending; volatile byte sentResetsSincePacket; + volatile bool autoPowerOff=false; void setAckBaseline(bool debug); //prog track only void setAckPending(bool debug); //prog track only byte getAck(bool debug); //prog track only 0=NACK, 1=ACK 2=keep waiting static bool progTrackSyncMain; // true when prog track is a siding switched to main + inline void doAutoPowerOff() { + if (autoPowerOff) { + setPowerMode(POWERMODE::OFF); + autoPowerOff=false; + } + }; private: static VirtualTimer * interruptTimer;