You need to sign in or sign up before continuing.
Commit e2c7ee41 authored by Lukáš Voborský's avatar Lukáš Voborský Committed by Nathaniel Wesley Filardo
Browse files

NmraDcc upstream version

parent 56c01fa5
......@@ -2,11 +2,21 @@
//
// Model Railroading with Arduino - NmraDcc.cpp
//
// Copyright (c) 2008 - 2017 Alex Shepherd
// Copyright (c) 2008 - 2020 Alex Shepherd
//
// This source file is subject of the GNU general public license 2,
// that is available at the world-wide-web at
// http://www.gnu.org/licenses/gpl.txt
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// 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
//
//------------------------------------------------------------------------
//
......@@ -27,7 +37,7 @@
// Minor fixes to pass NMRA Baseline Conformance Tests.
// 2018-12-17 added ESP32 support by Trusty (thierry@lapajaparis.net)
// 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
......@@ -35,30 +45,11 @@
//
//------------------------------------------------------------------------
// NodeMCU Lua port by @voborsky
// #define NODE_DEBUG
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include "platform.h"
#include "user_interface.h"
#include "task/task.h"
#include "driver/NmraDcc.h"
#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
#define BYTE_TO_BINARY(byte) \
(byte & 0x80 ? '1' : '0'), \
(byte & 0x40 ? '1' : '0'), \
(byte & 0x20 ? '1' : '0'), \
(byte & 0x10 ? '1' : '0'), \
(byte & 0x08 ? '1' : '0'), \
(byte & 0x04 ? '1' : '0'), \
(byte & 0x02 ? '1' : '0'), \
(byte & 0x01 ? '1' : '0')
#include "NmraDcc.h"
#include "EEPROM.h"
// Uncomment to print DEBUG messages
// #define DEBUG_PRINT
//------------------------------------------------------------------------
// DCC Receive Routine
......@@ -91,50 +82,149 @@
// DCC 1: _________XXXXXXXXX_________XXXXXXXXX_________
// |<--------146us------>|
// ^-INTx ^-INTx
// less than 138us: its a one-Bit
// less than 146us: its a one-Bit
//
//
// |<-----------------232us----------->|
// DCC 0: _________XXXXXXXXXXXXXXXXXX__________________XXXXXXXX__________
// |<--------146us------->|
// ^-INTx ^-INTx
// greater than 138us: its a zero bit
// greater than 146us: its a zero bit
//
//
//
//
//------------------------------------------------------------------------
#define abs(a) ((a) > 0 ? (a) : (0-a))
//------------------------------------------------------------------------
// 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_PRAEAMBEL 146
#define MAX_ONEBITHALF 82
#define MIN_ONEBITFULL 82
#define MIN_ONEBITHALF 35
#define MAX_BITDIFF 18
#ifdef NODE_DEBUG
#define PULLUP PLATFORM_GPIO_PULLUP
#define OUTPUT PLATFORM_GPIO_OUTPUT
#define HIGH PLATFORM_GPIO_HIGH
#define LOW PLATFORM_GPIO_LOW
#define MODE_TP1 platform_gpio_mode( 5, OUTPUT, PULLUP ); // GPIO 14
#define SET_TP1 platform_gpio_write(5, HIGH);
#define CLR_TP1 platform_gpio_write(5, LOW);
#define MODE_TP2 platform_gpio_mode( 6, OUTPUT, PULLUP ); // GPIO 12
#define SET_TP2 platform_gpio_write(6, HIGH);
#define CLR_TP2 platform_gpio_write(6, LOW);
#define MODE_TP3 platform_gpio_mode( 7, OUTPUT, PULLUP ); // GPIO 13
#define SET_TP3 platform_gpio_write(7, HIGH);
#define CLR_TP3 platform_gpio_write(7, LOW);
#define MODE_TP4 platform_gpio_mode( 8, OUTPUT, PULLUP ); // GPIO 15
#define SET_TP4 platform_gpio_write(8, HIGH);
#define CLR_TP4 platform_gpio_write(8, LOW);
#define MAX_BITDIFF 24
// Debug-Ports
//#define debug // Testpulse for logic analyser
#ifdef debug
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define MODE_TP1 DDRF |= (1<<2) //pinA2
#define SET_TP1 PORTF |= (1<<2)
#define CLR_TP1 PORTF &= ~(1<<2)
#define MODE_TP2 DDRF |= (1<<3) //pinA3
#define SET_TP2 PORTF |= (1<<3)
#define CLR_TP2 PORTF &= ~(1<<3)
#define MODE_TP3 DDRF |= (1<<4) //pinA4
#define SET_TP3 PORTF |= (1<<4)
#define CLR_TP3 PORTF &= ~(1<<4)
#define MODE_TP4 DDRF |= (1<<5) //pinA5
#define SET_TP4 PORTF |= (1<<5)
#define CLR_TP4 PORTF &= ~(1<<5)
#elif defined(__AVR_ATmega32U4__)
#define MODE_TP1 DDRF |= (1<<4) //A3
#define SET_TP1 PORTF |= (1<<4)
#define CLR_TP1 PORTF &= ~(1<<4)
#define MODE_TP2 DDRF |= (1<<5) //A2
#define SET_TP2 PORTF |= (1<<5)
#define CLR_TP2 PORTF &= ~(1<<5)
#define MODE_TP3
#define SET_TP3
#define CLR_TP3
#define MODE_TP4
#define SET_TP4
#define CLR_TP4
#elif defined(__AVR_ATmega328P__)
#define MODE_TP1 DDRC |= (1<<1) //A1
#define SET_TP1 PORTC |= (1<<1)
#define CLR_TP1 PORTC &= ~(1<<1)
#define MODE_TP2 DDRC |= (1<<2) // A2
#define SET_TP2 PORTC |= (1<<2)
#define CLR_TP2 PORTC &= ~(1<<2)
#define MODE_TP3 DDRC |= (1<<3) //A3
#define SET_TP3 PORTC |= (1<<3)
#define CLR_TP3 PORTC &= ~(1<<3)
#define MODE_TP4 DDRC |= (1<<4) //A4
#define SET_TP4 PORTC |= (1<<4)
#define CLR_TP4 PORTC &= ~(1<<4)
#elif defined(__arm__) && (defined(__MK20DX128__) || defined(__MK20DX256__))
// Teensys 3.x
#define MODE_TP1 pinMode( A1,OUTPUT ) // A1= PortC, Bit0
#define SET_TP1 GPIOC_PSOR = 0x01
#define CLR_TP1 GPIOC_PCOR = 0x01
#define MODE_TP2 pinMode( A2,OUTPUT ) // A2= PortB Bit0
#define SET_TP2 GPIOB_PSOR = 0x01
#define CLR_TP2 GPIOB_PCOR = 0x01
#define MODE_TP3 pinMode( A3,OUTPUT ) // A3 = PortB Bit1
#define SET_TP3 GPIOB_PSOR = 0x02
#define CLR_TP3 GPIOB_PCOR = 0x02
#define MODE_TP4 pinMode( A4,OUTPUT ) // A4 = PortB Bit3
#define SET_TP4 GPIOB_PSOR = 0x08
#define CLR_TP4 GPIOB_PCOR = 0x08
#elif defined (__STM32F1__)
// STM32F103...
#define MODE_TP1 pinMode( PB12,OUTPUT ) // TP1= PB12
#define SET_TP1 gpio_write_bit( GPIOB,12, HIGH );
#define CLR_TP1 gpio_write_bit( GPIOB,12, LOW );
#define MODE_TP2 pinMode( PB13,OUTPUT ) // TP2= PB13
#define SET_TP2 gpio_write_bit( GPIOB,13, HIGH );
#define CLR_TP2 gpio_write_bit( GPIOB,13, LOW );
#define MODE_TP3 pinMode( PB14,OUTPUT ) // TP3 = PB14
#define SET_TP3 gpio_write_bit( GPIOB,14, HIGH );
#define CLR_TP3 gpio_write_bit( GPIOB,14, LOW );
#define MODE_TP4 pinMode( PB15,OUTPUT ) // TP4 = PB15
#define SET_TP4 gpio_write_bit( GPIOB,15, HIGH );
#define CLR_TP4 gpio_write_bit( GPIOB,15, LOW );
#elif defined(ESP8266)
#define MODE_TP1 pinMode( D5,OUTPUT ) ; // GPIO 14
#define SET_TP1 GPOS = (1 << D5);
#define CLR_TP1 GPOC = (1 << D5);
#define MODE_TP2 pinMode( D6,OUTPUT ) ; // GPIO 12
#define SET_TP2 GPOS = (1 << D6);
#define CLR_TP2 GPOC = (1 << D6);
#define MODE_TP3 pinMode( D7,OUTPUT ) ; // GPIO 13
#define SET_TP3 GPOS = (1 << D7);
#define CLR_TP3 GPOC = (1 << D7);
#define MODE_TP4 pinMode( D8,OUTPUT ) ; // GPIO 15
#define SET_TP4 GPOS = (1 << D8);
#define CLR_TP4 GPOC = (1 << D8);
#elif defined(ESP32)
#define MODE_TP1 pinMode( 33,OUTPUT ) ; // GPIO 33
#define SET_TP1 GPOS = (1 << 33);
#define CLR_TP1 GPOC = (1 << 33);
#define MODE_TP2 pinMode( 25,OUTPUT ) ; // GPIO 25
#define SET_TP2 GPOS = (1 << 25);
#define CLR_TP2 GPOC = (1 << 25);
#define MODE_TP3 pinMode( 26,OUTPUT ) ; // GPIO 26
#define SET_TP3 GPOS = (1 << 26);
#define CLR_TP3 GPOC = (1 << 26);
#define MODE_TP4 pinMode( 27,OUTPUT ) ; // GPIO 27
#define SET_TP4 GPOS = (1 << 27);
#define CLR_TP4 GPOC = (1 << 27);
//#elif defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
#else
#define MODE_TP1
#define SET_TP1
#define CLR_TP1
#define MODE_TP2
#define SET_TP2
#define CLR_TP2
#define MODE_TP3
#define SET_TP3
#define CLR_TP3
#define MODE_TP4
#define SET_TP4
#define CLR_TP4
#endif
#else
#define MODE_TP1
#define SET_TP1
......@@ -148,17 +238,40 @@
#define MODE_TP4
#define SET_TP4
#define CLR_TP4
#endif
#ifdef DEBUG_PRINT
#define DB_PRINT( x, ... ) { char dbgbuf[80]; sprintf_P( dbgbuf, (const char*) F( x ) , ##__VA_ARGS__ ) ; Serial.println( dbgbuf ); }
#define DB_PRINT_( x, ... ) { char dbgbuf[80]; sprintf_P( dbgbuf, (const char*) F( x ) , ##__VA_ARGS__ ) ; Serial.print( dbgbuf ); }
#else
#define DB_PRINT( x, ... ) ;
#define DB_PRINT_( x, ... ) ;
#endif
static uint8_t ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
static int16_t bitMax, bitMin;
#ifdef DCC_DBGVAR
struct countOf_t countOf;
#endif
DCC_MSG Msg ;
#if defined ( __STM32F1__ )
static ExtIntTriggerMode ISREdge;
#elif defined ( ESP32 )
static byte ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
static byte ISRWatch; // Interrupt Handler Edge Filter
#else
static byte ISREdge; // Holder of the Next Edge we're looking for: RISING or FALLING
static byte ISRWatch; // Interrupt Handler Edge Filter
#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;
typedef enum
{
WAIT_PREAMBLE = 0,
WAIT_START_BIT,
#ifndef SYNC_ALWAYS
WAIT_START_BIT_FULL,
#endif
WAIT_DATA,
WAIT_END_BIT
}
......@@ -176,8 +289,10 @@ OpsInstructionType;
struct DccRx_t
{
DccRxWaitState State ;
uint8_t DataReady ;
uint8_t BitCount ;
uint8_t TempByte ;
uint8_t chkSum;
DCC_MSG PacketBuf;
DCC_MSG PacketCopy;
}
......@@ -192,86 +307,163 @@ typedef struct
uint8_t PageRegister ; // Used for Paged Operations in Service Mode Programming
uint8_t DuplicateCount ;
DCC_MSG LastMsg ;
uint8_t IntPin;
uint8_t IntBitmask;
uint8_t ExtIntNum;
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
uint8_t inAccDecDCCAddrNextReceivedMode;
uint8_t cv29Value;
#ifdef DCC_DEBUG
uint8_t IntCount;
uint8_t TickCount;
uint8_t NestedIrqCount;
#endif
}
DCC_PROCESSOR_STATE ;
DCC_PROCESSOR_STATE DccProcState ;
task_handle_t DataReady_taskid;
#ifdef ESP32
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
static uint32_t ICACHE_RAM_ATTR InterruptHandler (uint32_t ret_gpio_status)
void IRAM_ATTR ExternalInterruptHandler(void)
#elif defined(ESP8266)
void ICACHE_RAM_ATTR ExternalInterruptHandler(void)
#else
void ExternalInterruptHandler(void)
#endif
{
// This function really is running at interrupt level with everything
// else masked off. It should take as little time as necessary.
SET_TP3;
uint32 gpio_status = GPIO_REG_READ(GPIO_STATUS_ADDRESS);
if ((gpio_status & DccProcState.IntBitmask) == 0) {
return ret_gpio_status;
}
#ifdef ESP32
// switch (ISRWatch)
// {
// case RISING: if (digitalRead(DccProcState.ExtIntPinNum)) break;
// case FALLING: if (digitalRead(DccProcState.ExtIntPinNum)) return; break;
// }
// First compare the edge we're looking for to the pin state
switch (ISRWatch)
{
case CHANGE:
break;
GPIO_REG_WRITE(GPIO_STATUS_W1TC_ADDRESS, gpio_status & DccProcState.IntBitmask);
uint32_t actMicros = system_get_time();
ret_gpio_status &= ~(DccProcState.IntBitmask);
case RISING:
if (digitalRead(DccProcState.ExtIntPinNum) != HIGH)
return;
break;
// Bit evaluation without Timer 0 ------------------------------
case FALLING:
if (digitalRead(DccProcState.ExtIntPinNum) != LOW)
return;
break;
}
#endif
// Bit evaluation without Timer 0 ------------------------------
uint8_t DccBitVal;
static int8_t bit1, bit2 ;
static unsigned long lastMicros = 0;
static uint8_t halfBit;
unsigned long bitMicros;
static unsigned int lastMicros = 0;
static byte halfBit, DCC_IrqRunning, preambleBitCount;
unsigned int actMicros, bitMicros;
#ifdef ALLOW_NESTED_IRQ
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
}
#endif
actMicros = micros();
bitMicros = actMicros-lastMicros;
if ( bitMicros < bitMin ) {
// too short - my be false interrupt due to glitch or false protocol -> ignore
CLR_TP3; SET_TP3;
#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;
return ret_gpio_status; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
SET_TP4; /*delayMicroseconds(1); */ CLR_TP4;
return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
}
DccBitVal = ( bitMicros < bitMax );
CLR_TP3; SET_TP3;
lastMicros = actMicros;
#ifdef NODE_DEBUG
if(DccBitVal) {SET_TP2;} else {CLR_TP2;};
#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 DCC_DEBUG
DccProcState.TickCount++;
#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 );
#ifdef ALLOW_NESTED_IRQ
DCC_IrqRunning = true;
interrupts(); // time critical is only the micros() command,so allow nested irq's
#endif
#ifdef DCC_DEBUG
DccProcState.TickCount++;
#endif
switch( DccRx.State )
{
case WAIT_PREAMBLE:
if( DccBitVal )
{
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
// We don't have to do anything special - looking for a preamble condition is done always
SET_TP2;
break;
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[DccProcState.IntPin]), GPIO_PIN_INTR_ANYEDGE);
halfBit = 0;
bitMax = MAX_ONEBITHALF;
bitMin = MIN_ONEBITHALF;
#ifndef SYNC_ALWAYS
case WAIT_START_BIT_FULL:
// wait for startbit without level checking
if ( !DccBitVal ) {
// we got the startbit
CLR_TP2;CLR_TP1;
DccRx.State = WAIT_DATA ;
CLR_TP1;
}
} else {
SET_TP1;
// initialize packet buffer
DccRx.PacketBuf.Size = 0;
/*for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ )
DccRx.PacketBuf.Data[i] = 0;*/
DccRx.PacketBuf.PreambleBits = preambleBitCount;
DccRx.BitCount = 0 ;
CLR_TP1;
DccRx.chkSum = 0 ;
DccRx.TempByte = 0 ;
//SET_TP1;
}
break;
#endif
case WAIT_START_BIT:
// we are looking for first half "0" bit after preamble
switch ( halfBit ) {
case 0: //SET_TP1;
case 0:
// check first part
if ( DccBitVal ) {
// is still 1-bit (Preamble)
......@@ -279,27 +471,33 @@ static uint32_t ICACHE_RAM_ATTR InterruptHandler (uint32_t ret_gpio_status)
bit1=bitMicros;
} else {
// was "0" half bit, maybe the startbit
SET_TP1;
halfBit = 4;
CLR_TP1;
}
break;
case 1: //SET_TP1; // previous halfbit was '1'
case 1: // previous halfbit was '1'
if ( DccBitVal ) {
// its a '1' halfBit -> we are still in the preamble
halfBit = 0;
bit2=bitMicros;
DccRx.BitCount++;
preambleBitCount++;
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;
SET_TP4;
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[DccProcState.IntPin]), ISREdge);
preambleBitCount = 0;
// SET_TP2; CLR_TP2;
#if defined ( __STM32F1__ )
detachInterrupt( DccProcState.ExtIntNum );
#endif
#ifdef ESP32
ISRWatch = ISREdge;
#else
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
// enable level checking ( with direct port reading @ AVR )
ISRChkMask = DccProcState.ExtIntMask;
ISRLevel = (ISREdge==RISING)? DccProcState.ExtIntMask : 0 ;
#endif
SET_TP3;
CLR_TP4;
}
......@@ -311,67 +509,97 @@ static uint32_t ICACHE_RAM_ATTR InterruptHandler (uint32_t ret_gpio_status)
SET_TP3;
}
break;
case 3: //SET_TP1; // previous halfbit was '0' in second halfbit
case 3: // previous halfbit was '0' in second halfbit
if ( DccBitVal ) {
// 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;
preambleBitCount = 0;
// SET_TP2; CLR_TP2;
} else {
// we got two '0' halfbits -> it's the startbit
// but sync is NOT ok, change IRQ edge.
if ( ISREdge == GPIO_PIN_INTR_POSEDGE ) ISREdge = GPIO_PIN_INTR_NEGEDGE; else ISREdge = GPIO_PIN_INTR_POSEDGE;
CLR_TP2;CLR_TP1;
if ( ISREdge == RISING ) ISREdge = FALLING; else ISREdge = RISING;
DccRx.State = WAIT_DATA ;
CLR_TP1;
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++ )
DccRx.PacketBuf.Data[i] = 0;
DccRx.PacketBuf.PreambleBits = DccRx.BitCount;
/*for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ )
DccRx.PacketBuf.Data[i] = 0;*/
DccRx.PacketBuf.PreambleBits = preambleBitCount;
DccRx.BitCount = 0 ;
DccRx.chkSum = 0 ;
DccRx.TempByte = 0 ;
//SET_TP1;
}
SET_TP4;
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[DccProcState.IntPin]), ISREdge);
CLR_TP1;
CLR_TP4;
//SET_TP4;
#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_TP4;
break;
case 4: SET_TP1; // previous (first) halfbit was 0
case 4: // previous (first) halfbit was 0
// if this halfbit is 0 too, we got the startbit
if ( DccBitVal ) {
// second halfbit is 1 -> unknown protokoll
DccRx.State = WAIT_PREAMBLE;
bitMax = MAX_PRAEAMBEL;
bitMin = MIN_ONEBITFULL;
preambleBitCount = 0;
CLR_TP2;CLR_TP1;
DccRx.BitCount = 0;
} else {
// we got the startbit
CLR_TP2;CLR_TP1;
DccRx.State = WAIT_DATA ;
CLR_TP1;
bitMax = MAX_ONEBITFULL;
bitMin = MIN_ONEBITFULL;
// initialize packet buffer
DccRx.PacketBuf.Size = 0;
DccRx.PacketBuf.PreambleBits = 0;
for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ )
DccRx.PacketBuf.Data[i] = 0;
DccRx.PacketBuf.PreambleBits = DccRx.BitCount;
/*for(uint8_t i = 0; i< MAX_DCC_MESSAGE_LEN; i++ )
DccRx.PacketBuf.Data[i] = 0;*/
DccRx.PacketBuf.PreambleBits = preambleBitCount;
DccRx.BitCount = 0 ;
DccRx.chkSum = 0 ;
DccRx.TempByte = 0 ;
//SET_TP1;
}
CLR_TP1;
SET_TP4;
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[DccProcState.IntPin]), ISREdge);
CLR_TP4;
//SET_TP4;
#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_TP4;
break;
}
break;
case WAIT_DATA:
CLR_TP2;
DccRx.BitCount++;
DccRx.TempByte = ( DccRx.TempByte << 1 ) ;
if( DccBitVal )
......@@ -390,24 +618,44 @@ static uint32_t ICACHE_RAM_ATTR InterruptHandler (uint32_t ret_gpio_status)
{
DccRx.State = WAIT_END_BIT ;
DccRx.PacketBuf.Data[ DccRx.PacketBuf.Size++ ] = DccRx.TempByte ;
DccRx.chkSum ^= DccRx.TempByte;
}
}
break;
case WAIT_END_BIT:
SET_TP2;CLR_TP2;
DccRx.BitCount++;
if( DccBitVal ) // End of packet?
{
CLR_TP3;
if( DccBitVal ) { // End of packet?
CLR_TP3; SET_TP4;
DccRx.State = WAIT_PREAMBLE ;
DccRx.BitCount = 0 ;
bitMax = MAX_PRAEAMBEL;
bitMin = MIN_ONEBITFULL;
SET_TP1;
if ( DccRx.chkSum == 0 ) {
// Packet is valid
#ifdef ESP32
portENTER_CRITICAL_ISR(&mux);
#endif
DccRx.PacketCopy = DccRx.PacketBuf ;
uint8_t param;
task_post_high(DataReady_taskid, (os_param_t) &param);
SET_TP3;
DccRx.DataReady = 1 ;
#ifdef ESP32
portEXIT_CRITICAL_ISR(&mux);
#endif
// SET_TP2; CLR_TP2;
preambleBitCount = 0 ;
} else {
// Wrong checksum
CLR_TP1;
#ifdef DCC_DBGVAR
DB_PRINT("Cerr");
countOf.Err++;
#endif
}
else // Get next Byte
SET_TP3; CLR_TP4;
} else { // Get next Byte
// KGW - Abort immediately if packet is too long.
if( DccRx.PacketBuf.Size == MAX_DCC_MESSAGE_LEN ) // Packet is too long - abort
{
......@@ -424,10 +672,106 @@ static uint32_t ICACHE_RAM_ATTR InterruptHandler (uint32_t ret_gpio_status)
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
DCC_IrqRunning = false;
#endif
//CLR_TP1;
CLR_TP3;
return ret_gpio_status;
}
void ackCV(void)
{
if( notifyCVAck )
{
DB_PRINT("ackCV: Send Basic ACK");
notifyCVAck() ;
}
}
void ackAdvancedCV(void)
{
if( notifyAdvancedCVAck && (DccProcState.cv29Value & CV29_RAILCOM_ENABLE) )
{
DB_PRINT("ackAdvancedCV: Send RailCom ACK");
notifyAdvancedCVAck() ;
}
}
uint8_t readEEPROM( unsigned int CV )
{
return EEPROM.read(CV) ;
}
void writeEEPROM( unsigned int CV, uint8_t Value )
{
EEPROM.write(CV, Value) ;
#if defined(ESP8266)
EEPROM.commit();
#endif
#if defined(ESP32)
EEPROM.commit();
#endif
}
bool readyEEPROM()
{
#if defined ARDUINO_ARCH_MEGAAVR
return bit_is_clear(NVMCTRL.STATUS,NVMCTRL_EEBUSY_bp);
#elif defined __AVR_MEGA__
return eeprom_is_ready();
#else
return true;
#endif
}
uint8_t validCV( uint16_t CV, uint8_t Writable )
......@@ -437,14 +781,27 @@ uint8_t validCV( uint16_t CV, uint8_t Writable )
if( notifyCVValid )
return notifyCVValid( CV, Writable ) ;
return 0;
uint8_t Valid = 1 ;
if( CV > MAXCV )
Valid = 0 ;
if( Writable && ( ( CV ==CV_VERSION_ID ) || (CV == CV_MANUFACTURER_ID ) ) )
Valid = 0 ;
return Valid ;
}
uint8_t readCV( unsigned int CV )
{
uint8_t Value ;
if( notifyCVRead )
return notifyCVRead( CV ) ;
return 0;
Value = readEEPROM(CV);
return Value ;
}
uint8_t writeCV( unsigned int CV, uint8_t Value)
......@@ -453,6 +810,9 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
{
case CV_29_CONFIG:
// 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.Flags = ( DccProcState.Flags & ~FLAGS_CV29_BITS) | (Value & FLAGS_CV29_BITS);
// no break, because myDccAdress must also be reset
case CV_ACCESSORY_DECODER_ADDRESS_LSB: // Also same CV for CV_MULTIFUNCTION_PRIMARY_ADDRESS
......@@ -464,28 +824,36 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
if( notifyCVWrite )
return notifyCVWrite( CV, Value ) ;
return 0;
if( readEEPROM( CV ) != Value )
{
writeEEPROM( CV, Value ) ;
if( notifyCVChange )
notifyCVChange( CV, Value) ;
if( notifyDccCVChange && !(DccProcState.Flags & FLAGS_SETCV_CALLED) )
notifyDccCVChange( CV, Value );
}
return readEEPROM( CV ) ;
}
uint16_t getMyAddr(void)
{
uint8_t CV29Value ;
if( DccProcState.myDccAddress != -1 ) // See if we can return the cached value
return( DccProcState.myDccAddress );
CV29Value = readCV( CV_29_CONFIG ) ;
if( CV29Value & CV29_ACCESSORY_DECODER ) // Accessory Decoder?
if( DccProcState.cv29Value & CV29_ACCESSORY_DECODER ) // Accessory Decoder?
{
if( CV29Value & CV29_OUTPUT_ADDRESS_MODE )
if( DccProcState.cv29Value & CV29_OUTPUT_ADDRESS_MODE )
DccProcState.myDccAddress = ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) << 8 ) | readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB );
else
DccProcState.myDccAddress = ( ( readCV( CV_ACCESSORY_DECODER_ADDRESS_MSB ) & 0b00000111) << 6 ) | ( readCV( CV_ACCESSORY_DECODER_ADDRESS_LSB ) & 0b00111111) ;
}
else // Multi-Function Decoder?
{
if( CV29Value & CV29_EXT_ADDRESSING ) // Two Byte Address?
if( DccProcState.cv29Value & CV29_EXT_ADDRESSING ) // Two Byte Address?
DccProcState.myDccAddress = ( ( readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB ) - 192 ) << 8 ) | readCV( CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB ) ;
else
......@@ -495,7 +863,7 @@ uint16_t getMyAddr(void)
return DccProcState.myDccAddress ;
}
void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
void processDirectCVOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value, void (*ackFunction)() )
{
// is it a Byte Operation
if( Cmd & 0x04 )
......@@ -505,7 +873,19 @@ void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
{
if( validCV( CVAddr, 1 ) )
{
writeCV( CVAddr, Value );
DB_PRINT("CV: %d Byte Write: %02X", CVAddr, Value)
if( writeCV( CVAddr, Value ) == Value )
ackFunction();
}
}
else // Perform the Verify Operation
{
if( validCV( CVAddr, 0 ) )
{
DB_PRINT("CV: %d Byte Read: %02X", CVAddr, Value)
if( readCV( CVAddr ) == Value )
ackFunction();
}
}
}
......@@ -518,6 +898,8 @@ void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t 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
if( BitWrite )
{
......@@ -529,12 +911,32 @@ void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
else
tempValue &= ~BitMask ; // Turn the Bit Off
writeCV( CVAddr, tempValue );
if( writeCV( CVAddr, tempValue ) == tempValue )
ackFunction() ;
}
}
// Perform the Bit Verify Operation
else
{
if( validCV( CVAddr, 0 ) )
{
if( BitValue )
{
if( tempValue & BitMask )
ackFunction() ;
}
else
{
if( !( tempValue & BitMask) )
ackFunction() ;
}
}
}
}
}
/////////////////////////////////////////////////////////////////////////
#ifdef NMRA_DCC_PROCESS_MULTIFUNCTION
void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Cmd, uint8_t Data1, uint8_t Data2 )
{
......@@ -545,12 +947,9 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
uint8_t CmdMasked = Cmd & 0b11100000 ;
// NODE_DBG("[dcc_processMultiFunctionMessage] Addr: %d, Type: %d, Cmd: %d ("BYTE_TO_BINARY_PATTERN"), Data: %d, %d, CmdMasked="BYTE_TO_BINARY_PATTERN"\n", Addr, AddrType, Cmd, BYTE_TO_BINARY(Cmd), Data1, Data2, BYTE_TO_BINARY(CmdMasked));
// If we are an Accessory Decoder
if( DccProcState.Flags & FLAGS_DCC_ACCESSORY_DECODER )
{
// NODE_DBG("[dcc_processMultiFunctionMessage] DccProcState.Flags & FLAGS_DCC_ACCESSORY_DECODER\n");
// and this isn't an Ops Mode Write or we are NOT faking the Multifunction Ops mode address in CV 33+34 or
// it's not our fake address, then return
if( ( CmdMasked != 0b11100000 ) || ( DccProcState.OpsModeAddressBaseCV == 0 ) )
......@@ -567,16 +966,14 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
else if( ( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY ) && ( Addr != getMyAddr() ) && ( Addr != 0 ) )
return ;
NODE_DBG("[dcc_processMultiFunctionMessage] CmdMasked: %x\n", CmdMasked);
switch( CmdMasked )
{
case 0b00000000: // Decoder Control
switch( Cmd & 0b00001110 )
{
case 0b00000000:
if( notifyDccReset && ( Cmd & 0b00000001 ) ) // Hard Reset
if( notifyDccReset)
notifyDccReset( 1 ) ;
notifyDccReset( Cmd & 0b00000001 ) ;
break ;
case 0b00000010: // Factory Test
......@@ -625,7 +1022,7 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
case 0b01100000:
//TODO should we cache this info in DCC_PROCESSOR_STATE.Flags ?
#ifdef NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
speedSteps = (readCV( CV_29_CONFIG ) & CV29_F0_LOCATION) ? SPEED_STEP_28 : SPEED_STEP_14 ;
speedSteps = (DccProcState.cv29Value & CV29_F0_LOCATION) ? SPEED_STEP_28 : SPEED_STEP_14 ;
#else
speedSteps = SPEED_STEP_28 ;
#endif
......@@ -709,7 +1106,7 @@ void processMultiFunctionMessage( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t
case 0b11100000: // CV Access
CVAddr = ( ( ( Cmd & 0x03 ) << 8 ) | Data1 ) + 1 ;
processDirectOpsOperation( Cmd, CVAddr, Data2 ) ;
processDirectCVOperation( Cmd, CVAddr, Data2, ackAdvancedCV) ;
break;
}
}
......@@ -724,13 +1121,14 @@ void processServiceModeOperation( DCC_MSG * pDccMsg )
if( pDccMsg->Size == 3) // 3 Byte Packets are for Address Only, Register and Paged Mode
{
uint8_t RegisterAddr ;
NODE_DBG("[dcc_processServiceModeOperation] 3-BytePkt\n");
DB_PRINT("CV Address, Register & Paged Mode Operation");
RegisterAddr = pDccMsg->Data[0] & 0x07 ;
Value = pDccMsg->Data[1] ;
if( RegisterAddr == 5 )
{
DccProcState.PageRegister = Value ;
ackCV();
}
else
......@@ -748,7 +1146,17 @@ void processServiceModeOperation( DCC_MSG * pDccMsg )
{
if( validCV( CVAddr, 1 ) )
{
writeCV( CVAddr, Value );
if( writeCV( CVAddr, Value ) == Value )
ackCV();
}
}
else // Perform the Verify Operation
{
if( validCV( CVAddr, 0 ) )
{
if( readCV( CVAddr ) == Value )
ackCV();
}
}
}
......@@ -756,15 +1164,16 @@ void processServiceModeOperation( DCC_MSG * pDccMsg )
else if( pDccMsg->Size == 4) // 4 Byte Packets are for Direct Byte & Bit Mode
{
NODE_DBG("[dcc_processServiceModeOperation] BB-Mode\n");
DB_PRINT("CV Direct Byte and Bit Mode Mode Operation");
CVAddr = ( ( ( pDccMsg->Data[0] & 0x03 ) << 8 ) | pDccMsg->Data[1] ) + 1 ;
Value = pDccMsg->Data[2] ;
processDirectOpsOperation( pDccMsg->Data[0] & 0b00001100, CVAddr, Value ) ;
processDirectCVOperation( pDccMsg->Data[0] & 0b00001100, CVAddr, Value, ackCV) ;
}
}
#endif
/////////////////////////////////////////////////////////////////////////
void resetServiceModeTimer(uint8_t inServiceMode)
{
if (notifyServiceMode && inServiceMode != DccProcState.inServiceMode)
......@@ -774,13 +1183,14 @@ void resetServiceModeTimer(uint8_t inServiceMode)
// Set the Service Mode
DccProcState.inServiceMode = inServiceMode ;
DccProcState.LastServiceModeMillis = inServiceMode ? system_get_time() : 0 ;
DccProcState.LastServiceModeMillis = inServiceMode ? millis() : 0 ;
if (notifyServiceMode && inServiceMode != DccProcState.inServiceMode)
{
notifyServiceMode(inServiceMode);
}
}
/////////////////////////////////////////////////////////////////////////
void clearDccProcState(uint8_t inServiceMode)
{
resetServiceModeTimer( inServiceMode ) ;
......@@ -793,10 +1203,27 @@ void clearDccProcState(uint8_t inServiceMode)
memset( &DccProcState.LastMsg, 0, sizeof( DCC_MSG ) ) ;
}
void execDccProcessor( DCC_MSG * pDccMsg )
/////////////////////////////////////////////////////////////////////////
#ifdef DEBUG_PRINT
void SerialPrintPacketHex(const __FlashStringHelper *strLabel, DCC_MSG * pDccMsg)
{
NODE_DBG("[dcc_execDccProcessor]\n");
Serial.print( strLabel );
for( uint8_t i = 0; i < pDccMsg->Size; i++ )
{
if( pDccMsg->Data[i] <= 9)
Serial.print('0');
Serial.print( pDccMsg->Data[i], HEX );
Serial.write( ' ' );
}
Serial.println();
}
#endif
///////////////////////////////////////////////////////////////////////////////
void execDccProcessor( DCC_MSG * pDccMsg )
{
if( ( pDccMsg->Data[0] == 0 ) && ( pDccMsg->Data[1] == 0 ) )
{
if( notifyDccReset )
......@@ -818,12 +1245,13 @@ void execDccProcessor( DCC_MSG * pDccMsg )
{
resetServiceModeTimer( 1 ) ;
if( memcmp( pDccMsg, &DccProcState.LastMsg, sizeof( DCC_MSG ) ) )
//Only check the DCC Packet "Size" and "Data" fields and ignore the "PreambleBits" as they can be different to the previous packet
if(pDccMsg->Size != DccProcState.LastMsg.Size || memcmp( pDccMsg->Data, &DccProcState.LastMsg.Data, pDccMsg->Size ) != 0 )
{
DccProcState.DuplicateCount = 0 ;
memcpy( &DccProcState.LastMsg, pDccMsg, sizeof( DCC_MSG ) ) ;
}
// Wait until you see 2 identicle packets before acting on a Service Mode Packet
// Wait until you see 2 identical packets before acting on a Service Mode Packet
else
{
DccProcState.DuplicateCount++ ;
......@@ -848,6 +1276,7 @@ void execDccProcessor( DCC_MSG * pDccMsg )
// Multi Function Decoders (7-bit address)
else if( pDccMsg->Data[0] < 128 )
processMultiFunctionMessage( pDccMsg->Data[0], DCC_ADDR_SHORT, pDccMsg->Data[1], pDccMsg->Data[2], pDccMsg->Data[3] ) ;
// Basic Accessory Decoders (9-bit) & Extended Accessory Decoders (11-bit)
else if( pDccMsg->Data[0] < 192 )
#else
......@@ -860,29 +1289,29 @@ void execDccProcessor( DCC_MSG * pDccMsg )
int16_t OutputAddress ;
uint8_t TurnoutPairIndex ;
#ifdef NODE_DEBUG
// SerialPrintPacketHex(F( "eDP: AccCmd: "), pDccMsg);
#ifdef DEBUG_PRINT
SerialPrintPacketHex(F( "eDP: AccCmd: "), pDccMsg);
#endif
BoardAddress = ( ( (~pDccMsg->Data[1]) & 0b01110000 ) << 2 ) | ( pDccMsg->Data[0] & 0b00111111 ) ;
TurnoutPairIndex = (pDccMsg->Data[1] & 0b00000110) >> 1;
NODE_DBG("[dcc_execDccProcessor] eDP: BAddr:%d, Index:%d\n", BoardAddress, TurnoutPairIndex);
DB_PRINT("eDP: BAddr:%d, Index:%d", BoardAddress, TurnoutPairIndex);
// First check for Legacy Accessory Decoder Configuration Variable Access Instruction
// as it's got a different format to the others
if((pDccMsg->Size == 5) && ((pDccMsg->Data[1] & 0b10001100) == 0b00001100))
{
NODE_DBG( "eDP: Legacy Accessory Decoder CV Access Command");
DB_PRINT( "eDP: Legacy Accessory Decoder CV Access Command");
// Check if this command is for our address or the broadcast address
if((BoardAddress != getMyAddr()) && ( BoardAddress < 511 ))
{
NODE_DBG("[dcc_execDccProcessor] eDP: Board Address Not Matched\n");
DB_PRINT("eDP: Board Address Not Matched");
return;
}
uint16_t cvAddress = ((pDccMsg->Data[1] & 0b00000011) << 8) + pDccMsg->Data[2] + 1;
uint8_t cvValue = pDccMsg->Data[3];
NODE_DBG("[dcc_execDccProcessor] eDP: CV:%d Value:%d\n", cvAddress, cvValue );
DB_PRINT("eDP: CV:%d Value:%d", cvAddress, cvValue );
if(validCV( cvAddress, 1 ))
writeCV(cvAddress, cvValue);
return;
......@@ -891,13 +1320,13 @@ void execDccProcessor( DCC_MSG * pDccMsg )
OutputAddress = (((BoardAddress - 1) << 2 ) | TurnoutPairIndex) + 1 ; //decoder output addresses start with 1, packet address range starts with 0
// ( according to NMRA 9.2.2 )
NODE_DBG("[dcc_execDccProcessor] eDP: OAddr:%d\n", OutputAddress);
DB_PRINT("eDP: OAddr:%d", OutputAddress);
if( DccProcState.inAccDecDCCAddrNextReceivedMode)
{
if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE )
{
NODE_DBG("[dcc_execDccProcessor] eDP: Set OAddr:%d\n", OutputAddress);
DB_PRINT("eDP: Set OAddr:%d", OutputAddress);
//uint16_t storedOutputAddress = OutputAddress + 1; // The value stored in CV1 & 9 for Output Addressing Mode is + 1
writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(OutputAddress % 256));
writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(OutputAddress / 256));
......@@ -907,7 +1336,7 @@ void execDccProcessor( DCC_MSG * pDccMsg )
}
else
{
NODE_DBG("[dcc_execDccProcessor] eDP: Set BAddr:%d\n", BoardAddress);
DB_PRINT("eDP: Set BAddr:%d", BoardAddress);
writeCV(CV_ACCESSORY_DECODER_ADDRESS_LSB, (uint8_t)(BoardAddress % 64));
writeCV(CV_ACCESSORY_DECODER_ADDRESS_MSB, (uint8_t)(BoardAddress / 64));
......@@ -922,18 +1351,18 @@ void execDccProcessor( DCC_MSG * pDccMsg )
if( DccProcState.Flags & FLAGS_MY_ADDRESS_ONLY )
{
if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE ) {
NODE_DBG("[dcc_execDccProcessor] AddrChk: OAddr:%d, BAddr:%d, myAddr:%d Chk=%d\n", OutputAddress, BoardAddress, getMyAddr(), OutputAddress != getMyAddr() );
DB_PRINT(" AddrChk: OAddr:%d, BAddr:%d, myAddr:%d Chk=%d", OutputAddress, BoardAddress, getMyAddr(), OutputAddress != getMyAddr() );
if ( OutputAddress != getMyAddr() && OutputAddress < 2045 ) {
NODE_DBG("[dcc_execDccProcessor] eDP: OAddr:%d, myAddr:%d - no match\n", OutputAddress, getMyAddr() );
DB_PRINT(" eDP: OAddr:%d, myAddr:%d - no match", OutputAddress, getMyAddr() );
return;
}
} else {
if( ( BoardAddress != getMyAddr() ) && ( BoardAddress < 511 ) ) {
NODE_DBG("[dcc_execDccProcessor] eDP: BAddr:%d, myAddr:%d - no match\n", BoardAddress, getMyAddr() );
DB_PRINT(" eDP: BAddr:%d, myAddr:%d - no match", BoardAddress, getMyAddr() );
return;
}
}
NODE_DBG("[dcc_execDccProcessor] eDP: Address Matched\n");
DB_PRINT("eDP: Address Matched");
}
......@@ -942,9 +1371,13 @@ void execDccProcessor( DCC_MSG * pDccMsg )
// According to the NMRA Dcc Spec the Signal State should only use the lower 5 Bits,
// however some manufacturers seem to allow/use all 8 bits, so we'll relax that constraint for now
uint8_t state = pDccMsg->Data[2] ;
NODE_DBG("[dcc_execDccProcessor] eDP: OAddr:%d Extended State:%0X\n", OutputAddress, state);
DB_PRINT("eDP: OAddr:%d Extended State:%0X", OutputAddress, state);
if( notifyDccSigOutputState )
notifyDccSigOutputState(OutputAddress, state);
// old callback ( for compatibility with 1.4.2, not to be used in new designs )
if( notifyDccSigState )
notifyDccSigState( OutputAddress, TurnoutPairIndex, pDccMsg->Data[2] ) ;
}
else if(pDccMsg->Size == 3) // Basic Accessory Decoder Packet Format
......@@ -952,45 +1385,49 @@ void execDccProcessor( DCC_MSG * pDccMsg )
uint8_t direction = pDccMsg->Data[1] & 0b00000001;
uint8_t outputPower = (pDccMsg->Data[1] & 0b00001000) >> 3;
// old callback ( for compatibility with 1.4.2, not to be used in new designs )
if ( notifyDccAccState )
notifyDccAccState( OutputAddress, BoardAddress, pDccMsg->Data[1] & 0b00000111, outputPower );
if( DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE )
{
NODE_DBG("[dcc_execDccProcessor] eDP: OAddr:%d Turnout Dir:%d Output Power:%d\n", OutputAddress, direction, outputPower);
DB_PRINT("eDP: OAddr:%d Turnout Dir:%d Output Power:%d", OutputAddress, direction, outputPower);
if( notifyDccAccTurnoutOutput )
notifyDccAccTurnoutOutput( OutputAddress, direction, outputPower );
}
else
{
NODE_DBG("[dcc_execDccProcessor] eDP: Turnout Pair Index:%d Dir:%d Output Power: %d\n", TurnoutPairIndex, direction, outputPower);
DB_PRINT("eDP: Turnout Pair Index:%d Dir:%d Output Power: ", TurnoutPairIndex, direction, outputPower);
if( notifyDccAccTurnoutBoard )
notifyDccAccTurnoutBoard( BoardAddress, TurnoutPairIndex, direction, outputPower );
}
}
else if(pDccMsg->Size == 6) // Accessory Decoder OPS Mode Programming
{
NODE_DBG("[dcc_execDccProcessor] eDP: OPS Mode CV Programming Command\n");
DB_PRINT("eDP: OPS Mode CV Programming Command");
// Check for unsupported OPS Mode Addressing mode
if(((pDccMsg->Data[1] & 0b10001001) != 1) && ((pDccMsg->Data[1] & 0b10001111) != 0x80))
{
NODE_DBG("[dcc_execDccProcessor] eDP: Unsupported OPS Mode CV Addressing Mode\n");
DB_PRINT("eDP: Unsupported OPS Mode CV Addressing Mode");
return;
}
// Check if this command is for our address or the broadcast address
if(DccProcState.Flags & FLAGS_OUTPUT_ADDRESS_MODE)
{
NODE_DBG("[dcc_execDccProcessor] eDP: Check Output Address:%d\n", OutputAddress);
DB_PRINT("eDP: Check Output Address:%d", OutputAddress);
if((OutputAddress != getMyAddr()) && ( OutputAddress < 2045 ))
{
NODE_DBG("[dcc_execDccProcessor] eDP: Output Address Not Matched\n");
DB_PRINT("eDP: Output Address Not Matched");
return;
}
}
else
{
NODE_DBG("[dcc_execDccProcessor] eDP: Check Board Address:%d\n", BoardAddress);
DB_PRINT("eDP: Check Board Address:%d", BoardAddress);
if((BoardAddress != getMyAddr()) && ( BoardAddress < 511 ))
{
NODE_DBG("[dcc_execDccProcessor] eDP: Board Address Not Matched\n");
DB_PRINT("eDP: Board Address Not Matched");
return;
}
}
......@@ -1000,16 +1437,16 @@ void execDccProcessor( DCC_MSG * pDccMsg )
OpsInstructionType insType = (OpsInstructionType)((pDccMsg->Data[2] & 0b00001100) >> 2) ;
NODE_DBG("[dcc_execDccProcessor] eDP: OPS Mode Instruction:%d\n", insType);
DB_PRINT("eDP: OPS Mode Instruction:%d", insType);
switch(insType)
{
case OPS_INS_RESERVED:
case OPS_INS_VERIFY_BYTE:
NODE_DBG("[dcc_execDccProcessor] eDP: Unsupported OPS Mode Instruction:%d\n", insType);
DB_PRINT("eDP: Unsupported OPS Mode Instruction:%d", insType);
break; // We only support Write Byte or Bit Manipulation
case OPS_INS_WRITE_BYTE:
NODE_DBG("[dcc_execDccProcessor] eDP: CV:%d Value:%d\n", cvAddress, cvValue);
DB_PRINT("eDP: CV:%d Value:%d", cvAddress, cvValue);
if(validCV( cvAddress, 1 ))
writeCV(cvAddress, cvValue);
break;
......@@ -1053,52 +1490,64 @@ void execDccProcessor( DCC_MSG * pDccMsg )
}
}
static void process (os_param_t param, uint8_t prio)
////////////////////////////////////////////////////////////////////////
NmraDcc::NmraDcc()
{
// !!!!!! - this will not happen as we call process task only when data is ready
// if( DccProcState.inServiceMode )
// {
// if( (system_get_time() - DccProcState.LastServiceModeMillis ) > 20L )
// {
// clearDccProcState( 0 ) ;
// }
// }
// !!!!!!
// We need to do this check with interrupts disabled
//SET_TP4;
Msg = DccRx.PacketCopy ;
#ifdef DCC_DBGVAR
countOf.Tel++;
#endif
}
uint8_t xorValue = 0 ;
#ifdef digitalPinToInterrupt
void NmraDcc::pin( uint8_t ExtIntPinNum, uint8_t EnablePullup)
{
pin(digitalPinToInterrupt(ExtIntPinNum), ExtIntPinNum, EnablePullup);
}
#endif
for(uint8_t i = 0; i < DccRx.PacketCopy.Size; i++)
xorValue ^= DccRx.PacketCopy.Data[i];
if(xorValue) {
#ifdef DCC_DBGVAR
NODE_DBG("[dcc_process] Cerr\n");
NODE_DBG("[dcc_process] Data dump:");
for(uint8_t i = 0; i < DccRx.PacketCopy.Size; i++)
NODE_DBG(" %x", DccRx.PacketCopy.Data[i]);
NODE_DBG("\n");
countOf.Err++;
#endif
return;// 0 ;
} else {
NODE_DBG("[dcc_process] Size: %d\tPreambleBits: %d\t%d, %d, %d, %d, %d, %d\n",
Msg.Size, Msg.PreambleBits, Msg.Data[0], Msg.Data[1], Msg.Data[2], Msg.Data[3], Msg.Data[4], Msg.Data[5]);
execDccProcessor( &Msg );
}
void NmraDcc::pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup)
{
#if defined ( __STM32F1__ )
// with STM32F1 the interuptnumber is equal the pin number
DccProcState.ExtIntNum = ExtIntPinNum;
// because STM32F1 has a NVIC we must set interuptpriorities
const nvic_irq_num irqNum2nvic[] = { NVIC_EXTI0, NVIC_EXTI1, NVIC_EXTI2, NVIC_EXTI3, NVIC_EXTI4,
NVIC_EXTI_9_5, NVIC_EXTI_9_5, NVIC_EXTI_9_5, NVIC_EXTI_9_5, NVIC_EXTI_9_5,
NVIC_EXTI_15_10, NVIC_EXTI_15_10, NVIC_EXTI_15_10, NVIC_EXTI_15_10, NVIC_EXTI_15_10, NVIC_EXTI_15_10 };
exti_num irqNum = (exti_num)(PIN_MAP[ExtIntPinNum].gpio_bit);
// DCC-Input IRQ must be able to interrupt other long low priority ( level15 ) IRQ's
nvic_irq_set_priority ( irqNum2nvic[irqNum], PRIO_DCC_IRQ);
// Systic must be able to interrupt DCC-IRQ to always get correct micros() values
nvic_irq_set_priority(NVIC_SYSTICK, PRIO_SYSTIC);
#else
DccProcState.ExtIntNum = ExtIntNum;
#endif
DccProcState.ExtIntPinNum = ExtIntPinNum;
#ifdef __AVR_MEGA__
// because digitalRead at AVR is slow, we will read the dcc input in the ISR
// by direct port access.
DccProcState.ExtIntPort = portInputRegister( digitalPinToPort(ExtIntPinNum) );
DccProcState.ExtIntMask = digitalPinToBitMask( ExtIntPinNum );
#else
DccProcState.ExtIntMask = 1;
#endif
pinMode( ExtIntPinNum, EnablePullup ? INPUT_PULLUP : INPUT );
}
return;// 1 ;
////////////////////////////////////////////////////////////////////////
void NmraDcc::initAccessoryDecoder( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV )
{
init(ManufacturerId, VersionId, Flags | FLAGS_DCC_ACCESSORY_DECODER, OpsModeAddressBaseCV);
}
void dcc_setup(uint8_t pin, uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV)
////////////////////////////////////////////////////////////////////////
void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV )
{
NODE_DBG("[dcc_setup]\n");
#if defined(ESP8266)
EEPROM.begin(MAXCV);
#endif
#if defined(ESP32)
EEPROM.begin(MAXCV);
#endif
// Clear all the static member variables
memset( &DccRx, 0, sizeof( DccRx) );
......@@ -1106,34 +1555,30 @@ void dcc_setup(uint8_t pin, uint8_t ManufacturerId, uint8_t VersionId, uint8_t F
MODE_TP2;
MODE_TP3;
MODE_TP4;
CLR_TP1;
CLR_TP2;
CLR_TP3;
CLR_TP4;
bitMax = MAX_ONEBITFULL;
bitMin = MIN_ONEBITFULL;
DccProcState.Flags = Flags ;
DccProcState.OpsModeAddressBaseCV = OpsModeAddressBaseCV ;
DccProcState.myDccAddress = -1;
DccProcState.inAccDecDCCAddrNextReceivedMode = 0;
ISREdge = GPIO_PIN_INTR_POSEDGE;
DccProcState.IntPin = pin;
DccProcState.IntBitmask = 1 << pin_num[pin];
ISREdge = RISING;
// level checking to detect false IRQ's fired by glitches
ISRLevel = DccProcState.ExtIntMask;
ISRChkMask = DccProcState.ExtIntMask;
platform_gpio_mode(pin, PLATFORM_GPIO_INT, PLATFORM_GPIO_PULLUP);
NODE_DBG("[dcc_setup] platform_gpio_register_intr_hook - pin: %d, mask: %d\n", DccProcState.IntPin, DccProcState.IntBitmask);
platform_gpio_register_intr_hook(DccProcState.IntBitmask, InterruptHandler);
gpio_pin_intr_state_set(GPIO_ID_PIN(pin_num[pin]), GPIO_PIN_INTR_POSEDGE);
#ifdef ESP32
ISRWatch = ISREdge;
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE);
#else
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, RISING);
#endif
// Set the Bits that control Multifunction or Accessory behaviour
// and if the Accessory decoder optionally handles Output Addressing
// we need to peal off the top two bits
writeCV( CV_29_CONFIG, ( readCV( CV_29_CONFIG ) & ~FLAGS_CV29_BITS ) | (Flags & FLAGS_CV29_BITS) ) ; //!!!!!
DccProcState.cv29Value = writeCV( CV_29_CONFIG, ( readCV( CV_29_CONFIG ) & ~FLAGS_CV29_BITS ) | (Flags & FLAGS_CV29_BITS) ) ;
uint8_t doAutoFactoryDefault = 0;
if((Flags & FLAGS_AUTO_FACTORY_DEFAULT) && (readCV(CV_VERSION_ID) == 255) && (readCV(CV_MANUFACTURER_ID) == 255))
......@@ -1148,14 +1593,115 @@ void dcc_setup(uint8_t pin, uint8_t ManufacturerId, uint8_t VersionId, uint8_t F
notifyCVResetFactoryDefault();
}
void dcc_close()
////////////////////////////////////////////////////////////////////////
uint8_t NmraDcc::getCV( uint16_t CV )
{
return readCV(CV);
}
////////////////////////////////////////////////////////////////////////
uint8_t NmraDcc::setCV( uint16_t CV, uint8_t Value)
{
DccProcState.Flags |= FLAGS_SETCV_CALLED;
uint8_t returnValue = writeCV(CV,Value);
DccProcState.Flags &= ~FLAGS_SETCV_CALLED;
return returnValue;
}
////////////////////////////////////////////////////////////////////////
uint16_t NmraDcc::getAddr(void)
{
return getMyAddr();
}
////////////////////////////////////////////////////////////////////////
uint8_t NmraDcc::isSetCVReady(void)
{
NODE_DBG("[dcc_close]\n");
platform_gpio_mode(DccProcState.IntPin, PLATFORM_GPIO_INPUT, PLATFORM_GPIO_PULLUP);
if(notifyIsSetCVReady)
return notifyIsSetCVReady();
return readyEEPROM();
}
void dcc_init()
////////////////////////////////////////////////////////////////////////
#ifdef DCC_DEBUG
uint8_t NmraDcc::getIntCount(void)
{
return DccProcState.IntCount;
}
////////////////////////////////////////////////////////////////////////
uint8_t NmraDcc::getTickCount(void)
{
NODE_DBG("[dcc_init]\n");
DataReady_taskid = task_get_id((task_callback_t) process);
return DccProcState.TickCount;
}
////////////////////////////////////////////////////////////////////////
uint8_t NmraDcc::getNestedIrqCount(void)
{
return DccProcState.NestedIrqCount;
}
////////////////////////////////////////////////////////////////////////
uint8_t NmraDcc::getState(void)
{
return DccRx.State;
}
////////////////////////////////////////////////////////////////////////
uint8_t NmraDcc::getBitCount(void)
{
return DccRx.BitCount;
}
#endif
////////////////////////////////////////////////////////////////////////
void NmraDcc::setAccDecDCCAddrNextReceived(uint8_t enable)
{
DccProcState.inAccDecDCCAddrNextReceivedMode = enable;
}
////////////////////////////////////////////////////////////////////////
uint8_t NmraDcc::process()
{
if( DccProcState.inServiceMode )
{
if( (millis() - DccProcState.LastServiceModeMillis ) > 20L )
{
clearDccProcState( 0 ) ;
}
}
if( DccRx.DataReady )
{
// We need to do this check with interrupts disabled
#ifdef ESP32
portENTER_CRITICAL(&mux);
#else
noInterrupts();
#endif
Msg = DccRx.PacketCopy ;
DccRx.DataReady = 0 ;
#ifdef ESP32
portEXIT_CRITICAL(&mux);
#else
interrupts();
#endif
// Checking of the XOR-byte is now done in the ISR already
#ifdef DCC_DBGVAR
countOf.Tel++;
#endif
// Clear trailing bytes
for ( byte i=Msg.Size; i< MAX_DCC_MESSAGE_LEN; i++ ) Msg.Data[i] = 0;
if( notifyDccMsg ) notifyDccMsg( &Msg );
execDccProcessor( &Msg );
return 1 ;
}
return 0 ;
};
......@@ -2,11 +2,21 @@
//
// Model Railroading with Arduino - NmraDcc.h
//
// Copyright (c) 2008 - 2018 Alex Shepherd
// Copyright (c) 2008 - 2020 Alex Shepherd
//
// This source file is subject of the GNU general public license 2,
// that is available at the world-wide-web at
// http://www.gnu.org/licenses/gpl.txt
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// 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
//
//------------------------------------------------------------------------
//
......@@ -29,25 +39,30 @@
//
//------------------------------------------------------------------------
// NodeMCU Lua port by @voborsky
// #define NODE_DEBUG
// #define DCC_DEBUG
// #define DCC_DBGVAR
// Uncomment the following Line to Enable Service Mode CV Programming
#define NMRA_DCC_PROCESS_SERVICEMODE
// Uncomment the following line to Enable MultiFunction Decoder Operations
#define NMRA_DCC_PROCESS_MULTIFUNCTION
// #ifndef NMRADCC_IS_IN
// #define NMRADCC_IS_IN
// Uncomment the following line to Enable 14 Speed Step Support
//#define NMRA_DCC_ENABLE_14_SPEED_STEP_MODE
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#ifndef NMRADCC_IS_IN
#define NMRADCC_IS_IN
#define NMRADCC_VERSION 201 // Version 2.0.1
#define NMRADCC_VERSION 206 // Version 2.0.6
#define MAX_DCC_MESSAGE_LEN 6 // including XOR-Byte
//#define ALLOW_NESTED_IRQ // uncomment to enable nested IRQ's ( only for AVR! )
typedef struct
{
uint8_t Size ;
......@@ -91,11 +106,27 @@ typedef struct
#define CV_MANUFACTURER_ID 8
#define CV_29_CONFIG 29
#if defined(ESP32)
#include <esp_spi_flash.h>
#define MAXCV SPI_FLASH_SEC_SIZE
#elif defined(ESP8266)
#include <spi_flash.h>
#define MAXCV SPI_FLASH_SEC_SIZE
#elif defined( __STM32F1__)
#define MAXCV (EEPROM_PAGE_SIZE/4 - 1) // number of storage places (CV address could be larger
// because STM32 uses virtual adresses)
#undef ALLOW_NESTED_IRQ // This is done with NVIC on STM32
#define PRIO_DCC_IRQ 9
#define PRIO_SYSTIC 8 // MUST be higher priority than DCC Irq
#else
#define MAXCV E2END // the upper limit of the CV value currently defined to max memory.
#endif
typedef enum {
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_APS = 0b00000100, /** bit 2: Alternate Power Source (APS) "0" = NMRA Digital only, "1" = Alternate power source set by CV12 */
CV29_ADV_ACK = 0b00001000, /** bit 3: ACK, Advanced Acknowledge mode enabled if 1, disabled if 0 */
CV29_RAILCOM_ENABLE = 0b00001000, /** bit 3: BiDi ( RailCom ) is active */
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_OUTPUT_ADDRESS_MODE = 0b01000000, /** bit 6: "0" = Decoder Address Mode "1" = Output Address Mode */
......@@ -166,15 +197,24 @@ typedef enum
#define FN_BIT_27 0x40
#define FN_BIT_28 0x80
//#define DCC_DBGVAR
#ifdef DCC_DBGVAR
typedef struct countOf_t {
unsigned long Tel;
unsigned long Err;
}countOf_t ;
countOf_t countOf;
extern struct countOf_t countOf;
#endif
class NmraDcc
{
private:
DCC_MSG Msg ;
public:
NmraDcc();
// Flag values to be logically ORed together and passed into the init() method
#define FLAGS_MY_ADDRESS_ONLY 0x01 // Only process DCC Packets with My Address
#define FLAGS_AUTO_FACTORY_DEFAULT 0x02 // Call notifyCVResetFactoryDefault() if CV 7 & 8 == 255
......@@ -185,34 +225,192 @@ countOf_t countOf;
// Flag Bits that are cloned from CV29 relating the DCC Accessory Decoder
#define FLAGS_CV29_BITS (FLAGS_OUTPUT_ADDRESS_MODE | FLAGS_DCC_ACCESSORY_DECODER)
#define DCC_RESET 1
#define DCC_IDLE 2
#define DCC_SPEED 3
#define DCC_SPEED_RAW 4
#define DCC_FUNC 5
#define DCC_TURNOUT 6
#define DCC_ACCESSORY 7
#define DCC_RAW 8
#define DCC_SERVICEMODE 9
#define CV_VALID 10
#define CV_READ 11
#define CV_WRITE 12
#define CV_RESET 13
/*+
* pin() is called from setup() and sets up the pin used to receive DCC packets.
*
* Inputs:
* ExtIntNum - Interrupt number of the pin. Use digitalPinToInterrupt(ExtIntPinNum).
* ExtIntPinNum - Input pin number.
* EnablePullup - Set true to enable the pins pullup resistor.
*
* Returns:
* None.
*/
void pin( uint8_t ExtIntNum, uint8_t ExtIntPinNum, uint8_t EnablePullup);
/*+
* pin() is called from setup() and sets up the pin used to receive DCC packets.
* This relies on the internal function: digitalPinToInterrupt() to map the input pin number to the right interrupt
*
* Inputs:
* ExtIntPinNum - Input pin number.
* EnablePullup - Set true to enable the pins pullup resistor.
*
* Returns:
* None.
*/
#ifdef digitalPinToInterrupt
void pin( uint8_t ExtIntPinNum, uint8_t EnablePullup);
#endif
/*+
* init() is called from setup() after the pin() command is called.
* It initializes the NmDcc object and makes it ready to process packets.
*
* Inputs:
* ManufacturerId - Manufacturer ID returned in CV 8.
* Commonly MAN_ID_DIY.
* VersionId - Version ID returned in CV 7.
* Flags - ORed flags beginning with FLAGS_...
* FLAGS_MY_ADDRESS_ONLY - Only process packets with My Address.
* FLAGS_DCC_ACCESSORY_DECODER - Decoder is an accessory decoder.
* FLAGS_OUTPUT_ADDRESS_MODE - This flag applies to accessory decoders only.
* Accessory decoders normally have 4 paired outputs
* and a single address refers to all 4 outputs.
* Setting FLAGS_OUTPUT_ADDRESS_MODE causes each
* address to refer to a single output.
* OpsModeAddressBaseCV - Ops Mode base address. Set it to 0?
*
* Returns:
* None.
*/
void init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV );
void dcc_setup(uint8_t pin, uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV );
/*+
* initAccessoryDecoder() is called from setup() for accessory decoders.
* It calls init() with FLAGS_DCC_ACCESSORY_DECODER ORed into Flags.
*
* Inputs:
* ManufacturerId - Manufacturer ID returned in CV 8.
* Commonly MAN_ID_DIY.
* VersionId - Version ID returned in CV 7.
* Flags - ORed flags beginning with FLAGS_...
* FLAGS_DCC_ACCESSORY_DECODER will be set for init() call.
* OpsModeAddressBaseCV - Ops Mode base address. Set it to 0?
*
* Returns:
* None.
*/
void initAccessoryDecoder( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, uint8_t OpsModeAddressBaseCV );
/*+
* process() is called from loop() to process DCC packets.
* It must be called very frequently to keep up with the packets.
*
* Inputs:
* None.
*
* Returns:
* 1 - Packet succesfully parsed on this call to process().
* 0 - Packet not ready or received packet had an error.
*/
uint8_t process();
void dcc_close();
/*+
* getCV() returns the selected CV value.
*
* Inputs:
* CV - CV number. It must point to a valid CV.
*
* Returns:
* Value - CV value. Invalid CV numbers will return an undefined result
* since nothing will have been set in that EEPROM position.
* Calls notifyCVRead() if it is defined.
*/
uint8_t getCV( uint16_t CV );
void dcc_init();
/*+
* setCV() sets the value of a CV.
*
* Inputs:
* CV - CV number. It must point to a valid CV.
* Value - CV value.
*
* Returns:
* Value - CV value set by this call.
* since nothing will have been set in that EEPROM position.
* Calls notifyCVWrite() if it is defined.
* Calls notifyCVChange() if the value is changed by this call.
*/
uint8_t setCV( uint16_t CV, uint8_t Value);
/*+
* setAccDecDCCAddrNextReceived() enables/disables the setting of the board address from the next received turnout command
*
* Inputs:
* enable- boolean to enable or disable the mode
*
* Returns:
*/
void setAccDecDCCAddrNextReceived(uint8_t enable);
/*+
* isSetCVReady() returns 1 if EEPROM is ready to write.
*
* Inputs:
* CV - CV number. It must point to a valid CV.
* Value - CV value.
*
* Returns:
* ready - 1 if ready to write, 0 otherwise. AVR processor will block
* for several ms. for each write cycle so you should check this to avoid blocks.
* Note: It returns the value returned by notifyIsSetCVReady() if it is defined.
* Calls notifyIsSetCVReady() if it is defined.
*/
uint8_t isSetCVReady( void );
/*+
* getAddr() return the currently active decoder address.
* based on decoder type and current address size.
*
* Inputs:
* None.
*
* Returns:
* Adr - The current decoder address based on decoder type(Multifunction, Accessory)
* and short or long address selection for Multifunction decoders.
*/
uint16_t getAddr(void);
/*+
* getX() return debugging data if DCC_DEBUG is defined.
* You would really need to be modifying the library to need them.
*
* Inputs:
* None.
*
* Returns:
* getIntCount - Init to 0 and apparently never incremented?
* getTickCount - Init to 0 and incremented each time interrupt handler
* completes without an error.
* getBitCount - Bit count of valid packet, 0 otherwise. Only valid until
* start of the next packet.
* getState - Current WAIT_... state as defined by DccRxWaitState in NmraDcc.cpp.
* getNestedIrqCount - Init to 0 and incremented each time the interrupt handler
* is called before the previous interrupt was complete.
* This is an error indication and may indicate the system
* is not handling packets fast enough or some other error is occurring.
*/
// #define DCC_DEBUG
#ifdef DCC_DEBUG
uint8_t getIntCount(void);
uint8_t getTickCount(void);
uint8_t getBitCount(void);
uint8_t getState(void);
uint8_t getNestedIrqCount(void);
#endif
};
/************************************************************************************
Call-back functions
************************************************************************************/
#if defined (__cplusplus)
extern "C" {
#endif
/*+
* notifyDccReset(uint8_t hardReset) Callback for a DCC reset command.
*
......@@ -445,6 +643,45 @@ extern uint8_t notifyCVRead( uint16_t CV) __attribute__ ((weak));
*/
extern uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) __attribute__ ((weak));
/*+
* notifyIsSetCVReady() Callback to to determine if CVs can be written.
* This is called when the library needs to determine
* is ready to write without blocking or failing.
* Note: If defined, this callback
* MUST determine if a CV write would block or fail
* return the appropriate value.
* If this callback is not defined,
* the library determines if a write to the EEPROM
* would block.
*
* Inputs:
* None
*
* Returns:
* 1 - CV is ready to be written.
* 0 - CV is not ready to be written.
*/
extern uint8_t notifyIsSetCVReady(void) __attribute__ ((weak));
/*+
* notifyCVChange() Called when a CV value is changed.
* This is called whenever a CV's value is changed.
* notifyDccCVChange() Called only when a CV value is changed by a Dcc packet or a internal lib function.
* it is NOT called if the CV is changed by means of the setCV() method.
* Note: It is not called if notifyCVWrite() is defined
* or if the value in the EEPROM is the same as the value
* in the write command.
*
* Inputs:
* CV - CV number.
* Value - Value of the CV.
*
* Returns:
* None
*/
extern void notifyCVChange( uint16_t CV, uint8_t Value) __attribute__ ((weak));
extern void notifyDccCVChange( uint16_t CV, uint8_t Value) __attribute__ ((weak));
/*+
* notifyCVResetFactoryDefault() Called when CVs must be reset.
* This is called when CVs must be reset
......@@ -462,6 +699,29 @@ extern uint8_t notifyCVWrite( uint16_t CV, uint8_t Value) __attribute__ ((weak))
*/
extern void notifyCVResetFactoryDefault(void) __attribute__ ((weak));
/*+
* notifyCVAck() Called when a CV write must be acknowledged.
* This callback must increase the current drawn by this
* decoder by at least 60mA for 6ms +/- 1ms.
*
* Inputs:
* None
* *
* Returns:
* None
*/
extern void notifyCVAck(void) __attribute__ ((weak));
/*+
* notifyAdvancedCVAck() Called when a CV write must be acknowledged via Advanced Acknowledgement.
* This callback must send the Advanced Acknowledgement via RailComm.
*
* Inputs:
* None
* *
* Returns:
* None
*/
extern void notifyAdvancedCVAck(void) __attribute__ ((weak));
/*+
* notifyServiceMode(bool) Called when state of 'inServiceMode' changes
*
......@@ -475,5 +735,11 @@ extern void notifyServiceMode(bool) __attribute__ ((weak));
// Deprecated, only for backward compatibility with version 1.4.2.
// Don't use in new designs. These functions may be dropped in future versions
// extern void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State ) __attribute__ ((weak));
// extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ((weak));
extern void notifyDccAccState( uint16_t Addr, uint16_t BoardAddr, uint8_t OutputAddr, uint8_t State ) __attribute__ ((weak));
extern void notifyDccSigState( uint16_t Addr, uint8_t OutputIndex, uint8_t State) __attribute__ ((weak));
#if defined (__cplusplus)
}
#endif
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment