diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 25e57bf..8b6faac 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202306182208Z" +#define GITHUB_SHA "devel-202306190642Z" diff --git a/MotorDriver.cpp b/MotorDriver.cpp index e41e150..0eed19a 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -159,11 +159,7 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i // senseFactorInternal, raw2mA(1000),mA2raw(1000)); } - // prepare values for current detection - sampleDelay = 0; - lastSampleTaken = millis(); progTripValue = mA2raw(TRIP_CURRENT_PROG); - } bool MotorDriver::isPWMCapable() { @@ -172,6 +168,7 @@ bool MotorDriver::isPWMCapable() { void MotorDriver::setPower(POWERMODE mode) { + if (powerMode == mode) return; bool on=mode==POWERMODE::ON; if (on) { // when switching a track On, we need to check the crrentOffset with the pin OFF @@ -372,63 +369,123 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res } void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { - if (millis() - lastSampleTaken < sampleDelay) return; - lastSampleTaken = millis(); int tripValue= useProgLimit?progTripValue:getRawCurrentTripValue(); - - // Trackname for diag messages later + switch (powerMode) { case POWERMODE::OFF: - sampleDelay = POWER_SAMPLE_OFF_WAIT; + if (overloadNow) { + // reset overload condition as we have just turned off power + // DIAG(F("OVERLOAD POFF OFF")); + overloadNow=false; + lastPowerChange = micros(); + } + if (microsSinceLastPowerChange() > POWER_SAMPLE_ALL_GOOD) { + power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT; + } break; case POWERMODE::ON: // Check current lastCurrent=getCurrentRaw(); if (lastCurrent < 0) { - // We have a fault pin condition to take care of - lastCurrent = -lastCurrent; - setPower(POWERMODE::OVERLOAD); // Turn off, decide later how fast to turn on again - if (commonFaultPin) { - if (lastCurrent < tripValue) { - setPower(POWERMODE::ON); // maybe other track - } - // Write this after the fact as we want to turn on as fast as possible - // because we don't know which output actually triggered the fault pin - DIAG(F("COMMON FAULT PIN ACTIVE: POWERTOGGLE TRACK %c"), trackno + 'A'); - } else { - DIAG(F("TRACK %c FAULT PIN ACTIVE - OVERLOAD"), trackno + 'A'); - if (lastCurrent < tripValue) { - lastCurrent = tripValue; // exaggerate - } + // We have a fault pin condition to take care of + if (!overloadNow) { + // turn on overload condition as fault pin has gone active + // DIAG(F("OVERLOAD FPIN ON")); + overloadNow=true; + lastPowerChange = micros(); + } + lastCurrent = -lastCurrent; + if (commonFaultPin) { + if (lastCurrent < tripValue) { + // probably other track, do a fast toggle. + setPower(POWERMODE::OVERLOAD); // Turn off, decide later how fast to turn on again + setPower(POWERMODE::ON); // maybe other track + // Write this after the fact as we want to turn on as fast as possible + // because we don't know which output actually triggered the fault pin + DIAG(F("COMMON FAULT PIN ACTIVE: POWERTOGGLE TRACK %c"), trackno + 'A'); } + } else { + if (lastCurrent < tripValue) { + if (power_sample_overload_wait <= (POWER_SAMPLE_OVERLOAD_WAIT * 10) && // almost virgin + microsSinceLastPowerChange() < POWER_SAMPLE_IGNORE_FAULT_LOW) { + // Ignore 50ms fault pin if no current + DIAG(F("TRACK %c FAULT PIN 50ms ignore"), trackno + 'A'); + break; + } + lastCurrent = tripValue; // exaggerate so condition below (*) is true + } else { + if (power_sample_overload_wait <= POWER_SAMPLE_OVERLOAD_WAIT && // virgin + microsSinceLastPowerChange() < POWER_SAMPLE_IGNORE_FAULT_HIGH) { + // Ignore 5ms fault pin if we see current + DIAG(F("TRACK %c FAULT PIN 5ms ignore"), trackno + 'A'); + break; + } + } + DIAG(F("TRACK %c FAULT PIN ACTIVE"), trackno + 'A'); + } } - if (lastCurrent < tripValue) { - sampleDelay = POWER_SAMPLE_ON_WAIT; - if(power_good_counter<100) - power_good_counter++; - else - if (power_sample_overload_wait>POWER_SAMPLE_OVERLOAD_WAIT) power_sample_overload_wait=POWER_SAMPLE_OVERLOAD_WAIT; + // // // + // above we looked at fault pin, below we look at current + // // // + if (lastCurrent < tripValue) { // see above (*) + if (overloadNow) { + // current is below trip value, turn off overload condition + // DIAG(F("OVERLOAD PON OFF")); + overloadNow=false; + lastPowerChange = micros(); + } + if (microsSinceLastPowerChange() > POWER_SAMPLE_ALL_GOOD) { + power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT; + } } else { - setPower(POWERMODE::OVERLOAD); - unsigned int mA=raw2mA(lastCurrent); - unsigned int maxmA=raw2mA(tripValue); - power_good_counter=0; - sampleDelay = power_sample_overload_wait; - DIAG(F("TRACK %c POWER OVERLOAD %dmA (limit %dmA) shutdown for %dms"), trackno + 'A', mA, maxmA, sampleDelay); - if (power_sample_overload_wait >= 10000) - power_sample_overload_wait = 10000; - else - power_sample_overload_wait *= 2; + // too much current + if (!overloadNow) { + // current is over trip value, turn on overload condition + // DIAG(F("OVERLOAD PON ON")); + overloadNow=true; + lastPowerChange = micros(); + } + unsigned long uSecs = microsSinceLastPowerChange(); + if (power_sample_overload_wait > POWER_SAMPLE_OVERLOAD_WAIT || // not virgin + uSecs > POWER_SAMPLE_OFF_DELAY) { + // Overload has existed longer than delay (typ. 10ms) + setPower(POWERMODE::OVERLOAD); + if (overloadNow) { + // the setPower just turned off, so overload is now gone + // DIAG(F("OVERLOAD PON OFF")); + overloadNow=false; + lastPowerChange = micros(); + } + unsigned int mA=raw2mA(lastCurrent); + unsigned int maxmA=raw2mA(tripValue); + DIAG(F("TRACK %c POWER OVERLOAD %dmA (limit %dmA) shutdown after %lus for %lus"), + trackno + 'A', mA, maxmA, uSecs, power_sample_overload_wait); + } } break; - case POWERMODE::OVERLOAD: - // Try setting it back on after the OVERLOAD_WAIT + case POWERMODE::OVERLOAD: + if (overloadNow) { + // state overload mode means power is off, turn off overload condition flag as well + // DIAG(F("OVERLOAD POVER OFF")); + overloadNow=false; + lastPowerChange = micros(); + } + // Try setting it back on after the OVERLOAD_WAIT + if (microsSinceLastPowerChange() > power_sample_overload_wait) { + // adjust next wait time + power_sample_overload_wait *= 2; + if (power_sample_overload_wait > POWER_SAMPLE_RETRY_MAX) + power_sample_overload_wait = POWER_SAMPLE_RETRY_MAX; + // power on test setPower(POWERMODE::ON); - sampleDelay = POWER_SAMPLE_ON_WAIT; - // Debug code.... - DIAG(F("TRACK %c POWER RESTORE (check %dms)"), trackno + 'A', sampleDelay); - break; - default: - sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning. + // here we change power but not the overloadNow as that was + // already changed to false when we entered POWERMODE::OVERLOAD + // so we need to set the lastPowerChange anyway. + lastPowerChange = micros(); + DIAG(F("TRACK %c POWER RESTORE (was off %lus)"), trackno + 'A', power_sample_overload_wait); + } + break; + default: + break; } } diff --git a/MotorDriver.h b/MotorDriver.h index be77d2d..1eb4e95 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -186,6 +186,16 @@ class MotorDriver { inline void setTrackLetter(char c) { trackLetter = c; }; + // this returns how much time has passed since the last power change. If it + // was really long ago (approx > 52min) advance counter approx 35 min so that + // we are at 18 minutes again. Times for 32 bit unsigned long. + inline unsigned long microsSinceLastPowerChange() { + unsigned long now = micros(); + unsigned long diff = now - lastPowerChange; + if (diff > (1UL << (7 *sizeof(unsigned long)))) // 2^(4*7)us = 268.4 seconds + lastPowerChange = now - 30000000UL; // 30 seconds ago + return diff; + }; #ifdef ANALOG_READ_INTERRUPT bool sampleCurrentFromHW(); void startCurrentFromHW(); @@ -217,8 +227,8 @@ class MotorDriver { int rawCurrentTripValue; // current sampling POWERMODE powerMode; - unsigned long lastSampleTaken; - unsigned int sampleDelay; + bool overloadNow = false; + unsigned long lastPowerChange; // timestamp in microseconds int progTripValue; int lastCurrent; #ifdef ANALOG_READ_INTERRUPT @@ -228,10 +238,19 @@ class MotorDriver { int maxmA; int tripmA; - // Wait times for power management. Unit: milliseconds - static const int POWER_SAMPLE_ON_WAIT = 100; - static const int POWER_SAMPLE_OFF_WAIT = 1000; - static const int POWER_SAMPLE_OVERLOAD_WAIT = 20; + // Times for overload management. Unit: microseconds. + // Base for wait time until power is turned on again + static const unsigned long POWER_SAMPLE_OVERLOAD_WAIT = 100UL; + // Time after we consider all faults old and forgotten + static const unsigned long POWER_SAMPLE_ALL_GOOD = 5000000UL; + // How long to ignore fault pin if current is under limit + static const unsigned long POWER_SAMPLE_IGNORE_FAULT_LOW = 50000UL; + // How long to ignore fault pin if current is higher than limit + static const unsigned long POWER_SAMPLE_IGNORE_FAULT_HIGH = 5000UL; + // How long to wait between overcurrent and turning off + static const unsigned long POWER_SAMPLE_OFF_DELAY = 10000UL; + // Upper limit for retry period + static const unsigned long POWER_SAMPLE_RETRY_MAX = 10000000UL; // Trip current for programming track, 250mA. Change only if you really // need to be non-NMRA-compliant because of decoders that are not either.