From 865d9198028316f09c51cb5f883905010f4afdc0 Mon Sep 17 00:00:00 2001 From: Alex Shepherd Date: Sat, 16 Feb 2019 22:09:19 +1300 Subject: [PATCH 1/3] Added IRAM_ATTR changes for the ESP32 from Hans Tanner bumped version to 2.0.1 --- NmraDcc.cpp | 91 ++++++++++++++++++++++++++++++++++------------ library.properties | 4 +- 2 files changed, 69 insertions(+), 26 deletions(-) diff --git a/NmraDcc.cpp b/NmraDcc.cpp index 48ed11d..797badb 100644 --- a/NmraDcc.cpp +++ b/NmraDcc.cpp @@ -244,8 +244,10 @@ struct countOf_t countOf; #if defined ( __STM32F1__ ) static ExtIntTriggerMode ISREdge; +static ExtIntTriggerMode ISRWatch; #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; @@ -301,8 +303,35 @@ DCC_PROCESSOR_STATE ; DCC_PROCESSOR_STATE DccProcState ; +#ifdef ESP32 +portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + +void IRAM_ATTR ExternalInterruptHandler(void) +#else void ExternalInterruptHandler(void) +#endif { +// 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; + } // Bit evaluation without Timer 0 ------------------------------ uint8_t DccBitVal; static int8_t bit1, bit2 ; @@ -350,11 +379,9 @@ 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__ ) - detachInterrupt( DccProcState.ExtIntNum ); - #endif - attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE); - halfBit = 0; + halfBit = 0; + ISRWatch = CHANGE; + bitMax = MAX_ONEBITHALF; bitMin = MIN_ONEBITHALF; CLR_TP1; @@ -397,10 +424,9 @@ void ExternalInterruptHandler(void) bitMin = MIN_ONEBITFULL; DccRx.BitCount = 0; SET_TP4; - #if defined ( __STM32F1__ ) - detachInterrupt( DccProcState.ExtIntNum ); - #endif - attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge ); + + ISRWatch = ISREdge; + SET_TP3; CLR_TP4; } @@ -436,10 +462,9 @@ void ExternalInterruptHandler(void) DccRx.TempByte = 0 ; } SET_TP4; - #if defined ( __STM32F1__ ) - detachInterrupt( DccProcState.ExtIntNum ); - #endif - attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge ); + + ISRWatch = ISREdge; + CLR_TP1; CLR_TP4; break; @@ -468,10 +493,9 @@ void ExternalInterruptHandler(void) CLR_TP1; SET_TP4; - #if defined ( __STM32F1__ ) - detachInterrupt( DccProcState.ExtIntNum ); - #endif - attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, ISREdge ); + + ISRWatch = ISREdge; + CLR_TP4; break; @@ -509,8 +533,14 @@ void ExternalInterruptHandler(void) DccRx.State = WAIT_PREAMBLE ; 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 @@ -1326,16 +1356,18 @@ 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; + ISRWatch = ISREdge; + attachInterrupt( DccProcState.ExtIntNum, ExternalInterruptHandler, CHANGE); + // 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 @@ -1439,13 +1471,24 @@ 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(); - #ifdef DCC_DBGVAR - countOf.Tel++; - #endif +#endif + + #ifdef DCC_DBGVAR + countOf.Tel++; + #endif uint8_t xorValue = 0 ; diff --git a/library.properties b/library.properties index 0e6e49b..2622dbd 100644 --- a/library.properties +++ b/library.properties @@ -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.1 +author=Alex Shepherd, Wolfgang Kuffer, Geoff Bunza, Martin Pischky, Franz-Peter Müller, Sven (littleyoda), Hans Tanner maintainer=Alex Shepherd 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. From 5ba1ee3e8ecf1dc9a9b01e1648202a187739a4d4 Mon Sep 17 00:00:00 2001 From: Alex Shepherd Date: Thu, 2 May 2019 11:13:22 +1200 Subject: [PATCH 2/3] added another example of s simple DCC Multifunction (Locomotive) decoder --- .../NmraDccMultiFunctionMotorDecoder.ino | 314 ++++++++++++++++++ 1 file changed, 314 insertions(+) create mode 100644 examples/NmraDccMultiFunctionMotorDecoder/NmraDccMultiFunctionMotorDecoder.ino diff --git a/examples/NmraDccMultiFunctionMotorDecoder/NmraDccMultiFunctionMotorDecoder.ino b/examples/NmraDccMultiFunctionMotorDecoder/NmraDccMultiFunctionMotorDecoder.ino new file mode 100644 index 0000000..a645c12 --- /dev/null +++ b/examples/NmraDccMultiFunctionMotorDecoder/NmraDccMultiFunctionMotorDecoder.ino @@ -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 +// 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); + } +} From b2497625505690353dc4e86fb59b43415e18da2d Mon Sep 17 00:00:00 2001 From: Alex Shepherd Date: Thu, 2 May 2019 21:42:59 +1200 Subject: [PATCH 3/3] changed the version to 201 in the header --- NmraDcc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NmraDcc.h b/NmraDcc.h index d1834c7..55bf4b4 100644 --- a/NmraDcc.h +++ b/NmraDcc.h @@ -49,7 +49,7 @@ #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