1 Commits

Author SHA1 Message Date
Alex Shepherd
4b175e9229 Merge branch 'AdvancedCVAck' into ESP32-IRAM_ATTR
* AdvancedCVAck:
  split out ServiceMode ackCV from Ops Mode AdvancedCVAck as doing a ackCV in Ops Mode is wrong and adds 6ms busy delay add cache of CV29 value
  bumped version to 2.0.2
  reverted changes around lastMicros
  added conditional compilation for ESP8266 to add ICACHE_RAM_ATTR to ExternalInterruptHandler changed storage for Micros to unsigned long
  changed the version to 201 in the header

# Conflicts:
#	NmraDcc.cpp
reverted to unsigned int
2019-08-06 01:23:12 +12:00
5 changed files with 1601 additions and 1810 deletions

2
.gitignore vendored
View File

@@ -1,2 +0,0 @@
.development
*.zip

View File

@@ -2,21 +2,11 @@
// //
// Model Railroading with Arduino - NmraDcc.cpp // Model Railroading with Arduino - NmraDcc.cpp
// //
// Copyright (c) 2008 - 2020 Alex Shepherd // Copyright (c) 2008 - 2017 Alex Shepherd
// //
// This library is free software; you can redistribute it and/or // This source file is subject of the GNU general public license 2,
// modify it under the terms of the GNU Lesser General Public // that is available at the world-wide-web at
// License as published by the Free Software Foundation; either // http://www.gnu.org/licenses/gpl.txt
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// //
@@ -37,7 +27,7 @@
// Minor fixes to pass NMRA Baseline Conformance Tests. // Minor fixes to pass NMRA Baseline Conformance Tests.
// 2018-12-17 added ESP32 support by Trusty (thierry@lapajaparis.net) // 2018-12-17 added ESP32 support by Trusty (thierry@lapajaparis.net)
// 2019-02-17 added ESP32 specific changes by Hans Tanner // 2019-02-17 added ESP32 specific changes by Hans Tanner
// 2020-05-15 changes to pass NMRA Tests ( always search for preamble ) //
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// //
// purpose: Provide a simplified interface to decode NMRA DCC packets // purpose: Provide a simplified interface to decode NMRA DCC packets
@@ -46,10 +36,12 @@
//------------------------------------------------------------------------ //------------------------------------------------------------------------
#include "NmraDcc.h" #include "NmraDcc.h"
#include "EEPROM.h" #ifdef __AVR_MEGA__
#include <avr/eeprom.h>
#endif
// Uncomment to print DEBUG messages // Uncomment to print DEBUG messages
// #define DEBUG_PRINT //#define DEBUG_PRINT
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// DCC Receive Routine // DCC Receive Routine
@@ -82,26 +74,19 @@
// DCC 1: _________XXXXXXXXX_________XXXXXXXXX_________ // DCC 1: _________XXXXXXXXX_________XXXXXXXXX_________
// |<--------146us------>| // |<--------146us------>|
// ^-INTx ^-INTx // ^-INTx ^-INTx
// less than 146us: its a one-Bit // less than 138us: its a one-Bit
// //
// //
// |<-----------------232us----------->| // |<-----------------232us----------->|
// DCC 0: _________XXXXXXXXXXXXXXXXXX__________________XXXXXXXX__________ // DCC 0: _________XXXXXXXXXXXXXXXXXX__________________XXXXXXXX__________
// |<--------146us------->| // |<--------146us------->|
// ^-INTx ^-INTx // ^-INTx ^-INTx
// greater than 146us: its a zero bit // greater than 138us: its a zero bit
// //
// //
// //
// //
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// if this is commented out, bit synchronisation is only done after a wrong checksum
#define SYNC_ALWAYS
// if this is commented out, Zero-Bit_Stretching is not supported
// ( Bits longer than 2* MAX ONEBIT are treated as error )
#define SUPPORT_ZERO_BIT_STRETCHING
#define MAX_ONEBITFULL 146 #define MAX_ONEBITFULL 146
#define MAX_PRAEAMBEL 146 #define MAX_PRAEAMBEL 146
@@ -192,7 +177,7 @@
#define SET_TP3 GPOS = (1 << D7); #define SET_TP3 GPOS = (1 << D7);
#define CLR_TP3 GPOC = (1 << D7); #define CLR_TP3 GPOC = (1 << D7);
#define MODE_TP4 pinMode( D8,OUTPUT ) ; // GPIO 15 #define MODE_TP4 pinMode( D8,OUTPUT ) ; // GPIO 15
#define SET_TP4 GPOS = (1 << D8); #define SET_TP4 GPOC = (1 << D8);
#define CLR_TP4 GPOC = (1 << D8); #define CLR_TP4 GPOC = (1 << D8);
#elif defined(ESP32) #elif defined(ESP32)
#define MODE_TP1 pinMode( 33,OUTPUT ) ; // GPIO 33 #define MODE_TP1 pinMode( 33,OUTPUT ) ; // GPIO 33
@@ -205,7 +190,7 @@
#define SET_TP3 GPOS = (1 << 26); #define SET_TP3 GPOS = (1 << 26);
#define CLR_TP3 GPOC = (1 << 26); #define CLR_TP3 GPOC = (1 << 26);
#define MODE_TP4 pinMode( 27,OUTPUT ) ; // GPIO 27 #define MODE_TP4 pinMode( 27,OUTPUT ) ; // GPIO 27
#define SET_TP4 GPOS = (1 << 27); #define SET_TP4 GPOC = (1 << 27);
#define CLR_TP4 GPOC = (1 << 27); #define CLR_TP4 GPOC = (1 << 27);
@@ -261,17 +246,12 @@ static byte ISRWatch; // Interrupt Handler Edge Filter
static byte ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING static byte ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
static byte ISRWatch; // Interrupt Handler Edge Filter static byte ISRWatch; // Interrupt Handler Edge Filter
#endif #endif
byte ISRLevel; // expected Level at DCC input during ISR ( to detect glitches )
byte ISRChkMask; // Flag if Level must be checked
static word bitMax, bitMin; static word bitMax, bitMin;
typedef enum typedef enum
{ {
WAIT_PREAMBLE = 0, WAIT_PREAMBLE = 0,
WAIT_START_BIT, WAIT_START_BIT,
#ifndef SYNC_ALWAYS
WAIT_START_BIT_FULL,
#endif
WAIT_DATA, WAIT_DATA,
WAIT_END_BIT WAIT_END_BIT
} }
@@ -292,7 +272,6 @@ struct DccRx_t
uint8_t DataReady ; uint8_t DataReady ;
uint8_t BitCount ; uint8_t BitCount ;
uint8_t TempByte ; uint8_t TempByte ;
uint8_t chkSum;
DCC_MSG PacketBuf; DCC_MSG PacketBuf;
DCC_MSG PacketCopy; DCC_MSG PacketCopy;
} }
@@ -309,8 +288,6 @@ typedef struct
DCC_MSG LastMsg ; DCC_MSG LastMsg ;
uint8_t ExtIntNum; uint8_t ExtIntNum;
uint8_t ExtIntPinNum; uint8_t ExtIntPinNum;
volatile uint8_t *ExtIntPort; // use port and bitmask to read input at AVR in ISR
uint8_t ExtIntMask; // digitalRead is too slow on AVR
int16_t myDccAddress; // Cached value of DCC Address from CVs int16_t myDccAddress; // Cached value of DCC Address from CVs
uint8_t inAccDecDCCAddrNextReceivedMode; uint8_t inAccDecDCCAddrNextReceivedMode;
uint8_t cv29Value; uint8_t cv29Value;
@@ -334,8 +311,6 @@ void ICACHE_RAM_ATTR ExternalInterruptHandler(void)
void ExternalInterruptHandler(void) void ExternalInterruptHandler(void)
#endif #endif
{ {
SET_TP3;
#ifdef ESP32 #ifdef ESP32
// switch (ISRWatch) // switch (ISRWatch)
// { // {
@@ -363,7 +338,7 @@ void ExternalInterruptHandler(void)
uint8_t DccBitVal; uint8_t DccBitVal;
static int8_t bit1, bit2 ; static int8_t bit1, bit2 ;
static unsigned int lastMicros = 0; static unsigned int lastMicros = 0;
static byte halfBit, DCC_IrqRunning, preambleBitCount; static byte halfBit, DCC_IrqRunning;
unsigned int actMicros, bitMicros; unsigned int actMicros, bitMicros;
#ifdef ALLOW_NESTED_IRQ #ifdef ALLOW_NESTED_IRQ
if ( DCC_IrqRunning ) { if ( DCC_IrqRunning ) {
@@ -377,53 +352,17 @@ void ExternalInterruptHandler(void)
return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
} }
#endif #endif
SET_TP3;
actMicros = micros(); actMicros = micros();
bitMicros = actMicros-lastMicros; bitMicros = actMicros-lastMicros;
if ( bitMicros < bitMin ) {
CLR_TP3; SET_TP3; // too short - my be false interrupt due to glitch or false protocol -> ignore
#ifdef __AVR_MEGA__
if ( bitMicros < bitMin || ( DccRx.State != WAIT_START_BIT && (*DccProcState.ExtIntPort & DccProcState.ExtIntMask) != (ISRLevel) ) ) {
#else
if ( bitMicros < bitMin || ( DccRx.State != WAIT_START_BIT && digitalRead( DccProcState.ExtIntPinNum ) != (ISRLevel) ) ) {
#endif
// too short - my be false interrupt due to glitch or false protocol or level does not match RISING / FALLING edge -> ignore this IRQ
CLR_TP3; CLR_TP3;
SET_TP4; /*delayMicroseconds(1); */ CLR_TP4; SET_TP4; CLR_TP4;
return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
} }
CLR_TP3; SET_TP3;
lastMicros = actMicros;
#ifndef SUPPORT_ZERO_BIT_STRETCHING
//if ( bitMicros > MAX_ZEROBITFULL ) {
if ( bitMicros > (bitMax*2) ) {
// too long - my be false protocol -> start over
DccRx.State = WAIT_PREAMBLE ;
DccRx.BitCount = 0 ;
preambleBitCount = 0;
// SET_TP2; CLR_TP2;
bitMax = MAX_PRAEAMBEL;
bitMin = MIN_ONEBITFULL;
#if defined ( __STM32F1__ )
detachInterrupt( DccProcState.ExtIntNum );
#endif
#ifdef ESP32
ISRWatch = ISREdge;
#else
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
#endif
// enable level-checking
ISRChkMask = DccProcState.ExtIntMask;
ISRLevel = (ISREdge==RISING)? DccProcState.ExtIntMask : 0 ;
CLR_TP3;
//CLR_TP3;
return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
}
CLR_TP3;
SET_TP3;
#endif
DccBitVal = ( bitMicros < bitMax ); DccBitVal = ( bitMicros < bitMax );
lastMicros = actMicros;
#ifdef ALLOW_NESTED_IRQ #ifdef ALLOW_NESTED_IRQ
DCC_IrqRunning = true; DCC_IrqRunning = true;
@@ -433,37 +372,43 @@ void ExternalInterruptHandler(void)
#ifdef DCC_DEBUG #ifdef DCC_DEBUG
DccProcState.TickCount++; DccProcState.TickCount++;
#endif #endif
switch( DccRx.State ) switch( DccRx.State )
{ {
case WAIT_PREAMBLE: case WAIT_PREAMBLE:
// We don't have to do anything special - looking for a preamble condition is done always if( DccBitVal )
SET_TP2; {
break; SET_TP1;
DccRx.BitCount++;
if( DccRx.BitCount > 10 ) {
DccRx.State = WAIT_START_BIT ;
// While waiting for the start bit, detect halfbit lengths. We will detect the correct
// sync and detect whether we see a false (e.g. motorola) protocol
#ifndef SYNC_ALWAYS #if defined ( __STM32F1__ )
case WAIT_START_BIT_FULL: detachInterrupt( DccProcState.ExtIntNum );
// wait for startbit without level checking #endif
if ( !DccBitVal ) { #ifdef ESP32
// we got the startbit ISRWatch = CHANGE;
CLR_TP2;CLR_TP1; #else
DccRx.State = WAIT_DATA ; attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE);
#endif
halfBit = 0;
bitMax = MAX_ONEBITHALF;
bitMin = MIN_ONEBITHALF;
CLR_TP1; CLR_TP1;
// initialize packet buffer }
DccRx.PacketBuf.Size = 0; } else {
/*for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ ) SET_TP1;
DccRx.PacketBuf.Data[i] = 0;*/
DccRx.PacketBuf.PreambleBits = preambleBitCount;
DccRx.BitCount = 0 ; DccRx.BitCount = 0 ;
DccRx.chkSum = 0 ; CLR_TP1;
DccRx.TempByte = 0 ;
//SET_TP1;
} }
break; break;
#endif
case WAIT_START_BIT: case WAIT_START_BIT:
// we are looking for first half "0" bit after preamble // we are looking for first half "0" bit after preamble
switch ( halfBit ) { switch ( halfBit ) {
case 0: case 0: //SET_TP1;
// check first part // check first part
if ( DccBitVal ) { if ( DccBitVal ) {
// is still 1-bit (Preamble) // is still 1-bit (Preamble)
@@ -471,114 +416,104 @@ void ExternalInterruptHandler(void)
bit1=bitMicros; bit1=bitMicros;
} else { } else {
// was "0" half bit, maybe the startbit // was "0" half bit, maybe the startbit
halfBit = 4; SET_TP1;
} halfBit = 4;
CLR_TP1;
}
break; break;
case 1: // previous halfbit was '1' case 1: //SET_TP1; // previous halfbit was '1'
if ( DccBitVal ) { if ( DccBitVal ) {
// its a '1' halfBit -> we are still in the preamble // its a '1' halfBit -> we are still in the preamble
halfBit = 0; halfBit = 0;
bit2=bitMicros; bit2=bitMicros;
preambleBitCount++; DccRx.BitCount++;
if( abs(bit2-bit1) > MAX_BITDIFF ) { if( abs(bit2-bit1) > MAX_BITDIFF ) {
// the length of the 2 halfbits differ too much -> wrong protokoll // the length of the 2 halfbits differ too much -> wrong protokoll
DccRx.State = WAIT_PREAMBLE; DccRx.State = WAIT_PREAMBLE;
bitMax = MAX_PRAEAMBEL; bitMax = MAX_PRAEAMBEL;
bitMin = MIN_ONEBITFULL; bitMin = MIN_ONEBITFULL;
preambleBitCount = 0; DccRx.BitCount = 0;
// SET_TP2; CLR_TP2;
#if defined ( __STM32F1__ ) #if defined ( __STM32F1__ )
detachInterrupt( DccProcState.ExtIntNum ); detachInterrupt( DccProcState.ExtIntNum );
#endif #endif
#ifdef ESP32 #ifdef ESP32
ISRWatch = ISREdge; ISRWatch = ISREdge;
#else #else
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge ); attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
// enable level checking ( with direct port reading @ AVR )
ISRChkMask = DccProcState.ExtIntMask;
ISRLevel = (ISREdge==RISING)? DccProcState.ExtIntMask : 0 ;
#endif #endif
SET_TP3; SET_TP3;
CLR_TP4; CLR_TP4;
} }
} else { } else {
// first '0' half detected in second halfBit // first '0' half detected in second halfBit
// wrong sync or not a DCC protokoll // wrong sync or not a DCC protokoll
CLR_TP3; CLR_TP3;
halfBit = 3; halfBit = 3;
SET_TP3; SET_TP3;
} }
break; break;
case 3: // previous halfbit was '0' in second halfbit case 3: //SET_TP1; // previous halfbit was '0' in second halfbit
if ( DccBitVal ) { if ( DccBitVal ) {
// its a '1' halfbit -> we got only a half '0' bit -> cannot be DCC // its a '1' halfbit -> we got only a half '0' bit -> cannot be DCC
DccRx.State = WAIT_PREAMBLE; DccRx.State = WAIT_PREAMBLE;
bitMax = MAX_PRAEAMBEL; bitMax = MAX_PRAEAMBEL;
bitMin = MIN_ONEBITFULL; bitMin = MIN_ONEBITFULL;
preambleBitCount = 0; DccRx.BitCount = 0;
// SET_TP2; CLR_TP2;
} else { } else {
// we got two '0' halfbits -> it's the startbit // we got two '0' halfbits -> it's the startbit
// but sync is NOT ok, change IRQ edge. // but sync is NOT ok, change IRQ edge.
CLR_TP2;CLR_TP1;
if ( ISREdge == RISING ) ISREdge = FALLING; else ISREdge = RISING; if ( ISREdge == RISING ) ISREdge = FALLING; else ISREdge = RISING;
DccRx.State = WAIT_DATA ; DccRx.State = WAIT_DATA ;
CLR_TP1;
bitMax = MAX_ONEBITFULL; bitMax = MAX_ONEBITFULL;
bitMin = MIN_ONEBITFULL; bitMin = MIN_ONEBITFULL;
DccRx.PacketBuf.Size = 0; DccRx.PacketBuf.Size = 0;
/*for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ ) DccRx.PacketBuf.PreambleBits = 0;
DccRx.PacketBuf.Data[i] = 0;*/ for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ )
DccRx.PacketBuf.PreambleBits = preambleBitCount; DccRx.PacketBuf.Data[i] = 0;
DccRx.BitCount = 0 ;
DccRx.chkSum = 0 ;
DccRx.TempByte = 0 ;
//SET_TP1;
}
//SET_TP4;
#if defined ( __STM32F1__ ) DccRx.PacketBuf.PreambleBits = DccRx.BitCount;
detachInterrupt( DccProcState.ExtIntNum ); DccRx.BitCount = 0 ;
#endif DccRx.TempByte = 0 ;
#ifdef ESP32 }
ISRWatch = ISREdge; SET_TP4;
#else
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge ); #if defined ( __STM32F1__ )
#endif detachInterrupt( DccProcState.ExtIntNum );
// enable level-checking #endif
ISRChkMask = DccProcState.ExtIntMask; #ifdef ESP32
ISRLevel = (ISREdge==RISING)? DccProcState.ExtIntMask : 0 ; ISRWatch = ISREdge;
//CLR_TP4; #else
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
#endif
CLR_TP1;
CLR_TP4;
break; break;
case 4: // previous (first) halfbit was 0 case 4: SET_TP1; // previous (first) halfbit was 0
// if this halfbit is 0 too, we got the startbit // if this halfbit is 0 too, we got the startbit
if ( DccBitVal ) { if ( DccBitVal ) {
// second halfbit is 1 -> unknown protokoll // second halfbit is 1 -> unknown protokoll
DccRx.State = WAIT_PREAMBLE; DccRx.State = WAIT_PREAMBLE;
bitMax = MAX_PRAEAMBEL; bitMax = MAX_PRAEAMBEL;
bitMin = MIN_ONEBITFULL; bitMin = MIN_ONEBITFULL;
preambleBitCount = 0;
CLR_TP2;CLR_TP1;
DccRx.BitCount = 0; DccRx.BitCount = 0;
} else { } else {
// we got the startbit // we got the startbit
CLR_TP2;CLR_TP1;
DccRx.State = WAIT_DATA ; DccRx.State = WAIT_DATA ;
CLR_TP1;
bitMax = MAX_ONEBITFULL; bitMax = MAX_ONEBITFULL;
bitMin = MIN_ONEBITFULL; bitMin = MIN_ONEBITFULL;
// initialize packet buffer
DccRx.PacketBuf.Size = 0; DccRx.PacketBuf.Size = 0;
/*for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ ) DccRx.PacketBuf.PreambleBits = 0;
DccRx.PacketBuf.Data[i] = 0;*/ for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ )
DccRx.PacketBuf.PreambleBits = preambleBitCount; DccRx.PacketBuf.Data[i] = 0;
DccRx.PacketBuf.PreambleBits = DccRx.BitCount;
DccRx.BitCount = 0 ; DccRx.BitCount = 0 ;
DccRx.chkSum = 0 ;
DccRx.TempByte = 0 ; DccRx.TempByte = 0 ;
//SET_TP1;
} }
//SET_TP4; CLR_TP1;
SET_TP4;
#if defined ( __STM32F1__ ) #if defined ( __STM32F1__ )
detachInterrupt( DccProcState.ExtIntNum ); detachInterrupt( DccProcState.ExtIntNum );
@@ -588,18 +523,14 @@ void ExternalInterruptHandler(void)
#else #else
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge ); attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
#endif #endif
// enable level-checking
ISRChkMask = DccProcState.ExtIntMask;
ISRLevel = (ISREdge==RISING)? DccProcState.ExtIntMask : 0 ;
//CLR_TP4; CLR_TP4;
break; break;
} }
break; break;
case WAIT_DATA: case WAIT_DATA:
CLR_TP2;
DccRx.BitCount++; DccRx.BitCount++;
DccRx.TempByte = ( DccRx.TempByte << 1 ) ; DccRx.TempByte = ( DccRx.TempByte << 1 ) ;
if( DccBitVal ) if( DccBitVal )
@@ -618,44 +549,30 @@ void ExternalInterruptHandler(void)
{ {
DccRx.State = WAIT_END_BIT ; DccRx.State = WAIT_END_BIT ;
DccRx.PacketBuf.Data[ DccRx.PacketBuf.Size++ ] = DccRx.TempByte ; DccRx.PacketBuf.Data[ DccRx.PacketBuf.Size++ ] = DccRx.TempByte ;
DccRx.chkSum ^= DccRx.TempByte;
} }
} }
break; break;
case WAIT_END_BIT: case WAIT_END_BIT:
SET_TP2;CLR_TP2;
DccRx.BitCount++; DccRx.BitCount++;
if( DccBitVal ) { // End of packet? if( DccBitVal ) // End of packet?
CLR_TP3; SET_TP4; {
CLR_TP3;
DccRx.State = WAIT_PREAMBLE ; DccRx.State = WAIT_PREAMBLE ;
DccRx.BitCount = 0 ; DccRx.BitCount = 0 ;
bitMax = MAX_PRAEAMBEL; bitMax = MAX_PRAEAMBEL;
bitMin = MIN_ONEBITFULL; bitMin = MIN_ONEBITFULL;
SET_TP1; #ifdef ESP32
if ( DccRx.chkSum == 0 ) { portENTER_CRITICAL_ISR(&mux);
// Packet is valid #endif
#ifdef ESP32 DccRx.PacketCopy = DccRx.PacketBuf ;
portENTER_CRITICAL_ISR(&mux); DccRx.DataReady = 1 ;
#endif #ifdef ESP32
DccRx.PacketCopy = DccRx.PacketBuf ; portEXIT_CRITICAL_ISR(&mux);
DccRx.DataReady = 1 ; #endif
#ifdef ESP32 SET_TP3;
portEXIT_CRITICAL_ISR(&mux); }
#endif else // Get next Byte
// SET_TP2; CLR_TP2;
preambleBitCount = 0 ;
} else {
// Wrong checksum
CLR_TP1;
#ifdef DCC_DBGVAR
DB_PRINT("Cerr");
countOf.Err++;
#endif
}
SET_TP3; CLR_TP4;
} else { // Get next Byte
// KGW - Abort immediately if packet is too long. // KGW - Abort immediately if packet is too long.
if( DccRx.PacketBuf.Size == MAX_DCC_MESSAGE_LEN ) // Packet is too long - abort if( DccRx.PacketBuf.Size == MAX_DCC_MESSAGE_LEN ) // Packet is too long - abort
{ {
@@ -671,89 +588,32 @@ void ExternalInterruptHandler(void)
DccRx.BitCount = 0 ; DccRx.BitCount = 0 ;
DccRx.TempByte = 0 ; DccRx.TempByte = 0 ;
} }
}
} }
// unless we're already looking for the start bit
// we always search for a preamble ( ( 10 or more consecutive 1 bits )
// if we found it within a packet, the packet decoding is aborted because
// that much one bits cannot be valid in a packet.
if ( DccRx.State != WAIT_START_BIT ) {
if( DccBitVal )
{
preambleBitCount++;
//SET_TP2;
if( preambleBitCount > 10 ) {
CLR_TP2;
#ifndef SYNC_ALWAYS
if ( DccRx.chkSum == 0 ) {
// sync must be correct if chksum was ok, no need to check sync
DccRx.State = WAIT_START_BIT_FULL;
} else {
#endif
DccRx.State = WAIT_START_BIT ;
SET_TP2;
// While waiting for the start bit, detect halfbit lengths. We will detect the correct
// sync and detect whether we see a false (e.g. motorola) protocol
#if defined ( __STM32F1__ )
detachInterrupt( DccProcState.ExtIntNum );
#endif
#ifdef ESP32
ISRWatch = CHANGE;
#else
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE);
#endif
ISRChkMask = 0; // AVR level check is always true with this settings
ISRLevel = 0; // ( there cannot be false edge IRQ's with CHANGE )
halfBit = 0;
bitMax = MAX_ONEBITHALF;
bitMin = MIN_ONEBITHALF;
//CLR_TP1;
#ifndef SYNC_ALWAYS
}
#endif
}
} else {
CLR_TP1;
preambleBitCount = 0 ;
// SET_TP2; CLR_TP2;
}
}
#ifdef ALLOW_NESTED_IRQ #ifdef ALLOW_NESTED_IRQ
DCC_IrqRunning = false; DCC_IrqRunning = false;
#endif #endif
//CLR_TP1; CLR_TP1;
CLR_TP3; CLR_TP3;
} }
void ackCV(void) void ackCV(void)
{ {
if( notifyCVAck ) if( notifyCVAck )
{
DB_PRINT("ackCV: Send Basic ACK");
notifyCVAck() ; notifyCVAck() ;
}
} }
void ackAdvancedCV(void) void ackAdvancedCV(void)
{ {
if( notifyAdvancedCVAck && (DccProcState.cv29Value & CV29_RAILCOM_ENABLE) ) if( notifyAdvancedCVAck && (DccProcState.cv29Value & CV29_ADV_ACK) )
{
DB_PRINT("ackAdvancedCV: Send RailCom ACK");
notifyAdvancedCVAck() ; notifyAdvancedCVAck() ;
}
} }
uint8_t readEEPROM( unsigned int CV ) uint8_t readEEPROM( unsigned int CV ) {
{
return EEPROM.read(CV) ; return EEPROM.read(CV) ;
} }
void writeEEPROM( unsigned int CV, uint8_t Value ) void writeEEPROM( unsigned int CV, uint8_t Value ) {
{
EEPROM.write(CV, Value) ; EEPROM.write(CV, Value) ;
#if defined(ESP8266) #if defined(ESP8266)
EEPROM.commit(); EEPROM.commit();
@@ -763,17 +623,15 @@ void writeEEPROM( unsigned int CV, uint8_t Value )
#endif #endif
} }
bool readyEEPROM() bool readyEEPROM() {
{ #ifdef __AVR_MEGA__
#if defined ARDUINO_ARCH_MEGAAVR return eeprom_is_ready();
return bit_is_clear(NVMCTRL.STATUS,NVMCTRL_EEBUSY_bp); #else
#elif defined __AVR_MEGA__ return true;
return eeprom_is_ready(); #endif
#else
return true;
#endif
} }
uint8_t validCV( uint16_t CV, uint8_t Writable ) uint8_t validCV( uint16_t CV, uint8_t Writable )
{ {
if( notifyCVResetFactoryDefault && (CV == CV_MANUFACTURER_ID ) && Writable ) if( notifyCVResetFactoryDefault && (CV == CV_MANUFACTURER_ID ) && Writable )
@@ -810,8 +668,6 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
{ {
case CV_29_CONFIG: case CV_29_CONFIG:
// copy addressmode Bit to Flags // copy addressmode Bit to Flags
Value = Value & ~CV29_RAILCOM_ENABLE; // Bidi (RailCom) Bit must not be enabled,
// because you cannot build a Bidi decoder with this lib.
DccProcState.cv29Value = Value; DccProcState.cv29Value = Value;
DccProcState.Flags = ( DccProcState.Flags & ~FLAGS_CV29_BITS) | (Value & FLAGS_CV29_BITS); DccProcState.Flags = ( DccProcState.Flags & ~FLAGS_CV29_BITS) | (Value & FLAGS_CV29_BITS);
// no break, because myDccAdress must also be reset // no break, because myDccAdress must also be reset
@@ -863,7 +719,7 @@ uint16_t getMyAddr(void)
return DccProcState.myDccAddress ; return DccProcState.myDccAddress ;
} }
void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void (*ackFunction)() ) void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
{ {
// is it a Byte Operation // is it a Byte Operation
if( Cmd & 0x04 ) if( Cmd & 0x04 )
@@ -873,9 +729,8 @@ void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void
{ {
if( validCV( CVAddr, 1 ) ) if( validCV( CVAddr, 1 ) )
{ {
DB_PRINT("CV: %d Byte Write: %02X", CVAddr, Value)
if( writeCV( CVAddr, Value ) == Value ) if( writeCV( CVAddr, Value ) == Value )
ackFunction(); ackAdvancedCV();
} }
} }
@@ -883,9 +738,8 @@ void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void
{ {
if( validCV( CVAddr, 0 ) ) if( validCV( CVAddr, 0 ) )
{ {
DB_PRINT("CV: %d Byte Read: %02X", CVAddr, Value)
if( readCV( CVAddr ) == Value ) if( readCV( CVAddr ) == Value )
ackFunction(); ackAdvancedCV();
} }
} }
} }
@@ -898,8 +752,6 @@ void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void
uint8_t tempValue = readCV( CVAddr ) ; // Read the Current CV Value uint8_t tempValue = readCV( CVAddr ) ; // Read the Current CV Value
DB_PRINT("CV: %d Current Value: %02X Bit-Wise Mode: %s Mask: %02X Value: %02X", CVAddr, tempValue, BitWrite ? "Write":"Read", BitMask, BitValue);
// Perform the Bit Write Operation // Perform the Bit Write Operation
if( BitWrite ) if( BitWrite )
{ {
@@ -912,7 +764,7 @@ void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void
tempValue &= ~BitMask ; // Turn the Bit Off tempValue &= ~BitMask ; // Turn the Bit Off
if( writeCV( CVAddr, tempValue ) == tempValue ) if( writeCV( CVAddr, tempValue ) == tempValue )
ackFunction() ; ackAdvancedCV() ;
} }
} }
@@ -924,12 +776,12 @@ void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void
if( BitValue ) if( BitValue )
{ {
if( tempValue & BitMask ) if( tempValue & BitMask )
ackFunction() ; ackAdvancedCV() ;
} }
else else
{ {
if( !( tempValue & BitMask) ) if( !( tempValue & BitMask) )
ackFunction() ; ackAdvancedCV() ;
} }
} }
} }
@@ -972,8 +824,9 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
switch( Cmd & 0b00001110 ) switch( Cmd & 0b00001110 )
{ {
case 0b00000000: case 0b00000000:
if( notifyDccReset) if( notifyDccReset && ( Cmd & 0b00000001 ) ) // Hard Reset
notifyDccReset( Cmd & 0b00000001 ) ; if( notifyDccReset)
notifyDccReset( 1 ) ;
break ; break ;
case 0b00000010: // Factory Test case 0b00000010: // Factory Test
@@ -1106,7 +959,7 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
case 0b11100000: // CV Access case 0b11100000: // CV Access
CVAddr = ( ( ( Cmd & 0x03 ) << 8 ) | Data1 ) + 1 ; CVAddr = ( ( ( Cmd & 0x03 ) << 8 ) | Data1 ) + 1 ;
processDirectCVOperation( Cmd, CVAddr, Data2, ackAdvancedCV) ; processDirectOpsOperation( Cmd, CVAddr, Data2 ) ;
break; break;
} }
} }
@@ -1121,7 +974,7 @@ void processServiceModeOperation( DCC_MSG * pDccMsg )
if( pDccMsg->Size == 3) // 3 Byte Packets are for Address Only, Register and Paged Mode if( pDccMsg->Size == 3) // 3 Byte Packets are for Address Only, Register and Paged Mode
{ {
uint8_t RegisterAddr ; uint8_t RegisterAddr ;
DB_PRINT("CV Address, Register & Paged Mode Operation"); DB_PRINT("3-BytePkt");
RegisterAddr = pDccMsg->Data[0] & 0x07 ; RegisterAddr = pDccMsg->Data[0] & 0x07 ;
Value = pDccMsg->Data[1] ; Value = pDccMsg->Data[1] ;
@@ -1164,11 +1017,11 @@ void processServiceModeOperation( DCC_MSG * pDccMsg )
else if( pDccMsg->Size == 4) // 4 Byte Packets are for Direct Byte & Bit Mode else if( pDccMsg->Size == 4) // 4 Byte Packets are for Direct Byte & Bit Mode
{ {
DB_PRINT("CV Direct Byte and Bit Mode Mode Operation"); DB_PRINT("BB-Mode");
CVAddr = ( ( ( pDccMsg->Data[0] & 0x03 ) << 8 ) | pDccMsg->Data[1] ) + 1 ; CVAddr = ( ( ( pDccMsg->Data[0] & 0x03 ) << 8 ) | pDccMsg->Data[1] ) + 1 ;
Value = pDccMsg->Data[2] ; Value = pDccMsg->Data[2] ;
processDirectCVOperation( pDccMsg->Data[0] & 0b00001100, CVAddr, Value, ackCV) ; processDirectOpsOperation( pDccMsg->Data[0] & 0b00001100, CVAddr, Value ) ;
} }
} }
#endif #endif
@@ -1245,13 +1098,12 @@ void execDccProcessor( DCC_MSG * pDccMsg )
{ {
resetServiceModeTimer( 1 ) ; resetServiceModeTimer( 1 ) ;
//Only check the DCC Packet "Size" and "Data" fields and ignore the "PreambleBits" as they can be different to the previous packet if( memcmp( pDccMsg, &DccProcState.LastMsg, sizeof( DCC_MSG ) ) )
if(pDccMsg->Size != DccProcState.LastMsg.Size || memcmp( pDccMsg->Data, &DccProcState.LastMsg.Data, pDccMsg->Size ) != 0 )
{ {
DccProcState.DuplicateCount = 0 ; DccProcState.DuplicateCount = 0 ;
memcpy( &DccProcState.LastMsg, pDccMsg, sizeof( DCC_MSG ) ) ; memcpy( &DccProcState.LastMsg, pDccMsg, sizeof( DCC_MSG ) ) ;
} }
// Wait until you see 2 identical packets before acting on a Service Mode Packet // Wait until you see 2 identicle packets before acting on a Service Mode Packet
else else
{ {
DccProcState.DuplicateCount++ ; DccProcState.DuplicateCount++ ;
@@ -1522,15 +1374,10 @@ void NmraDcc::pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup
DccProcState.ExtIntNum = ExtIntNum; DccProcState.ExtIntNum = ExtIntNum;
#endif #endif
DccProcState.ExtIntPinNum = ExtIntPinNum; DccProcState.ExtIntPinNum = ExtIntPinNum;
#ifdef __AVR_MEGA__
// because digitalRead at AVR is slow, we will read the dcc input in the ISR pinMode( ExtIntPinNum, INPUT );
// by direct port access. if( EnablePullup )
DccProcState.ExtIntPort = portInputRegister( digitalPinToPort(ExtIntPinNum) ); digitalWrite(ExtIntPinNum, HIGH);
DccProcState.ExtIntMask = digitalPinToBitMask( ExtIntPinNum );
#else
DccProcState.ExtIntMask = 1;
#endif
pinMode( ExtIntPinNum, EnablePullup ? INPUT_PULLUP : INPUT );
} }
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
@@ -1564,9 +1411,6 @@ void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui
DccProcState.inAccDecDCCAddrNextReceivedMode = 0; DccProcState.inAccDecDCCAddrNextReceivedMode = 0;
ISREdge = RISING; ISREdge = RISING;
// level checking to detect false IRQ's fired by glitches
ISRLevel = DccProcState.ExtIntMask;
ISRChkMask = DccProcState.ExtIntMask;
#ifdef ESP32 #ifdef ESP32
ISRWatch = ISREdge; ISRWatch = ISREdge;
@@ -1677,6 +1521,7 @@ uint8_t NmraDcc::process()
if( DccRx.DataReady ) if( DccRx.DataReady )
{ {
// We need to do this check with interrupts disabled // We need to do this check with interrupts disabled
//SET_TP4;
#ifdef ESP32 #ifdef ESP32
portENTER_CRITICAL(&mux); portENTER_CRITICAL(&mux);
#else #else
@@ -1690,16 +1535,25 @@ uint8_t NmraDcc::process()
#else #else
interrupts(); interrupts();
#endif #endif
// Checking of the XOR-byte is now done in the ISR already #ifdef DCC_DBGVAR
#ifdef DCC_DBGVAR countOf.Tel++;
countOf.Tel++; #endif
#endif
// Clear trailing bytes
for ( byte i=Msg.Size; i< MAX_DCC_MESSAGE_LEN; i++ ) Msg.Data[i] = 0;
if( notifyDccMsg ) notifyDccMsg( &Msg ); uint8_t xorValue = 0 ;
execDccProcessor( &Msg ); for(uint8_t i = 0; i < DccRx.PacketCopy.Size; i++)
xorValue ^= DccRx.PacketCopy.Data[i];
if(xorValue) {
#ifdef DCC_DBGVAR
DB_PRINT("Cerr");
countOf.Err++;
#endif
return 0 ;
} else {
if( notifyDccMsg ) notifyDccMsg( &Msg );
execDccProcessor( &Msg );
}
return 1 ; return 1 ;
} }

View File

@@ -2,21 +2,11 @@
// //
// Model Railroading with Arduino - NmraDcc.h // Model Railroading with Arduino - NmraDcc.h
// //
// Copyright (c) 2008 - 2020 Alex Shepherd // Copyright (c) 2008 - 2018 Alex Shepherd
// //
// This library is free software; you can redistribute it and/or // This source file is subject of the GNU general public license 2,
// modify it under the terms of the GNU Lesser General Public // that is available at the world-wide-web at
// License as published by the Free Software Foundation; either // http://www.gnu.org/licenses/gpl.txt
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// //
//------------------------------------------------------------------------ //------------------------------------------------------------------------
// //
@@ -54,10 +44,12 @@
#include "WProgram.h" #include "WProgram.h"
#endif #endif
#include "EEPROM.h"
#ifndef NMRADCC_IS_IN #ifndef NMRADCC_IS_IN
#define NMRADCC_IS_IN #define NMRADCC_IS_IN
#define NMRADCC_VERSION 206 // Version 2.0.6 #define NMRADCC_VERSION 201 // Version 2.0.1
#define MAX_DCC_MESSAGE_LEN 6 // including XOR-Byte #define MAX_DCC_MESSAGE_LEN 6 // including XOR-Byte
@@ -126,7 +118,7 @@ typedef enum {
CV29_LOCO_DIR = 0b00000001, /** bit 0: Locomotive Direction: "0" = normal, "1" = reversed */ CV29_LOCO_DIR = 0b00000001, /** bit 0: Locomotive Direction: "0" = normal, "1" = reversed */
CV29_F0_LOCATION = 0b00000010, /** bit 1: F0 location: "0" = bit 4 in Speed and Direction instructions, "1" = bit 4 in function group one instruction */ CV29_F0_LOCATION = 0b00000010, /** bit 1: F0 location: "0" = bit 4 in Speed and Direction instructions, "1" = bit 4 in function group one instruction */
CV29_APS = 0b00000100, /** bit 2: Alternate Power Source (APS) "0" = NMRA Digital only, "1" = Alternate power source set by CV12 */ CV29_APS = 0b00000100, /** bit 2: Alternate Power Source (APS) "0" = NMRA Digital only, "1" = Alternate power source set by CV12 */
CV29_RAILCOM_ENABLE = 0b00001000, /** bit 3: BiDi ( RailCom ) is active */ CV29_ADV_ACK = 0b00001000, /** bit 3: ACK, Advanced Acknowledge mode enabled if 1, disabled if 0 */
CV29_SPEED_TABLE_ENABLE = 0b00010000, /** bit 4: STE, Speed Table Enable, "0" = values in CVs 2, 4 and 6, "1" = Custom table selected by CV 25 */ CV29_SPEED_TABLE_ENABLE = 0b00010000, /** bit 4: STE, Speed Table Enable, "0" = values in CVs 2, 4 and 6, "1" = Custom table selected by CV 25 */
CV29_EXT_ADDRESSING = 0b00100000, /** bit 5: "0" = one byte addressing, "1" = two byte addressing */ CV29_EXT_ADDRESSING = 0b00100000, /** bit 5: "0" = one byte addressing, "1" = two byte addressing */
CV29_OUTPUT_ADDRESS_MODE = 0b01000000, /** bit 6: "0" = Decoder Address Mode "1" = Output Address Mode */ CV29_OUTPUT_ADDRESS_MODE = 0b01000000, /** bit 6: "0" = Decoder Address Mode "1" = Output Address Mode */
@@ -712,8 +704,9 @@ extern void notifyCVResetFactoryDefault(void) __attribute__ ((weak));
*/ */
extern void notifyCVAck(void) __attribute__ ((weak)); extern void notifyCVAck(void) __attribute__ ((weak));
/*+ /*+
* notifyAdvancedCVAck() Called when a CV write must be acknowledged via Advanced Acknowledgement. * notifyAdvancedCVAck() Called when a CV write must be acknowledged.
* This callback must send the Advanced Acknowledgement via RailComm. * This callback must increase the current drawn by this
* decoder by at least 60mA for 6ms +/- 1ms.
* *
* Inputs: * Inputs:
* None * None

View File

@@ -1,8 +1,8 @@
// DCC Stepper Motor Controller ( A4988 ) Example for Model Railroad Turntable Control // DCC Stepper Motor Controller ( A4988 ) Example for Model Railroad Turntable Control
// //
// See: https://www.dccinterface.com/product/arduino-model-railway-dcc-stepper-motor-controller-a4988-assembled/ // See: https://www.dccinterface.com/how-to/assemblyguide/
// //
// Author: Alex Shepherd 2020-06-01 // Author: Alex Shepherd 2017-12-04
// //
// This example requires two Arduino Libraries: // This example requires two Arduino Libraries:
// //
@@ -22,22 +22,12 @@
// The lines below define the pins used to connect to the A4988 driver module // The lines below define the pins used to connect to the A4988 driver module
#define A4988_STEP_PIN 4 #define A4988_STEP_PIN 4
#define A4988_DIRECTION_PIN 5 #define A4988_DIRECTION_PIN 5
#define A4988_ENABLE_PIN 6
#ifdef A4988_ENABLE_PIN
// Uncomment the next line to enable Powering-Off the Stepper when its not running to reduce heating the motor and driver // Uncomment the next line to enable Powering-Off the Stepper when its not running to reduce heating the motor and driver
#define DISABLE_OUTPUTS_IDLE #define A4988_ENABLE_PIN 6
#endif
// By default the stepper motor will move the shortest distance to the desired position.
// If you need the turntable to only move in the Positive/Increasing or Negative/Decreasing step numbers to better handle backlash in the mechanism
// Then uncomment the appropriate line below
//#define ALWAYS_MOVE_POSITIVE
//#define ALWAYS_MOVE_NEGATIVE
// The lines below define the stepping speed and acceleration, which you may need to tune for your application // The lines below define the stepping speed and acceleration, which you may need to tune for your application
#define STEPPER_MAX_SPEED 800 // Sets the maximum permitted speed #define STEPPER_MAX_SPEED 800 // Sets the maximum permitted speed
#define STEPPER_ACCELARATION 1000 // Sets the acceleration/deceleration rate #define STEPPER_ACCELARATION 1000 // Sets the acceleration/deceleration rate
#define STEPPER_SPEED 300 // Sets the desired constant speed for use with runSpeed() #define STEPPER_SPEED 300 // Sets the desired constant speed for use with runSpeed()
// The line below defines the number of "Full Steps" your stepper motor does for a full rotation // The line below defines the number of "Full Steps" your stepper motor does for a full rotation
@@ -102,9 +92,6 @@ TurnoutPosition turnoutPositions[] = {
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
// You shouldn't need to edit anything below this line unless you're needing to make big changes... ;) // You shouldn't need to edit anything below this line unless you're needing to make big changes... ;)
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
#if defined(ALWAYS_MOVE_POSITIVE) && defined(ALWAYS_MOVE_NEGATIVE)
#error ONLY uncomment one of ALWAYS_MOVE_POSITIVE or ALWAYS_MOVE_NEGATIVE but NOT both
#endif
#define MAX_TURNOUT_POSITIONS (sizeof(turnoutPositions) / sizeof(TurnoutPosition)) #define MAX_TURNOUT_POSITIONS (sizeof(turnoutPositions) / sizeof(TurnoutPosition))
@@ -117,12 +104,11 @@ NmraDcc Dcc ;
// Variables to store the last DCC Turnout message Address and Direction // Variables to store the last DCC Turnout message Address and Direction
uint16_t lastAddr = 0xFFFF ; uint16_t lastAddr = 0xFFFF ;
uint8_t lastDirection = 0xFF; uint8_t lastDirection = 0xFF;
int lastStep = 0;
// This function is called whenever a normal DCC Turnout Packet is received // This function is called whenever a normal DCC Turnout Packet is received
void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower ) void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower )
{ {
Serial.print(F("notifyDccAccTurnoutOutput: ")); Serial.print("notifyDccAccTurnoutOutput: ") ;
Serial.print(Addr,DEC) ; Serial.print(Addr,DEC) ;
Serial.print(','); Serial.print(',');
Serial.print(Direction,DEC) ; Serial.print(Direction,DEC) ;
@@ -145,50 +131,23 @@ void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t Output
#ifdef A4988_ENABLE_PIN #ifdef A4988_ENABLE_PIN
stepper1.enableOutputs(); stepper1.enableOutputs();
#endif #endif
if (Direction)
int newStep; {
if(Direction) Serial.println(turnoutPositions[i].positionFront, DEC);
newStep = turnoutPositions[i].positionFront; stepper1.moveTo(turnoutPositions[i].positionFront);
break;
}
else else
newStep = turnoutPositions[i].positionBack; {
Serial.println(turnoutPositions[i].positionBack, DEC);
Serial.print(newStep, DEC); stepper1.moveTo(turnoutPositions[i].positionBack);
break;
Serial.print(F(" Last Step: ")); }
Serial.print(lastStep, DEC);
int diffStep = newStep - lastStep;
Serial.print(F(" Diff Step: "));
Serial.print(diffStep, DEC);
#if defined ALWAYS_MOVE_POSITIVE
Serial.print(F(" Positive"));
if(diffStep < 0)
diffStep += FULL_TURN_STEPS;
#elif defined ALWAYS_MOVE_NEGATIVE
Serial.print(F(" Negative"));
if(diffStep > 0)
diffStep -= FULL_TURN_STEPS;
#else
if(diffStep > HALF_TURN_STEPS)
diffStep = diffStep - FULL_TURN_STEPS;
else if(diffStep < -HALF_TURN_STEPS)
diffStep = diffStep + FULL_TURN_STEPS;
#endif
Serial.print(F(" Move: "));
Serial.println(diffStep, DEC);
stepper1.move(diffStep);
lastStep = newStep;
break;
} }
} }
}; };
#ifdef DISABLE_OUTPUTS_IDLE #ifdef A4988_ENABLE_PIN
bool lastIsRunningState ; bool lastIsRunningState ;
#endif #endif
@@ -204,10 +163,6 @@ void setupStepperDriver()
stepper1.setSpeed(STEPPER_SPEED); // Sets the desired constant speed for use with runSpeed() stepper1.setSpeed(STEPPER_SPEED); // Sets the desired constant speed for use with runSpeed()
#ifdef A4988_ENABLE_PIN #ifdef A4988_ENABLE_PIN
stepper1.enableOutputs();
#endif
#ifdef DISABLE_OUTPUTS_IDLE
lastIsRunningState = stepper1.isRunning(); lastIsRunningState = stepper1.isRunning();
#endif #endif
} }
@@ -218,19 +173,14 @@ bool moveToHomePosition()
pinMode(HOME_SENSOR_PIN, INPUT_PULLUP); pinMode(HOME_SENSOR_PIN, INPUT_PULLUP);
#ifdef ALWAYS_MOVE_NEGATIVE
stepper1.move(0 - (FULL_TURN_STEPS * 2));
#else
stepper1.move(FULL_TURN_STEPS * 2); stepper1.move(FULL_TURN_STEPS * 2);
#endif
while(digitalRead(HOME_SENSOR_PIN) != HOME_SENSOR_ACTIVE_STATE) while(digitalRead(HOME_SENSOR_PIN) != HOME_SENSOR_ACTIVE_STATE)
stepper1.run(); stepper1.run();
if(digitalRead(HOME_SENSOR_PIN) == HOME_SENSOR_ACTIVE_STATE) if(digitalRead(HOME_SENSOR_PIN) == HOME_SENSOR_ACTIVE_STATE)
{ {
stepper1.stop();
stepper1.setCurrentPosition(0);
Serial.println(F("Found Home Position - Setting Current Position to 0")); Serial.println(F("Found Home Position - Setting Current Position to 0"));
stepper1.setCurrentPosition(0);
return true; return true;
} }
else else
@@ -260,32 +210,28 @@ void setup()
Serial.print(F("Full Rotation Steps: ")); Serial.print(F("Full Rotation Steps: "));
Serial.println(FULL_TURN_STEPS); Serial.println(FULL_TURN_STEPS);
Serial.print(F("Movement Strategy: "));
#if defined ALWAYS_MOVE_POSITIVE
Serial.println(F("Positive Direction Only"));
#elif defined ALWAYS_MOVE_NEGATIVE
Serial.println(F("Negative Direction Only"));
#else
Serial.println(F("Shortest Distance"));
#endif
for(uint8_t i = 0; i < MAX_TURNOUT_POSITIONS; i++) for(uint8_t i = 0; i < MAX_TURNOUT_POSITIONS; i++)
{ {
Serial.print(F("DCC Addr: ")); Serial.print("DCC Addr: ");
Serial.print(turnoutPositions[i].dccAddress); Serial.print(turnoutPositions[i].dccAddress);
Serial.print(F(" Front: ")); Serial.print(" Front: ");
Serial.print(turnoutPositions[i].positionFront); Serial.print(turnoutPositions[i].positionFront);
Serial.print(F(" Back: ")); Serial.print(" Back: ");
Serial.println(turnoutPositions[i].positionBack); Serial.println(turnoutPositions[i].positionBack);
} }
setupStepperDriver(); setupStepperDriver();
if(moveToHomePosition()); if(moveToHomePosition());
{ {
setupDCCDecoder(); setupDCCDecoder();
#ifdef A4988_ENABLE_PIN
stepper1.enableOutputs();
#endif
// Fake a DCC Packet to cause the Turntable to move to Position 1 // Fake a DCC Packet to cause the Turntable to move to Position 1
notifyDccAccTurnoutOutput(POSITION_01_DCC_ADDRESS, 1, 1); notifyDccAccTurnoutOutput(POSITION_01_DCC_ADDRESS, 1, 1);
} }
@@ -299,7 +245,7 @@ void loop()
// Process the Stepper Library // Process the Stepper Library
stepper1.run(); stepper1.run();
#ifdef DISABLE_OUTPUTS_IDLE #ifdef A4988_ENABLE_PIN
if(stepper1.isRunning() != lastIsRunningState) if(stepper1.isRunning() != lastIsRunningState)
{ {
lastIsRunningState = stepper1.isRunning(); lastIsRunningState = stepper1.isRunning();

View File

@@ -1,5 +1,5 @@
name=NmraDcc name=NmraDcc
version=2.0.6 version=2.0.2
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda), Hans Tanner author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda), Hans Tanner
maintainer=Alex Shepherd <kiwi64ajs@gmail.com> maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
sentence=Enables NMRA DCC Communication sentence=Enables NMRA DCC Communication