1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-23 08:06:13 +01:00
CommandStation-EX/DCCWaveform.cpp

243 lines
6.9 KiB
C++
Raw Normal View History

2020-05-24 00:02:54 +02:00
#include <Arduino.h>
#include "Hardware.h"
2020-05-24 00:02:54 +02:00
#include "DCCWaveform.h"
#include "DIAG.h"
DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true);
DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
2020-05-24 00:02:54 +02:00
void DCCWaveform::begin() {
Hardware::init();
Hardware::setCallback(58, interruptHandler);
mainTrack.beginTrack();
progTrack.beginTrack();
2020-05-24 00:02:54 +02:00
}
void DCCWaveform::loop() {
mainTrack.checkPowerOverload();
progTrack.checkPowerOverload();
}
2020-05-24 00:02:54 +02:00
// static //
void DCCWaveform::interruptHandler() {
// call the timer edge sensitive actions for progtrack and maintrack
bool mainCall2 = mainTrack.interrupt1();
bool progCall2 = progTrack.interrupt1();
// 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();
2020-05-24 00:02:54 +02:00
}
// An instance of this class handles the DCC transmissions for one track. (main or prog)
// Interrupts are marshalled via the statics.
// A track has a current transmit buffer, and a pending buffer.
// When the current buffer is exhausted, either the pending buffer (if there is one waiting) or an idle buffer.
2020-05-24 00:02:54 +02:00
// This bitmask has 9 entries as each byte is trasmitted as a zero + 8 bits.
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;
requiredPreambles = preambleBits;
bytes_sent = 0;
bits_sent = 0;
nextSampleDue = 0;
}
void DCCWaveform::beginTrack() {
setPowerMode(POWERMODE::ON);
2020-05-24 00:02:54 +02:00
}
POWERMODE DCCWaveform::getPowerMode() {
2020-05-24 00:02:54 +02:00
return powerMode;
}
void DCCWaveform::setPowerMode(POWERMODE mode) {
powerMode = mode;
Hardware::setPower(isMainTrack, mode == POWERMODE::ON);
if (mode == POWERMODE::ON) delay(200);
}
2020-05-24 00:02:54 +02:00
void DCCWaveform::checkPowerOverload() {
if (millis() < nextSampleDue) return;
2020-05-24 00:02:54 +02:00
int current;
int delay;
2020-05-24 00:02:54 +02:00
switch (powerMode) {
case POWERMODE::OFF:
delay = POWER_SAMPLE_OFF_WAIT;
2020-05-24 00:02:54 +02:00
break;
case POWERMODE::ON:
// Check current
current = Hardware::getCurrentMilliamps(isMainTrack);
if (current < POWER_SAMPLE_MAX) delay = POWER_SAMPLE_ON_WAIT;
2020-05-24 00:02:54 +02:00
else {
setPowerMode(POWERMODE::OVERLOAD);
DIAG(F("\n*** %s TRACK POWER OVERLOAD current=%d max=%d ***\n"), isMainTrack ? "MAIN" : "PROG", current, POWER_SAMPLE_MAX);
delay = POWER_SAMPLE_OVERLOAD_WAIT;
2020-05-24 00:02:54 +02:00
}
break;
case POWERMODE::OVERLOAD:
2020-05-24 00:02:54 +02:00
// Try setting it back on after the OVERLOAD_WAIT
setPowerMode(POWERMODE::ON);
delay = POWER_SAMPLE_ON_WAIT;
2020-05-24 00:02:54 +02:00
break;
default:
delay = 999; // cant get here..meaningless statement to avoid compiler warning.
2020-05-24 00:02:54 +02:00
}
nextSampleDue = millis() + delay;
2020-05-24 00:02:54 +02:00
}
// process time-edge sensitive part of interrupt
// return true if second level required
2020-05-24 00:02:54 +02:00
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.
2020-05-24 00:02:54 +02:00
switch (state) {
case 0: // start of bit transmission
Hardware::setSignal(isMainTrack, HIGH);
2020-05-24 11:27:33 +02:00
checkRailcom();
2020-05-24 00:02:54 +02:00
state = 1;
2020-05-24 11:27:33 +02:00
return true; // must call interrupt2 to set currentBit
2020-05-24 00:02:54 +02:00
2020-05-24 11:27:33 +02:00
case 1: // 58us after case 0
2020-05-24 00:02:54 +02:00
if (currentBit) {
Hardware::setSignal(isMainTrack, LOW);
2020-05-24 00:02:54 +02:00
state = 0;
}
else state = 2;
break;
2020-05-24 11:27:33 +02:00
case 2: // 116us after case 0
Hardware::setSignal(isMainTrack, LOW);
2020-05-24 00:02:54 +02:00
state = 3;
break;
case 3: // finished sending zero bit
2020-05-24 11:27:33 +02:00
state = 0;
break;
2020-05-24 00:02:54 +02:00
}
return false;
2020-05-24 00:02:54 +02:00
}
2020-05-24 00:02:54 +02:00
void DCCWaveform::interrupt2() {
// set currentBit to be the next bit to be sent.
2020-05-24 00:02:54 +02:00
if (remainingPreambles > 0 ) {
currentBit = true;
2020-05-24 00:02:54 +02:00
remainingPreambles--;
return;
}
// beware OF 9-BIT MASK generating a zero to start each byte
currentBit = transmitPacket[bytes_sent] & bitMask[bits_sent];
2020-05-24 00:02:54 +02:00
bits_sent++;
// If this is the last bit of a byte, prepare for the next byte
if (bits_sent == 9) { // zero followed by 8 bits of a byte
//end of Byte
bits_sent = 0;
bytes_sent++;
// if this is the last byte, prepere for next packet
if (bytes_sent >= transmitLength) {
// end of transmission buffer... repeat or switch to next message
bytes_sent = 0;
remainingPreambles = requiredPreambles;
if (transmitRepeats > 0) {
transmitRepeats--;
}
else if (packetPending) {
// Copy pending packet to transmit packet
for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b];
transmitLength = pendingLength;
transmitRepeats = pendingRepeats;
packetPending = false;
}
else {
// Fortunately reset and idle packets are the same length
memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket));
transmitLength = sizeof(idlePacket);
transmitRepeats = 0;
2020-05-24 00:02:54 +02:00
}
}
}
}
2020-05-24 11:27:33 +02:00
void DCCWaveform::checkRailcom() {
if (isMainTrack && RAILCOM_CUTOUT) {
byte preamble = PREAMBLE_BITS_MAIN - remainingPreambles;
if (preamble == RAILCOM_PREAMBLES_BEFORE_CUTOUT) {
// TODO Railcom::startCutout();
}
}
2020-05-24 11:27:33 +02:00
}
2020-05-24 00:02:54 +02:00
// Wait until there is no packet pending, then make this pending
2020-05-24 00:02:54 +02:00
void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) {
if (byteCount >= MAX_PACKET_SIZE) return; // allow for chksum
while (packetPending);
byte checksum = 0;
for (int b = 0; b < byteCount; b++) {
checksum ^= buffer[b];
pendingPacket[b] = buffer[b];
2020-05-24 00:02:54 +02:00
}
pendingPacket[byteCount] = checksum;
pendingLength = byteCount + 1;
pendingRepeats = repeats;
packetPending = true;
}
2020-05-24 00:02:54 +02:00
bool DCCWaveform::getAck()
{
if (isMainTrack) return false; // cant do this on main track
while (packetPending); // wait until transmitter has started transmitting the message
unsigned long timeout = millis() + ACK_TIMEOUT;
int maxCurrent = 0;
bool result = false;
int upsamples = 0;
int downsamples = 0;
// Monitor looking for a reading high enough to be an ack
while (result == false && timeout > millis()) {
2020-05-24 00:02:54 +02:00
upsamples++;
int current = Hardware::getCurrentMilliamps(isMainTrack);
maxCurrent = max(maxCurrent, current);
result = current > ACK_MIN_PULSE;
2020-05-24 00:02:54 +02:00
}
// Monitor current until ack signal dies back
if (result) while ( true) {
downsamples++;
int current = Hardware::getCurrentMilliamps(isMainTrack);
maxCurrent = max(maxCurrent, current);
if (current <= ACK_MAX_NOT_PULSE) break;
}
// The following DIAG is really useful as it can show how long and how far the
2020-05-24 00:22:12 +02:00
// current changes during an ACK from the decoder.
DIAG(F("\nack=%d max=%d, up=%d, down=%d "), result, maxCurrent, upsamples, downsamples);
2020-05-24 00:02:54 +02:00
return result;
}