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 7d76e59..cffa16a 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -47,15 +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]; + + + // 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(); - // 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(); } @@ -70,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 = 0; + 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; @@ -112,7 +126,6 @@ void DCCWaveform::checkPowerOverload() { break; case POWERMODE::ON: // Check current - lastCurrent = motorDriver->getCurrentRaw(); if (lastCurrent <= tripValue) { sampleDelay = POWER_SAMPLE_ON_WAIT; if(power_good_counter<100) @@ -141,68 +154,38 @@ 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. - switch (state) { - case 0: // start of bit transmission - setSignal(HIGH); - state = 1; - return true; // must call interrupt2 to set currentBit - - case 1: // 58us after case 0 - if (currentBit) { - setSignal(LOW); - state = 0; - } - else { - setSignal(HIGH); // jitter prevention - state = 2; - } - break; - case 2: // 116us after case 0 - setSignal(LOW); - state = 3; - break; - case 3: // finished sending zero bit - setSignal(LOW); // jitter prevention - state = 0; - break; - } - - // 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) checkAck(); - - return false; - -} - -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); -} - +// 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}; + void DCCWaveform::interrupt2() { - // set currentBit to be 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 ) { - currentBit = true; + state=WAVE_MID_1; // switch state to trigger LOW on next interrupt remainingPreambles--; 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 - currentBit = transmitPacket[bytes_sent] & bitMask[bits_sent]; + state=(transmitPacket[bytes_sent] & bitMask[bits_sent])? WAVE_MID_1 : WAVE_HIGH_0; bits_sent++; // If this is the last bit of a byte, prepare for the next byte @@ -267,7 +250,7 @@ int DCCWaveform::getLastCurrent() { void DCCWaveform::setAckBaseline() { if (isMainTrack) return; - int baseline = motorDriver->getCurrentRaw(); + int baseline = lastCurrent; ackThreshold= baseline + motorDriver->mA2raw(ackLimitmA); if (Diag::ACK) DIAG(F("\nACK baseline=%d/%dmA Threshold=%d/%dmA Duration: %dus <= pulse <= %dus"), baseline,motorDriver->raw2mA(baseline), @@ -302,7 +285,6 @@ void DCCWaveform::checkAck() { return; } - lastCurrent=motorDriver->getCurrentRaw(); if (lastCurrent > ackMaxCurrent) ackMaxCurrent=lastCurrent; // An ACK is a pulse lasting between minAckPulseDuration and maxAckPulseDuration uSecs (refer @haba) diff --git a/DCCWaveform.h b/DCCWaveform.h index c193545..18ed6aa 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -29,15 +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 class POWERMODE : byte { OFF, ON, OVERLOAD }; const byte idlePacket[] = {0xFF, 0x00, 0xFF}; const byte resetPacket[] = {0x00, 0x00, 0x00}; @@ -103,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; @@ -118,15 +125,13 @@ class DCCWaveform { byte transmitRepeats; // remaining repeats of transmission byte remainingPreambles; byte requiredPreambles; - bool currentBit; // bit to be transmitted byte bits_sent; // 0-8 (yes 9 bits) sent for current byte byte bytes_sent; // number of bytes sent from transmitPacket - byte state; // wave generator state machine - + WAVE_STATE state; // wave generator state machine byte pendingPacket[MAX_PACKET_SIZE]; byte pendingLength; byte pendingRepeats; - int lastCurrent; + volatile int lastCurrent; int maxmA; int tripmA; diff --git a/MotorDriver.cpp b/MotorDriver.cpp index ae832e9..c4ef893 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -45,6 +45,7 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8 faultPin=fault_pin; tripMilliamps=trip_milliamps; rawCurrentTripValue=(int)(trip_milliamps / sense_factor); + simulatedOverload=(int)(32000/senseFactor); pinMode(powerPin, OUTPUT); pinMode(brakePin < 0 ? -brakePin : brakePin, OUTPUT); setBrake(false); @@ -91,7 +92,7 @@ void MotorDriver::setSignal( bool high) { int MotorDriver::getCurrentRaw() { if (faultPin != UNUSED_PIN && ReadPin(faultPin) == LOW && ReadPin(powerPin) == HIGH) - return (int)(32000/senseFactor); + return simulatedOverload; // IMPORTANT: This function can be called in Interrupt() time within the 56uS timer // The default analogRead takes ~100uS which is catastrphic diff --git a/MotorDriver.h b/MotorDriver.h index 3f67080..077ab1d 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -43,5 +43,6 @@ class MotorDriver { float senseFactor; unsigned int tripMilliamps; int rawCurrentTripValue; + int simulatedOverload; }; #endif 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