Compare commits
12 Commits
2.0.0
...
ESP32-IRAM
Author | SHA1 | Date | |
---|---|---|---|
|
4b175e9229 | ||
|
71bb657e3a | ||
|
f3a2b87693 | ||
|
e06f6b3bce | ||
|
6a7e206032 | ||
|
6d12e6cd3f | ||
|
b249762550 | ||
|
5cee0d28ed | ||
|
5ba1ee3e8e | ||
|
bb2a659ebe | ||
|
865d919802 | ||
|
ff3e24dff4 |
182
NmraDcc.cpp
182
NmraDcc.cpp
@@ -25,6 +25,8 @@
|
||||
// 2017-01-19 added STM32F1 support by Franz-Peter
|
||||
// 2017-11-29 Ken West (kgw4449@gmail.com):
|
||||
// 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
|
||||
//
|
||||
//------------------------------------------------------------------------
|
||||
//
|
||||
@@ -91,7 +93,7 @@
|
||||
#define MAX_ONEBITHALF 82
|
||||
#define MIN_ONEBITFULL 82
|
||||
#define MIN_ONEBITHALF 35
|
||||
#define MAX_BITDIFF 18
|
||||
#define MAX_BITDIFF 24
|
||||
|
||||
|
||||
// Debug-Ports
|
||||
@@ -174,9 +176,22 @@
|
||||
#define MODE_TP3 pinMode( D7,OUTPUT ) ; // GPIO 13
|
||||
#define SET_TP3 GPOS = (1 << D7);
|
||||
#define CLR_TP3 GPOC = (1 << D7);
|
||||
#define MODE_TP4 pinMode( D7,OUTPUT ); // GPIO 15
|
||||
#define MODE_TP4 pinMode( D8,OUTPUT ) ; // GPIO 15
|
||||
#define SET_TP4 GPOC = (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 GPOC = (1 << 27);
|
||||
#define CLR_TP4 GPOC = (1 << 27);
|
||||
|
||||
|
||||
//#elif defined(__AVR_ATmega128__) ||defined(__AVR_ATmega1281__)||defined(__AVR_ATmega2561__)
|
||||
@@ -202,18 +217,12 @@
|
||||
#define MODE_TP2
|
||||
#define SET_TP2
|
||||
#define CLR_TP2
|
||||
//#define MODE_TP2 DDRC |= (1<<2) // A2
|
||||
//#define SET_TP2 PORTC |= (1<<2)
|
||||
//#define CLR_TP2 PORTC &= ~(1<<2)
|
||||
#define MODE_TP3
|
||||
#define SET_TP3
|
||||
#define CLR_TP3
|
||||
#define MODE_TP4
|
||||
#define SET_TP4
|
||||
#define CLR_TP4
|
||||
//#define MODE_TP4 DDRC |= (1<<4) //A4
|
||||
//#define SET_TP4 PORTC |= (1<<4)
|
||||
//#define CLR_TP4 PORTC &= ~(1<<4)
|
||||
|
||||
#endif
|
||||
#ifdef DEBUG_PRINT
|
||||
@@ -230,8 +239,12 @@ struct countOf_t countOf;
|
||||
|
||||
#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; // 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
|
||||
#endif
|
||||
static word bitMax, bitMin;
|
||||
|
||||
@@ -277,6 +290,7 @@ typedef struct
|
||||
uint8_t ExtIntPinNum;
|
||||
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;
|
||||
@@ -287,14 +301,46 @@ DCC_PROCESSOR_STATE ;
|
||||
|
||||
DCC_PROCESSOR_STATE DccProcState ;
|
||||
|
||||
#ifdef ESP32
|
||||
portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
||||
void IRAM_ATTR ExternalInterruptHandler(void)
|
||||
#elif defined(ESP8266)
|
||||
void ICACHE_RAM_ATTR ExternalInterruptHandler(void)
|
||||
#else
|
||||
void ExternalInterruptHandler(void)
|
||||
#endif
|
||||
{
|
||||
#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;
|
||||
|
||||
case RISING:
|
||||
if (digitalRead(DccProcState.ExtIntPinNum) != HIGH)
|
||||
return;
|
||||
break;
|
||||
|
||||
case FALLING:
|
||||
if (digitalRead(DccProcState.ExtIntPinNum) != LOW)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
// Bit evaluation without Timer 0 ------------------------------
|
||||
uint8_t DccBitVal;
|
||||
static int8_t bit1, bit2 ;
|
||||
static word lastMicros;
|
||||
static unsigned int lastMicros = 0;
|
||||
static byte halfBit, DCC_IrqRunning;
|
||||
unsigned int actMicros, bitMicros;
|
||||
#ifdef ALLOW_NESTED_IRQ
|
||||
if ( DCC_IrqRunning ) {
|
||||
// nested DCC IRQ - obviously there are glitches
|
||||
// ignore this interrupt and increment glitchcounter
|
||||
@@ -305,6 +351,7 @@ void ExternalInterruptHandler(void)
|
||||
SET_TP3;
|
||||
return; //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> abort IRQ
|
||||
}
|
||||
#endif
|
||||
SET_TP3;
|
||||
actMicros = micros();
|
||||
bitMicros = actMicros-lastMicros;
|
||||
@@ -316,11 +363,12 @@ void ExternalInterruptHandler(void)
|
||||
}
|
||||
DccBitVal = ( bitMicros < bitMax );
|
||||
lastMicros = actMicros;
|
||||
#ifdef debug
|
||||
if(DccBitVal) {SET_TP2;} else {CLR_TP2;};
|
||||
#endif
|
||||
|
||||
#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
|
||||
@@ -336,10 +384,15 @@ void ExternalInterruptHandler(void)
|
||||
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
|
||||
#if defined ( __STM32F1__ )
|
||||
|
||||
#if defined ( __STM32F1__ )
|
||||
detachInterrupt( DccProcState.ExtIntNum );
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
ISRWatch = CHANGE;
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE);
|
||||
#endif
|
||||
halfBit = 0;
|
||||
bitMax = MAX_ONEBITHALF;
|
||||
bitMin = MIN_ONEBITHALF;
|
||||
@@ -376,17 +429,19 @@ void ExternalInterruptHandler(void)
|
||||
DccRx.BitCount++;
|
||||
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;
|
||||
#if defined ( __STM32F1__ )
|
||||
|
||||
#if defined ( __STM32F1__ )
|
||||
detachInterrupt( DccProcState.ExtIntNum );
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
|
||||
#endif
|
||||
SET_TP3;
|
||||
CLR_TP4;
|
||||
}
|
||||
@@ -422,10 +477,15 @@ void ExternalInterruptHandler(void)
|
||||
DccRx.TempByte = 0 ;
|
||||
}
|
||||
SET_TP4;
|
||||
|
||||
#if defined ( __STM32F1__ )
|
||||
detachInterrupt( DccProcState.ExtIntNum );
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
|
||||
#endif
|
||||
CLR_TP1;
|
||||
CLR_TP4;
|
||||
break;
|
||||
@@ -454,10 +514,16 @@ void ExternalInterruptHandler(void)
|
||||
|
||||
CLR_TP1;
|
||||
SET_TP4;
|
||||
|
||||
#if defined ( __STM32F1__ )
|
||||
detachInterrupt( DccProcState.ExtIntNum );
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
ISRWatch = ISREdge;
|
||||
#else
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge );
|
||||
#endif
|
||||
|
||||
CLR_TP4;
|
||||
break;
|
||||
|
||||
@@ -493,10 +559,17 @@ void ExternalInterruptHandler(void)
|
||||
{
|
||||
CLR_TP3;
|
||||
DccRx.State = WAIT_PREAMBLE ;
|
||||
DccRx.BitCount = 0 ;
|
||||
bitMax = MAX_PRAEAMBEL;
|
||||
bitMin = MIN_ONEBITFULL;
|
||||
#ifdef ESP32
|
||||
portENTER_CRITICAL_ISR(&mux);
|
||||
#endif
|
||||
DccRx.PacketCopy = DccRx.PacketBuf ;
|
||||
DccRx.DataReady = 1 ;
|
||||
#ifdef ESP32
|
||||
portEXIT_CRITICAL_ISR(&mux);
|
||||
#endif
|
||||
SET_TP3;
|
||||
}
|
||||
else // Get next Byte
|
||||
@@ -516,9 +589,11 @@ void ExternalInterruptHandler(void)
|
||||
DccRx.TempByte = 0 ;
|
||||
}
|
||||
}
|
||||
#ifdef ALLOW_NESTED_IRQ
|
||||
DCC_IrqRunning = false;
|
||||
#endif
|
||||
CLR_TP1;
|
||||
CLR_TP3;
|
||||
DCC_IrqRunning = false;
|
||||
}
|
||||
|
||||
void ackCV(void)
|
||||
@@ -527,6 +602,13 @@ void ackCV(void)
|
||||
notifyCVAck() ;
|
||||
}
|
||||
|
||||
void ackAdvancedCV(void)
|
||||
{
|
||||
if( notifyAdvancedCVAck && (DccProcState.cv29Value & CV29_ADV_ACK) )
|
||||
notifyAdvancedCVAck() ;
|
||||
}
|
||||
|
||||
|
||||
uint8_t readEEPROM( unsigned int CV ) {
|
||||
return EEPROM.read(CV) ;
|
||||
}
|
||||
@@ -536,6 +618,9 @@ void writeEEPROM( unsigned int CV, uint8_t Value ) {
|
||||
#if defined(ESP8266)
|
||||
EEPROM.commit();
|
||||
#endif
|
||||
#if defined(ESP32)
|
||||
EEPROM.commit();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool readyEEPROM() {
|
||||
@@ -583,6 +668,7 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
|
||||
{
|
||||
case CV_29_CONFIG:
|
||||
// copy addressmode Bit to Flags
|
||||
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
|
||||
@@ -611,23 +697,19 @@ uint8_t writeCV( unsigned int CV, uint8_t Value)
|
||||
|
||||
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
|
||||
@@ -648,7 +730,7 @@ void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
|
||||
if( validCV( CVAddr, 1 ) )
|
||||
{
|
||||
if( writeCV( CVAddr, Value ) == Value )
|
||||
ackCV();
|
||||
ackAdvancedCV();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -657,7 +739,7 @@ void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
|
||||
if( validCV( CVAddr, 0 ) )
|
||||
{
|
||||
if( readCV( CVAddr ) == Value )
|
||||
ackCV();
|
||||
ackAdvancedCV();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -682,7 +764,7 @@ void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
|
||||
tempValue &= ~BitMask ; // Turn the Bit Off
|
||||
|
||||
if( writeCV( CVAddr, tempValue ) == tempValue )
|
||||
ackCV() ;
|
||||
ackAdvancedCV() ;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -694,12 +776,12 @@ void processDirectOpsOperation( uint8_t Cmd, uint16_t CVAddr, uint8_t Value )
|
||||
if( BitValue )
|
||||
{
|
||||
if( tempValue & BitMask )
|
||||
ackCV() ;
|
||||
ackAdvancedCV() ;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !( tempValue & BitMask) )
|
||||
ackCV() ;
|
||||
ackAdvancedCV() ;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -793,7 +875,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
|
||||
@@ -1277,6 +1359,17 @@ 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
|
||||
@@ -1299,6 +1392,9 @@ void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui
|
||||
#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) );
|
||||
|
||||
@@ -1306,20 +1402,27 @@ void NmraDcc::init( uint8_t ManufacturerId, uint8_t VersionId, uint8_t Flags, ui
|
||||
MODE_TP2;
|
||||
MODE_TP3;
|
||||
MODE_TP4;
|
||||
ISREdge = RISING;
|
||||
bitMax = MAX_ONEBITFULL;
|
||||
bitMin = MIN_ONEBITFULL;
|
||||
attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, RISING);
|
||||
|
||||
DccProcState.Flags = Flags ;
|
||||
DccProcState.OpsModeAddressBaseCV = OpsModeAddressBaseCV ;
|
||||
DccProcState.myDccAddress = -1;
|
||||
DccProcState.inAccDecDCCAddrNextReceivedMode = 0;
|
||||
|
||||
ISREdge = RISING;
|
||||
|
||||
#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))
|
||||
@@ -1419,10 +1522,19 @@ uint8_t NmraDcc::process()
|
||||
{
|
||||
// We need to do this check with interrupts disabled
|
||||
//SET_TP4;
|
||||
#ifdef ESP32
|
||||
portENTER_CRITICAL(&mux);
|
||||
#else
|
||||
noInterrupts();
|
||||
#endif
|
||||
Msg = DccRx.PacketCopy ;
|
||||
DccRx.DataReady = 0 ;
|
||||
|
||||
#ifdef ESP32
|
||||
portEXIT_CRITICAL(&mux);
|
||||
#else
|
||||
interrupts();
|
||||
#endif
|
||||
#ifdef DCC_DBGVAR
|
||||
countOf.Tel++;
|
||||
#endif
|
||||
|
36
NmraDcc.h
36
NmraDcc.h
@@ -32,7 +32,7 @@
|
||||
// Uncomment the following Line to Enable Service Mode CV Programming
|
||||
#define NMRA_DCC_PROCESS_SERVICEMODE
|
||||
|
||||
// Uncomment the following line to Enable MutliFunction Decoder Operations
|
||||
// Uncomment the following line to Enable MultiFunction Decoder Operations
|
||||
#define NMRA_DCC_PROCESS_MULTIFUNCTION
|
||||
|
||||
// Uncomment the following line to Enable 14 Speed Step Support
|
||||
@@ -49,10 +49,12 @@
|
||||
#ifndef NMRADCC_IS_IN
|
||||
#define NMRADCC_IS_IN
|
||||
|
||||
#define NMRADCC_VERSION 200 // Version 2.0.0
|
||||
#define NMRADCC_VERSION 201 // Version 2.0.1
|
||||
|
||||
#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 ;
|
||||
@@ -96,14 +98,20 @@ typedef struct
|
||||
#define CV_MANUFACTURER_ID 8
|
||||
#define CV_29_CONFIG 29
|
||||
|
||||
#if defined(ESP8266)
|
||||
#include <spi_flash.h>
|
||||
#define MAXCV SPI_FLASH_SEC_SIZE
|
||||
#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
|
||||
#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.
|
||||
#define MAXCV E2END // the upper limit of the CV value currently defined to max memory.
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
@@ -651,7 +659,7 @@ 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 chaged by means of the setCV() method.
|
||||
* 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.
|
||||
@@ -695,6 +703,18 @@ extern void notifyCVResetFactoryDefault(void) __attribute__ ((weak));
|
||||
* None
|
||||
*/
|
||||
extern void notifyCVAck(void) __attribute__ ((weak));
|
||||
/*+
|
||||
* notifyAdvancedCVAck() 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 notifyAdvancedCVAck(void) __attribute__ ((weak));
|
||||
/*+
|
||||
* notifyServiceMode(bool) Called when state of 'inServiceMode' changes
|
||||
*
|
||||
|
@@ -0,0 +1,314 @@
|
||||
// NMRA Dcc Multifunction Motor Decoder Demo
|
||||
//
|
||||
// Author: Alex Shepherd 2019-03-30
|
||||
//
|
||||
// This example requires these Arduino Libraries:
|
||||
//
|
||||
// 1) The NmraDcc Library from: http://mrrwa.org/download/
|
||||
//
|
||||
// These libraries can be found and installed via the Arduino IDE Library Manager
|
||||
//
|
||||
// This is a simple demo of how to drive and motor speed and direction using PWM and a motor H-Bridge
|
||||
// It uses vStart and vHigh CV values to customise the PWM values to the motor response
|
||||
// It also uses the Headling Function to drive 2 LEDs for Directional Headlights
|
||||
// Apart from that there's nothing fancy like Lighting Effects or a function matrix or Speed Tables - its just the basics...
|
||||
//
|
||||
|
||||
#include <NmraDcc.h>
|
||||
// Uncomment any of the lines below to enable debug messages for different parts of the code
|
||||
//#define DEBUG_FUNCTIONS
|
||||
//#define DEBUG_SPEED
|
||||
//#define DEBUG_PWM
|
||||
//#define DEBUG_DCC_ACK
|
||||
//#define DEBUG_DCC_MSG
|
||||
|
||||
#if defined(DEBUG_FUNCTIONS) or defined(DEBUG_SPEED) or defined(DEBUG_PWM) or defined(DEBUG_DCC_ACK) or defined(DEBUG_DCC_MSG)
|
||||
#define DEBUG_PRINT
|
||||
#endif
|
||||
|
||||
// This is the default DCC Address
|
||||
#define DEFAULT_DECODER_ADDRESS 3
|
||||
|
||||
// This section defines the Arduino UNO Pins to use
|
||||
#ifdef __AVR_ATmega328P__
|
||||
|
||||
#define DCC_PIN 2
|
||||
|
||||
#define LED_PIN_FWD 5
|
||||
#define LED_PIN_REV 6
|
||||
#define MOTOR_DIR_PIN 12
|
||||
#define MOTOR_PWM_PIN 3
|
||||
|
||||
// This section defines the Arduino ATTiny85 Pins to use
|
||||
#elif ARDUINO_AVR_ATTINYX5
|
||||
|
||||
#define DCC_PIN 2
|
||||
|
||||
#define LED_PIN_FWD 0
|
||||
#define LED_PIN_REV 1
|
||||
#define MOTOR_DIR_PIN 3
|
||||
#define MOTOR_PWM_PIN 4
|
||||
|
||||
#else
|
||||
#error "Unsupported CPU, you need to add another configuration section for your CPU"
|
||||
#endif
|
||||
|
||||
// Some global state variables
|
||||
uint8_t newLedState = 0;
|
||||
uint8_t lastLedState = 0;
|
||||
|
||||
uint8_t newDirection = 0;
|
||||
uint8_t lastDirection = 0;
|
||||
|
||||
uint8_t newSpeed = 0;
|
||||
uint8_t lastSpeed = 0;
|
||||
uint8_t numSpeedSteps = SPEED_STEP_128;
|
||||
|
||||
uint8_t vStart;
|
||||
uint8_t vHigh;
|
||||
|
||||
// Structure for CV Values Table
|
||||
struct CVPair
|
||||
{
|
||||
uint16_t CV;
|
||||
uint8_t Value;
|
||||
};
|
||||
|
||||
// CV Addresses we will be using
|
||||
#define CV_VSTART 2
|
||||
#define CV_VHIGH 5
|
||||
|
||||
// Default CV Values Table
|
||||
CVPair FactoryDefaultCVs [] =
|
||||
{
|
||||
// The CV Below defines the Short DCC Address
|
||||
{CV_MULTIFUNCTION_PRIMARY_ADDRESS, DEFAULT_DECODER_ADDRESS},
|
||||
|
||||
// Three Step Speed Table
|
||||
{CV_VSTART, 120},
|
||||
{CV_VHIGH, 255},
|
||||
|
||||
// These two CVs define the Long DCC Address
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_MSB, 0},
|
||||
{CV_MULTIFUNCTION_EXTENDED_ADDRESS_LSB, DEFAULT_DECODER_ADDRESS},
|
||||
|
||||
// ONLY uncomment 1 CV_29_CONFIG line below as approprate
|
||||
// {CV_29_CONFIG, 0}, // Short Address 14 Speed Steps
|
||||
{CV_29_CONFIG, CV29_F0_LOCATION}, // Short Address 28/128 Speed Steps
|
||||
// {CV_29_CONFIG, CV29_EXT_ADDRESSING | CV29_F0_LOCATION}, // Long Address 28/128 Speed Steps
|
||||
};
|
||||
|
||||
NmraDcc Dcc ;
|
||||
|
||||
uint8_t FactoryDefaultCVIndex = 0;
|
||||
|
||||
// This call-back function is called when a CV Value changes so we can update CVs we're using
|
||||
void notifyCVChange( uint16_t CV, uint8_t Value)
|
||||
{
|
||||
switch(CV)
|
||||
{
|
||||
case CV_VSTART:
|
||||
vStart = Value;
|
||||
break;
|
||||
|
||||
case CV_VHIGH:
|
||||
vHigh = Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void notifyCVResetFactoryDefault()
|
||||
{
|
||||
// Make FactoryDefaultCVIndex non-zero and equal to num CV's to be reset
|
||||
// to flag to the loop() function that a reset to Factory Defaults needs to be done
|
||||
FactoryDefaultCVIndex = sizeof(FactoryDefaultCVs)/sizeof(CVPair);
|
||||
};
|
||||
|
||||
// This call-back function is called whenever we receive a DCC Speed packet for our address
|
||||
void notifyDccSpeed( uint16_t Addr, DCC_ADDR_TYPE AddrType, uint8_t Speed, DCC_DIRECTION Dir, DCC_SPEED_STEPS SpeedSteps )
|
||||
{
|
||||
#ifdef DEBUG_SPEED
|
||||
Serial.print("notifyDccSpeed: Addr: ");
|
||||
Serial.print(Addr,DEC);
|
||||
Serial.print( (AddrType == DCC_ADDR_SHORT) ? "-S" : "-L" );
|
||||
Serial.print(" Speed: ");
|
||||
Serial.print(Speed,DEC);
|
||||
Serial.print(" Steps: ");
|
||||
Serial.print(SpeedSteps,DEC);
|
||||
Serial.print(" Dir: ");
|
||||
Serial.println( (Dir == DCC_DIR_FWD) ? "Forward" : "Reverse" );
|
||||
#endif
|
||||
|
||||
newDirection = Dir;
|
||||
newSpeed = Speed;
|
||||
numSpeedSteps = SpeedSteps;
|
||||
};
|
||||
|
||||
// This call-back function is called whenever we receive a DCC Function packet for our address
|
||||
void notifyDccFunc(uint16_t Addr, DCC_ADDR_TYPE AddrType, FN_GROUP FuncGrp, uint8_t FuncState)
|
||||
{
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print("notifyDccFunc: Addr: ");
|
||||
Serial.print(Addr,DEC);
|
||||
Serial.print( (AddrType == DCC_ADDR_SHORT) ? 'S' : 'L' );
|
||||
Serial.print(" Function Group: ");
|
||||
Serial.print(FuncGrp,DEC);
|
||||
#endif
|
||||
|
||||
if(FuncGrp == FN_0_4)
|
||||
{
|
||||
newLedState = (FuncState & FN_BIT_00) ? 1 : 0;
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.print(" FN 0: ");
|
||||
Serial.print(newLedState);
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.println();
|
||||
#endif
|
||||
}
|
||||
|
||||
// This call-back function is called whenever we receive a DCC Packet
|
||||
#ifdef DEBUG_DCC_MSG
|
||||
void notifyDccMsg( DCC_MSG * Msg)
|
||||
{
|
||||
Serial.print("notifyDccMsg: ") ;
|
||||
for(uint8_t i = 0; i < Msg->Size; i++)
|
||||
{
|
||||
Serial.print(Msg->Data[i], HEX);
|
||||
Serial.write(' ');
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
#endif
|
||||
|
||||
// This call-back function is called by the NmraDcc library when a DCC ACK needs to be sent
|
||||
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read
|
||||
// So we will just turn the motor on for 8ms and then turn it off again.
|
||||
|
||||
void notifyCVAck(void)
|
||||
{
|
||||
#ifdef DEBUG_DCC_ACK
|
||||
Serial.println("notifyCVAck") ;
|
||||
#endif
|
||||
|
||||
digitalWrite(MOTOR_DIR_PIN, HIGH);
|
||||
digitalWrite(MOTOR_PWM_PIN, HIGH);
|
||||
|
||||
delay( 8 );
|
||||
|
||||
digitalWrite(MOTOR_DIR_PIN, LOW);
|
||||
digitalWrite(MOTOR_PWM_PIN, LOW);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
#ifdef DEBUG_PRINT
|
||||
Serial.begin(115200);
|
||||
Serial.println("NMRA Dcc Multifunction Motor Decoder Demo");
|
||||
#endif
|
||||
|
||||
// Setup the Pins for the Fwd/Rev LED for Function 0 Headlight
|
||||
pinMode(LED_PIN_FWD, OUTPUT);
|
||||
pinMode(LED_PIN_REV, OUTPUT);
|
||||
|
||||
// Setup the Pins for the Motor H-Bridge Driver
|
||||
pinMode(MOTOR_DIR_PIN, OUTPUT);
|
||||
pinMode(MOTOR_PWM_PIN, OUTPUT);
|
||||
|
||||
|
||||
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
|
||||
Dcc.pin(DCC_PIN, 0);
|
||||
|
||||
Dcc.init( MAN_ID_DIY, 10, FLAGS_MY_ADDRESS_ONLY | FLAGS_AUTO_FACTORY_DEFAULT, 0 );
|
||||
|
||||
// Uncomment to force CV Reset to Factory Defaults
|
||||
// notifyCVResetFactoryDefault();
|
||||
|
||||
// Read the current CV values for vStart and vHigh
|
||||
vStart = Dcc.getCV(CV_VSTART);
|
||||
vHigh = Dcc.getCV(CV_VHIGH);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
|
||||
Dcc.process();
|
||||
|
||||
// Handle Speed changes
|
||||
if(lastSpeed != newSpeed)
|
||||
{
|
||||
lastSpeed = newSpeed;
|
||||
|
||||
// Stop if speed = 0 or 1
|
||||
|
||||
if(newSpeed <= 1)
|
||||
digitalWrite(MOTOR_PWM_PIN, LOW);
|
||||
|
||||
// Calculate PWM value in the range 1..255
|
||||
else
|
||||
{
|
||||
uint8_t vScaleFactor;
|
||||
|
||||
if((vHigh > 1) && (vHigh > vStart))
|
||||
vScaleFactor = vHigh - vStart;
|
||||
else
|
||||
vScaleFactor = 255 - vStart;
|
||||
|
||||
uint8_t modSpeed = newSpeed - 1;
|
||||
uint8_t modSteps = numSpeedSteps - 1;
|
||||
|
||||
uint8_t newPwm = (uint8_t) vStart + modSpeed * vScaleFactor / modSteps;
|
||||
|
||||
#ifdef DEBUG_PWM
|
||||
Serial.print("New Speed: vStart: ");
|
||||
Serial.print(vStart);
|
||||
Serial.print(" vHigh: ");
|
||||
Serial.print(vHigh);
|
||||
Serial.print(" modSpeed: ");
|
||||
Serial.print(modSpeed);
|
||||
Serial.print(" vScaleFactor: ");
|
||||
Serial.print(vScaleFactor);
|
||||
Serial.print(" modSteps: ");
|
||||
Serial.print(modSteps);
|
||||
Serial.print(" newPwm: ");
|
||||
Serial.println(newPwm);
|
||||
#endif
|
||||
|
||||
analogWrite(MOTOR_PWM_PIN, newPwm);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Direction and Headlight changes
|
||||
if((lastDirection != newDirection) || (lastLedState != newLedState))
|
||||
{
|
||||
lastDirection = newDirection;
|
||||
lastLedState = newLedState;
|
||||
|
||||
digitalWrite(MOTOR_DIR_PIN, newDirection);
|
||||
|
||||
if(newLedState)
|
||||
{
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.println("LED On");
|
||||
#endif
|
||||
digitalWrite(LED_PIN_FWD, newDirection ? LOW : HIGH);
|
||||
digitalWrite(LED_PIN_REV, newDirection ? HIGH : LOW);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG_FUNCTIONS
|
||||
Serial.println("LED Off");
|
||||
#endif
|
||||
digitalWrite(LED_PIN_FWD, LOW);
|
||||
digitalWrite(LED_PIN_REV, LOW);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle resetting CVs back to Factory Defaults
|
||||
if( FactoryDefaultCVIndex && Dcc.isSetCVReady())
|
||||
{
|
||||
FactoryDefaultCVIndex--; // Decrement first as initially it is the size of the array
|
||||
Dcc.setCV( FactoryDefaultCVs[FactoryDefaultCVIndex].CV, FactoryDefaultCVs[FactoryDefaultCVIndex].Value);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
name=NmraDcc
|
||||
version=2.0.0
|
||||
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda)
|
||||
version=2.0.2
|
||||
author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda), Hans Tanner
|
||||
maintainer=Alex Shepherd <kiwi64ajs@gmail.com>
|
||||
sentence=Enables NMRA DCC Communication
|
||||
paragraph=This library allows you to interface to a NMRA DCC track signal and receive DCC commands. The library has been tested on AVR ATTiny84/85 & ATMega88/168/328/32u4, ESP8266 and Teensy 3.x using the INT0/1 Hardware Interrupt and micros() ONLY and no longer uses Timer0 Compare Match B, which makes it much more portable to other platforms.
|
||||
|
Reference in New Issue
Block a user