From 70d4c016ef79e09af5189735154073061ff85693 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 2 Jul 2023 01:33:41 +0200 Subject: [PATCH] completely new overcurrent detection --- GITHUB_SHA.h | 2 +- MotorDriver.cpp | 193 +++++++++++++++++++++++------------------------- MotorDriver.h | 48 ++++++------ version.h | 3 +- 4 files changed, 121 insertions(+), 125 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 0f05328..07d9123 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-overcurrent-202306300005Z" +#define GITHUB_SHA "devel-overcurrent-202307012332Z" diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 1d7b2bc..e05059b 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -173,7 +173,11 @@ bool MotorDriver::isPWMCapable() { void MotorDriver::setPower(POWERMODE mode) { if (powerMode == mode) return; - bool on=mode==POWERMODE::ON; + //DIAG(F("POWERMODE=%d"), (int)mode); + lastPowerChange[(int)mode] = micros(); + if (mode == POWERMODE::OVERLOAD) + globalOverloadStart = lastPowerChange[(int)mode]; + bool on=(mode==POWERMODE::ON || mode ==POWERMODE::ALERT); if (on) { // when switching a track On, we need to check the crrentOffset with the pin OFF if (powerMode==POWERMODE::OFF && currentPin!=UNUSED_PIN) { @@ -213,8 +217,8 @@ bool MotorDriver::canMeasureCurrent() { return currentPin!=UNUSED_PIN; } /* - * Return the current reading as pin reading 0 to 1023. If the fault - * pin is activated return a negative current to show active fault pin. + * Return the current reading as pin reading 0 to max resolution (1024 or 4096). + * If the fault pin is activated return a negative current to show active fault pin. * As there is no -0, cheat a little and return -1 in that case. * * senseOffset handles the case where a shield returns values above or below @@ -373,118 +377,103 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res } void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { - int tripValue= useProgLimit?progTripValue:getRawCurrentTripValue(); switch (powerMode) { - case POWERMODE::OFF: - if (overloadNow) { - // reset overload condition as we have just turned off power - // DIAG(F("OVERLOAD POFF OFF")); - overloadNow=false; - setLastPowerChange(); - } - 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 - if (!overloadNow) { - // turn on overload condition as fault pin has gone active - // DIAG(F("OVERLOAD FPIN ON")); - overloadNow=true; - setLastPowerChangeOverload(); - } - lastCurrent = -lastCurrent; - { - 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; - } - setPower(POWERMODE::OVERLOAD); - overloadNow=false; - DIAG(F("TRACK %c FAULT PIN"), 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 AND OVERCURRENT"), trackno + 'A'); - } - } - // // // - // 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; - setLastPowerChange(); - } - if (microsSinceLastPowerChange() > POWER_SAMPLE_ALL_GOOD) { - power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT; - } + + case POWERMODE::OFF: { + lastPowerMode = POWERMODE::OFF; + power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT; + break; + } + + case POWERMODE::ON: { + lastPowerMode = POWERMODE::ON; + bool cF = checkFault(); + bool cC = checkCurrent(useProgLimit); + if(cF || cC ) { + if (cC) { + unsigned int mA=raw2mA(lastCurrent); + DIAG(F("TRACK %c ALERT %s %dmA"), trackno + 'A', + cF ? "FAULT" : "", + mA); } else { - // too much current - if (!overloadNow) { - // current is over trip value, turn on overload condition - // DIAG(F("OVERLOAD PON ON")); - overloadNow=true; - setLastPowerChange(); - } - unsigned long uSecs = microsSinceLastPowerChange(); - if (/*power_sample_overload_wait > POWER_SAMPLE_OVERLOAD_WAIT || */ // not virgin - uSecs > POWER_SAMPLE_OFF_DELAY) { - /* - if (micros() - overloadStart > 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; - setLastPowerChangeOverload(); - } - unsigned int mA=raw2mA(lastCurrent); - unsigned int maxmA=raw2mA(tripValue); - DIAG(F("TRACK %c POWER OVERLOAD %4dmA (max %4dmA) detected after %4M. Pause %4M"), - trackno + 'A', mA, maxmA, uSecs, power_sample_overload_wait); - } + DIAG(F("TRACK %c ALERT FAULT"), trackno + 'A'); } + setPower(POWERMODE::ALERT); break; - case POWERMODE::OVERLOAD: - { - // Try setting it back on after the OVERLOAD_WAIT - unsigned long mslpc = (commonFaultPin ? (micros() - globalOverloadStart) : microsSinceLastPowerChange()); + } + // all well + if (microsSinceLastPowerChange(POWERMODE::ON) > POWER_SAMPLE_ALL_GOOD) { + power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT; + } + break; + } + + case POWERMODE::ALERT: { + // set local flags that handle how much is output to diag (do not output duplicates) + bool notFromOverload = (lastPowerMode != POWERMODE::OVERLOAD); + bool newPowerMode = (powerMode != lastPowerMode); + unsigned long now = micros(); + if (newPowerMode) + lastBadSample = now; + lastPowerMode = POWERMODE::ALERT; + // check how long we have been in this state + unsigned long mslpc = microsSinceLastPowerChange(POWERMODE::ALERT); + if(checkFault()) { + lastBadSample = now; + unsigned long timeout = checkCurrent(useProgLimit) ? POWER_SAMPLE_IGNORE_FAULT_HIGH : POWER_SAMPLE_IGNORE_FAULT_LOW; + if ( mslpc < timeout) { + if (newPowerMode) + DIAG(F("TRACK %c FAULT PIN (%M ignore)"), trackno + 'A', timeout); + break; + } + DIAG(F("TRACK %c FAULT PIN detected after %4M. Pause %4M)"), trackno + 'A', mslpc, power_sample_overload_wait); + setPower(POWERMODE::OVERLOAD); + break; + } + if (checkCurrent(useProgLimit)) { + lastBadSample = now; + if (mslpc < POWER_SAMPLE_IGNORE_CURRENT) { + if (newPowerMode) { + unsigned int mA=raw2mA(lastCurrent); + DIAG(F("TRACK %c CURRENT (%M ignore) %dmA"), trackno + 'A', POWER_SAMPLE_IGNORE_CURRENT, mA); + } + break; + } + unsigned int mA=raw2mA(lastCurrent); + unsigned int maxmA=raw2mA(tripValue); + DIAG(F("TRACK %c POWER OVERLOAD %4dmA (max %4dmA) detected after %4M. Pause %4M"), + trackno + 'A', mA, maxmA, mslpc, power_sample_overload_wait); + setPower(POWERMODE::OVERLOAD); + break; + } + // all well + unsigned long goodtime = micros() - lastBadSample; + if (goodtime > POWER_SAMPLE_ALERT_GOOD) { + if (true || notFromOverload) { // we did a RESTORE message XXX + unsigned int mA=raw2mA(lastCurrent); + DIAG(F("TRACK %c NORMAL (after %M/%M) %dmA"), trackno + 'A', goodtime, mslpc, mA); + } + setPower(POWERMODE::ON); + } + break; + } + + case POWERMODE::OVERLOAD: { + lastPowerMode = POWERMODE::OVERLOAD; + unsigned long mslpc = (commonFaultPin ? (micros() - globalOverloadStart) : microsSinceLastPowerChange(POWERMODE::OVERLOAD)); if (mslpc > power_sample_overload_wait) { // adjust next wait time - power_sample_overload_wait *= 2; + power_sample_overload_wait *= 4; if (power_sample_overload_wait > POWER_SAMPLE_RETRY_MAX) power_sample_overload_wait = POWER_SAMPLE_RETRY_MAX; // power on test - setPower(POWERMODE::ON); - // 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. - overloadNow=false; - setLastPowerChange(); DIAG(F("TRACK %c POWER RESTORE (after %4M)"), trackno + 'A', mslpc); + setPower(POWERMODE::ALERT); } + break; } - break; + default: break; } diff --git a/MotorDriver.h b/MotorDriver.h index b6f4670..0ebf494 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -107,7 +107,7 @@ extern volatile portreg_t shadowPORTA; extern volatile portreg_t shadowPORTB; extern volatile portreg_t shadowPORTC; -enum class POWERMODE : byte { OFF, ON, OVERLOAD }; +enum class POWERMODE : byte { OFF, ON, OVERLOAD, ALERT }; class MotorDriver { public: @@ -192,24 +192,13 @@ class MotorDriver { // 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() { + inline unsigned long microsSinceLastPowerChange(POWERMODE mode) { unsigned long now = micros(); - unsigned long diff = now - lastPowerChange; + unsigned long diff = now - lastPowerChange[(int)mode]; if (diff > (1UL << (7 *sizeof(unsigned long)))) // 2^(4*7)us = 268.4 seconds - lastPowerChange = now - 30000000UL; // 30 seconds ago + lastPowerChange[(int)mode] = now - 30000000UL; // 30 seconds ago return diff; }; - inline void setLastPowerChange() { - lastPowerChange = micros(); - }; - // as setLastPowerChange but sets the global timestamp as well which - // is only used to sync power restore in case of common Fault pin. - inline void setLastPowerChangeOverload() { - if (commonFaultPin) - globalOverloadStart = lastPowerChange = micros(); - else - setLastPowerChange(); - }; #ifdef ANALOG_READ_INTERRUPT bool sampleCurrentFromHW(); void startCurrentFromHW(); @@ -218,9 +207,22 @@ class MotorDriver { char trackLetter = '?'; bool isProgTrack = false; // tells us if this is a prog track void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result); - void getFastPin(const FSH* type,int pin, FASTPIN & result) { + inline void getFastPin(const FSH* type,int pin, FASTPIN & result) { getFastPin(type, pin, 0, result); - } + }; + // side effect sets lastCurrent and tripValue + inline bool checkCurrent(bool useProgLimit) { + tripValue= useProgLimit?progTripValue:getRawCurrentTripValue(); + lastCurrent = getCurrentRaw(); + if (lastCurrent < 0) + lastCurrent = -lastCurrent; + return lastCurrent >= tripValue; + }; + // side effect sets lastCurrent + inline bool checkFault() { + lastCurrent = getCurrentRaw(); + return lastCurrent < 0; + }; VPIN powerPin; byte signalPin, signalPin2, currentPin, faultPin, brakePin; FASTPIN fastSignalPin, fastSignalPin2, fastBrakePin,fastFaultPin; @@ -241,12 +243,14 @@ class MotorDriver { int rawCurrentTripValue; // current sampling POWERMODE powerMode; - bool overloadNow = false; - unsigned long lastPowerChange; // timestamp in microseconds + POWERMODE lastPowerMode; + unsigned long lastPowerChange[4]; // timestamp in microseconds + unsigned long lastBadSample; // timestamp in microseconds // used to sync restore time when common Fault pin detected static unsigned long globalOverloadStart; // timestamp in microseconds int progTripValue; - int lastCurrent; + int lastCurrent; //temp value + int tripValue; //temp value #ifdef ANALOG_READ_INTERRUPT volatile unsigned long sampleCurrentTimestamp; volatile uint16_t sampleCurrent; @@ -259,12 +263,14 @@ class MotorDriver { static const unsigned long POWER_SAMPLE_OVERLOAD_WAIT = 10000UL; // Time after we consider all faults old and forgotten static const unsigned long POWER_SAMPLE_ALL_GOOD = 5000000UL; + // Time after which we consider a ALERT over + static const unsigned long POWER_SAMPLE_ALERT_GOOD = 20000UL; // 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 = 100000UL; + static const unsigned long POWER_SAMPLE_IGNORE_CURRENT = 100000UL; // Upper limit for retry period static const unsigned long POWER_SAMPLE_RETRY_MAX = 10000000UL; diff --git a/version.h b/version.h index 2325ceb..3741dc4 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.61" +#define VERSION "4.2.62pre1" +// 4.2.62 - completely new overcurrent detection // 4.2.61 - MAX_CURRENT restriction (caps motor shield value) // 4.2.60 - Add mDNS capability to ESP32 for autodiscovery // 4.2.59 - Fix: AP SSID was DCC_ instead of DCCEX_