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
This commit is contained in:
103
NmraDcc.cpp
103
NmraDcc.cpp
@@ -249,6 +249,8 @@ typedef struct
|
|||||||
DCC_MSG LastMsg ;
|
DCC_MSG LastMsg ;
|
||||||
uint8_t ExtIntNum;
|
uint8_t ExtIntNum;
|
||||||
uint8_t ExtIntPinNum;
|
uint8_t ExtIntPinNum;
|
||||||
|
int16_t myDccAddress; // Cached value of DCC Address from CVs
|
||||||
|
uint8_t inAccDecDCCAddrNextReceivedMode;
|
||||||
#ifdef DCC_DEBUG
|
#ifdef DCC_DEBUG
|
||||||
uint8_t IntCount;
|
uint8_t IntCount;
|
||||||
uint8_t TickCount;
|
uint8_t TickCount;
|
||||||
@@ -536,6 +538,15 @@ uint8_t readCV( unsigned int CV )
|
|||||||
|
|
||||||
uint8_t writeCV( unsigned int CV, uint8_t Value)
|
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 )
|
if( notifyCVWrite )
|
||||||
return notifyCVWrite( CV, Value ) ;
|
return notifyCVWrite( CV, Value ) ;
|
||||||
|
|
||||||
@@ -551,24 +562,30 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
|
|||||||
|
|
||||||
uint16_t getMyAddr(void)
|
uint16_t getMyAddr(void)
|
||||||
{
|
{
|
||||||
uint16_t Addr ;
|
|
||||||
uint8_t CV29Value ;
|
uint8_t CV29Value ;
|
||||||
|
|
||||||
|
if( DccProcState.myDccAddress != -1 ) // See if we can return the cached value
|
||||||
|
return( DccProcState.myDccAddress );
|
||||||
|
|
||||||
CV29Value = readCV( CV_29_CONFIG ) ;
|
CV29Value = readCV( CV_29_CONFIG ) ;
|
||||||
|
|
||||||
if( CV29Value & CV29_ACCESSORY_DECODER ) // Accessory Decoder?
|
if( CV29Value & CV29_ACCESSORY_DECODER ) // Accessory Decoder?
|
||||||
Addr = ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) << 6 ) | readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) ;
|
{
|
||||||
|
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?
|
else // Multi-Function Decoder?
|
||||||
{
|
{
|
||||||
if( CV29Value & CV29_EXT_ADDRESSING ) // Two Byte Address?
|
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
|
else
|
||||||
Addr = readCV( 1 ) ;
|
DccProcState.myDccAddress = readCV( 1 ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Addr ;
|
return DccProcState.myDccAddress ;
|
||||||
}
|
}
|
||||||
|
|
||||||
void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
|
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 ) ;
|
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 ;
|
OutputAddress = pDccMsg->Data[1] & 0b00000111 ;
|
||||||
|
|
||||||
OutputIndex = OutputAddress >> 1;
|
OutputIndex = OutputAddress >> 1;
|
||||||
|
|
||||||
Address = ( ( ( BoardAddress - 1 ) << 2 ) | OutputIndex ) + 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)
|
if(pDccMsg->Data[1] & 0b10000000)
|
||||||
{
|
{
|
||||||
uint8_t direction = OutputAddress & 0x01;
|
uint8_t direction = OutputAddress & 0x01;
|
||||||
@@ -981,18 +1026,28 @@ void execDccProcessor( DCC_MSG * pDccMsg )
|
|||||||
|
|
||||||
if( notifyDccAccState )
|
if( notifyDccAccState )
|
||||||
notifyDccAccState( Address, BoardAddress, OutputAddress, pDccMsg->Data[1] & 0b00001000 ) ;
|
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 );
|
notifyDccAccTurnoutBoard( BoardAddress, OutputIndex, direction, outputPower );
|
||||||
|
}
|
||||||
if( notifyDccAccTurnoutOutput )
|
|
||||||
notifyDccAccTurnoutOutput( Address, direction, outputPower );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
uint8_t state = pDccMsg->Data[2] & 0b00011111;
|
||||||
|
|
||||||
if( notifyDccSigState )
|
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.Flags = Flags ;
|
||||||
DccProcState.OpsModeAddressBaseCV = OpsModeAddressBaseCV ;
|
DccProcState.OpsModeAddressBaseCV = OpsModeAddressBaseCV ;
|
||||||
|
DccProcState.myDccAddress = -1;
|
||||||
|
DccProcState.inAccDecDCCAddrNextReceivedMode = 0;
|
||||||
|
|
||||||
// Set the Bits that control Multifunction or Accessory behaviour
|
// Set the Bits that control Multifunction or Accessory behaviour
|
||||||
// and if the Accessory decoder optionally handles Output Addressing
|
// 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
|
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 ) ;
|
writeCV( CV_29_CONFIG, (readCV( CV_29_CONFIG ) & ~cv29Mask) | (Flags & cv29Mask));
|
||||||
|
|
||||||
writeCV( 7, VersionId ) ;
|
writeCV( 7, VersionId ) ;
|
||||||
writeCV( 8, ManufacturerId ) ;
|
writeCV( 8, ManufacturerId ) ;
|
||||||
|
|
||||||
@@ -1117,6 +1173,11 @@ uint8_t NmraDcc::getBitCount(void)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void NmraDcc::setAccDecDCCAddrNextReceived(uint8_t enable)
|
||||||
|
{
|
||||||
|
DccProcState.inAccDecDCCAddrNextReceivedMode = enable;
|
||||||
|
}
|
||||||
|
|
||||||
uint8_t NmraDcc::process()
|
uint8_t NmraDcc::process()
|
||||||
{
|
{
|
||||||
if( DccProcState.inServiceMode )
|
if( DccProcState.inServiceMode )
|
||||||
|
25
NmraDcc.h
25
NmraDcc.h
@@ -196,24 +196,25 @@ class NmraDcc
|
|||||||
NmraDcc();
|
NmraDcc();
|
||||||
|
|
||||||
// Flag values to be logically ORed together and passed into the init() method
|
// 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_MY_ADDRESS_ONLY 0x01 // Only process DCC Packets with My Address
|
||||||
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6
|
#define FLAGS_OUTPUT_ADDRESS_MODE 0x40 // CV 29/541 bit 6
|
||||||
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7
|
#define FLAGS_DCC_ACCESSORY_DECODER 0x80 // CV 29/541 bit 7
|
||||||
void pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup);
|
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 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 );
|
void initAccessoryDecoder( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV );
|
||||||
uint8_t process();
|
uint8_t process();
|
||||||
uint8_t getCV( uint16_t CV );
|
uint8_t getCV( uint16_t CV );
|
||||||
uint8_t setCV( uint16_t CV, uint8_t Value);
|
uint8_t setCV( uint16_t CV, uint8_t Value);
|
||||||
uint8_t isSetCVReady( void );
|
void setAccDecDCCAddrNextReceived(uint8_t enable);
|
||||||
uint16_t getAddr(void);
|
uint8_t isSetCVReady( void );
|
||||||
|
uint16_t getAddr(void);
|
||||||
|
|
||||||
// #define DCC_DEBUG
|
// #define DCC_DEBUG
|
||||||
#ifdef DCC_DEBUG
|
#ifdef DCC_DEBUG
|
||||||
uint8_t getIntCount(void);
|
uint8_t getIntCount(void);
|
||||||
uint8_t getTickCount(void);
|
uint8_t getTickCount(void);
|
||||||
uint8_t getBitCount(void);
|
uint8_t getBitCount(void);
|
||||||
uint8_t getState(void);
|
uint8_t getState(void);
|
||||||
uint8_t getNestedIrqCount(void);
|
uint8_t getNestedIrqCount(void);
|
||||||
#endif
|
#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 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 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 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));
|
extern void notifyDccMsg( DCC_MSG * Msg ) __attribute__ ((weak));
|
||||||
|
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
name=NmraDcc
|
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)
|
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda)
|
||||||
maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
|
maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
|
||||||
sentence=Enables NMRA DCC Communication
|
sentence=Enables NMRA DCC Communication
|
||||||
|
Reference in New Issue
Block a user