From 665a540a735beab73f5734f2a62bed59eeaecc63 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 8 Sep 2020 09:24:37 +0200 Subject: [PATCH 1/8] checkResets takes numResets argument --- DCC.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index bd9e6df..92aa4d7 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -494,13 +494,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 +513,13 @@ void DCC::ackManagerLoop(bool blocking) { // if blocking then we must ONLY return AFTER callback issued switch (opcode) { case BASELINE: - if (checkResets(blocking)) return; + if (checkResets(blocking, 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 +530,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 +540,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 +551,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 }; From 5c775531f63eeac6f7cda647d924deca185885f4 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 8 Sep 2020 09:47:40 +0200 Subject: [PATCH 2/8] Introduce POWERON and POWEROFF opcodes --- DCC.cpp | 15 +++++++++++++++ DCC.h | 4 +++- DCCWaveform.h | 1 + 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/DCC.cpp b/DCC.cpp index 92aa4d7..81f2d43 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -643,6 +643,21 @@ void DCC::ackManagerLoop(bool blocking) { opcode=pgm_read_byte_near(ackManagerProg); } break; + case POWERON: + if (DCCWaveform::progTrack.getPowerMode() == POWERMODE::OFF) { + DCCWaveform::progTrack.setPowerMode(POWERMODE::ON); + DCCWaveform::progTrack.sentResetsSincePacket = 0; + DCCWaveform::progTrack.autoPowerOff=true; + return; + } + if (checkResets(blocking, 20)) return; + break; + case POWEROFF: + if (DCCWaveform::progTrack.autoPowerOff) { + DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF); + DCCWaveform::progTrack.autoPowerOff=false; + } + break; case SKIPTARGET: break; default: diff --git a/DCC.h b/DCC.h index 195a918..6b3e499 100644 --- a/DCC.h +++ b/DCC.h @@ -44,6 +44,8 @@ SETCV, // sets cv number to next prog byte STASHLOCOID, // keeps current byte value for later COMBINELOCOID, // combines current value with stashed value and returns it ITSKIP, // skip to SKIPTARGET if ack true +POWERON, // check if power on prog track is on and remember state +POWEROFF, // turn power off again if it was off before POWERON SKIPTARGET=0xFF // jump to target }; @@ -120,7 +122,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.h b/DCCWaveform.h index 6537611..ebb543e 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -60,6 +60,7 @@ 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 From f3ac7fa403a3dacb11dbaa66249563314d256dc3 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 8 Sep 2020 10:00:59 +0200 Subject: [PATCH 3/8] Use POWERON/OFF in progs --- DCC.cpp | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 81f2d43..7645591 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -208,40 +208,49 @@ void DCC::setProgTrackSyncMain(bool on) { } const ackOp PROGMEM WRITE_BIT0_PROG[] = { + POWERON, BASELINE, W0,WACK, V0, WACK, // validate bit is 0 ITC1, // if acked, callback(1) - FAIL // callback (-1) + FAIL, // callback (-1) + POWEROFF }; const ackOp PROGMEM WRITE_BIT1_PROG[] = { + POWERON, BASELINE, W1,WACK, V1, WACK, // validate bit is 1 ITC1, // if acked, callback(1) - FAIL // callback (-1) + FAIL, // callback (-1) + POWEROFF }; const ackOp PROGMEM READ_BIT_PROG[] = { + POWERON, BASELINE, V1, WACK, // validate bit is 1 ITC1, // if acked, callback(1) V0, WACK, // validate bit is zero ITC0, // if acked callback 0 - FAIL // bit not readable - }; + FAIL, // bit not readable + POWEROFF +}; const ackOp PROGMEM WRITE_BYTE_PROG[] = { + POWERON, BASELINE, WB,WACK, // Write VB,WACK, // validate byte ITC1, // if ok callback (1) - FAIL // callback (-1) - }; + FAIL, // callback (-1) + POWEROFF +}; const ackOp PROGMEM READ_CV_PROG[] = { + POWERON, BASELINE, STARTMERGE, //clear bit and byte values ready for merge pass // each bit is validated against 0 and the result inverted in MERGE @@ -256,10 +265,13 @@ const ackOp PROGMEM READ_CV_PROG[] = { V0, WACK, MERGE, V0, WACK, MERGE, VB, WACK, ITCB, // verify merged byte and return it if acked ok - FAIL }; // verification failed + FAIL, // verification failed + POWEROFF +}; const ackOp PROGMEM LOCO_ID_PROG[] = { + POWERON, BASELINE, SETCV,(ackOp)29, SETBIT,(ackOp)5, @@ -304,8 +316,9 @@ const ackOp PROGMEM LOCO_ID_PROG[] = { V0, WACK, MERGE, V0, WACK, MERGE, VB, WACK, ITCB, // verify merged byte and callback - FAIL - }; + FAIL, + POWEROFF +}; // On the following prog-track functions blocking defaults to false. From 0582d939c5f00a8622562caddb5263ac6757dea4 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 8 Sep 2020 10:12:40 +0200 Subject: [PATCH 4/8] Fastfail if no CV there --- DCC.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 7645591..ad76707 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -256,9 +256,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, From ad4928043c7389f74dfb43c8767d8a1b1cd46d05 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 8 Sep 2020 21:39:54 +0200 Subject: [PATCH 5/8] Revert "Use POWERON/OFF in progs" This reverts commit f3ac7fa403a3dacb11dbaa66249563314d256dc3. --- DCC.cpp | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index ad76707..9471054 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -208,49 +208,40 @@ void DCC::setProgTrackSyncMain(bool on) { } const ackOp PROGMEM WRITE_BIT0_PROG[] = { - POWERON, BASELINE, W0,WACK, V0, WACK, // validate bit is 0 ITC1, // if acked, callback(1) - FAIL, // callback (-1) - POWEROFF + FAIL // callback (-1) }; const ackOp PROGMEM WRITE_BIT1_PROG[] = { - POWERON, BASELINE, W1,WACK, V1, WACK, // validate bit is 1 ITC1, // if acked, callback(1) - FAIL, // callback (-1) - POWEROFF + FAIL // callback (-1) }; const ackOp PROGMEM READ_BIT_PROG[] = { - POWERON, BASELINE, V1, WACK, // validate bit is 1 ITC1, // if acked, callback(1) V0, WACK, // validate bit is zero ITC0, // if acked callback 0 - FAIL, // bit not readable - POWEROFF -}; + FAIL // bit not readable + }; const ackOp PROGMEM WRITE_BYTE_PROG[] = { - POWERON, BASELINE, WB,WACK, // Write VB,WACK, // validate byte ITC1, // if ok callback (1) - FAIL, // callback (-1) - POWEROFF -}; + FAIL // callback (-1) + }; const ackOp PROGMEM READ_CV_PROG[] = { - POWERON, BASELINE, STARTMERGE, //clear bit and byte values ready for merge pass // each bit is validated against 0 and the result inverted in MERGE @@ -270,13 +261,10 @@ const ackOp PROGMEM READ_CV_PROG[] = { V0, WACK, MERGE, V0, WACK, MERGE, VB, WACK, ITCB, // verify merged byte and return it if acked ok - FAIL, // verification failed - POWEROFF -}; + FAIL }; // verification failed const ackOp PROGMEM LOCO_ID_PROG[] = { - POWERON, BASELINE, SETCV,(ackOp)29, SETBIT,(ackOp)5, @@ -321,9 +309,8 @@ const ackOp PROGMEM LOCO_ID_PROG[] = { V0, WACK, MERGE, V0, WACK, MERGE, VB, WACK, ITCB, // verify merged byte and callback - FAIL, - POWEROFF -}; + FAIL + }; // On the following prog-track functions blocking defaults to false. From 7a008938ec7393ff2dd72bdeefbaee7a299a11ef Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 8 Sep 2020 22:14:05 +0200 Subject: [PATCH 6/8] autopower on/off merged into BASELINE and exit codes --- DCC.cpp | 26 +++++++++++++------------- DCCWaveform.h | 6 ++++++ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 9471054..092fe1e 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -518,7 +518,13 @@ void DCC::ackManagerLoop(bool blocking) { // if blocking then we must ONLY return AFTER callback issued switch (opcode) { case BASELINE: - if (checkResets(blocking, 3)) 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 @@ -582,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; } @@ -590,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; } @@ -598,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; } @@ -605,7 +614,8 @@ void DCC::ackManagerLoop(bool blocking) { case FAIL: // callback(-1) ackManagerProg = NULL; - callback(-1); + DCCWaveform::progTrack.doAutoPowerOff(); + callback(-1); return; case STARTMERGE: @@ -637,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; @@ -649,19 +660,8 @@ void DCC::ackManagerLoop(bool blocking) { } break; case POWERON: - if (DCCWaveform::progTrack.getPowerMode() == POWERMODE::OFF) { - DCCWaveform::progTrack.setPowerMode(POWERMODE::ON); - DCCWaveform::progTrack.sentResetsSincePacket = 0; - DCCWaveform::progTrack.autoPowerOff=true; - return; - } - if (checkResets(blocking, 20)) return; break; case POWEROFF: - if (DCCWaveform::progTrack.autoPowerOff) { - DCCWaveform::progTrack.setPowerMode(POWERMODE::OFF); - DCCWaveform::progTrack.autoPowerOff=false; - } break; case SKIPTARGET: break; diff --git a/DCCWaveform.h b/DCCWaveform.h index ebb543e..fb47d0a 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -65,6 +65,12 @@ class DCCWaveform { 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; From d3506583d58796532eb69ddc93a81e399fb1e1ed Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 8 Sep 2020 22:15:42 +0200 Subject: [PATCH 7/8] remove unused POWER* opcodes --- DCC.cpp | 4 ---- DCC.h | 2 -- 2 files changed, 6 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 092fe1e..679f771 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -659,10 +659,6 @@ void DCC::ackManagerLoop(bool blocking) { opcode=pgm_read_byte_near(ackManagerProg); } break; - case POWERON: - break; - case POWEROFF: - break; case SKIPTARGET: break; default: diff --git a/DCC.h b/DCC.h index 6b3e499..ea14cbf 100644 --- a/DCC.h +++ b/DCC.h @@ -44,8 +44,6 @@ SETCV, // sets cv number to next prog byte STASHLOCOID, // keeps current byte value for later COMBINELOCOID, // combines current value with stashed value and returns it ITSKIP, // skip to SKIPTARGET if ack true -POWERON, // check if power on prog track is on and remember state -POWEROFF, // turn power off again if it was off before POWERON SKIPTARGET=0xFF // jump to target }; From 106c68ce13aa7f83b006f5b1d3f07263bd2bb833 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 8 Sep 2020 22:43:25 +0200 Subject: [PATCH 8/8] copyright --- DCCWaveform.cpp | 1 + DCCWaveform.h | 1 + 2 files changed, 2 insertions(+) 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 fb47d0a..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 *