From 07933e42a8a1c1a0ec1deb96429d85c58061bb68 Mon Sep 17 00:00:00 2001 From: Alex Shepherd Date: Thu, 27 Apr 2017 01:26:25 +1200 Subject: [PATCH 1/4] Fixed some bugs around DCC Accessory Output Mode Addressing and handling of edge cases Add function to set Accessory Decoder Address from next received Accessory Decoder command and new notify call-backs when a new address is set Added new notifyDccSigOutputState() with no OutputIndex parameter Bumped version but have NOT tagged the library as it needs more testing and checking for breakage --- NmraDcc.cpp | 103 ++++++++++++++++++++++++++++++++++++--------- NmraDcc.h | 25 ++++++----- library.properties | 2 +- 3 files changed, 98 insertions(+), 32 deletions(-) diff --git a/NmraDcc.cpp b/NmraDcc.cpp index 51920c0..6c8f87f 100644 --- a/NmraDcc.cpp +++ b/NmraDcc.cpp @@ -249,6 +249,8 @@ typedef struct DCC_MSG LastMsg ; uint8_t ExtIntNum; uint8_t ExtIntPinNum; + int16_t myDccAddress; // Cached value of DCC Address from CVs + uint8_t inAccDecDCCAddrNextReceivedMode; #ifdef DCC_DEBUG uint8_t IntCount; uint8_t TickCount; @@ -536,6 +538,15 @@ uint8_t readCV( unsigned int CV ) uint8_t writeCV( unsigned int CV, uint8_t Value) { + switch( CV ) + { + case CV_ACCESSORY_DECODER_ADDRESS_LSB: // Also same CV for CV_MULTIFUNCTION_PRIMARY_ADDRESS + case CV_ACCESSORY_DECODER_ADDRESS_MSB: + case CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB: + case CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB: + DccProcState.myDccAddress = -1; // Assume any CV Write Operation might change the Address + } + if( notifyCVWrite ) return notifyCVWrite( CV, Value ) ; @@ -551,24 +562,30 @@ uint8_t writeCV( unsigned int CV, uint8_t Value) uint16_t getMyAddr(void) { - uint16_t Addr ; uint8_t CV29Value ; + + if( DccProcState.myDccAddress != -1 ) // See if we can return the cached value + return( DccProcState.myDccAddress ); CV29Value = readCV( CV_29_CONFIG ) ; - if( CV29Value & CV29_ACCESSORY_DECODER ) // Accessory Decoder? - Addr = ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) << 6 ) | readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) ; - + if( CV29Value & CV29_ACCESSORY_DECODER ) // Accessory Decoder? + { + if( CV29Value & CV29_OUTPUT_ADDRESS_MODE ) + DccProcState.myDccAddress = ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) << 8 ) | readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) ; + else + DccProcState.myDccAddress = ( ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) & 0b00000111) << 6 ) | ( readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) & 0b00111111) ; + } else // Multi-Function Decoder? { if( CV29Value & CV29_EXT_ADDRESSING ) // Two Byte Address? - Addr = ( ( readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB ) - 192 ) << 8 ) | readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB ) ; + DccProcState.myDccAddress = ( ( readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB ) - 192 ) << 8 ) | readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB ) ; else - Addr = readCV( 1 ) ; + DccProcState.myDccAddress = readCV( 1 ) ; } - - return Addr ; + + return DccProcState.myDccAddress ; } void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value ) @@ -964,16 +981,44 @@ void execDccProcessor( DCC_MSG * pDccMsg ) BoardAddress = ( ( (~pDccMsg->Data[1]) & 0b01110000 ) << 2 ) | ( pDccMsg->Data[0] & 0b00111111 ) ; - // If we're filtering was it my board address Our or a broadcast address - if( ( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY ) && ( BoardAddress != getMyAddr() ) && ( BoardAddress != 511 ) ) - return; - OutputAddress = pDccMsg->Data[1] & 0b00000111 ; OutputIndex = OutputAddress >> 1; Address = ( ( ( BoardAddress - 1 ) << 2 ) | OutputIndex ) + 1 ; + + if( DccProcState.inAccDecDCCAddrNextReceivedMode) + { + if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) + { + writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(Address % 256)); + writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(Address / 256)); + + if( notifyDccAccOutputAddrSet ) + notifyDccAccOutputAddrSet(Address); + } + else + { + writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(BoardAddress % 64)); + writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(BoardAddress / 64)); + + if( notifyDccAccBoardAddrSet ) + notifyDccAccBoardAddrSet(BoardAddress); + } + + DccProcState.inAccDecDCCAddrNextReceivedMode = 0; // Reset the mode now that we have set the address + } + // If we're filtering was it my board address Our or a broadcast address + if( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY ) + { + if( ( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) && ( Address != getMyAddr() ) && ( Address < 2045 ) ) + return; + + else if( ( BoardAddress != getMyAddr() ) && ( BoardAddress != 511 ) ) + return; + } + if(pDccMsg->Data[1] & 0b10000000) { uint8_t direction = OutputAddress & 0x01; @@ -981,18 +1026,28 @@ void execDccProcessor( DCC_MSG * pDccMsg ) if( notifyDccAccState ) notifyDccAccState( Address, BoardAddress, OutputAddress, pDccMsg->Data[1] & 0b00001000 ) ; - - if( notifyDccAccTurnoutBoard ) + + if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) + { + if( notifyDccAccTurnoutOutput ) + notifyDccAccTurnoutOutput( Address, direction, outputPower ); + } + else + { + if( notifyDccAccTurnoutBoard ) notifyDccAccTurnoutBoard( BoardAddress, OutputIndex, direction, outputPower ); - - if( notifyDccAccTurnoutOutput ) - notifyDccAccTurnoutOutput( Address, direction, outputPower ); + } } else { + uint8_t state = pDccMsg->Data[2] & 0b00011111; + if( notifyDccSigState ) - notifyDccSigState( Address, OutputIndex, pDccMsg->Data[2] ) ; + notifyDccSigState( Address, OutputIndex, state ) ; + + if( notifyDccSigOutputState ) + notifyDccSigOutputState(Address, state); } } } @@ -1056,12 +1111,13 @@ void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui DccProcState.Flags = Flags ; DccProcState.OpsModeAddressBaseCV = OpsModeAddressBaseCV ; + DccProcState.myDccAddress = -1; + DccProcState.inAccDecDCCAddrNextReceivedMode = 0; // Set the Bits that control Multifunction or Accessory behaviour // and if the Accessory decoder optionally handles Output Addressing - uint8_t cv29Mask = Flags & (CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE) ; // peal off the top two bits - writeCV( CV_29_CONFIG, ( readCV( CV_29_CONFIG ) & ~cv29Mask ) | Flags ) ; - + uint8_t cv29Mask = CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE ; // peal off the top two bits + writeCV( CV_29_CONFIG, (readCV( CV_29_CONFIG ) & ~cv29Mask) | (Flags & cv29Mask)); writeCV( 7, VersionId ) ; writeCV( 8, ManufacturerId ) ; @@ -1117,6 +1173,11 @@ uint8_t NmraDcc::getBitCount(void) } #endif +void NmraDcc::setAccDecDCCAddrNextReceived(uint8_t enable) +{ + DccProcState.inAccDecDCCAddrNextReceivedMode = enable; +} + uint8_t NmraDcc::process() { if( DccProcState.inServiceMode ) diff --git a/NmraDcc.h b/NmraDcc.h index a97ad0e..db8e13b 100644 --- a/NmraDcc.h +++ b/NmraDcc.h @@ -196,24 +196,25 @@ class NmraDcc NmraDcc(); // Flag values to be logically ORed together and passed into the init() method -#define FLAGS_MY_ADDRESS_ONLY 0x01 // Only process DCC Packets with My Address -#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6 -#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7 - void pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup); +#define FLAGS_MY_ADDRESS_ONLY 0x01 // Only process DCC Packets with My Address +#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6 +#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7 + void pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup); void init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV ); void initAccessoryDecoder( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV ); uint8_t process(); uint8_t getCV( uint16_t CV ); uint8_t setCV( uint16_t CV, uint8_t Value); - uint8_t isSetCVReady( void ); - uint16_t getAddr(void); + void setAccDecDCCAddrNextReceived(uint8_t enable); + uint8_t isSetCVReady( void ); + uint16_t getAddr(void); // #define DCC_DEBUG #ifdef DCC_DEBUG - uint8_t getIntCount(void); - uint8_t getTickCount(void); - uint8_t getBitCount(void); - uint8_t getState(void); + uint8_t getIntCount(void); + uint8_t getTickCount(void); + uint8_t getBitCount(void); + uint8_t getState(void); uint8_t getNestedIrqCount(void); #endif @@ -239,7 +240,11 @@ extern void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t Output extern void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak)); extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak)); +extern void notifyDccAccBoardAddrSet( uint16_t BoardAddr) __attribute__ ((weak)); +extern void notifyDccAccOutputAddrSet( uint16_t Addr) __attribute__ ((weak)); + extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ((weak)); +extern void notifyDccSigOutputState( uint16_t Addr, uint8_t State) __attribute__ ((weak)); extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak)); diff --git a/library.properties b/library.properties index 6019e10..b204526 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=NmraDcc -version=1.4.2 +version=1.4.3 author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda) maintainer=Alex Shepherd sentence=Enables NMRA DCC Communication From 0d2e8daeafbf556fb17b99b5d05863bf5dff8548 Mon Sep 17 00:00:00 2001 From: Franz-Peter Date: Thu, 25 May 2017 05:19:17 +0200 Subject: [PATCH 2/4] With eeprom_is_ready() for AVR-processors (#13) * with eepromReady for AVR * with eepromReady for AVR --- NmraDcc.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/NmraDcc.cpp b/NmraDcc.cpp index 6c8f87f..dc14160 100644 --- a/NmraDcc.cpp +++ b/NmraDcc.cpp @@ -2,7 +2,7 @@ // // Model Railroading with Arduino - NmraDcc.cpp // -// Copyright (c) 2008 - 2105 Alex Shepherd +// Copyright (c) 2008 - 2017 Alex Shepherd // // This source file is subject of the GNU general public license 2, // that is available at the world-wide-web at @@ -32,6 +32,9 @@ //------------------------------------------------------------------------ #include "NmraDcc.h" +#ifdef __AVR_MEGA__ +#include +#endif //------------------------------------------------------------------------ // DCC Receive Routine @@ -290,9 +293,9 @@ void ExternalInterruptHandler(void) } DccBitVal = ( bitMicros < bitMax ); lastMicros = actMicros; - //#ifdef debug + #ifdef debug if(DccBitVal) {SET_TP2;} else {CLR_TP2;}; - //#endif + #endif DCC_IrqRunning = true; interrupts(); // time critical is only the micros() command,so allow nested irq's #ifdef DCC_DEBUG @@ -502,7 +505,11 @@ void writeEEPROM( unsigned int CV, uint8_t Value ) { } bool readyEEPROM() { - return true; + #ifdef __AVR_MEGA__ + return eeprom_is_ready(); + #else + return true; + #endif } From 0656f58c5b556e109ffda9986c3520a58d392aaf Mon Sep 17 00:00:00 2001 From: Alex Shepherd Date: Thu, 8 Jun 2017 09:58:41 +1200 Subject: [PATCH 3/4] Added heaps of DEBUG PRINT to the Accessory Decoder section to follow/test the various test cases through the code and to figure out how to make this stuff work. Added more code to make the existing supported functions to be more selective about which packet bits patterns they take notice of as it was too broad previously Will remove some of the notifyCall-Back functions as some were not well conceived at the time and now need to go Testing is NOT complete as there were issues in JMRI that also need to be resolved in sync with this so we're not quite there yet.. --- NmraDcc.cpp | 252 +++++++++++++++++++++++++++++++++++++++++++++------- NmraDcc.h | 22 +++-- 2 files changed, 229 insertions(+), 45 deletions(-) diff --git a/NmraDcc.cpp b/NmraDcc.cpp index dc14160..cfd241c 100644 --- a/NmraDcc.cpp +++ b/NmraDcc.cpp @@ -36,6 +36,8 @@ #include #endif +#define DEBUG_PRINT // Uncomment to print DEBUG messages + //------------------------------------------------------------------------ // DCC Receive Routine // @@ -230,6 +232,15 @@ typedef enum } DccRxWaitState ; +typedef enum +{ + OPS_INS_RESERVED = 0, + OPS_INS_VERIFY_BYTE, + OPS_INS_BIT_MANIPULATION, + OPS_INS_WRITE_BYTE +} +OpsInstructionType; + struct DccRx_t { DccRxWaitState State ; @@ -819,12 +830,12 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t case 0b11000000: // Feature Expansion Instruction switch(Cmd & 0b00011111) { - case 0B00011110: + case 0b00011110: if( notifyDccFunc ) notifyDccFunc( Addr, AddrType, FN_13_20, Data1 ) ; break; - case 0B00011111: + case 0b00011111: if( notifyDccFunc ) notifyDccFunc( Addr, AddrType, FN_21_28, Data1 ) ; break; @@ -919,6 +930,24 @@ void clearDccProcState(uint8_t inServiceMode) memset( &DccProcState.LastMsg, 0, sizeof( DCC_MSG ) ) ; } +#ifdef DEBUG_PRINT +void SerialPrintPacketHex(const __FlashStringHelper *strLabel, DCC_MSG * pDccMsg) +{ + Serial.print( strLabel ); + + for( uint8_t i = 0; i < pDccMsg->Size; i++ ) + { + if( pDccMsg->Data[i] <= 9) + Serial.print('0'); + + Serial.print( pDccMsg->Data[i], HEX ); + Serial.write( ' ' ); + } + Serial.println(); +} +#endif + + void execDccProcessor( DCC_MSG * pDccMsg ) { if( ( pDccMsg->Data[0] == 0 ) && ( pDccMsg->Data[1] == 0 ) ) @@ -982,30 +1011,73 @@ void execDccProcessor( DCC_MSG * pDccMsg ) if( DccProcState.Flags & FLAGS_DCC_ACCESSORY_DECODER ) { uint16_t BoardAddress ; - uint8_t OutputAddress ; - uint8_t OutputIndex ; - uint16_t Address ; + uint16_t OutputAddress ; + uint8_t TurnoutPairIndex ; + +#ifdef DEBUG_PRINT + SerialPrintPacketHex(F( "execDccProcessor: Accessory Decoder Command: "), pDccMsg); +#endif BoardAddress = ( ( (~pDccMsg->Data[1]) & 0b01110000 ) << 2 ) | ( pDccMsg->Data[0] & 0b00111111 ) ; - OutputAddress = pDccMsg->Data[1] & 0b00000111 ; - - OutputIndex = OutputAddress >> 1; +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: Board Addr: ")); + Serial.println(BoardAddress); +#endif + // First check for Legacy Accessory Decoder Configuration Variable Access Instruction + // as it's got a different format to the others + if((pDccMsg->Size == 5) && ((pDccMsg->Data[1] & 0b10001100) == 0b00001100)) + { +#ifdef DEBUG_PRINT + Serial.println(F( "execDccProcessor: Legacy Accessory Decoder CV Access Command")); +#endif + // Check if this command is for our address or the broadcast address + if((BoardAddress != getMyAddr()) && ( OutputAddress < 511 )) + { +#ifdef DEBUG_PRINT + Serial.println(F("execDccProcessor: Board Address Not Matched")); +#endif + return; + } - Address = ( ( ( BoardAddress - 1 ) << 2 ) | OutputIndex ) + 1 ; + uint16_t cvAddress = ((pDccMsg->Data[1] & 0b00000011) << 8) + pDccMsg->Data[2] + 1; + uint8_t cvValue = pDccMsg->Data[3]; + +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: CV: ")); + Serial.print(cvAddress); + Serial.print(F(" Value: ")); + Serial.println(cvValue); +#endif + if(validCV( cvAddress, 1 )) + writeCV(cvAddress, cvValue); + return; + } + + TurnoutPairIndex = (pDccMsg->Data[1] & 0b00000110) >> 1; + + OutputAddress = ( BoardAddress << 2 ) | TurnoutPairIndex ; if( DccProcState.inAccDecDCCAddrNextReceivedMode) { if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) { - writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(Address % 256)); - writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(Address / 256)); +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: Set Output Addr: ")); + Serial.println(OutputAddress); +#endif + writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(OutputAddress % 256)); + writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(OutputAddress / 256)); if( notifyDccAccOutputAddrSet ) - notifyDccAccOutputAddrSet(Address); + notifyDccAccOutputAddrSet(OutputAddress); } else { +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: Set Board Addr: ")); + Serial.println(BoardAddress); +#endif writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(BoardAddress % 64)); writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(BoardAddress / 64)); @@ -1016,45 +1088,159 @@ void execDccProcessor( DCC_MSG * pDccMsg ) DccProcState.inAccDecDCCAddrNextReceivedMode = 0; // Reset the mode now that we have set the address } - // If we're filtering was it my board address Our or a broadcast address + // If we're filtering addresses, does the address match our address or is it a broadcast address? If NOT then return if( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY ) { - if( ( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) && ( Address != getMyAddr() ) && ( Address < 2045 ) ) + if( ( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) && ( OutputAddress != getMyAddr() ) && ( OutputAddress < 2045 ) ) return; - else if( ( BoardAddress != getMyAddr() ) && ( BoardAddress != 511 ) ) + else if( ( BoardAddress != getMyAddr() ) && ( BoardAddress < 511 ) ) return; + +#ifdef DEBUG_PRINT + Serial.println(F("execDccProcessor: Address Matched")); +#endif } - - if(pDccMsg->Data[1] & 0b10000000) - { - uint8_t direction = OutputAddress & 0x01; + + if((pDccMsg->Size == 4) && ((pDccMsg->Data[1] & 0b10001001) == 1)) // Extended Accessory Decoder Control Packet Format + { + uint8_t state = pDccMsg->Data[2] & 0b00011111; + +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: Output Addr: ")); + Serial.print(OutputAddress); + Serial.print(F(" Extended State: ")); + Serial.println(state); +#endif + if( notifyDccSigOutputState ) + notifyDccSigOutputState(OutputAddress, state); + } + + else if(pDccMsg->Size == 3) // Basic Accessory Decoder Packet Format + { + uint8_t direction = pDccMsg->Data[1] & 0b00000001; uint8_t outputPower = (pDccMsg->Data[1] & 0b00001000) >> 3; - if( notifyDccAccState ) - notifyDccAccState( Address, BoardAddress, OutputAddress, pDccMsg->Data[1] & 0b00001000 ) ; - if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) { +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: Output Addr: ")); + Serial.print(OutputAddress); + Serial.print(F(" Turnout Dir: ")); + Serial.print(direction); + Serial.print(F(" Output Power: ")); + Serial.println(outputPower); +#endif if( notifyDccAccTurnoutOutput ) - notifyDccAccTurnoutOutput( Address, direction, outputPower ); + notifyDccAccTurnoutOutput( OutputAddress, direction, outputPower ); } else { +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: Turnout Pair Index: ")); + Serial.print(TurnoutPairIndex); + Serial.print(F(" Dir: ")); + Serial.print(direction); + Serial.print(F(" Output Power: ")); + Serial.println(outputPower); +#endif if( notifyDccAccTurnoutBoard ) - notifyDccAccTurnoutBoard( BoardAddress, OutputIndex, direction, outputPower ); + notifyDccAccTurnoutBoard( BoardAddress, TurnoutPairIndex, direction, outputPower ); } } - - else - { - uint8_t state = pDccMsg->Data[2] & 0b00011111; - - if( notifyDccSigState ) - notifyDccSigState( Address, OutputIndex, state ) ; + else if(pDccMsg->Size == 6) // Accessory Decoder OPS Mode Programming + { +#ifdef DEBUG_PRINT + Serial.println(F("execDccProcessor: OPS Mode CV Programming Command")); +#endif + // Check for unsupported OPS Mode Addressing mode + if(((pDccMsg->Data[1] & 0b10001001) != 1) && ((pDccMsg->Data[1] & 0b10001111) != 0x80)) + { +#ifdef DEBUG_PRINT + Serial.println(F("execDccProcessor: Unsupported OPS Mode CV Addressing Mode")); +#endif + return; + } - if( notifyDccSigOutputState ) - notifyDccSigOutputState(Address, state); + // Check if this command is for our address or the broadcast address + if(DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE) + { +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: Check Output Address: ")); + Serial.println(OutputAddress); +#endif + if((OutputAddress != getMyAddr()) && ( OutputAddress < 2045 )) + { +#ifdef DEBUG_PRINT + Serial.println(F("execDccProcessor: Output Address Not Matched")); +#endif + return; + } + } + else + { +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: Check Board Address: ")); + Serial.println(BoardAddress); +#endif + if((BoardAddress != getMyAddr()) && ( BoardAddress < 511 )) + { +#ifdef DEBUG_PRINT + Serial.println(F("execDccProcessor: Board Address Not Matched")); +#endif + return; + } + } + + uint16_t cvAddress = ((pDccMsg->Data[2] & 0b00000011) << 8) + pDccMsg->Data[3] + 1; + uint8_t cvValue = pDccMsg->Data[4]; + + OpsInstructionType insType = (OpsInstructionType)((pDccMsg->Data[2] & 0b00001100) >> 2) ; + +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: OPS Mode Instruction: ")); + Serial.println(insType); +#endif + switch(insType) + { + case OPS_INS_RESERVED: + case OPS_INS_VERIFY_BYTE: +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: Unsupported OPS Mode Instruction: ")); + Serial.println(insType); +#endif + break; // We only support Write Byte or Bit Manipulation + + case OPS_INS_WRITE_BYTE: +#ifdef DEBUG_PRINT + Serial.print(F("execDccProcessor: CV: ")); + Serial.print(cvAddress); + Serial.print(F(" Value: ")); + Serial.println(cvValue); +#endif + if(validCV( cvAddress, 1 )) + writeCV(cvAddress, cvValue); + break; + + // 111CDBBB + // Where BBB represents the bit position within the CV, + // D contains the value of the bit to be verified or written, + // and C describes whether the operation is a verify bit or a write bit operation. + // C = "1" WRITE BIT + // C = "0" VERIFY BIT + case OPS_INS_BIT_MANIPULATION: + // Make sure its a Write Bit Manipulation + if((cvValue & 0b00010000) && validCV(cvAddress, 1 )) + { + uint8_t currentValue = readCV(cvAddress); + uint8_t newValueMask = 1 << (cvValue & 0b00000111); + if(cvValue & 0b00001000) + writeCV(cvAddress, cvValue | newValueMask); + else + writeCV(cvAddress, cvValue & ~newValueMask); + } + break; + } } } } diff --git a/NmraDcc.h b/NmraDcc.h index db8e13b..c47301e 100644 --- a/NmraDcc.h +++ b/NmraDcc.h @@ -228,23 +228,21 @@ class NmraDcc extern "C" { #endif -extern void notifyDccReset(uint8_t hardReset ) __attribute__ ((weak)); -extern void notifyDccIdle(void) __attribute__ ((weak)); +extern void notifyDccReset(uint8_t hardReset ) __attribute__ ((weak)); +extern void notifyDccIdle(void) __attribute__ ((weak)); -extern void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps ) __attribute__ ((weak)); -extern void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) __attribute__ ((weak)); +extern void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps ) __attribute__ ((weak)); +extern void notifyDccSpeedRaw( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Raw) __attribute__ ((weak)); -extern void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) __attribute__ ((weak)); +extern void notifyDccFunc( uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState) __attribute__ ((weak)); -extern void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State ) __attribute__ ((weak)); -extern void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak)); -extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak)); +extern void notifyDccAccTurnoutBoard( uint16_t BoardAddr, uint8_t OutputPair, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak)); +extern void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) __attribute__ ((weak)); -extern void notifyDccAccBoardAddrSet( uint16_t BoardAddr) __attribute__ ((weak)); -extern void notifyDccAccOutputAddrSet( uint16_t Addr) __attribute__ ((weak)); +extern void notifyDccAccBoardAddrSet( uint16_t BoardAddr) __attribute__ ((weak)); +extern void notifyDccAccOutputAddrSet( uint16_t Addr) __attribute__ ((weak)); -extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ((weak)); -extern void notifyDccSigOutputState( uint16_t Addr, uint8_t State) __attribute__ ((weak)); +extern void notifyDccSigOutputState( uint16_t Addr, uint8_t State) __attribute__ ((weak)); extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak)); From 844171f2acea7ecc9d88ab433bd6bcb9b57ee4ff Mon Sep 17 00:00:00 2001 From: Alex Shepherd Date: Sun, 25 Jun 2017 20:13:19 +1200 Subject: [PATCH 4/4] Fixed off-by-one (x4) error with DCC Accessory Output Mode Addressing. Made compatible with the DCC Spec for DCC Accessory Output Mode Addressing CV storage in CV 1 & 9. Its a bit wierd but... --- NmraDcc.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/NmraDcc.cpp b/NmraDcc.cpp index cfd241c..b8e0cad 100644 --- a/NmraDcc.cpp +++ b/NmraDcc.cpp @@ -36,7 +36,7 @@ #include #endif -#define DEBUG_PRINT // Uncomment to print DEBUG messages +// #define DEBUG_PRINT // Uncomment to print DEBUG messages //------------------------------------------------------------------------ // DCC Receive Routine @@ -590,7 +590,7 @@ uint16_t getMyAddr(void) if( CV29Value & CV29_ACCESSORY_DECODER ) // Accessory Decoder? { if( CV29Value & CV29_OUTPUT_ADDRESS_MODE ) - DccProcState.myDccAddress = ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) << 8 ) | readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) ; + DccProcState.myDccAddress = ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) << 8 ) | readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) - 1; else DccProcState.myDccAddress = ( ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) & 0b00000111) << 6 ) | ( readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) & 0b00111111) ; } @@ -1056,7 +1056,7 @@ void execDccProcessor( DCC_MSG * pDccMsg ) TurnoutPairIndex = (pDccMsg->Data[1] & 0b00000110) >> 1; - OutputAddress = ( BoardAddress << 2 ) | TurnoutPairIndex ; + OutputAddress = (((BoardAddress - 1) << 2 ) | TurnoutPairIndex) + 1 ; if( DccProcState.inAccDecDCCAddrNextReceivedMode) { @@ -1066,8 +1066,9 @@ void execDccProcessor( DCC_MSG * pDccMsg ) Serial.print(F("execDccProcessor: Set Output Addr: ")); Serial.println(OutputAddress); #endif - writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(OutputAddress % 256)); - writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(OutputAddress / 256)); + uint16_t storedOutputAddress = OutputAddress + 1; // The value stored in CV1 & 9 for Output Addressing Mode is + 1 + writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(storedOutputAddress % 256)); + writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(storedOutputAddress / 256)); if( notifyDccAccOutputAddrSet ) notifyDccAccOutputAddrSet(OutputAddress);