first added to GitHub
This commit is contained in:
766
DCC_Decoder.cpp
Normal file
766
DCC_Decoder.cpp
Normal file
@@ -0,0 +1,766 @@
|
||||
//
|
||||
// DCC_Decoder.cpp - Arduino library for NMRA DCC Decoding.
|
||||
// Written by Kevin Snow, MynaBay.com, November, 2011.
|
||||
// Questions: dcc@mynabay.com
|
||||
// Released into the public domain.
|
||||
//
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "DCC_Decoder.h"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Global Decoder object
|
||||
//
|
||||
|
||||
DCC_Decoder DCC;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// NMRA DCC Definitions
|
||||
//
|
||||
// Microsecond 0 & 1 timings
|
||||
#define kONE_Min 52
|
||||
#define kONE_Max 64
|
||||
|
||||
#define kZERO_Min 90
|
||||
#define kZERO_Max 10000
|
||||
|
||||
// Minimum preamble length
|
||||
#define kPREAMBLE_MIN 10
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Interrupt handling
|
||||
//
|
||||
unsigned long DCC_Decoder::gInterruptMicros = 0;
|
||||
byte DCC_Decoder::gInterruptTimeIndex = 0;
|
||||
volatile unsigned int DCC_Decoder::gInterruptTime[2];
|
||||
volatile unsigned int DCC_Decoder::gInterruptChaos;
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void DCC_Decoder::DCC_Interrupt()
|
||||
{
|
||||
unsigned long ms = micros();
|
||||
gInterruptTime[gInterruptTimeIndex] = ms - gInterruptMicros;
|
||||
gInterruptMicros = ms;
|
||||
gInterruptChaos += gInterruptTimeIndex;
|
||||
gInterruptTimeIndex ^= 0x01;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void DCC_Decoder::ShiftInterruptAlignment()
|
||||
{
|
||||
noInterrupts();
|
||||
gInterruptTime[0] = gInterruptTime[1];
|
||||
gInterruptTimeIndex = 1;
|
||||
interrupts();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
|
||||
void DCC_Decoder::StartInterrupt(byte interrupt)
|
||||
{
|
||||
gInterruptTimeIndex = 0;
|
||||
gInterruptTime[0] = gInterruptTime[1] = 0;
|
||||
gInterruptChaos = 0;
|
||||
gInterruptMicros = micros();
|
||||
|
||||
attachInterrupt( interrupt, DCC_Interrupt, CHANGE );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Globals
|
||||
//
|
||||
typedef void(*StateFunc)();
|
||||
|
||||
// Current state function pointer
|
||||
StateFunc DCC_Decoder::gState; // Current state function pointer
|
||||
|
||||
// Timing data from last interrupt
|
||||
unsigned int DCC_Decoder::gLastChaos; // Interrupt chaos count we processed
|
||||
|
||||
// Preamble bit count
|
||||
int DCC_Decoder::gPreambleCount; // Bit count for reading preamble
|
||||
|
||||
// Reset reason
|
||||
byte DCC_Decoder::gResetReason; // Result code of last reason decoder was reset
|
||||
boolean DCC_Decoder::gHandledAsRawPacket;
|
||||
|
||||
// Packet data
|
||||
byte DCC_Decoder::gPacket[kPACKET_LEN_MAX]; // The packet data.
|
||||
byte DCC_Decoder::gPacketIndex; // Byte index to write to.
|
||||
byte DCC_Decoder::gPacketMask; // Bit index to write to. 0x80,0x40,0x20,...0x01
|
||||
boolean DCC_Decoder::gPacketEndedWith1; // Set true if packet ended on 1. Spec requires that the
|
||||
// packet end bit can count as a bit in next preamble.
|
||||
// CV Storage
|
||||
byte DCC_Decoder::gCV[kCV_MAX]; // CV Storage (TODO - Move to PROGMEM)
|
||||
|
||||
// Packet arrival timing
|
||||
unsigned long DCC_Decoder::gThisPacketMS; // Milliseconds of this packet being parsed
|
||||
boolean DCC_Decoder::gLastPacketToThisAddress; // Was last pack processed to this decoder's address?
|
||||
|
||||
unsigned long DCC_Decoder::gLastValidPacketMS; // Milliseconds of last valid packet
|
||||
unsigned long DCC_Decoder::gLastValidPacketToAddressMS; // Milliseconds of last valid packet to this decoder
|
||||
unsigned long DCC_Decoder::gLastValidIdlePacketMS; // Milliseconds of last valid idle packet
|
||||
unsigned long DCC_Decoder::gLastValidResetPacketMS; // Milliseconds of last valid reset packet
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Packet Timing Support
|
||||
//
|
||||
unsigned long DCC_Decoder::MillisecondsSinceLastValidPacket()
|
||||
{
|
||||
return millis() - gLastValidPacketMS;
|
||||
}
|
||||
|
||||
unsigned long DCC_Decoder::MillisecondsSinceLastPacketToThisDecoder()
|
||||
{
|
||||
return millis() - gLastValidPacketToAddressMS;
|
||||
}
|
||||
|
||||
unsigned long DCC_Decoder::MillisecondsSinceLastIdlePacket()
|
||||
{
|
||||
return millis() - gLastValidIdlePacketMS;
|
||||
}
|
||||
|
||||
unsigned long DCC_Decoder::MillisecondsSinceLastResetPacket()
|
||||
{
|
||||
return millis() - gLastValidResetPacketMS;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// CV Support
|
||||
//
|
||||
byte DCC_Decoder::ReadCV(int cv)
|
||||
{
|
||||
if( cv>=kCV_PrimaryAddress && cv<kCV_MAX )
|
||||
{
|
||||
return gCV[cv];
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void DCC_Decoder::WriteCV(int cv, byte data)
|
||||
{
|
||||
if( cv>=kCV_PrimaryAddress && cv<kCV_MAX && cv!=kCV_ManufacturerVersionNo && cv!=kCV_ManufacturerVersionNo )
|
||||
{
|
||||
gCV[cv] = data;
|
||||
}
|
||||
}
|
||||
|
||||
int DCC_Decoder::Address()
|
||||
{
|
||||
int address;
|
||||
|
||||
byte cv29 = DCC_Decoder::ReadCV(kCV_ConfigurationData1);
|
||||
|
||||
if( cv29 & 0x80 ) // Is this an accessory decoder?
|
||||
{
|
||||
address = DCC_Decoder::ReadCV(kCV_AddressMSB)<<6 | DCC_Decoder::ReadCV(kCV_AddressMSB);
|
||||
}else{
|
||||
if( cv29 & 0x20 ) // Multifunction using extended addresses?
|
||||
{
|
||||
address = DCC_Decoder::ReadCV(kCV_ExtendedAddress1)<<8 | DCC_Decoder::ReadCV(kCV_ExtendedAddress2);
|
||||
}else{
|
||||
address = DCC_Decoder::ReadCV(kCV_PrimaryAddress);
|
||||
}
|
||||
}
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Handlers
|
||||
//
|
||||
BaselineControlPacket DCC_Decoder::func_BaselineControlPacket = NULL;
|
||||
boolean DCC_Decoder::func_BaselineControlPacket_All_Packets = false;
|
||||
|
||||
void DCC_Decoder::SetBaselineControlPacketHandler(BaselineControlPacket func, boolean allPackets)
|
||||
{
|
||||
func_BaselineControlPacket = func;
|
||||
func_BaselineControlPacket_All_Packets = allPackets;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
RawPacket DCC_Decoder::func_RawPacket = NULL;
|
||||
|
||||
void DCC_Decoder::SetRawPacketHandler(RawPacket func)
|
||||
{
|
||||
func_RawPacket = func;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
BasicAccDecoderPacket DCC_Decoder::func_BasicAccPacket = NULL;
|
||||
boolean DCC_Decoder::func_BasicAccPacket_All_Packets = false;
|
||||
|
||||
void DCC_Decoder::SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket func, boolean allPackets)
|
||||
{
|
||||
func_BasicAccPacket = func;
|
||||
func_BasicAccPacket_All_Packets = allPackets;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
ExtendedAccDecoderPacket DCC_Decoder::func_ExtdAccPacket = NULL;
|
||||
boolean DCC_Decoder::func_ExtdAccPacket_All_Packets = false;
|
||||
|
||||
void DCC_Decoder::SetExtendedAccessoryDecoderPacketHandler(ExtendedAccDecoderPacket func, boolean allPackets)
|
||||
{
|
||||
func_ExtdAccPacket = func;
|
||||
func_ExtdAccPacket_All_Packets = allPackets;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
IdleResetPacket DCC_Decoder::func_IdlePacket = NULL;
|
||||
|
||||
void DCC_Decoder::SetIdlePacketHandler(IdleResetPacket func)
|
||||
{
|
||||
func_IdlePacket = func;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
IdleResetPacket DCC_Decoder::func_ResetPacket = NULL;
|
||||
|
||||
void DCC_Decoder::SetResetPacketHandler(IdleResetPacket func)
|
||||
{
|
||||
func_ResetPacket = func;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
DecodingEngineCompletion DCC_Decoder::func_DecodingEngineCompletion = NULL;
|
||||
|
||||
void DCC_Decoder::SetDecodingEngineCompletionStatusHandler(DecodingEngineCompletion func)
|
||||
{
|
||||
func_DecodingEngineCompletion = func;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// State Change Macros
|
||||
//
|
||||
#define GOTO_DecoderReset(reason) { gState = DCC_Decoder::State_Reset; gResetReason = reason; return; }
|
||||
#define GOTO_ExecutePacket() { gState = DCC_Decoder::State_Execute; return; }
|
||||
#define GOTO_ReadPacketState() { gState = DCC_Decoder::State_ReadPacket; return; }
|
||||
#define GOTO_PreambleState() { gState = DCC_Decoder::State_ReadPreamble; return; }
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Execute packet
|
||||
//
|
||||
void DCC_Decoder::State_Execute()
|
||||
{
|
||||
int address;
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Test error dectection
|
||||
byte errorDectection = gPacket[0] ^ gPacket[1];
|
||||
if( gPacketIndex > 3 ) errorDectection ^= gPacket[2];
|
||||
if( gPacketIndex > 4 ) errorDectection ^= gPacket[3];
|
||||
if( gPacketIndex > 5 ) errorDectection ^= gPacket[4];
|
||||
if( errorDectection != gPacket[gPacketIndex-1] )
|
||||
{
|
||||
GOTO_DecoderReset( kDCC_ERR_DETECTION_FAILED );
|
||||
}
|
||||
|
||||
// Save off milliseconds of this valid packet
|
||||
gThisPacketMS = millis();
|
||||
gLastPacketToThisAddress = false;
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Dispatch to RawPacketHandler - All packets go to raw (except idle and reset above)
|
||||
//
|
||||
// gHandledAsRawPacket cleared in Reset. If packet is handled here this flag avoids
|
||||
// sending to another dispatch routine. We don't just return here because we need to
|
||||
// figure out packet type and update time fields.
|
||||
if( func_RawPacket )
|
||||
{
|
||||
gHandledAsRawPacket = (func_RawPacket)(gPacketIndex,gPacket);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////
|
||||
// Handle 3 byte packets
|
||||
if( gPacketIndex == 3 )
|
||||
{
|
||||
///////////////////////////////////////////////////////////
|
||||
// Decoder idle & reset packets as defined in 9.2.
|
||||
if( gPacket[1]==0x00 )
|
||||
{
|
||||
// Broadcast idle packet
|
||||
if( gPacket[0]==0xFF )
|
||||
{
|
||||
if( !gHandledAsRawPacket && func_IdlePacket )
|
||||
{
|
||||
(func_IdlePacket)(gPacketIndex,gPacket);
|
||||
}
|
||||
GOTO_DecoderReset( kDCC_OK_IDLE );
|
||||
}else{
|
||||
// Broadcast reset packet
|
||||
if( gPacket[0]==0x00 )
|
||||
{
|
||||
if( !gHandledAsRawPacket && func_ResetPacket )
|
||||
{
|
||||
(func_ResetPacket)(gPacketIndex,gPacket);
|
||||
}
|
||||
GOTO_DecoderReset( kDCC_OK_RESET );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Handle as a basic accessory decoder packet
|
||||
if( ((gPacket[0] & 0xC0) == 0x80) && ((gPacket[1] & 0x80) == 0x80) )
|
||||
{
|
||||
address = ~gPacket[1] & 0x70;
|
||||
address = (address<<2) + (gPacket[0] & 0x3F);
|
||||
gLastPacketToThisAddress = (address==DCC.Address());
|
||||
if( gLastPacketToThisAddress || address == 0x003F || func_BasicAccPacket_All_Packets ) // 0x003F is broadcast packet
|
||||
{
|
||||
if( !gHandledAsRawPacket && func_BasicAccPacket )
|
||||
{
|
||||
// Call BasicAccHandler Activate bit data bits
|
||||
(func_BasicAccPacket)( address, ((gPacket[1] & 0x08) ? true : false), (gPacket[1] & 0x07));
|
||||
}
|
||||
}
|
||||
GOTO_DecoderReset( kDCC_OK_BASIC_ACCESSORY );
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Handle as a baseline packet
|
||||
|
||||
// What decoder is this addressed to?
|
||||
if( gPacket[0] & 0x80 )
|
||||
{
|
||||
GOTO_DecoderReset( kDCC_ERR_BASELINE_ADDR );
|
||||
}
|
||||
|
||||
// Baseline instruction packet?
|
||||
if( (gPacket[1] & 0xC0) != 0x40 )
|
||||
{
|
||||
GOTO_DecoderReset( kDCC_ERR_BASELINE_INSTR );
|
||||
}
|
||||
|
||||
// bits as defined in 9.2
|
||||
byte addressByte = gPacket[0] & 0x7F;
|
||||
byte directionBit = gPacket[1] & 0x20;
|
||||
byte cBit = gPacket[1] & 0x10;
|
||||
byte speedBits = gPacket[1] & 0x0F;
|
||||
|
||||
// Stop or estop??
|
||||
if( speedBits==0 )
|
||||
{
|
||||
speedBits = kDCC_STOP_SPEED;
|
||||
}else{
|
||||
if( speedBits== 1 )
|
||||
{
|
||||
speedBits = kDCC_ESTOP_SPEED;
|
||||
}else{
|
||||
if( gCV[kCV_ConfigurationData1] & 0x02 ) // Bit 1 of CV29: 0=14speeds, 1=28Speeds
|
||||
{
|
||||
speedBits = ((speedBits << 1 ) & (cBit ? 1 : 0)) - 3; // speedBits = 1..28
|
||||
}else{
|
||||
speedBits -= 1; // speedBits = 1..14
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make callback
|
||||
gLastPacketToThisAddress = (addressByte==DCC.ReadCV(kCV_PrimaryAddress));
|
||||
if( func_BaselineControlPacket_All_Packets || gLastPacketToThisAddress )
|
||||
{
|
||||
if( !gHandledAsRawPacket && func_BaselineControlPacket )
|
||||
{
|
||||
(*func_BaselineControlPacket)(addressByte,speedBits,directionBit);
|
||||
}
|
||||
}
|
||||
GOTO_DecoderReset( kDCC_OK_BASELINE );
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////
|
||||
// Handle 4 byte packets
|
||||
if( gPacketIndex == 4 )
|
||||
{
|
||||
///////////////////////////////////////////////////////////
|
||||
// Handle as a extd accessory decoder packet (4 bytes)
|
||||
if( ((gPacket[0] & 0xC0) == 0x80) && ((gPacket[1] & 0x85) == 0x01) )
|
||||
{
|
||||
int msb = (gPacket[1] & 0x06);
|
||||
address = (gPacket[1] & 0x70);
|
||||
address = (msb<<8) + (address<<2) + (gPacket[0] & 0x3F);
|
||||
gLastPacketToThisAddress = (address==DCC.Address());
|
||||
if( gLastPacketToThisAddress || address == 0x033F || func_ExtdAccPacket_All_Packets ) // 0x033F is broadcast packet
|
||||
{
|
||||
if( !gHandledAsRawPacket && func_ExtdAccPacket )
|
||||
{
|
||||
// Call ExtAccHandler data bits
|
||||
(*func_ExtdAccPacket)( address, gPacket[2] & 0x1F);
|
||||
}
|
||||
}
|
||||
GOTO_DecoderReset( kDCC_OK_EXTENDED_ACCESSORY );
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////
|
||||
// Handle 5 byte packets
|
||||
if( gPacketIndex == 5 )
|
||||
{
|
||||
// TODO - Implement
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////
|
||||
// Handle 6 byte packets
|
||||
if( gPacketIndex == 6 )
|
||||
{
|
||||
// TODO - Implement
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Done!
|
||||
GOTO_DecoderReset( kDCC_OK );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Standard interrupt reader - If a complete bit has been read it places timing in periodA & periodB and flows out bottom.
|
||||
//
|
||||
#define StandardInterruptHeader(behalfOf) \
|
||||
noInterrupts(); \
|
||||
if( gInterruptChaos == gLastChaos ) \
|
||||
{ \
|
||||
interrupts(); \
|
||||
return; \
|
||||
} \
|
||||
if( gInterruptChaos-gLastChaos > 1 ) \
|
||||
{ \
|
||||
interrupts(); \
|
||||
GOTO_DecoderReset( kDCC_ERR_MISSED_BITS ); \
|
||||
} \
|
||||
unsigned int periodA = gInterruptTime[0]; \
|
||||
unsigned int periodB = gInterruptTime[1]; \
|
||||
gLastChaos = gInterruptChaos; \
|
||||
interrupts(); \
|
||||
boolean aIs1 = ( periodA >= kONE_Min && periodA <= kONE_Max ); \
|
||||
if( !aIs1 && (periodA < kZERO_Min || periodA > kZERO_Max) ) \
|
||||
{ \
|
||||
GOTO_DecoderReset( kDCC_ERR_NOT_0_OR_1 ); \
|
||||
} \
|
||||
boolean bIs1 = ( periodB >= kONE_Min && periodB <= kONE_Max ); \
|
||||
if( !bIs1 && (periodB < kZERO_Min || periodB > kZERO_Max) ) \
|
||||
{ \
|
||||
GOTO_DecoderReset( kDCC_ERR_NOT_0_OR_1 ); \
|
||||
} \
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Read packet bytes
|
||||
//
|
||||
void DCC_Decoder::State_ReadPacket()
|
||||
{
|
||||
// Interrupt header
|
||||
StandardInterruptHeader();
|
||||
|
||||
// Normally the two halves match. If not, reset
|
||||
if( aIs1 == bIs1 )
|
||||
{
|
||||
// 8 out of 9 times through we'll have a mask and be writing bits
|
||||
if( gPacketMask )
|
||||
{
|
||||
// Write the bit.
|
||||
if( aIs1 )
|
||||
{
|
||||
gPacket[gPacketIndex] |= gPacketMask;
|
||||
}
|
||||
// advance the bit mask
|
||||
gPacketMask = gPacketMask >> 1;
|
||||
|
||||
}else{
|
||||
// Getting here is the 9th time and the it's the data start bit between bytes.
|
||||
// Zero indicates more data, 1 indicates end of packet
|
||||
|
||||
// Advance index and reset mask
|
||||
gPacketIndex++;
|
||||
gPacketMask = 0x80;
|
||||
|
||||
// Data start bit is a 1, that's the end of packet! Execute.
|
||||
if( aIs1 )
|
||||
{
|
||||
gPacketEndedWith1 = true;
|
||||
if( gPacketIndex>=kPACKET_LEN_MIN && gPacketIndex<=kPACKET_LEN_MAX )
|
||||
{
|
||||
GOTO_ExecutePacket();
|
||||
}
|
||||
GOTO_DecoderReset( kDCC_ERR_INVALID_LENGTH );
|
||||
}else{
|
||||
// Data start bit is a 0. Do we have room for more data?
|
||||
if( gPacketIndex >= kPACKET_LEN_MAX )
|
||||
{
|
||||
GOTO_DecoderReset( kDCC_ERR_MISSING_END_BIT );
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
GOTO_DecoderReset( kDCC_ERR_NOT_0_OR_1 );
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Watch for Preamble
|
||||
//
|
||||
void DCC_Decoder::State_ReadPreamble()
|
||||
{
|
||||
// Interrupt header
|
||||
StandardInterruptHeader();
|
||||
|
||||
// If we get here, booleans aIs1 and bIs1 are set to the two halves of the next bit.
|
||||
|
||||
// If both are 1, it's a 1 bit.
|
||||
if( aIs1 && bIs1 )
|
||||
{
|
||||
// Increment preamble bit count
|
||||
++gPreambleCount;
|
||||
}else{
|
||||
// If they equal it's a 0.
|
||||
if( aIs1 == bIs1 )
|
||||
{
|
||||
if( gPreambleCount >= kPREAMBLE_MIN )
|
||||
{
|
||||
// BANG! Read preamble plus trailing 0. Go read the packet.
|
||||
GOTO_ReadPacketState();
|
||||
}
|
||||
}else{
|
||||
// One is 0 the other 1. Shift alignment.
|
||||
ShiftInterruptAlignment();
|
||||
}
|
||||
// Not enough bits in preamble or shifted alignment. Start over at zero preamble.
|
||||
gPreambleCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Reset handling (Part 2)
|
||||
//
|
||||
void DCC_Decoder::State_Reset()
|
||||
{
|
||||
// EngineReset Handler (Debugging)
|
||||
if( func_DecodingEngineCompletion )
|
||||
{
|
||||
(func_DecodingEngineCompletion)(gHandledAsRawPacket ? kDCC_OK_MAX : gResetReason);
|
||||
}
|
||||
gHandledAsRawPacket = false;
|
||||
|
||||
// If reset with an OK code, this was a valid packet. Save off times
|
||||
if( gResetReason < kDCC_OK_MAX )
|
||||
{
|
||||
// Save MS of last valid packet
|
||||
gLastValidPacketMS = gThisPacketMS;
|
||||
|
||||
// Save off other times
|
||||
switch( gResetReason )
|
||||
{
|
||||
case kDCC_OK_IDLE:
|
||||
gLastValidIdlePacketMS = gThisPacketMS;
|
||||
break;
|
||||
case kDCC_OK_RESET:
|
||||
gLastValidResetPacketMS = gThisPacketMS;
|
||||
break;
|
||||
case kDCC_OK_BASELINE:
|
||||
case kDCC_OK_BASIC_ACCESSORY:
|
||||
case kDCC_OK_EXTENDED_ACCESSORY:
|
||||
if(gLastPacketToThisAddress)
|
||||
{
|
||||
gLastValidPacketToAddressMS = gThisPacketMS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Reset packet data
|
||||
gPacket[0] = gPacket[1] = gPacket[2] = gPacket[3] = gPacket[4] = gPacket[5] = 0;
|
||||
gPacketIndex = 0;
|
||||
gPacketMask = 0x80;
|
||||
|
||||
// Copy last time and reset chaos
|
||||
noInterrupts();
|
||||
gPreambleCount = (gPacketEndedWith1 && gLastChaos==gInterruptChaos) ? 1 : 0;
|
||||
gLastChaos = gInterruptChaos = 0;
|
||||
interrupts();
|
||||
|
||||
// Clear packet ended 1 flag
|
||||
gPacketEndedWith1 = false;
|
||||
|
||||
// Go find preamble
|
||||
GOTO_PreambleState();
|
||||
}
|
||||
|
||||
void DCC_Decoder::State_Boot()
|
||||
{
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// SetupDecoder
|
||||
//
|
||||
void DCC_Decoder::SetupDecoder(byte mfgID, byte mfgVers, byte interrupt)
|
||||
{
|
||||
if( gInterruptMicros == 0 )
|
||||
{
|
||||
// Save mfg info
|
||||
gCV[kCV_ManufacturerVersionNo] = mfgID;
|
||||
gCV[kCV_ManufacturedID] = mfgVers;
|
||||
|
||||
// Attach the DCC interrupt
|
||||
StartInterrupt(interrupt);
|
||||
|
||||
// Start decoder in reset state
|
||||
GOTO_DecoderReset( kDCC_OK_BOOT );
|
||||
}
|
||||
}
|
||||
|
||||
void DCC_Decoder::SetupMonitor(byte interrupt)
|
||||
{
|
||||
if( gInterruptMicros == 0 )
|
||||
{
|
||||
// Attach the DCC interrupt
|
||||
StartInterrupt(interrupt);
|
||||
|
||||
// Start decoder in reset state
|
||||
GOTO_DecoderReset( kDCC_OK_BOOT );
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Hearbeat function. Dispatch the dcc_decoder library state machine.
|
||||
//
|
||||
void DCC_Decoder::loop()
|
||||
{
|
||||
(gState)();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Constructor (Not really).
|
||||
//
|
||||
DCC_Decoder::DCC_Decoder()
|
||||
{
|
||||
gState = DCC_Decoder::State_Boot;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Human readable error strings
|
||||
//
|
||||
|
||||
const char PROGMEM*
|
||||
DCC_Decoder::ResultString(byte resultCode)
|
||||
{
|
||||
static const char PROGMEM* const gResults[] =
|
||||
{
|
||||
"OK",
|
||||
"OK - Unhandled",
|
||||
"OK - Boot",
|
||||
"OK - Idle packet",
|
||||
"OK - Reset packet",
|
||||
"OK - Handled raw",
|
||||
"OK - Handled baseline",
|
||||
"OK - Handled basic accessory",
|
||||
"OK - Handled extended accessory",
|
||||
};
|
||||
|
||||
static const char PROGMEM* const gErrors[] =
|
||||
{
|
||||
"ERROR - Detection failed",
|
||||
"ERROR - Baseline address",
|
||||
"ERROR - Baseline instruction",
|
||||
"ERROR - Missed bits",
|
||||
"ERROR - Not 0 or 1",
|
||||
"ERROR - Invalid packet length",
|
||||
"ERROR - Missing packet end bits",
|
||||
};
|
||||
|
||||
static const char PROGMEM* const gErrorsBadCode = "ERROR - Bad result code";
|
||||
|
||||
if( resultCode>=0 && resultCode<(sizeof(gResults)/sizeof(gResults[0])) )
|
||||
{
|
||||
return gResults[resultCode];
|
||||
}
|
||||
if( resultCode>=100 && (resultCode-100)<(byte)(sizeof(gErrors)/sizeof(gErrors[0])) )
|
||||
{
|
||||
return gErrors[resultCode-100];
|
||||
}
|
||||
return gErrorsBadCode;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Helper to make packet strings
|
||||
//
|
||||
char* DCC_Decoder::MakePacketString(char* buffer60Bytes, byte byteCount, byte* packet)
|
||||
{
|
||||
buffer60Bytes[0] = 0;
|
||||
if( byteCount>=kPACKET_LEN_MIN && byteCount<=kPACKET_LEN_MAX )
|
||||
{
|
||||
int i = 0;
|
||||
for(byte byt=0; byt<byteCount; ++byt)
|
||||
{
|
||||
byte bit=0x80;
|
||||
while(bit)
|
||||
{
|
||||
buffer60Bytes[i++] = (packet[byt] & bit) ? '1' : '0';
|
||||
bit=bit>>1;
|
||||
}
|
||||
buffer60Bytes[i++] = ' ';
|
||||
}
|
||||
buffer60Bytes[--i] = 0;
|
||||
}
|
||||
return buffer60Bytes;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Helper to return preamble length
|
||||
//
|
||||
int DCC_Decoder::LastPreambleBitCount()
|
||||
{
|
||||
return gPreambleCount;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
206
DCC_Decoder.h
Normal file
206
DCC_Decoder.h
Normal file
@@ -0,0 +1,206 @@
|
||||
//
|
||||
// DCC_Decoder.h - Arduino library for NMRA DCC Decoding.
|
||||
// Written by Kevin Snow, MynaBay.com, November, 2011.
|
||||
// Questions: dcc@mynabay.com
|
||||
// Released into the public domain.
|
||||
//
|
||||
|
||||
#ifndef __DCC_DECODER_H__
|
||||
#define __DCC_DECODER_H__
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define kDCC_STOP_SPEED 0xFE
|
||||
#define kDCC_ESTOP_SPEED 0xFF
|
||||
|
||||
// Multifunction Decoders
|
||||
#define kCV_PrimaryAddress 1
|
||||
#define kCV_Vstart 2
|
||||
#define kCV_AccelerationRate 3
|
||||
#define kCV_Deceleration Rate 4
|
||||
#define kCV_ManufacturerVersionNo 7
|
||||
#define kCV_ManufacturedID 8
|
||||
#define kCV_ExtendedAddress1 17
|
||||
#define kCV_ExtendedAddress2 18
|
||||
#define kCV_ConfigurationData1 29
|
||||
|
||||
// Accessory Decoders
|
||||
#define kCV_AddressLSB 1
|
||||
#define kCV_AddressMSB 9
|
||||
|
||||
|
||||
// DCC_Decoder results/errors
|
||||
#define kDCC_OK 0
|
||||
#define kDCC_OK_UNHANDLED 1
|
||||
#define kDCC_OK_BOOT 2
|
||||
#define kDCC_OK_IDLE 3
|
||||
#define kDCC_OK_RESET 4
|
||||
#define kDCC_OK_RAW 5
|
||||
#define kDCC_OK_BASELINE 6
|
||||
#define kDCC_OK_BASIC_ACCESSORY 7
|
||||
#define kDCC_OK_EXTENDED_ACCESSORY 8
|
||||
#define kDCC_OK_MAX 99
|
||||
|
||||
#define kDCC_ERR_DETECTION_FAILED 100
|
||||
#define kDCC_ERR_BASELINE_ADDR 101
|
||||
#define kDCC_ERR_BASELINE_INSTR 102 // Baseline packet instruction isn't 0x01DCSSSS
|
||||
#define kDCC_ERR_MISSED_BITS 103
|
||||
#define kDCC_ERR_NOT_0_OR_1 104
|
||||
#define kDCC_ERR_INVALID_LENGTH 105
|
||||
#define kDCC_ERR_MISSING_END_BIT 106
|
||||
|
||||
// Min and max valid packet lengths
|
||||
#define kPACKET_LEN_MIN 3
|
||||
#define kPACKET_LEN_MAX 6
|
||||
|
||||
// CV 1..256 are supported
|
||||
#define kCV_MAX 257
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef boolean (*RawPacket)(byte byteCount, byte* packetBytes);
|
||||
|
||||
typedef void (*IdleResetPacket)(byte byteCount, byte* packetBytes);
|
||||
|
||||
typedef void (*BaselineControlPacket)(int address, int speed, int direction);
|
||||
|
||||
typedef void (*BasicAccDecoderPacket)(int address, boolean activate, byte data);
|
||||
typedef void (*ExtendedAccDecoderPacket)(int address, byte data);
|
||||
|
||||
typedef void (*DecodingEngineCompletion)(byte resultOfLastPacket);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
typedef void(*StateFunc)();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class DCC_Decoder
|
||||
{
|
||||
public:
|
||||
DCC_Decoder();
|
||||
|
||||
// Called from setup in Arduino Sketch. Set mfgID, mfgVers and interrupt. Call one SetupXXX
|
||||
void SetupDecoder(byte mfgID, byte mfgVers, byte interrupt); // Used for Decoder
|
||||
void SetupMonitor(byte interrupt); // Used when building a monitor
|
||||
|
||||
// All packets are sent to RawPacketHandler. Return true to stop dispatching to other handlers.
|
||||
void SetRawPacketHandler(RawPacket func);
|
||||
|
||||
// S 9.2 defines two special packets. Idle and reset.
|
||||
void SetIdlePacketHandler(IdleResetPacket func);
|
||||
void SetResetPacketHandler(IdleResetPacket func);
|
||||
|
||||
// Handler for S 9.2 baseline packets. Speed value will be 1-14, 1-28, kDCC_STOP_SPEED or kDCC_ESTOP_SPEED
|
||||
void SetBaselineControlPacketHandler(BaselineControlPacket func, boolean allPackets);
|
||||
|
||||
// Handler for RP 9.2.1 Accessory Decoders.
|
||||
void SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket func, boolean allPackets);
|
||||
void SetExtendedAccessoryDecoderPacketHandler(ExtendedAccDecoderPacket func, boolean allPackets);
|
||||
|
||||
// Read/Write CVs
|
||||
byte ReadCV(int cv);
|
||||
void WriteCV(int cv, byte data);
|
||||
|
||||
// Helper function to read decoder address
|
||||
int Address();
|
||||
|
||||
// Call at least once from mainloop. Not calling frequently enough and library will miss data bits!
|
||||
void loop();
|
||||
|
||||
// Returns the packet data in string form.
|
||||
char* MakePacketString(char* buffer60Bytes, byte packetByteCount, byte* packet);
|
||||
|
||||
// Returns the number of bits in last preamble
|
||||
int LastPreambleBitCount();
|
||||
|
||||
// Timing functions. These return MS since various packets
|
||||
unsigned long MillisecondsSinceLastValidPacket();
|
||||
unsigned long MillisecondsSinceLastPacketToThisDecoder();
|
||||
unsigned long MillisecondsSinceLastIdlePacket();
|
||||
unsigned long MillisecondsSinceLastResetPacket();
|
||||
|
||||
|
||||
//======================= Debugging =======================//
|
||||
// Everytime the DCC Decoder engine starts looking for preamble bits this will be
|
||||
// called with result of last packet. (Debugging)
|
||||
void SetDecodingEngineCompletionStatusHandler(DecodingEngineCompletion func);
|
||||
// Converts code passed into completionStatusHandler to human readable string.
|
||||
const char PROGMEM* ResultString(byte resultCode);
|
||||
|
||||
//======================= Library Internals =======================//
|
||||
private:
|
||||
// State machine functions
|
||||
static void State_Boot();
|
||||
static void State_ReadPreamble();
|
||||
static void State_ReadPacket();
|
||||
static void State_Execute();
|
||||
static void State_Reset();
|
||||
|
||||
// Function pointers for the library callbacks
|
||||
static RawPacket func_RawPacket;
|
||||
static IdleResetPacket func_IdlePacket;
|
||||
static IdleResetPacket func_ResetPacket;
|
||||
|
||||
static BasicAccDecoderPacket func_BasicAccPacket;
|
||||
static boolean func_BasicAccPacket_All_Packets;
|
||||
static ExtendedAccDecoderPacket func_ExtdAccPacket;
|
||||
static boolean func_ExtdAccPacket_All_Packets;
|
||||
|
||||
static BaselineControlPacket func_BaselineControlPacket;
|
||||
static boolean func_BaselineControlPacket_All_Packets;
|
||||
|
||||
static DecodingEngineCompletion func_DecodingEngineCompletion;
|
||||
|
||||
// Current state function pointer
|
||||
static StateFunc gState; // Current state function pointer
|
||||
|
||||
// Timing data from last interrupt
|
||||
static unsigned int gLastChaos; // Interrupt chaos count we processed
|
||||
|
||||
// Preamble bit count
|
||||
static int gPreambleCount; // Bit count for reading preamble
|
||||
|
||||
// Reset reason
|
||||
static byte gResetReason; // Result code of last reason decoder was reset
|
||||
static boolean gHandledAsRawPacket;
|
||||
|
||||
// Packet data
|
||||
static byte gPacket[kPACKET_LEN_MAX]; // The packet data.
|
||||
static byte gPacketIndex; // Byte index to write to.
|
||||
static byte gPacketMask; // Bit index to write to. 0x80,0x40,0x20,...0x01
|
||||
static boolean gPacketEndedWith1; // Set true if packet ended on 1. Spec requires that the
|
||||
// packet end bit can count as a bit in next preamble.
|
||||
// CV Storage
|
||||
static byte gCV[kCV_MAX]; // CV Storage (TODO - Storage in PROGMEM)
|
||||
|
||||
// Packet arrival timing
|
||||
static unsigned long gThisPacketMS; // Milliseconds of this packet being parsed
|
||||
static boolean gLastPacketToThisAddress; // Was last pack processed to this decoder's address?
|
||||
|
||||
static unsigned long gLastValidPacketMS; // Milliseconds of last valid packet
|
||||
static unsigned long gLastValidPacketToAddressMS; // Milliseconds of last valid packet to this decoder
|
||||
static unsigned long gLastValidIdlePacketMS; // Milliseconds of last valid idle packet
|
||||
static unsigned long gLastValidResetPacketMS; // Milliseconds of last valid reset packet
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// Interrupt Support
|
||||
static void StartInterrupt(byte interrupt);
|
||||
static void DCC_Interrupt();
|
||||
static void ShiftInterruptAlignment();
|
||||
|
||||
static unsigned long gInterruptMicros;
|
||||
static byte gInterruptTimeIndex;
|
||||
static volatile unsigned int gInterruptTime[2];
|
||||
static volatile unsigned int gInterruptChaos;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
extern DCC_Decoder DCC;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#endif
|
235
examples/DCC_Basic_Acc_Decoder/DCC_Basic_Acc_Decoder.pde
Normal file
235
examples/DCC_Basic_Acc_Decoder/DCC_Basic_Acc_Decoder.pde
Normal file
@@ -0,0 +1,235 @@
|
||||
#include <DCC_Decoder.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Defines and structures
|
||||
//
|
||||
#define kDCC_INTERRUPT 0
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int address; // Address to respond to
|
||||
byte output; // State of output 1=on, 0=off
|
||||
int outputPin; // Arduino output pin to drive
|
||||
boolean isDigital; // true=digital, false=analog. If analog must also set analogValue field
|
||||
boolean isFlasher; // true=flash output, false=no time, no flash.
|
||||
byte analogValue; // Value to use with analog type.
|
||||
int durationMilli; // Milliseconds to leave output on for. 0 means don't auto off
|
||||
|
||||
unsigned long onMilli; // Used internally for timing
|
||||
unsigned long offMilli; //
|
||||
} DCCAccessoryAddress;
|
||||
|
||||
DCCAccessoryAddress gAddresses[8];
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Decoder Init
|
||||
//
|
||||
void ConfigureDecoder()
|
||||
{
|
||||
gAddresses[0].address = 714;
|
||||
gAddresses[0].output = 0;
|
||||
gAddresses[0].outputPin = 5;
|
||||
gAddresses[0].isDigital = false;
|
||||
gAddresses[0].isFlasher = false;
|
||||
gAddresses[0].analogValue = 250;
|
||||
gAddresses[0].durationMilli = 500;
|
||||
|
||||
gAddresses[1].address = 715;
|
||||
gAddresses[1].output = 0;
|
||||
gAddresses[1].outputPin = 6;
|
||||
gAddresses[1].isDigital = true;
|
||||
gAddresses[1].isFlasher = false;
|
||||
gAddresses[1].analogValue = 0;
|
||||
gAddresses[1].durationMilli = 500;
|
||||
|
||||
gAddresses[2].address = 814;
|
||||
gAddresses[2].output = 0;
|
||||
gAddresses[2].outputPin = 5;
|
||||
gAddresses[2].isDigital = false;
|
||||
gAddresses[2].isFlasher = true;
|
||||
gAddresses[2].analogValue = 250;
|
||||
gAddresses[2].durationMilli = 500;
|
||||
|
||||
gAddresses[3].address = 815;
|
||||
gAddresses[3].output = 0;
|
||||
gAddresses[3].outputPin = 6;
|
||||
gAddresses[3].isDigital = true;
|
||||
gAddresses[3].isFlasher = true;
|
||||
gAddresses[3].analogValue = 0;
|
||||
gAddresses[3].durationMilli = 500;
|
||||
|
||||
gAddresses[4].address = 914;
|
||||
gAddresses[4].output = 0;
|
||||
gAddresses[4].outputPin = 5;
|
||||
gAddresses[4].isDigital = false;
|
||||
gAddresses[4].isFlasher = false;
|
||||
gAddresses[4].analogValue = 250;
|
||||
gAddresses[4].durationMilli = 0;
|
||||
|
||||
gAddresses[5].address = 915;
|
||||
gAddresses[5].output = 0;
|
||||
gAddresses[5].outputPin = 6;
|
||||
gAddresses[5].isDigital = true;
|
||||
gAddresses[5].isFlasher = false;
|
||||
gAddresses[5].analogValue = 0;
|
||||
gAddresses[5].durationMilli = 0;
|
||||
|
||||
gAddresses[6].address = 0;
|
||||
gAddresses[6].output = 0;
|
||||
gAddresses[6].outputPin = 0;
|
||||
gAddresses[6].isDigital = false;
|
||||
gAddresses[6].isFlasher = false;
|
||||
gAddresses[6].analogValue = 0;
|
||||
gAddresses[6].durationMilli = 0;
|
||||
|
||||
gAddresses[7].address = 0;
|
||||
gAddresses[7].output = 0;
|
||||
gAddresses[7].outputPin = 0;
|
||||
gAddresses[7].isDigital = false;
|
||||
gAddresses[7].isFlasher = false;
|
||||
gAddresses[7].analogValue = 0;
|
||||
gAddresses[7].durationMilli = 0;
|
||||
|
||||
// Setup output pins
|
||||
for(int i=0; i<(int)(sizeof(gAddresses)/sizeof(gAddresses[0])); i++)
|
||||
{
|
||||
if( gAddresses[i].outputPin )
|
||||
{
|
||||
pinMode( gAddresses[i].outputPin, OUTPUT );
|
||||
}
|
||||
gAddresses[i].onMilli = 0;
|
||||
gAddresses[i].offMilli = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Basic accessory packet handler
|
||||
//
|
||||
void BasicAccDecoderPacket_Handler(int address, boolean activate, byte data)
|
||||
{
|
||||
// Convert NMRA packet address format to human address
|
||||
address -= 1;
|
||||
address *= 4;
|
||||
address += 1;
|
||||
address += (data & 0x06) >> 1;
|
||||
|
||||
boolean enable = (data & 0x01) ? 1 : 0;
|
||||
|
||||
for(int i=0; i<(int)(sizeof(gAddresses)/sizeof(gAddresses[0])); i++)
|
||||
{
|
||||
if( address == gAddresses[i].address )
|
||||
{
|
||||
Serial.print("Basic addr: ");
|
||||
Serial.print(address,DEC);
|
||||
Serial.print(" activate: ");
|
||||
Serial.println(enable,DEC);
|
||||
|
||||
if( enable )
|
||||
{
|
||||
gAddresses[i].output = 1;
|
||||
gAddresses[i].onMilli = millis();
|
||||
gAddresses[i].offMilli = 0;
|
||||
}else{
|
||||
gAddresses[i].output = 0;
|
||||
gAddresses[i].onMilli = 0;
|
||||
gAddresses[i].offMilli = millis();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Setup
|
||||
//
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
DCC.SetBasicAccessoryDecoderPacketHandler(BasicAccDecoderPacket_Handler, true);
|
||||
ConfigureDecoder();
|
||||
DCC.SetupDecoder( 0x00, 0x00, kDCC_INTERRUPT );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Main loop
|
||||
//
|
||||
void loop()
|
||||
{
|
||||
static int addr = 0;
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Loop DCC library
|
||||
DCC.loop();
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Bump to next address to test
|
||||
if( ++addr >= (int)(sizeof(gAddresses)/sizeof(gAddresses[0])) )
|
||||
{
|
||||
addr = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Turn off output?
|
||||
if( gAddresses[addr].offMilli && gAddresses[addr].offMilli<millis() )
|
||||
{
|
||||
// Clear off time
|
||||
gAddresses[addr].offMilli = 0;
|
||||
|
||||
// Disable output
|
||||
if( gAddresses[addr].isDigital )
|
||||
{
|
||||
digitalWrite( gAddresses[addr].outputPin, LOW);
|
||||
}else{
|
||||
analogWrite( gAddresses[addr].outputPin, 0);
|
||||
}
|
||||
|
||||
// If still enabled and a flash type, set on time
|
||||
if( gAddresses[addr].output && gAddresses[addr].isFlasher)
|
||||
{
|
||||
gAddresses[addr].onMilli = millis() + gAddresses[addr].durationMilli;
|
||||
}else{
|
||||
gAddresses[addr].output = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Turn on output?
|
||||
if( gAddresses[addr].onMilli && gAddresses[addr].onMilli<=millis() )
|
||||
{
|
||||
// Clear off time
|
||||
gAddresses[addr].onMilli = 0;
|
||||
|
||||
// Enable output
|
||||
if( gAddresses[addr].isDigital )
|
||||
{
|
||||
digitalWrite( gAddresses[addr].outputPin, HIGH);
|
||||
}else{
|
||||
analogWrite( gAddresses[addr].outputPin, gAddresses[addr].analogValue);
|
||||
}
|
||||
|
||||
// If still enabled and a flash type, set off time
|
||||
if( gAddresses[addr].durationMilli )
|
||||
{
|
||||
gAddresses[addr].offMilli = millis() + gAddresses[addr].durationMilli;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
171
examples/DCC_Monitor/DCC_Monitor.pde
Normal file
171
examples/DCC_Monitor/DCC_Monitor.pde
Normal file
@@ -0,0 +1,171 @@
|
||||
#include <DCC_Decoder.h>
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Defines and structures
|
||||
//
|
||||
#define kDCC_INTERRUPT 0
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int count;
|
||||
byte validBytes;
|
||||
byte data[6];
|
||||
} DCCPacket;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// The dcc decoder object and global data
|
||||
//
|
||||
int gPacketCount = 0;
|
||||
int gIdlePacketCount = 0;
|
||||
int gLongestPreamble = 0;
|
||||
|
||||
DCCPacket gPackets[25];
|
||||
|
||||
static unsigned long lastMillis = millis();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Packet handlers
|
||||
//
|
||||
|
||||
// ALL packets are sent to the RawPacket handler. Returning true indicates that packet was handled. DCC library starts watching for
|
||||
// next preamble. Returning false and library continue parsing packet and finds another handler to call.
|
||||
boolean RawPacket_Handler(byte byteCount, byte* packetBytes)
|
||||
{
|
||||
// Bump global packet count
|
||||
++gPacketCount;
|
||||
|
||||
int thisPreamble = DCC.LastPreambleBitCount();
|
||||
if( thisPreamble > gLongestPreamble )
|
||||
{
|
||||
gLongestPreamble = thisPreamble;
|
||||
}
|
||||
|
||||
// Walk table and look for a matching packet
|
||||
for( int i=0; i<(int)(sizeof(gPackets)/sizeof(gPackets[0])); ++i )
|
||||
{
|
||||
if( gPackets[i].validBytes )
|
||||
{
|
||||
// Not an empty slot. Does this slot match this packet? If so, bump count.
|
||||
if( gPackets[i].validBytes==byteCount )
|
||||
{
|
||||
char isPacket = true;
|
||||
for( int j=0; j<byteCount; j++)
|
||||
{
|
||||
if( gPackets[i].data[j] != packetBytes[j] )
|
||||
{
|
||||
isPacket = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( isPacket )
|
||||
{
|
||||
gPackets[i].count++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
// Empty slot, just copy over data
|
||||
gPackets[i].count++;
|
||||
gPackets[i].validBytes = byteCount;
|
||||
for( int j=0; j<byteCount; j++)
|
||||
{
|
||||
gPackets[i].data[j] = packetBytes[j];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Idle packets are sent here (unless handled in rawpacket handler).
|
||||
void IdlePacket_Handler(byte byteCount, byte* packetBytes)
|
||||
{
|
||||
++gIdlePacketCount;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Setup
|
||||
//
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(9600);
|
||||
|
||||
DCC.SetRawPacketHandler(RawPacket_Handler);
|
||||
DCC.SetIdlePacketHandler(IdlePacket_Handler);
|
||||
|
||||
DCC.SetupMonitor( kDCC_INTERRUPT );
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DumpAndResetTable()
|
||||
{
|
||||
char buffer60Bytes[60];
|
||||
|
||||
Serial.print("Total Packet Count: ");
|
||||
Serial.println(gPacketCount, DEC);
|
||||
|
||||
Serial.print("Idle Packet Count: ");
|
||||
Serial.println(gIdlePacketCount, DEC);
|
||||
|
||||
Serial.print("Longest Preamble: ");
|
||||
Serial.println(gLongestPreamble, DEC);
|
||||
|
||||
Serial.println("Count Packet_Data");
|
||||
for( int i=0; i<(int)(sizeof(gPackets)/sizeof(gPackets[0])); ++i )
|
||||
{
|
||||
if( gPackets[i].validBytes > 0 )
|
||||
{
|
||||
Serial.print(gPackets[i].count, DEC);
|
||||
if( gPackets[i].count < 10 )
|
||||
{
|
||||
Serial.print(" ");
|
||||
}else{
|
||||
if( gPackets[i].count < 100 )
|
||||
{
|
||||
Serial.print(" ");
|
||||
}else{
|
||||
Serial.print(" ");
|
||||
}
|
||||
}
|
||||
Serial.println( DCC.MakePacketString(buffer60Bytes, gPackets[i].validBytes, &gPackets[i].data[0]) );
|
||||
}
|
||||
gPackets[i].validBytes = 0;
|
||||
gPackets[i].count = 0;
|
||||
}
|
||||
Serial.println("============================================");
|
||||
|
||||
gPacketCount = 0;
|
||||
gIdlePacketCount = 0;
|
||||
gLongestPreamble = 0;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Main loop
|
||||
//
|
||||
void loop()
|
||||
{
|
||||
DCC.loop();
|
||||
|
||||
if( millis()-lastMillis > 2000 )
|
||||
{
|
||||
DumpAndResetTable();
|
||||
lastMillis = millis();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
34
keywords.txt
Executable file
34
keywords.txt
Executable file
@@ -0,0 +1,34 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For Matrix
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
DCC_Decoder KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
SetupDecoder KEYWORD2
|
||||
SetupMonitor KEYWORD2
|
||||
SetIdlePacketHandler KEYWORD2
|
||||
SetResetPacketHandler KEYWORD2
|
||||
SetRawPacketHandler KEYWORD2
|
||||
SetBasicAccessoryDecoderPacketHandler KEYWORD2
|
||||
SetExtendedAccessoryDecoderPacketHandler KEYWORD2
|
||||
SetBaselineControlPacketHandler KEYWORD2
|
||||
SetDecodingEngineCompletionStatusHandler KEYWORD2
|
||||
ReadCV KEYWORD2
|
||||
WriteCV KEYWORD2
|
||||
MakePacketString KEYWORD2
|
||||
ResultString KEYWORD2
|
||||
loop KEYWORD2
|
||||
Address KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
|
29
readme.md
Executable file
29
readme.md
Executable file
@@ -0,0 +1,29 @@
|
||||
This is a library for Arduino for decoding dcc signals.
|
||||
Uses Arduino 1.6 IDE.
|
||||
|
||||
Installation
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
To install this library, just place this entire folder as a subfolder in your
|
||||
Arduino/lib/targets/libraries folder.
|
||||
|
||||
When installed, this library should look like:
|
||||
|
||||
libraries/DCC_Decoder (this library's folder)
|
||||
libraries/DCC_Decoder/DCC_Decoder.cpp (the library implementation file)
|
||||
libraries/DCC_Decoder/DCC_Decoder.h (the library header file)
|
||||
libraries/DCC_Decoder/keywords.txt (the syntax coloring file)
|
||||
libraries/DCC_Decoder/examples (the examples in the "open" menu)
|
||||
libraries/DCC_Decoder/readme.txt (this file)
|
||||
|
||||
Building
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
After this library is installed, you just have to start the Arduino application.
|
||||
|
||||
To use this library in a sketch, go to the Sketch | Import Library menu and
|
||||
select DCC_Decoder. This will add a corresponding line to the top of your sketch:
|
||||
|
||||
#include <DCC_Decoder.h>
|
||||
|
||||
To stop using this library, delete that line from your sketch.
|
29
readme.txt
Executable file
29
readme.txt
Executable file
@@ -0,0 +1,29 @@
|
||||
This is a library for Arduino for decoding dcc signals.
|
||||
Uses Arduino 1.6 IDE.
|
||||
|
||||
Installation
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
To install this library, just place this entire folder as a subfolder in your
|
||||
Arduino/lib/targets/libraries folder.
|
||||
|
||||
When installed, this library should look like:
|
||||
|
||||
libraries/DCC_Decoder (this library's folder)
|
||||
libraries/DCC_Decoder/DCC_Decoder.cpp (the library implementation file)
|
||||
libraries/DCC_Decoder/DCC_Decoder.h (the library header file)
|
||||
libraries/DCC_Decoder/keywords.txt (the syntax coloring file)
|
||||
libraries/DCC_Decoder/examples (the examples in the "open" menu)
|
||||
libraries/DCC_Decoder/readme.txt (this file)
|
||||
|
||||
Building
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
After this library is installed, you just have to start the Arduino application.
|
||||
|
||||
To use this library in a sketch, go to the Sketch | Import Library menu and
|
||||
select DCC_Decoder. This will add a corresponding line to the top of your sketch:
|
||||
|
||||
#include <DCC_Decoder.h>
|
||||
|
||||
To stop using this library, delete that line from your sketch.
|
Reference in New Issue
Block a user