mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-12-24 13:21:23 +01:00
Microtuning waveform
Significant reduction in code parths and call overheads
This commit is contained in:
parent
4e6f79589a
commit
7df07b03e4
2
DCC.h
2
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,
|
||||
|
114
DCCWaveform.cpp
114
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];
|
||||
|
||||
// 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();
|
||||
// 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();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
|
||||
@ -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.
|
||||
|
||||
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);
|
||||
}
|
||||
// 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() {
|
||||
// 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++;
|
||||
|
@ -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};
|
||||
@ -105,11 +107,15 @@ 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;
|
||||
@ -125,7 +131,7 @@ class DCCWaveform {
|
||||
byte pendingPacket[MAX_PACKET_SIZE];
|
||||
byte pendingLength;
|
||||
byte pendingRepeats;
|
||||
int lastCurrent;
|
||||
volatile int lastCurrent;
|
||||
int maxmA;
|
||||
int tripmA;
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user