From 54e1284e014316df9e96cc337a7fac4bbd3961bd Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 24 May 2020 16:07:16 +0100 Subject: [PATCH] Implemebnt DCC API layer --- CVReader.ino | 54 ++----------- DCC.cpp | 208 +++++++++++++++++++++++++++++++++++++++++++++++++++ DCC.h | 37 +++++++++ 3 files changed, 250 insertions(+), 49 deletions(-) create mode 100644 DCC.cpp create mode 100644 DCC.h diff --git a/CVReader.ino b/CVReader.ino index da01ca5..88933ef 100644 --- a/CVReader.ino +++ b/CVReader.ino @@ -1,4 +1,4 @@ -#include "DCCWaveform.h" +#include "DCC.h" #include "DIAG.h" /* this code is here to test the waveforwe generator and reveal the issues involved in programming track operations. @@ -16,18 +16,17 @@ */ -bool verifyCV(int cv, byte bValue); -int readCv(int cv); + const int cvnums[]={1,2,3,4,5,17,18,19,21,22,29}; void setup() { Serial.begin(115200); - DCCWaveform::begin(); + DCC::begin(); DIAG(F("\n===== CVReader begin ==============================\n")); for (byte x=0;x=0?" VERIFIED OK":"FAILED VERIFICATION"); } @@ -35,48 +34,5 @@ void setup() { } void loop() { - DCCWaveform::loop(); + DCC::loop(); } - -// Two helpers to make API functions look simpler - -byte cv1(byte opcode, int cv) { - cv--; - return (highByte(cv) & (byte)0x03) | opcode; -} -byte cv2(int cv) { - cv--; - return lowByte(cv); -} - -// The functions below are lifted from the DCCApi and then fixed up -// Once reliable, tha DCCApi should be updated to match -bool verifyCV(int cv, byte value) { - byte message[] = { - cv1(0x74,cv) , // set-up to re-verify entire byte - cv2(cv), - value - }; - - DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5); - return DCCWaveform::progTrack.getAck(); -} - -int readCV(int cv) -{ - - byte message[]={ cv1(0x78,cv) , // any CV>1023 will become modulus(1024) due to bit-mask of 0x03 - cv2(cv), - 0}; // trailing zero will be updated in loop below - - byte value = 0; - - for (int i = 0; i<8; i++) { - message[2] = 0xE8 + i; - DCCWaveform::progTrack.schedulePacket(message,sizeof(message), 4); // NMRA recommends 5 read packets - value+= (DCCWaveform::progTrack.getAck()< 127) + b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address + + b[nB++] = lowByte(cab); + b[nB++] = 0x3F; // 128-step speed control byte + if (tSpeed > 0) + b[nB++] = tSpeed + (tSpeed > 0) + tDirection * 128; // max speed is 126, but speed codes range from 2-127 (0=stop, 1=emergency stop) + else { + b[nB++] = 1; + tSpeed = 0; + } + + DCCWaveform::mainTrack.schedulePacket(b, nB, 0); +} + +void DCC::setFunction(int cab, byte byte1) { + uint8_t b[4]; + uint8_t nB = 0; + + if (cab > 127) + b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address + b[nB++] = lowByte(cab); + b[nB++] = (byte1 | 0x80) & 0xBF; + + DCCWaveform::mainTrack.schedulePacket(b, nB, 4); // Repeat the packet four times +} + +void DCC::setFunction(int cab, byte byte1, byte byte2) { + byte b[5]; + byte nB = 0; + + if (cab > 127) + b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address + b[nB++] = lowByte(cab); + b[nB++] = (byte1 | 0xDE) & 0xDF; // for safety this guarantees that first byte will either be 0xDE (for F13-F20) or 0xDF (for F21-F28) + b[nB++] = byte2; + + DCCWaveform::mainTrack.schedulePacket(b, nB, 4); // Repeat the packet four times +} + +void DCC::setAccessory(int address, byte number, bool activate) { + byte b[3]; // save space for checksum byte + + b[0] = address % 64 + 128; // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address + b[1] = ((((address / 64) % 8) << 4) + (number % 4 << 1) + activate % 2) ^ 0xF8; // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate + + DCCWaveform::mainTrack.schedulePacket(b, 2, 4); // Repeat the packet four times +} + +void DCC::writeCVByteMain(int cab, int cv, byte bValue) { + byte b[6]; // save space for checksum byte + byte nB = 0; + if (cab > 127) + b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address + + b[nB++] = lowByte(cab); + b[nB++] = cv1(0xEC, cv); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03 + b[nB++] = cv2(cv); + b[nB++] = bValue; + + DCCWaveform::mainTrack.schedulePacket(b, nB, 4); +} + +void DCC::writeCVBitMain(int cab, int cv, byte bNum, bool bValue) { + byte b[6]; // save space for checksum byte + byte nB = 0; + bValue = bValue % 2; + bNum = bNum % 8; + + if (cab > 127) + b[nB++] = highByte(cab) | 0xC0; // convert train number into a two-byte address + + b[nB++] = lowByte(cab); + b[nB++] = cv1(0xE8, cv); // any CV>1023 will become modulus(1024) due to bit-mask of 0x03 + b[nB++] = cv2(cv); + b[nB++] = 0xF0 + bValue * 8 + bNum; + + DCCWaveform::mainTrack.schedulePacket(b, nB, 4); +} + +bool DCC::writeCVByte(int cv, byte bValue) { + uint8_t message[] = {cv1(0x7C, cv), cv2(cv), bValue}; + DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6); // NMRA recommends 6 write or reset packets for decoder recovery time + return verifyCV(cv, bValue); +} + + +bool DCC::writeCVBit(int cv, byte bNum, bool bValue) { + bValue = bValue % 2; + bNum = bNum % 8; + uint8_t message[] = {cv1(0x78, cv), cv2(cv), 0xF0 + bValue * 8 + bNum}; + DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 6); // NMRA recommends 6 write or reset packets for decoder recovery time + + /* TODO... what is the verify opcode here? + bitWrite(message[2],4,1); // change instruction code from Write Bit to Verify Bit + DCCWaveform::progTrack.schedulePacket(message,sizeof(message),6); // NMRA recommends 6 write or reset packets for decoder recovery time + */ + + return DCCWaveform::progTrack.getAck(); +} + + +int DCC::readCV(int cv) { + byte message[] = { cv1(0x78, cv) , // any CV>1023 will become modulus(1024) due to bit-mask of 0x03 + cv2(cv), + 0 + }; // trailing zero will be updated in loop below + + byte value = 0; + + // get each bit individually + for (int i = 0; i < 8; i++) { + message[2] = 0xE8 + i; + DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 4); // NMRA recommends 5 read packets + value += (DCCWaveform::progTrack.getAck() << i); + } + + return verifyCV(cv, value) ? value : -1; +} + +void DCC::loop() { + DCCWaveform::loop(); // powwer overload checks + // if the main track transmitter still has a pending packet, skip this loop. + if ( DCCWaveform::mainTrack.packetPending) return; + + // each time around the Arduino loop, we resend a loco speed packet reminder + for (; nextLoco < MAX_LOCOS; nextLoco++) { + if (speedTable[nextLoco].loco > 0) { + setThrottle2(speedTable[nextLoco].loco, speedTable[nextLoco].speed, speedTable[nextLoco].forward); + nextLoco++; + return; + } + } + for (nextLoco = 0; nextLoco < MAX_LOCOS; nextLoco++) { + if (speedTable[nextLoco].loco > 0) { + setThrottle2(speedTable[nextLoco].loco, speedTable[nextLoco].speed, speedTable[nextLoco].forward); + nextLoco++; + return; + } + } +} + +///// Private helper functions below here ///////////////////// +byte DCC::cv1(byte opcode, int cv) { + cv--; + return (highByte(cv) & (byte)0x03) | opcode; +} +byte DCC::cv2(int cv) { + cv--; + return lowByte(cv); +} + +bool DCC::verifyCV(int cv, byte value) { + byte message[] = { cv1(0x74, cv), cv2(cv), value}; + DCCWaveform::progTrack.schedulePacket(message, sizeof(message), 5); + return DCCWaveform::progTrack.getAck(); +} + + +void DCC::updateLocoReminder(int loco, byte tSpeed, bool forward) { + // determine speed reg for this loco + int reg; + int firstEmpty = MAX_LOCOS; + for (reg = 0; reg < MAX_LOCOS; reg++) { + if (speedTable[reg].loco == loco) break; + if (speedTable[reg].loco == 0 && firstEmpty == MAX_LOCOS) firstEmpty = reg; + } + if (reg == MAX_LOCOS) reg = firstEmpty; + if (reg >= MAX_LOCOS) { + DIAG(F("\nToo many locos\n")); + return; + } + speedTable[reg].loco = loco; + speedTable[reg].speed = tSpeed; + speedTable[reg].forward = forward; +} +DCC::LOCO DCC::speedTable[MAX_LOCOS]; +int DCC::nextLoco=0; diff --git a/DCC.h b/DCC.h new file mode 100644 index 0000000..e7c763c --- /dev/null +++ b/DCC.h @@ -0,0 +1,37 @@ +#include +const byte MAX_LOCOS=200; + + +class DCC { + public: + + static void begin(); + static void loop(); + + // Public DCC API functions + static void setThrottle( uint16_t cab, uint8_t tSpeed, bool tDirection); + static int readCV(int cv); + static bool writeCVByte(int cv, byte bValue) ; + static bool writeCVBit(int cv, byte bNum, bool bValue); + static void writeCVByteMain(int cab, int cv, byte bValue); + static void writeCVBitMain(int cab, int cv, byte bNum, bool bValue); + static void setFunction( int cab, byte fByte, byte eByte); + static void setFunction( int cab, byte fByte); + static void setAccessory(int aAdd, byte aNum, bool activate) ; + static bool writeTextPacket( byte *b, int nBytes); + +private: + struct LOCO { + int loco; + byte speed; + bool forward; + }; + static bool verifyCV(int cv,byte bValue); + static void setThrottle2( uint16_t cab, uint8_t tSpeed, bool tDirection); + static void updateLocoReminder(int loco, byte tSpeed, bool forward); + static int nextLoco; + static LOCO speedTable[MAX_LOCOS]; + static byte cv1(byte opcode, int cv); + static byte cv2(int cv); + +};