mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-24 00:26:13 +01:00
Merge remote-tracking branch 'origin/nanoEvery2' into dex/unowifi
This commit is contained in:
commit
50d104289d
2
DCC.h
2
DCC.h
|
@ -24,7 +24,7 @@
|
||||||
#include "FSH.h"
|
#include "FSH.h"
|
||||||
typedef void (*ACK_CALLBACK)(int result);
|
typedef void (*ACK_CALLBACK)(int result);
|
||||||
|
|
||||||
enum ackOp
|
enum ackOp : byte
|
||||||
{ // Program opcodes for the ack Manager
|
{ // Program opcodes for the ack Manager
|
||||||
BASELINE, // ensure enough resets sent before starting and obtain baseline current
|
BASELINE, // ensure enough resets sent before starting and obtain baseline current
|
||||||
W0,
|
W0,
|
||||||
|
|
116
DCCWaveform.cpp
116
DCCWaveform.cpp
|
@ -47,15 +47,30 @@ void DCCWaveform::loop() {
|
||||||
|
|
||||||
void DCCWaveform::interruptHandler() {
|
void DCCWaveform::interruptHandler() {
|
||||||
// call the timer edge sensitive actions for progtrack and maintrack
|
// call the timer edge sensitive actions for progtrack and maintrack
|
||||||
bool mainCall2 = mainTrack.interrupt1();
|
// member functions would be cleaner but have more overhead
|
||||||
bool progCall2 = progTrack.interrupt1();
|
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) {
|
DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
|
||||||
// establish appropriate pins
|
|
||||||
isMainTrack = isMain;
|
isMainTrack = isMain;
|
||||||
packetPending = false;
|
packetPending = false;
|
||||||
memcpy(transmitPacket, idlePacket, sizeof(idlePacket));
|
memcpy(transmitPacket, idlePacket, sizeof(idlePacket));
|
||||||
state = 0;
|
state = WAVE_START;
|
||||||
// The +1 below is to allow the preamble generator to create the stop bit
|
// 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;
|
requiredPreambles = preambleBits+1;
|
||||||
bytes_sent = 0;
|
bytes_sent = 0;
|
||||||
bits_sent = 0;
|
bits_sent = 0;
|
||||||
|
@ -112,7 +126,6 @@ void DCCWaveform::checkPowerOverload() {
|
||||||
break;
|
break;
|
||||||
case POWERMODE::ON:
|
case POWERMODE::ON:
|
||||||
// Check current
|
// Check current
|
||||||
lastCurrent = motorDriver->getCurrentRaw();
|
|
||||||
if (lastCurrent <= tripValue) {
|
if (lastCurrent <= tripValue) {
|
||||||
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
||||||
if(power_good_counter<100)
|
if(power_good_counter<100)
|
||||||
|
@ -141,68 +154,38 @@ void DCCWaveform::checkPowerOverload() {
|
||||||
sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning.
|
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
|
// For each state of the wave, signal pin is HIGH or LOW
|
||||||
// return true if second level required
|
const bool DCCWaveform::signalTransform[]={
|
||||||
bool DCCWaveform::interrupt1() {
|
/* WAVE_START -> */ HIGH,
|
||||||
// NOTE: this must consume transmission buffers even if the power is off
|
/* WAVE_MID_1 -> */ LOW,
|
||||||
// otherwise can cause hangs in main loop waiting for the pendingBuffer.
|
/* WAVE_HIGH_0 -> */ HIGH,
|
||||||
switch (state) {
|
/* WAVE_MID_0 -> */ LOW,
|
||||||
case 0: // start of bit transmission
|
/* WAVE_LOW_0 -> */ LOW,
|
||||||
setSignal(HIGH);
|
/* WAVE_PENDING (should not happen) -> */ LOW};
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DCCWaveform::interrupt2() {
|
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 ) {
|
if (remainingPreambles > 0 ) {
|
||||||
currentBit = true;
|
state=WAVE_MID_1; // switch state to trigger LOW on next interrupt
|
||||||
remainingPreambles--;
|
remainingPreambles--;
|
||||||
return;
|
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
|
// 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++;
|
bits_sent++;
|
||||||
|
|
||||||
// If this is the last bit of a byte, prepare for the next byte
|
// If this is the last bit of a byte, prepare for the next byte
|
||||||
|
@ -267,7 +250,7 @@ int DCCWaveform::getLastCurrent() {
|
||||||
|
|
||||||
void DCCWaveform::setAckBaseline() {
|
void DCCWaveform::setAckBaseline() {
|
||||||
if (isMainTrack) return;
|
if (isMainTrack) return;
|
||||||
int baseline = motorDriver->getCurrentRaw();
|
int baseline = lastCurrent;
|
||||||
ackThreshold= baseline + motorDriver->mA2raw(ackLimitmA);
|
ackThreshold= baseline + motorDriver->mA2raw(ackLimitmA);
|
||||||
if (Diag::ACK) DIAG(F("\nACK baseline=%d/%dmA Threshold=%d/%dmA Duration: %dus <= pulse <= %dus"),
|
if (Diag::ACK) DIAG(F("\nACK baseline=%d/%dmA Threshold=%d/%dmA Duration: %dus <= pulse <= %dus"),
|
||||||
baseline,motorDriver->raw2mA(baseline),
|
baseline,motorDriver->raw2mA(baseline),
|
||||||
|
@ -302,7 +285,6 @@ void DCCWaveform::checkAck() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
lastCurrent=motorDriver->getCurrentRaw();
|
|
||||||
if (lastCurrent > ackMaxCurrent) ackMaxCurrent=lastCurrent;
|
if (lastCurrent > ackMaxCurrent) ackMaxCurrent=lastCurrent;
|
||||||
// An ACK is a pulse lasting between minAckPulseDuration and maxAckPulseDuration uSecs (refer @haba)
|
// An ACK is a pulse lasting between minAckPulseDuration and maxAckPulseDuration uSecs (refer @haba)
|
||||||
|
|
||||||
|
|
|
@ -29,15 +29,18 @@ const int POWER_SAMPLE_OVERLOAD_WAIT = 20;
|
||||||
// Number of preamble bits.
|
// Number of preamble bits.
|
||||||
const int PREAMBLE_BITS_MAIN = 16;
|
const int PREAMBLE_BITS_MAIN = 16;
|
||||||
const int PREAMBLE_BITS_PROG = 22;
|
const int PREAMBLE_BITS_PROG = 22;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const byte MAX_PACKET_SIZE = 12;
|
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
|
// NOTE: static functions are used for the overall controller, then
|
||||||
// one instance is created for each track.
|
// 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 idlePacket[] = {0xFF, 0x00, 0xFF};
|
||||||
const byte resetPacket[] = {0x00, 0x00, 0x00};
|
const byte resetPacket[] = {0x00, 0x00, 0x00};
|
||||||
|
@ -103,12 +106,16 @@ class DCCWaveform {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
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();
|
static void interruptHandler();
|
||||||
bool interrupt1();
|
|
||||||
void interrupt2();
|
void interrupt2();
|
||||||
void checkAck();
|
void checkAck();
|
||||||
void setSignal(bool high);
|
|
||||||
|
|
||||||
bool isMainTrack;
|
bool isMainTrack;
|
||||||
MotorDriver* motorDriver;
|
MotorDriver* motorDriver;
|
||||||
|
@ -118,15 +125,13 @@ class DCCWaveform {
|
||||||
byte transmitRepeats; // remaining repeats of transmission
|
byte transmitRepeats; // remaining repeats of transmission
|
||||||
byte remainingPreambles;
|
byte remainingPreambles;
|
||||||
byte requiredPreambles;
|
byte requiredPreambles;
|
||||||
bool currentBit; // bit to be transmitted
|
|
||||||
byte bits_sent; // 0-8 (yes 9 bits) sent for current byte
|
byte bits_sent; // 0-8 (yes 9 bits) sent for current byte
|
||||||
byte bytes_sent; // number of bytes sent from transmitPacket
|
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 pendingPacket[MAX_PACKET_SIZE];
|
||||||
byte pendingLength;
|
byte pendingLength;
|
||||||
byte pendingRepeats;
|
byte pendingRepeats;
|
||||||
int lastCurrent;
|
volatile int lastCurrent;
|
||||||
int maxmA;
|
int maxmA;
|
||||||
int tripmA;
|
int tripmA;
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8
|
||||||
faultPin=fault_pin;
|
faultPin=fault_pin;
|
||||||
tripMilliamps=trip_milliamps;
|
tripMilliamps=trip_milliamps;
|
||||||
rawCurrentTripValue=(int)(trip_milliamps / sense_factor);
|
rawCurrentTripValue=(int)(trip_milliamps / sense_factor);
|
||||||
|
simulatedOverload=(int)(32000/senseFactor);
|
||||||
pinMode(powerPin, OUTPUT);
|
pinMode(powerPin, OUTPUT);
|
||||||
pinMode(brakePin < 0 ? -brakePin : brakePin, OUTPUT);
|
pinMode(brakePin < 0 ? -brakePin : brakePin, OUTPUT);
|
||||||
setBrake(false);
|
setBrake(false);
|
||||||
|
@ -91,7 +92,7 @@ void MotorDriver::setSignal( bool high) {
|
||||||
|
|
||||||
int MotorDriver::getCurrentRaw() {
|
int MotorDriver::getCurrentRaw() {
|
||||||
if (faultPin != UNUSED_PIN && ReadPin(faultPin) == LOW && ReadPin(powerPin) == HIGH)
|
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
|
// IMPORTANT: This function can be called in Interrupt() time within the 56uS timer
|
||||||
// The default analogRead takes ~100uS which is catastrphic
|
// The default analogRead takes ~100uS which is catastrphic
|
||||||
|
|
|
@ -43,5 +43,6 @@ class MotorDriver {
|
||||||
float senseFactor;
|
float senseFactor;
|
||||||
unsigned int tripMilliamps;
|
unsigned int tripMilliamps;
|
||||||
int rawCurrentTripValue;
|
int rawCurrentTripValue;
|
||||||
|
int simulatedOverload;
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -15,12 +15,12 @@ class WifiInboundHandler {
|
||||||
static WifiInboundHandler * singleton;
|
static WifiInboundHandler * singleton;
|
||||||
|
|
||||||
|
|
||||||
enum INBOUND_STATE {
|
enum INBOUND_STATE : byte {
|
||||||
INBOUND_BUSY, // keep calling in loop()
|
INBOUND_BUSY, // keep calling in loop()
|
||||||
INBOUND_IDLE // Nothing happening, outbound may xcall CIPSEND
|
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...
|
ANYTHING, // ready for +IPD, n CLOSED, n CONNECTED, busy etc...
|
||||||
SKIPTOEND, // skip to newline
|
SKIPTOEND, // skip to newline
|
||||||
|
|
||||||
|
|
|
@ -10,5 +10,7 @@ ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt
|
||||||
avr-objdump -x -C %ELF% | find ".data" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt
|
avr-objdump -x -C %ELF% | find ".data" | sort /+25 /R >>%TMP%\OBJDUMP_%a%.txt
|
||||||
ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt
|
ECHO ++++++++++++++++++++++++++++++++++ >>%TMP%\OBJDUMP_%a%.txt
|
||||||
avr-objdump -x -C %ELF% | find ".bss" | sort /+25 /R >>%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
|
notepad %TMP%\OBJDUMP_%a%.txt
|
||||||
EXIT
|
EXIT
|
||||||
|
|
Loading…
Reference in New Issue
Block a user