From 7df07b03e43b581852797dce21114006a4acc508 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 27 Jan 2021 16:58:42 +0000 Subject: [PATCH] Microtuning waveform Significant reduction in code parths and call overheads --- DCC.h | 2 +- DCCWaveform.cpp | 114 ++++++++++++++++--------------------------- DCCWaveform.h | 26 ++++++---- WifiInboundHandler.h | 4 +- objdump.bat | 2 + 5 files changed, 64 insertions(+), 84 deletions(-) diff --git a/DCC.h b/DCC.h index 524c05c..a7646a8 100644 --- a/DCC.h +++ b/DCC.h @@ -24,7 +24,7 @@ #include "FSH.h" typedef void (*ACK_CALLBACK)(int result); -enum ackOp +enum ackOp : byte { // Program opcodes for the ack Manager BASELINE, // ensure enough resets sent before starting and obtain baseline current W0, diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 54647dd..cffa16a 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -47,19 +47,30 @@ void DCCWaveform::loop() { void DCCWaveform::interruptHandler() { // call the timer edge sensitive actions for progtrack and maintrack - bool mainCall2 = mainTrack.interrupt1(); - bool progCall2 = progTrack.interrupt1(); + // member functions would be cleaner but have more overhead + byte sigMain=signalTransform[mainTrack.state]; + byte sigProg=progTrackSyncMain? sigMain : signalTransform[progTrack.state]; + + // Set the signal state for both tracks + mainTrack.motorDriver->setSignal(sigMain); + progTrack.motorDriver->setSignal(sigProg); + + // Move on in the state engine + mainTrack.state=stateTransform[mainTrack.state]; + progTrack.state=stateTransform[progTrack.state]; - // call (if necessary) the procs to get the current bits - // these must complete within 50microsecs of the interrupt - // but they are only called ONCE PER BIT TRANSMITTED - // after the rising edge of the signal - if (mainCall2) mainTrack.interrupt2(); - if (progCall2) progTrack.interrupt2(); - // Read current if in high middle of zero wave (state is set for NEXT interrupt!) - if (mainTrack.state==WAVE_MID_0) mainTrack.lastCurrent=mainTrack.motorDriver->getCurrentRaw(); - if (progTrack.state==WAVE_MID_0) progTrack.lastCurrent=progTrack.motorDriver->getCurrentRaw(); + // WAVE_PENDING means we dont yet know what the next bit is + // so we dont check cutrrent on this cycle + if (mainTrack.state!=WAVE_PENDING && progTrack.state!=WAVE_PENDING) { + mainTrack.lastCurrent=mainTrack.motorDriver->getCurrentRaw(); + progTrack.lastCurrent=progTrack.motorDriver->getCurrentRaw(); + } + + if (mainTrack.state==WAVE_PENDING) mainTrack.interrupt2(); + if (progTrack.state==WAVE_PENDING) progTrack.interrupt2(); + else if (progTrack.ackPending) progTrack.checkAck(); + } @@ -74,13 +85,12 @@ const byte bitMask[] = {0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) { - // establish appropriate pins isMainTrack = isMain; packetPending = false; memcpy(transmitPacket, idlePacket, sizeof(idlePacket)); state = WAVE_START; // The +1 below is to allow the preamble generator to create the stop bit - // fpr the previous packet. + // for the previous packet. requiredPreambles = preambleBits+1; bytes_sent = 0; bits_sent = 0; @@ -144,67 +154,28 @@ void DCCWaveform::checkPowerOverload() { sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning. } } +// For each state of the wave nextState=stateTransform[currentState] +const WAVE_STATE DCCWaveform::stateTransform[]={ + /* WAVE_START -> */ WAVE_PENDING, + /* WAVE_MID_1 -> */ WAVE_START, + /* WAVE_HIGH_0 -> */ WAVE_MID_0, + /* WAVE_MID_0 -> */ WAVE_LOW_0, + /* WAVE_LOW_0 -> */ WAVE_START, + /* WAVE_PENDING (should not happen) -> */ WAVE_PENDING}; -// process time-edge sensitive part of interrupt -// return true if second level required - -bool DCCWaveform::interrupt1() { - // NOTE: this must consume transmission buffers even if the power is off - // otherwise can cause hangs in main loop waiting for the pendingBuffer. - byte sigwave; - switch (state) { - // Each section of this case is designed to run as near as possible in the same cpu time - // hence some unnecessary duplication and pin setting. - // Breaking this causes jitter in the prog track waveform. +// For each state of the wave, signal pin is HIGH or LOW +const bool DCCWaveform::signalTransform[]={ + /* WAVE_START -> */ HIGH, + /* WAVE_MID_1 -> */ LOW, + /* WAVE_HIGH_0 -> */ HIGH, + /* WAVE_MID_0 -> */ LOW, + /* WAVE_LOW_0 -> */ LOW, + /* WAVE_PENDING (should not happen) -> */ LOW}; - case WAVE_START: // start of bit transmission - sigwave=HIGH; - state = WAVE_PENDING; - break; // must call interrupt2 to set next state - - case WAVE_MID_1: // 58us after case 0 with currentbit=1 - sigwave=LOW; - state = WAVE_START; - break; - - case WAVE_HIGH_0: // 58us after case 0 with currentbit=0 - sigwave=HIGH; - state = WAVE_MID_0; - break; - - case WAVE_MID_0: // 116us after case 0 with currentbit=0 - sigwave=LOW; - state = WAVE_LOW_0; - break; - - case WAVE_LOW_0: // half way through zero-low - sigwave=LOW; // jitter prevention - state = WAVE_START; - break; - } - - setSignal(sigwave); - - // ACK check is prog track only and will only be checked if - // this is not case(0) which needs relatively expensive packet change code to be called. - if (ackPending && state!=WAVE_PENDING) checkAck(); - - return state==WAVE_PENDING; // true, caller must call Interrupt2 -} - -void DCCWaveform::setSignal(bool high) { - if (progTrackSyncMain) { - if (!isMainTrack) return; // ignore PROG track waveform while in sync - // set both tracks to same signal - motorDriver->setSignal(high); - progTrack.motorDriver->setSignal(high); - return; - } - motorDriver->setSignal(high); -} - void DCCWaveform::interrupt2() { - // calculate the next bit to be sent. + // calculate the next bit to be sent: + // set state WAVE_MID_1 for a 1=bit + // or WAVE_HIGH_0 for a 0 bit. if (remainingPreambles > 0 ) { state=WAVE_MID_1; // switch state to trigger LOW on next interrupt @@ -212,6 +183,7 @@ void DCCWaveform::interrupt2() { return; } + // Wave has gone HIGH but what happens next depends on the bit to be transmitted // beware OF 9-BIT MASK generating a zero to start each byte state=(transmitPacket[bytes_sent] & bitMask[bits_sent])? WAVE_MID_1 : WAVE_HIGH_0; bits_sent++; diff --git a/DCCWaveform.h b/DCCWaveform.h index f89367c..18ed6aa 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -29,16 +29,18 @@ const int POWER_SAMPLE_OVERLOAD_WAIT = 20; // Number of preamble bits. const int PREAMBLE_BITS_MAIN = 16; const int PREAMBLE_BITS_PROG = 22; - - - const byte MAX_PACKET_SIZE = 12; + +// The WAVE_STATE enum is deliberately numbered because a change of order would be catastrophic +// to the transform array. +enum WAVE_STATE : byte {WAVE_START=0,WAVE_MID_1=1,WAVE_HIGH_0=2,WAVE_MID_0=3,WAVE_LOW_0=4,WAVE_PENDING=5}; + + // NOTE: static functions are used for the overall controller, then // one instance is created for each track. -enum class POWERMODE { OFF, ON, OVERLOAD }; -enum WAVE_STATE {WAVE_START,WAVE_MID_1,WAVE_HIGH_0,WAVE_MID_0,WAVE_LOW_0,WAVE_PENDING}; +enum class POWERMODE : byte { OFF, ON, OVERLOAD }; const byte idlePacket[] = {0xFF, 0x00, 0xFF}; const byte resetPacket[] = {0x00, 0x00, 0x00}; @@ -104,12 +106,16 @@ class DCCWaveform { } private: - + +// For each state of the wave nextState=stateTransform[currentState] + static const WAVE_STATE stateTransform[6]; + +// For each state of the wave, signal pin is HIGH or LOW + static const bool signalTransform[6]; + static void interruptHandler(); - bool interrupt1(); void interrupt2(); void checkAck(); - void setSignal(bool high); bool isMainTrack; MotorDriver* motorDriver; @@ -122,10 +128,10 @@ class DCCWaveform { byte bits_sent; // 0-8 (yes 9 bits) sent for current byte byte bytes_sent; // number of bytes sent from transmitPacket WAVE_STATE state; // wave generator state machine - byte pendingPacket[MAX_PACKET_SIZE]; + byte pendingPacket[MAX_PACKET_SIZE]; byte pendingLength; byte pendingRepeats; - int lastCurrent; + volatile int lastCurrent; int maxmA; int tripmA; diff --git a/WifiInboundHandler.h b/WifiInboundHandler.h index c2c8865..8a25d9c 100644 --- a/WifiInboundHandler.h +++ b/WifiInboundHandler.h @@ -15,12 +15,12 @@ class WifiInboundHandler { static WifiInboundHandler * singleton; - enum INBOUND_STATE { + enum INBOUND_STATE : byte { INBOUND_BUSY, // keep calling in loop() INBOUND_IDLE // Nothing happening, outbound may xcall CIPSEND }; - enum LOOP_STATE { + enum LOOP_STATE : byte { ANYTHING, // ready for +IPD, n CLOSED, n CONNECTED, busy etc... SKIPTOEND, // skip to newline diff --git a/objdump.bat b/objdump.bat index af30cb5..4b50fba 100644 --- a/objdump.bat +++ b/objdump.bat @@ -10,5 +10,7 @@ ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt avr-objdump -x -C %ELF% | find ".data" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt avr-objdump -x -C %ELF% | find ".bss" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt +ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt +avr-objdump -D -S %ELF% >>%TMP%\OBJDUMP_%a%.txt notepad %TMP%\OBJDUMP_%a%.txt EXIT