1
0
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:
Dex's Lab 2021-01-27 16:16:51 -05:00
commit 50d104289d
7 changed files with 73 additions and 82 deletions

2
DCC.h
View File

@ -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,

View File

@ -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)

View File

@ -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;

View File

@ -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

View File

@ -43,5 +43,6 @@ class MotorDriver {
float senseFactor; float senseFactor;
unsigned int tripMilliamps; unsigned int tripMilliamps;
int rawCurrentTripValue; int rawCurrentTripValue;
int simulatedOverload;
}; };
#endif #endif

View File

@ -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

View File

@ -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