|
|
|
@@ -20,6 +20,8 @@
|
|
|
|
|
// Experimental Version to support 14 speed steps
|
|
|
|
|
// and new signature of notifyDccSpeed and notifyDccFunc
|
|
|
|
|
// 2015-12-16 Version without use of Timer0 by Franz-Peter Müller
|
|
|
|
|
// 2016-07-16 handle glitches on DCC line
|
|
|
|
|
// 2016-08-20 added ESP8266 support by Sven (littleyoda)
|
|
|
|
|
//
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
//
|
|
|
|
@@ -29,7 +31,12 @@
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#include "NmraDcc.h"
|
|
|
|
|
|
|
|
|
|
#if defined(ESP8266)
|
|
|
|
|
#include <EEPROM.h>
|
|
|
|
|
#else
|
|
|
|
|
#include <avr/eeprom.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
// DCC Receive Routine
|
|
|
|
@@ -60,14 +67,14 @@
|
|
|
|
|
//
|
|
|
|
|
// |<-----116us----->|
|
|
|
|
|
// DCC 1: _________XXXXXXXXX_________XXXXXXXXX_________
|
|
|
|
|
// |<--------138us------>|
|
|
|
|
|
// |<--------146us------>|
|
|
|
|
|
// ^-INTx ^-INTx
|
|
|
|
|
// less than 138us: its a one-Bit
|
|
|
|
|
//
|
|
|
|
|
//
|
|
|
|
|
// |<-----------------232us----------->|
|
|
|
|
|
// DCC 0: _________XXXXXXXXXXXXXXXXXX__________________XXXXXXXX__________
|
|
|
|
|
// |<--------138us------->|
|
|
|
|
|
// |<--------146us------->|
|
|
|
|
|
// ^-INTx ^-INTx
|
|
|
|
|
// greater than 138us: its a zero bit
|
|
|
|
|
//
|
|
|
|
@@ -76,8 +83,12 @@
|
|
|
|
|
//
|
|
|
|
|
//------------------------------------------------------------------------
|
|
|
|
|
#define MAX_ONEBITFULL 146
|
|
|
|
|
#define MAX_PRAEAMBEL 146 //138
|
|
|
|
|
#define MAX_PRAEAMBEL 146
|
|
|
|
|
#define MAX_ONEBITHALF 82
|
|
|
|
|
#define MIN_ONEBITFULL 82
|
|
|
|
|
#define MIN_ONEBITHALF 35
|
|
|
|
|
#define MAX_BITDIFF 18
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Debug-Ports
|
|
|
|
|
//#define debug // Testpulse for logic analyser
|
|
|
|
@@ -192,7 +203,7 @@ struct countOf_t countOf;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static byte ISREdge; // RISING or FALLING
|
|
|
|
|
static word bitMax;
|
|
|
|
|
static word bitMax, bitMin;
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
@@ -228,6 +239,7 @@ typedef struct
|
|
|
|
|
#ifdef DCC_DEBUG
|
|
|
|
|
uint8_t IntCount;
|
|
|
|
|
uint8_t TickCount;
|
|
|
|
|
uint8_t NestedIrqCount;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
DCC_PROCESSOR_STATE ;
|
|
|
|
@@ -240,19 +252,38 @@ void ExternalInterruptHandler(void)
|
|
|
|
|
uint8_t DccBitVal;
|
|
|
|
|
static int8_t bit1, bit2 ;
|
|
|
|
|
static word lastMicros;
|
|
|
|
|
static byte halfBit;
|
|
|
|
|
static byte halfBit, DCC_IrqRunning;
|
|
|
|
|
unsigned int actMicros, bitMicros;
|
|
|
|
|
if ( DCC_IrqRunning ) {
|
|
|
|
|
// nested DCC IRQ - obviously there are glitches
|
|
|
|
|
// ignore this interrupt and increment glitchcounter
|
|
|
|
|
CLR_TP3;
|
|
|
|
|
#ifdef DCC_DEBUG
|
|
|
|
|
DccProcState.NestedIrqCount++;
|
|
|
|
|
#endif
|
|
|
|
|
SET_TP3;
|
|
|
|
|
return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
|
|
|
|
|
}
|
|
|
|
|
SET_TP3;
|
|
|
|
|
actMicros = micros();
|
|
|
|
|
bitMicros = actMicros-lastMicros;
|
|
|
|
|
if ( bitMicros < bitMin ) {
|
|
|
|
|
// too short - my be false interrupt due to glitch or false protocol -> ignore
|
|
|
|
|
CLR_TP3;
|
|
|
|
|
SET_TP4;
|
|
|
|
|
SET_TP4;
|
|
|
|
|
CLR_TP4;
|
|
|
|
|
return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
|
|
|
|
|
}
|
|
|
|
|
DccBitVal = ( bitMicros < bitMax );
|
|
|
|
|
lastMicros = actMicros;
|
|
|
|
|
//#ifdef debug
|
|
|
|
|
if(DccBitVal) {SET_TP2} else {CLR_TP2};
|
|
|
|
|
if(DccBitVal) {SET_TP2;} else {CLR_TP2;};
|
|
|
|
|
//#endif
|
|
|
|
|
DCC_IrqRunning = true;
|
|
|
|
|
sei(); // time critical is only the micros() command,so allow nested irq's
|
|
|
|
|
#ifdef DCC_DEBUG
|
|
|
|
|
DccProcState.TickCount++;
|
|
|
|
|
DccProcState.TickCount++;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
switch( DccRx.State )
|
|
|
|
@@ -269,6 +300,7 @@ void ExternalInterruptHandler(void)
|
|
|
|
|
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE);
|
|
|
|
|
halfBit = 0;
|
|
|
|
|
bitMax = MAX_ONEBITHALF;
|
|
|
|
|
bitMin = MIN_ONEBITHALF;
|
|
|
|
|
CLR_TP1;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@@ -299,12 +331,13 @@ void ExternalInterruptHandler(void)
|
|
|
|
|
halfBit = 0;
|
|
|
|
|
bit2=bitMicros;
|
|
|
|
|
DccRx.BitCount++;
|
|
|
|
|
if( abs(bit2-bit1) > 14 ) {
|
|
|
|
|
// the length of the 2 halbits differ too much -> wrong protokoll
|
|
|
|
|
if( abs(bit2-bit1) > MAX_BITDIFF ) {
|
|
|
|
|
// the length of the 2 halfbits differ too much -> wrong protokoll
|
|
|
|
|
CLR_TP2;
|
|
|
|
|
CLR_TP3;
|
|
|
|
|
DccRx.State = WAIT_PREAMBLE;
|
|
|
|
|
bitMax = MAX_PRAEAMBEL;
|
|
|
|
|
bitMin = MIN_ONEBITFULL;
|
|
|
|
|
DccRx.BitCount = 0;
|
|
|
|
|
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
|
|
|
|
|
SET_TP3;
|
|
|
|
@@ -320,6 +353,7 @@ void ExternalInterruptHandler(void)
|
|
|
|
|
// its a '1' halfbit -> we got only a half '0' bit -> cannot be DCC
|
|
|
|
|
DccRx.State = WAIT_PREAMBLE;
|
|
|
|
|
bitMax = MAX_PRAEAMBEL;
|
|
|
|
|
bitMin = MIN_ONEBITFULL;
|
|
|
|
|
DccRx.BitCount = 0;
|
|
|
|
|
} else {
|
|
|
|
|
// we got two '0' halfbits -> it's the startbit
|
|
|
|
@@ -327,6 +361,7 @@ void ExternalInterruptHandler(void)
|
|
|
|
|
if ( ISREdge == RISING ) ISREdge = FALLING; else ISREdge = RISING;
|
|
|
|
|
DccRx.State = WAIT_DATA ;
|
|
|
|
|
bitMax = MAX_ONEBITFULL;
|
|
|
|
|
bitMin = MIN_ONEBITFULL;
|
|
|
|
|
DccRx.PacketBuf.Size = 0;
|
|
|
|
|
DccRx.PacketBuf.PreambleBits = 0;
|
|
|
|
|
for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ )
|
|
|
|
@@ -345,11 +380,13 @@ void ExternalInterruptHandler(void)
|
|
|
|
|
// second halfbit is 1 -> unknown protokoll
|
|
|
|
|
DccRx.State = WAIT_PREAMBLE;
|
|
|
|
|
bitMax = MAX_PRAEAMBEL;
|
|
|
|
|
bitMin = MIN_ONEBITFULL;
|
|
|
|
|
DccRx.BitCount = 0;
|
|
|
|
|
} else {
|
|
|
|
|
// we got the startbit
|
|
|
|
|
DccRx.State = WAIT_DATA ;
|
|
|
|
|
bitMax = MAX_ONEBITFULL;
|
|
|
|
|
bitMin = MIN_ONEBITFULL;
|
|
|
|
|
DccRx.PacketBuf.Size = 0;
|
|
|
|
|
DccRx.PacketBuf.PreambleBits = 0;
|
|
|
|
|
for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ )
|
|
|
|
@@ -378,6 +415,7 @@ void ExternalInterruptHandler(void)
|
|
|
|
|
{
|
|
|
|
|
DccRx.State = WAIT_PREAMBLE ;
|
|
|
|
|
bitMax = MAX_PRAEAMBEL;
|
|
|
|
|
bitMin = MIN_ONEBITFULL;
|
|
|
|
|
DccRx.BitCount = 0 ;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
@@ -395,6 +433,7 @@ void ExternalInterruptHandler(void)
|
|
|
|
|
CLR_TP3;
|
|
|
|
|
DccRx.State = WAIT_PREAMBLE ;
|
|
|
|
|
bitMax = MAX_PRAEAMBEL;
|
|
|
|
|
bitMin = MIN_ONEBITFULL;
|
|
|
|
|
DccRx.PacketCopy = DccRx.PacketBuf ;
|
|
|
|
|
DccRx.DataReady = 1 ;
|
|
|
|
|
SET_TP3;
|
|
|
|
@@ -407,6 +446,7 @@ void ExternalInterruptHandler(void)
|
|
|
|
|
}
|
|
|
|
|
CLR_TP1;
|
|
|
|
|
CLR_TP3;
|
|
|
|
|
DCC_IrqRunning = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ackCV(void)
|
|
|
|
@@ -415,6 +455,32 @@ void ackCV(void)
|
|
|
|
|
notifyCVAck() ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t readEEPROM( unsigned int CV ) {
|
|
|
|
|
#if defined(ESP8266)
|
|
|
|
|
return EEPROM.read(CV) ;
|
|
|
|
|
#else
|
|
|
|
|
return eeprom_read_byte( (uint8_t*) CV );
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void writeEEPROM( unsigned int CV, uint8_t Value ) {
|
|
|
|
|
#if defined(ESP8266)
|
|
|
|
|
EEPROM.write(CV, Value) ;
|
|
|
|
|
EEPROM.commit();
|
|
|
|
|
#else
|
|
|
|
|
eeprom_write_byte( (uint8_t*) CV, Value ) ;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool readyEEPROM() {
|
|
|
|
|
#if defined(ESP8266)
|
|
|
|
|
return true;
|
|
|
|
|
#else
|
|
|
|
|
return eeprom_is_ready();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint8_t validCV( uint16_t CV, uint8_t Writable )
|
|
|
|
|
{
|
|
|
|
|
if( notifyCVResetFactoryDefault && (CV == CV_MANUFACTURER_ID ) && Writable )
|
|
|
|
@@ -425,7 +491,7 @@ uint8_t validCV( uint16_t CV, uint8_t Writable )
|
|
|
|
|
|
|
|
|
|
uint8_t Valid = 1 ;
|
|
|
|
|
|
|
|
|
|
if( CV > E2END )
|
|
|
|
|
if( CV > MAXCV )
|
|
|
|
|
Valid = 0 ;
|
|
|
|
|
|
|
|
|
|
if( Writable && ( ( CV ==CV_VERSION_ID ) || (CV == CV_MANUFACTURER_ID ) ) )
|
|
|
|
@@ -441,8 +507,7 @@ uint8_t readCV( unsigned int CV )
|
|
|
|
|
if( notifyCVRead )
|
|
|
|
|
return notifyCVRead( CV ) ;
|
|
|
|
|
|
|
|
|
|
Value = eeprom_read_byte( (uint8_t*) CV ) ;
|
|
|
|
|
|
|
|
|
|
Value = readEEPROM(CV);
|
|
|
|
|
return Value ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -451,14 +516,14 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
|
|
|
|
|
if( notifyCVWrite )
|
|
|
|
|
return notifyCVWrite( CV, Value ) ;
|
|
|
|
|
|
|
|
|
|
if( eeprom_read_byte( (uint8_t*) CV ) != Value )
|
|
|
|
|
if( readEEPROM( CV ) != Value )
|
|
|
|
|
{
|
|
|
|
|
eeprom_write_byte( (uint8_t*) CV, Value ) ;
|
|
|
|
|
writeEEPROM( CV, Value ) ;
|
|
|
|
|
|
|
|
|
|
if( notifyCVChange )
|
|
|
|
|
notifyCVChange( CV, Value) ;
|
|
|
|
|
}
|
|
|
|
|
return eeprom_read_byte( (uint8_t*) CV ) ;
|
|
|
|
|
return readEEPROM( CV ) ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t getMyAddr(void)
|
|
|
|
@@ -946,6 +1011,9 @@ void NmraDcc::initAccessoryDecoder( uint8_t ManufacturerId, uint8_t VersionId, u
|
|
|
|
|
|
|
|
|
|
void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV )
|
|
|
|
|
{
|
|
|
|
|
#if defined(ESP8266)
|
|
|
|
|
EEPROM.begin(MAXCV);
|
|
|
|
|
#endif
|
|
|
|
|
// Clear all the static member variables
|
|
|
|
|
memset( &DccRx, 0, sizeof( DccRx) );
|
|
|
|
|
|
|
|
|
@@ -955,6 +1023,7 @@ void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui
|
|
|
|
|
MODE_TP4;
|
|
|
|
|
ISREdge = RISING;
|
|
|
|
|
bitMax = MAX_ONEBITFULL;
|
|
|
|
|
bitMin = MIN_ONEBITFULL;
|
|
|
|
|
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, RISING);
|
|
|
|
|
|
|
|
|
|
DccProcState.Flags = Flags ;
|
|
|
|
@@ -990,8 +1059,7 @@ uint8_t NmraDcc::isSetCVReady(void)
|
|
|
|
|
{
|
|
|
|
|
if(notifyIsSetCVReady)
|
|
|
|
|
return notifyIsSetCVReady();
|
|
|
|
|
|
|
|
|
|
return eeprom_is_ready();
|
|
|
|
|
return readyEEPROM();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DCC_DEBUG
|
|
|
|
@@ -1005,6 +1073,11 @@ uint8_t NmraDcc::getTickCount(void)
|
|
|
|
|
return DccProcState.TickCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t NmraDcc::getNestedIrqCount(void)
|
|
|
|
|
{
|
|
|
|
|
return DccProcState.NestedIrqCount;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t NmraDcc::getState(void)
|
|
|
|
|
{
|
|
|
|
|
return DccRx.State;
|
|
|
|
|