From 8ba4c0dca8ddaf98ea0bc2129048721cece80fdf Mon Sep 17 00:00:00 2001 From: Philipp Gahtow Date: Mon, 10 Jan 2022 20:10:03 +0100 Subject: [PATCH] update to v5.9.1 fix ACK detection for ESP8266 WeMos D1 mini with on Board voltage devider. --- DCCHardware.cpp | 7 +- DCCPacketScheduler.cpp | 148 +++++++++++++++-------------------------- DCCPacketScheduler.h | 26 +++++--- DDCHardware_config.h | 10 +-- README.txt | 12 ++-- library.properties | 2 +- 6 files changed, 86 insertions(+), 119 deletions(-) diff --git a/DCCHardware.cpp b/DCCHardware.cpp index 649369d..4269ffc 100644 --- a/DCCHardware.cpp +++ b/DCCHardware.cpp @@ -122,7 +122,7 @@ static void ICACHE_RAM_ATTR onTimerISR() #elif defined (ESP32) static void IRAM_ATTR onTimerISR(void) #else -void DCC_ARM_TC_SIGNAL(void) +void DCC_ARM_TC_SIGNAL #endif { #if defined(ESP32) @@ -280,8 +280,7 @@ void DCC_ARM_TC_SIGNAL(void) } else DCC_state = dos_send_bstart; } - - //break; + //break; } /// long Preamble: producess additional '1's for Service Mode data else if (DCC_state == dos_send_longpreamble) { @@ -371,7 +370,7 @@ void DCC_ARM_TC_SIGNAL(void) //break; } //} //END SWITCH CASE - + } //END the pin is high. #if defined(ESP32) diff --git a/DCCPacketScheduler.cpp b/DCCPacketScheduler.cpp index 2e37d7b..5882c53 100644 --- a/DCCPacketScheduler.cpp +++ b/DCCPacketScheduler.cpp @@ -42,8 +42,8 @@ z21nvsClass EEPROMDCC; extern portMUX_TYPE timerMux; #endif +/// Request the next packet for the rails extern volatile bool get_next_packet; - /// The Pin where the DCC Waveform comes out. extern uint8_t DCCPin; extern uint8_t DCCPin2; @@ -55,15 +55,14 @@ extern uint8_t current_packet[6]; extern volatile uint8_t current_packet_service; /// How many data uint8_ts in the queued packet? extern volatile uint8_t current_packet_size; +/// current status of railcom +extern volatile uint8_t RailComActiv; -volatile uint8_t current_load_pin = 0; //Pin where current loadis detected volatile uint8_t current_ack_read = false; //ack is detected volatile uint16_t current_cv = 0; //cv that we are working on volatile uint8_t current_cv_value = 0; //value that is read volatile uint8_t current_cv_bit = 0xFF; //bit that will be read - 0xFF = ready, nothing to read! -volatile uint8_t ack_received_now = false; //ACK is send by the decoder -volatile long ack_received_time = 0; //micro time that the ACK start -volatile long ack_monitor_time = 0; //time when a ACK packet is prepared! +volatile uint8_t cv_read_count = 0; //count number of cv read #if defined(ESP32) extern hw_timer_t * timer; @@ -637,11 +636,9 @@ bool DCCPacketScheduler::opsProgDirectCV(uint16_t CV, uint8_t CV_data) setpower(SERVICE, true); current_cv_bit = 0xFF; //write the byte! current_ack_read = false; - ack_monitor_time = micros(); current_cv = CV; current_cv_value = CV_data; - - opsDecoderReset(3); //send first a Reset Packet + opsDecoderReset(RSTcRepeat); //send first a Reset Packet ops_programmming_queue.insertPacket(&p); //-----opsDecoderReset(RSTcRepeat); //send a Reset while waiting to finish return opsVerifyDirectCV(current_cv,current_cv_value); //verify bit read @@ -677,13 +674,14 @@ bool DCCPacketScheduler::opsVerifyDirectCV(uint16_t CV, uint8_t CV_data) setpower(SERVICE, true); current_cv_bit = 0xF0; //verify the byte! current_ack_read = false; - ack_monitor_time = micros(); current_cv = CV; current_cv_value = CV_data; + if (notifyCurrentSence) //get the Base rail current + BaseVAmpSence = notifyCurrentSence(); - opsDecoderReset(3); //send first a Reset Packet + opsDecoderReset(RSTcRepeat); //send first a Reset Packet ops_programmming_queue.insertPacket(&p); - return opsDecoderReset(RSTcRepeat); //send a Reset while waiting to finish + return opsDecoderReset(3); //send a Reset while waiting to finish //return opsDecoderReset(RSTcRepeat); //send a Reset while waiting to finish } @@ -719,8 +717,9 @@ bool DCCPacketScheduler::opsReadDirectCV(uint16_t CV, uint8_t bitToRead, bool bi */ setpower(SERVICE, true); current_ack_read = false; - ack_monitor_time = micros(); current_cv = CV; + if (notifyCurrentSence) //get the Base rail current + BaseVAmpSence = notifyCurrentSence(); DCCPacket p(((CV >> 8) & B11) | B01111000); uint8_t data[] = { 0x00 , 0x00}; @@ -730,9 +729,9 @@ bool DCCPacketScheduler::opsReadDirectCV(uint16_t CV, uint8_t bitToRead, bool bi p.setKind(ops_mode_programming_kind); //always use short Adress Mode! p.setRepeat(ProgRepeat); - opsDecoderReset(3); //send first a Reset Packet + opsDecoderReset(RSTcRepeat); //send first a Reset Packet ops_programmming_queue.insertPacket(&p); - return opsDecoderReset(RSTcRepeat); //send a Reset while waiting to finish + return opsDecoderReset(3); //send a Reset while waiting to finish //--------opsDecoderReset(RSTcRepeat); //send a Reset while waiting to finish //return opsDecoderReset(RSTcRepeat); //send a Reset while waiting to finish } @@ -752,12 +751,9 @@ bool DCCPacketScheduler::opsProgramCV(uint16_t address, uint16_t CV, uint8_t CV_ return false; DCCPacket p(address); - uint8_t data[] = { 0x00, 0x00, 0x00 }; - + // split the CV address up among data uint8_ts 0 and 1 - data[0] = ((CV >> 8) & B11) | B11101100; - data[1] = CV & 0xFF; - data[2] = CV_data; + uint8_t data[] = { ((CV >> 8) & B11) | B11101100, CV & 0xFF, CV_data }; p.addData(data, 3); p.setKind(pom_mode_programming_kind); @@ -785,12 +781,8 @@ bool DCCPacketScheduler::opsPOMwriteBit(uint16_t address, uint16_t CV, uint8_t B return false; DCCPacket p(address); - uint8_t data[] = { 0x00, 0x00, 0x00 }; - // split the CV address up among data uint8_ts 0 and 1 - data[0] = ((CV >> 8) & B11) | B11101000; - data[1] = CV & 0xFF; - data[2] = Bit_data & 0x0F; + uint8_t data[] = { ((CV >> 8) & B11) | B11101000, CV & 0xFF, Bit_data & 0x0F}; p.addData(data, 3); p.setKind(pom_mode_programming_kind); @@ -814,12 +806,8 @@ bool DCCPacketScheduler::opsPOMreadCV(uint16_t address, uint16_t CV) return false; DCCPacket p(address); - uint8_t data[] = { 0x00, 0x00, 0x00 }; - // split the CV address up among data uint8_ts 0 and 1 - data[0] = ((CV >> 8) & B11) | B11100100; - data[1] = CV & 0xFF; - data[2] = 0; + uint8_t data[] = { ((CV >> 8) & B11) | B11100100, CV & 0xFF, 0x00 }; p.addData(data, 3); p.setKind(pom_mode_programming_kind); @@ -887,54 +875,24 @@ bool DCCPacketScheduler::eStop(uint16_t address) } */ - -//for CV read, to detect ACK -void DCCPacketScheduler::setCurrentLoadPin(uint8_t pin) { - current_load_pin = pin; - pinMode(pin, INPUT); -} - //to be called periodically within loop() //checks queues, puts whatever's pending on the rails via global current_packet void DCCPacketScheduler::update(void) { //CV read on Prog.Track: - if (current_packet_service == true && current_ack_read == false && (micros() - ack_monitor_time >= ACK_TIME_WAIT_TO_MONITOR) ) { - - #if !defined(ACKBOOSTER) - uint16_t current_load_now = analogRead(current_load_pin); //get current value - /* - Serial.print(current_load_now); - Serial.print(","); - Serial.print(analogRead(A8)); - Serial.print(","); - Serial.println(digitalRead(A8)); - */ - - //was there a ACK for this packet? - if (current_load_now > ACK_SENCE_VALUE) { //AREF 1.1 Volt = 200 | AREF 5.0 Volt = 15 - if (ack_received_now == false) { - ack_received_now = true; //we are inside a ACK - ack_received_time = micros(); - - //Serial.println(); - } + if (current_packet_service == true && current_ack_read == false) { + + uint16_t current_load_now = 0; + if (notifyCurrentSence) //get the Base rail current + current_load_now = notifyCurrentSence(); + /* + Serial.print(BaseVAmpSence); + Serial.print(","); + Serial.println(current_load_now); + */ + if ( (BaseVAmpSence < current_load_now) && ((current_load_now - BaseVAmpSence) > ACK_SENCE_VALUE) ){ + + //Serial.println("ACK"); - } - else { - if (ack_received_now == true) { - ack_received_now = false; - if (micros() - ack_received_time >= ACK_SENCE_TIME) { //length of ack received? - /* - Serial.print(micros() - ack_monitor_time); - Serial.println("*"); - Serial.print(micros() - ack_received_time); - Serial.print("-b"); - Serial.println(current_cv_bit); - */ - #endif - #if defined(ACKBOOSTER) - if (analogRead(current_load_pin) > ACK_SENCE_VALUE) { - #endif current_ack_read = true; if (current_cv_bit <= 7) //CV read....? bitWrite(current_cv_value,current_cv_bit,1); //ACK, so bit is 'one'! @@ -942,19 +900,17 @@ void DCCPacketScheduler::update(void) { if (current_cv_bit == 0xF0) { if (notifyCVVerify) //Verify the Value to device! notifyCVVerify(current_cv,current_cv_value); - + cv_read_count = CV_WAIT_AFTER_READ; //wait a bit to switch into normal Mode current_cv_bit = 0xFF; //clear! } - //No Power ON! - //setpower(ON, true); //need to switch to accept more programming information } } - #if !defined(ACKBOOSTER) - } - } - #endif - //ENDE mesure currend load for CV# read + } + + else if (current_packet_service == true && current_ack_read == false) { + if (notifyCurrentSence) //get the Base rail current + BaseVAmpSence = notifyCurrentSence(); } //Get next packet: @@ -969,15 +925,21 @@ void DCCPacketScheduler::update(void) { if (railpower == SERVICE) { //if command station was in ops Service Mode, switch power off! if (current_ack_read == false && !(current_cv_bit <= 7) && current_cv_bit != 0xFF) { //No ACK for the Data!!! - if (current_cv_value > 0) { //read only again if there is any response - + if (current_cv_value > 0 && current_cv > 0 && cv_read_count < CV_MAX_TRY_READ) { //read only again if there is any response + /* //Serial.println("wrong!"); - - opsReadDirectCV(current_cv, 0); //read again!! + Serial.print("again CV#"); + Serial.print(current_cv+1); + Serial.print("-"); + Serial.println(current_cv_value); + */ + opsReadDirectCV(current_cv); //read again!! ops_programmming_queue.readPacket(&p); + + cv_read_count++; //count times we try to read this cv! } else { //Return no ACK while programming - + cv_read_count = CV_WAIT_AFTER_READ; //wait a bit to switch into normal Mode if (notifyCVNack) notifyCVNack(current_cv); current_cv_bit = 0xFF; //clear @@ -992,7 +954,7 @@ void DCCPacketScheduler::update(void) { Serial.print(current_cv_bit); Serial.print("-"); Serial.println(current_ack_read); - */ + */ current_cv_bit++; //get next bit if (current_cv_bit > 7) { //READY: read all 8 bit of the CV value: @@ -1020,8 +982,11 @@ void DCCPacketScheduler::update(void) { else { if (railpower == SERVICE) { + if (cv_read_count == 0) { //switch to "normal" Mode! setpower(ON, true); //force to leave Service Mode! + } + else cv_read_count++; } if (e_stop_queue.notEmpty() && (packet_counter % ONCE_REFRESH_INTERVAL)) { //if there's an e_stop packet, send it now! @@ -1067,17 +1032,14 @@ void DCCPacketScheduler::update(void) { current_packet_service = false; } - //#if defined(ESP32) - //portENTER_CRITICAL_ISR(&timerMux); - //#endif - //Ready: Next Packet for the ISR! get_next_packet = false; - //#if defined(ESP32) - //portEXIT_CRITICAL_ISR(&timerMux); - //#endif } } +bool DCCPacketScheduler::getRailComStatus (void) { + return RailComActiv; +} + /* -> old function: void DCCPacketScheduler::update(void) diff --git a/DCCPacketScheduler.h b/DCCPacketScheduler.h index 84dda32..13f2f7f 100644 --- a/DCCPacketScheduler.h +++ b/DCCPacketScheduler.h @@ -1,5 +1,5 @@ /* - * DCC Waveform Generator v5.8 + * DCC Waveform Generator v5.9 * * Author: Philipp Gahtow digitalmoba@arcor.de * Don Goodman-Wilson dgoodman@artificial-science.org @@ -74,6 +74,8 @@ * - add CV to notifyCVNack return * - fix reset start packet count * - fix direct programming handle + * - add request for actual RailCom cutout status + * - fix ACK Value for ESP8266 */ @@ -83,16 +85,14 @@ #include "DCCPacketQueue.h" /*******************************************************************/ -#define ACKBOOSTER //Verstärkung für den ACK Impus beim CV lesen durch LM357 - -#define ACK_TIME_WAIT_TO_MONITOR 20000 //time in microseconds to wait after packet preparing before to check ACK value -#if defined(ACKBOOSTER) -#define ACK_SENCE_VALUE 1000 //Value = 1000 for use with AREF = 1.1 Volt analog Refence Voltage and LM357 +#if defined(ESP8266) //ESP8266 or WeMos D1 mini +#define ACK_SENCE_VALUE 20 //WeMos has a voltage divider that we not want to remove! #else -#define ACK_SENCE_VALUE 60 //Value = 200 for use with AREF = 1.1 Volt analog Refence Voltage; (Value = 15 for AREF = 5.0 Volt) +#define ACK_SENCE_VALUE 100 //Value = 200 for use with AREF = 1.1 Volt analog Refence Voltage; (Value = 15 for AREF = 5.0 Volt) #endif -#define ACK_SENCE_TIME 1500 //time in microseconds that a decoder ACK needs to be long to detect +#define CV_MAX_TRY_READ 5 //read value again if verify fails +#define CV_WAIT_AFTER_READ 200 //255 - x = rounds to count after we finish reading until we switch back automatic to Normal Mode /*******************************************************************/ //When loco request the first time (new loco), start also to send drive information directly @@ -106,7 +106,7 @@ #define SlotMax 255 //Slots für Lokdaten #define PERIODIC_REFRESH_QUEUE_SIZE 255 -#elif defined (ARDUINO_ESP8266_ESP01) || defined(ARDUINO_ESP8266_WEMOS_D1MINI) +#elif defined(ESP8266) //ESP8266 or WeMos D1 mini // Arduino ESP8266 Board follows #define AccessoryMax 4096 //max DCC 2048 Weichen / 8 = 255 byte #define SlotMax 255 //Slots für Lokdaten @@ -255,11 +255,13 @@ class DCCPacketScheduler bool opsPOMwriteBit(uint16_t address, uint16_t CV, uint8_t Bit_data); //POM write bit bool opsPOMreadCV(uint16_t address, uint16_t CV); //POM read - void setCurrentLoadPin(uint8_t pin); //for CV read, to detect ACK + //void setCurrentLoadPin(uint8_t pin); //for CV read, to detect ACK //to be called periodically within loop() void update(void); //checks queues, puts whatever's pending on the rails via global current_packet. easy-peasy + bool getRailComStatus(void); //return the actual RailComStatus on the rails + #if defined(GLOBALRAILCOMREADER) // public only for easy access by interrupt handlers //static inline void handle_RX_interrupt(); //Serial RX Interrupt bearbeiten @@ -278,6 +280,8 @@ class DCCPacketScheduler uint8_t TrntFormat; // The Addressing of BasicAccessory Messages uint8_t DCCdefaultSteps; //default Speed Steps volatile byte railpower = 0xFF; // actual state of the power that goes to the rails + + uint16_t BaseVAmpSence = 0; //Save the level of mA on the Rail for ACK detection byte BasicAccessory[AccessoryMax / 8]; //Speicher für Weichenzustände NetLok LokDataUpdate[SlotMax]; //Speicher zu widerholdene Lok Daten @@ -335,6 +339,8 @@ extern "C" { extern void notifyRailpower(uint8_t state) __attribute__((weak)); + extern uint16_t notifyCurrentSence(void) __attribute__((weak)); //request the CurrentSence value + extern void notifyCVNack(uint16_t CV) __attribute__((weak)); //no ACK while programming #if defined (__cplusplus) diff --git a/DDCHardware_config.h b/DDCHardware_config.h index 6640a86..a94d100 100644 --- a/DDCHardware_config.h +++ b/DDCHardware_config.h @@ -1,5 +1,5 @@ /**************************************************************************** -* Copyright (C) 2016-2021 Philipp Gahtow +* Copyright (C) 2016-2022 Philipp Gahtow * * DCC Waveform Timer Configuration * @@ -107,10 +107,10 @@ Dauer des Teil-Nullbits: t ≥ 100 µs, normal: 116µs /******************************************/ //ESP32 DCC Signal generation with Timer1 #elif defined(ESP32) -#define half_one_count 29 //29usec pulse -#define one_count 58 // 290 - Calls every 58µs -#define zero_high_count 100 // 500 - Calls every 100µs -#define zero_low_count 100 // Calls every 100µs +#define half_one_count 30 //29usec pulse +#define one_count 60 // Calls every 58µs +#define zero_high_count 104 // Calls every 100µs +#define zero_low_count 104 // Calls every 100µs #define DCC_ESP_TIMER_ID 1 //the Timer number from 0 to 3 #define DCC_ESP_TIMER_PRESCALE 80 //prescale the value of the time divider #define DCC_ESP_TIMER_FLAG true //flag true to count on the rising edge, false to count on the falling edge diff --git a/README.txt b/README.txt index 0887982..8f07925 100644 --- a/README.txt +++ b/README.txt @@ -4,10 +4,10 @@ base on CmdrArduino This library create a DCC Signal with RailCom (optional). It can handle two DCC outputs, one with Power feature and one permanent for driving LocoNet Railsync or S88N Raildata line. More about the features: http://pgahtow.de/wiki/index.php?title=DCC#Arduino_DCC_Library -I build up the library to use it with the Arduino Z21pg central station: http://pgahtow.de/wiki/index.php?title=Zentrale +I build up the library to use it with the Arduino Z21pg central station: http://pgahtow.de/wiki/index.php?title=Zentrale =========== - + modified by Philipp Gahtow 2015-2021 digitalmoba@arcor.de * - add a store for active loco, so you can request the actual state * - add a store for BasicAccessory states @@ -24,7 +24,7 @@ modified by Philipp Gahtow 2015-2021 digitalmoba@arcor.de * - fix bug on ESP32 (https://github.com/crosstool-ng/crosstool-ng/issues/1330) * - fix DCC ACK Detection (ACK Booster LM358) =========== - - -To install, see the general instructions for Arduino library installation here: -http://arduino.cc/en/Guide/Environment#libraries + + +To install, see the general instructions for Arduino library installation here: +http://arduino.cc/en/Guide/Environment#libraries diff --git a/library.properties b/library.properties index 74c1b8d..a93896c 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DCCInterfaceMaster -version=5.8.1 +version=5.9.0 author=Philipp Gahtow maintainer=Philipp Gahtow sentence=Enables NMRA DCC Communication