mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-25 09:06:13 +01:00
Compare commits
13 Commits
995c6f8ede
...
0d679ad993
Author | SHA1 | Date | |
---|---|---|---|
|
0d679ad993 | ||
|
dd80260781 | ||
|
08f41415dc | ||
|
2f65d4347e | ||
|
08c00d275d | ||
|
1888073dc2 | ||
|
6dd175f63b | ||
|
f83be05220 | ||
|
cade89ba16 | ||
|
277825c530 | ||
|
95fe7aafe0 | ||
|
f99deb4276 | ||
|
f5d4dcb97c |
|
@ -467,7 +467,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
||||||
bool prog=false;
|
bool prog=false;
|
||||||
bool join=false;
|
bool join=false;
|
||||||
if (params > 1) break;
|
if (params > 1) break;
|
||||||
if (params==0 || MotorDriver::commonFaultPin) { // <1> or tracks can not be handled individually
|
if (params==0) { // All
|
||||||
main=true;
|
main=true;
|
||||||
prog=true;
|
prog=true;
|
||||||
}
|
}
|
||||||
|
@ -500,7 +500,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
||||||
bool main=false;
|
bool main=false;
|
||||||
bool prog=false;
|
bool prog=false;
|
||||||
if (params > 1) break;
|
if (params > 1) break;
|
||||||
if (params==0 || MotorDriver::commonFaultPin) { // <0> or tracks can not be handled individually
|
if (params==0) { // All
|
||||||
main=true;
|
main=true;
|
||||||
prog=true;
|
prog=true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
#define GITHUB_SHA "devel-202306182208Z"
|
#define GITHUB_SHA "devel-202306210849Z"
|
||||||
|
|
140
MotorDriver.cpp
140
MotorDriver.cpp
|
@ -27,7 +27,7 @@
|
||||||
#include "DCCTimer.h"
|
#include "DCCTimer.h"
|
||||||
#include "DIAG.h"
|
#include "DIAG.h"
|
||||||
|
|
||||||
bool MotorDriver::commonFaultPin=false;
|
unsigned long MotorDriver::globalOverloadStart = 0;
|
||||||
|
|
||||||
volatile portreg_t shadowPORTA;
|
volatile portreg_t shadowPORTA;
|
||||||
volatile portreg_t shadowPORTB;
|
volatile portreg_t shadowPORTB;
|
||||||
|
@ -159,11 +159,7 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
|
||||||
// senseFactorInternal, raw2mA(1000),mA2raw(1000));
|
// senseFactorInternal, raw2mA(1000),mA2raw(1000));
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare values for current detection
|
|
||||||
sampleDelay = 0;
|
|
||||||
lastSampleTaken = millis();
|
|
||||||
progTripValue = mA2raw(TRIP_CURRENT_PROG);
|
progTripValue = mA2raw(TRIP_CURRENT_PROG);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MotorDriver::isPWMCapable() {
|
bool MotorDriver::isPWMCapable() {
|
||||||
|
@ -172,6 +168,7 @@ bool MotorDriver::isPWMCapable() {
|
||||||
|
|
||||||
|
|
||||||
void MotorDriver::setPower(POWERMODE mode) {
|
void MotorDriver::setPower(POWERMODE mode) {
|
||||||
|
if (powerMode == mode) return;
|
||||||
bool on=mode==POWERMODE::ON;
|
bool on=mode==POWERMODE::ON;
|
||||||
if (on) {
|
if (on) {
|
||||||
// when switching a track On, we need to check the crrentOffset with the pin OFF
|
// when switching a track On, we need to check the crrentOffset with the pin OFF
|
||||||
|
@ -372,63 +369,112 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) {
|
void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) {
|
||||||
if (millis() - lastSampleTaken < sampleDelay) return;
|
|
||||||
lastSampleTaken = millis();
|
|
||||||
int tripValue= useProgLimit?progTripValue:getRawCurrentTripValue();
|
int tripValue= useProgLimit?progTripValue:getRawCurrentTripValue();
|
||||||
|
|
||||||
// Trackname for diag messages later
|
|
||||||
switch (powerMode) {
|
switch (powerMode) {
|
||||||
case POWERMODE::OFF:
|
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;
|
||||||
|
setLastPowerChange();
|
||||||
|
}
|
||||||
|
if (microsSinceLastPowerChange() > POWER_SAMPLE_ALL_GOOD) {
|
||||||
|
power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case POWERMODE::ON:
|
case POWERMODE::ON:
|
||||||
// Check current
|
// Check current
|
||||||
lastCurrent=getCurrentRaw();
|
lastCurrent=getCurrentRaw();
|
||||||
if (lastCurrent < 0) {
|
if (lastCurrent < 0) {
|
||||||
// We have a fault pin condition to take care of
|
// We have a fault pin condition to take care of
|
||||||
lastCurrent = -lastCurrent;
|
if (!overloadNow) {
|
||||||
setPower(POWERMODE::OVERLOAD); // Turn off, decide later how fast to turn on again
|
// turn on overload condition as fault pin has gone active
|
||||||
if (commonFaultPin) {
|
// DIAG(F("OVERLOAD FPIN ON"));
|
||||||
if (lastCurrent < tripValue) {
|
overloadNow=true;
|
||||||
setPower(POWERMODE::ON); // maybe other track
|
setLastPowerChangeOverload();
|
||||||
}
|
}
|
||||||
// Write this after the fact as we want to turn on as fast as possible
|
lastCurrent = -lastCurrent;
|
||||||
// because we don't know which output actually triggered the fault pin
|
{
|
||||||
DIAG(F("COMMON FAULT PIN ACTIVE: POWERTOGGLE TRACK %c"), trackno + 'A');
|
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 {
|
} else {
|
||||||
DIAG(F("TRACK %c FAULT PIN ACTIVE - OVERLOAD"), trackno + 'A');
|
if (power_sample_overload_wait <= POWER_SAMPLE_OVERLOAD_WAIT && // virgin
|
||||||
if (lastCurrent < tripValue) {
|
microsSinceLastPowerChange() < POWER_SAMPLE_IGNORE_FAULT_HIGH) {
|
||||||
lastCurrent = tripValue; // exaggerate
|
// 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"), trackno + 'A');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (lastCurrent < tripValue) {
|
// // //
|
||||||
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
// above we looked at fault pin, below we look at current
|
||||||
if(power_good_counter<100)
|
// // //
|
||||||
power_good_counter++;
|
if (lastCurrent < tripValue) { // see above (*)
|
||||||
else
|
if (overloadNow) {
|
||||||
if (power_sample_overload_wait>POWER_SAMPLE_OVERLOAD_WAIT) power_sample_overload_wait=POWER_SAMPLE_OVERLOAD_WAIT;
|
// 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;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setPower(POWERMODE::OVERLOAD);
|
// too much current
|
||||||
unsigned int mA=raw2mA(lastCurrent);
|
if (!overloadNow) {
|
||||||
unsigned int maxmA=raw2mA(tripValue);
|
// current is over trip value, turn on overload condition
|
||||||
power_good_counter=0;
|
// DIAG(F("OVERLOAD PON ON"));
|
||||||
sampleDelay = power_sample_overload_wait;
|
overloadNow=true;
|
||||||
DIAG(F("TRACK %c POWER OVERLOAD %dmA (limit %dmA) shutdown for %dms"), trackno + 'A', mA, maxmA, sampleDelay);
|
setLastPowerChange();
|
||||||
if (power_sample_overload_wait >= 10000)
|
}
|
||||||
power_sample_overload_wait = 10000;
|
unsigned long uSecs = microsSinceLastPowerChange();
|
||||||
else
|
if (power_sample_overload_wait > POWER_SAMPLE_OVERLOAD_WAIT || // not virgin
|
||||||
power_sample_overload_wait *= 2;
|
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;
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case POWERMODE::OVERLOAD:
|
case POWERMODE::OVERLOAD:
|
||||||
// Try setting it back on after the OVERLOAD_WAIT
|
{
|
||||||
|
// Try setting it back on after the OVERLOAD_WAIT
|
||||||
|
unsigned long mslpc = (commonFaultPin ? (micros() - globalOverloadStart) : microsSinceLastPowerChange());
|
||||||
|
if (mslpc > 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);
|
setPower(POWERMODE::ON);
|
||||||
sampleDelay = POWER_SAMPLE_ON_WAIT;
|
// here we change power but not the overloadNow as that was
|
||||||
// Debug code....
|
// already changed to false when we entered POWERMODE::OVERLOAD
|
||||||
DIAG(F("TRACK %c POWER RESTORE (check %dms)"), trackno + 'A', sampleDelay);
|
// so we need to set the lastPowerChange anyway.
|
||||||
break;
|
overloadNow=false;
|
||||||
default:
|
setLastPowerChange();
|
||||||
sampleDelay = 999; // cant get here..meaningless statement to avoid compiler warning.
|
DIAG(F("TRACK %c POWER RESTORE (after %4M)"), trackno + 'A', mslpc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -175,7 +175,10 @@ class MotorDriver {
|
||||||
bool isPWMCapable();
|
bool isPWMCapable();
|
||||||
bool canMeasureCurrent();
|
bool canMeasureCurrent();
|
||||||
bool trackPWM = false; // this track uses PWM timer to generate the DCC waveform
|
bool trackPWM = false; // this track uses PWM timer to generate the DCC waveform
|
||||||
static bool commonFaultPin; // This is a stupid motor shield which has only a common fault pin for both outputs
|
bool commonFaultPin = false; // This is a stupid motor shield which has only a common fault pin for both outputs
|
||||||
|
inline byte setCommonFaultPin() {
|
||||||
|
return commonFaultPin = true;
|
||||||
|
}
|
||||||
inline byte getFaultPin() {
|
inline byte getFaultPin() {
|
||||||
return faultPin;
|
return faultPin;
|
||||||
}
|
}
|
||||||
|
@ -186,6 +189,27 @@ class MotorDriver {
|
||||||
inline void setTrackLetter(char c) {
|
inline void setTrackLetter(char c) {
|
||||||
trackLetter = 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;
|
||||||
|
};
|
||||||
|
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
|
#ifdef ANALOG_READ_INTERRUPT
|
||||||
bool sampleCurrentFromHW();
|
bool sampleCurrentFromHW();
|
||||||
void startCurrentFromHW();
|
void startCurrentFromHW();
|
||||||
|
@ -217,8 +241,10 @@ class MotorDriver {
|
||||||
int rawCurrentTripValue;
|
int rawCurrentTripValue;
|
||||||
// current sampling
|
// current sampling
|
||||||
POWERMODE powerMode;
|
POWERMODE powerMode;
|
||||||
unsigned long lastSampleTaken;
|
bool overloadNow = false;
|
||||||
unsigned int sampleDelay;
|
unsigned long lastPowerChange; // timestamp in microseconds
|
||||||
|
// used to sync restore time when common Fault pin detected
|
||||||
|
static unsigned long globalOverloadStart; // timestamp in microseconds
|
||||||
int progTripValue;
|
int progTripValue;
|
||||||
int lastCurrent;
|
int lastCurrent;
|
||||||
#ifdef ANALOG_READ_INTERRUPT
|
#ifdef ANALOG_READ_INTERRUPT
|
||||||
|
@ -228,10 +254,19 @@ class MotorDriver {
|
||||||
int maxmA;
|
int maxmA;
|
||||||
int tripmA;
|
int tripmA;
|
||||||
|
|
||||||
// Wait times for power management. Unit: milliseconds
|
// Times for overload management. Unit: microseconds.
|
||||||
static const int POWER_SAMPLE_ON_WAIT = 100;
|
// Base for wait time until power is turned on again
|
||||||
static const int POWER_SAMPLE_OFF_WAIT = 1000;
|
static const unsigned long POWER_SAMPLE_OVERLOAD_WAIT = 100UL;
|
||||||
static const int POWER_SAMPLE_OVERLOAD_WAIT = 20;
|
// 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
|
// Trip current for programming track, 250mA. Change only if you really
|
||||||
// need to be non-NMRA-compliant because of decoders that are not either.
|
// need to be non-NMRA-compliant because of decoders that are not either.
|
||||||
|
|
|
@ -117,6 +117,24 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) {
|
||||||
case 'o': stream->print(va_arg(args, int), OCT); break;
|
case 'o': stream->print(va_arg(args, int), OCT); break;
|
||||||
case 'x': stream->print((unsigned int)va_arg(args, unsigned int), HEX); break;
|
case 'x': stream->print((unsigned int)va_arg(args, unsigned int), HEX); break;
|
||||||
case 'X': stream->print((unsigned long)va_arg(args, unsigned long), HEX); break;
|
case 'X': stream->print((unsigned long)va_arg(args, unsigned long), HEX); break;
|
||||||
|
case 'M':
|
||||||
|
{ // this prints a unsigned long microseconds time in readable format
|
||||||
|
unsigned long time = va_arg(args, long);
|
||||||
|
if (time >= 2000) {
|
||||||
|
time = time / 1000;
|
||||||
|
if (time >= 2000) {
|
||||||
|
printPadded(stream, time/1000, formatWidth, formatLeft);
|
||||||
|
stream->print(F("sec"));
|
||||||
|
} else {
|
||||||
|
printPadded(stream,time, formatWidth, formatLeft);
|
||||||
|
stream->print(F("msec"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printPadded(stream,time, formatWidth, formatLeft);
|
||||||
|
stream->print(F("usec"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
//case 'f': stream->print(va_arg(args, double), 2); break;
|
//case 'f': stream->print(va_arg(args, double), 2); break;
|
||||||
//format width prefix
|
//format width prefix
|
||||||
case '-':
|
case '-':
|
||||||
|
|
|
@ -123,9 +123,17 @@ void TrackManager::Setup(const FSH * shieldname,
|
||||||
setTrackMode(1,TRACK_MODE_MAIN);
|
setTrackMode(1,TRACK_MODE_MAIN);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO Fault pin config for odd motor boards (example pololu)
|
// Fault pin config for odd motor boards (example pololu)
|
||||||
// MotorDriver::commonFaultPin = ((mainDriver->getFaultPin() == progDriver->getFaultPin())
|
FOR_EACH_TRACK(t) {
|
||||||
// && (mainDriver->getFaultPin() != UNUSED_PIN));
|
for (byte s=t+1;s<=lastTrack;s++) {
|
||||||
|
if (track[t]->getFaultPin() != UNUSED_PIN &&
|
||||||
|
track[t]->getFaultPin() == track[s]->getFaultPin()) {
|
||||||
|
track[t]->setCommonFaultPin();
|
||||||
|
track[s]->setCommonFaultPin();
|
||||||
|
DIAG(F("Common Fault pin tracks %c and %c"), t+'A', s+'A');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
DCC::begin(shieldname);
|
DCC::begin(shieldname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
#include "StringFormatter.h"
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
|
|
||||||
#define VERSION "4.2.56"
|
#define VERSION "4.2.57"
|
||||||
|
// 4.2.57 - New overload handling (faster and handles commonFaultPin again)
|
||||||
|
// - Optimize analog read STM32
|
||||||
// 4.2.56 - Update IO_RotaryEncoder.h:
|
// 4.2.56 - Update IO_RotaryEncoder.h:
|
||||||
// - Improved I2C communication, non-blocking reads
|
// - Improved I2C communication, non-blocking reads
|
||||||
// - Enable sending positions to the encoder from EXRAIL via SERVO()
|
// - Enable sending positions to the encoder from EXRAIL via SERVO()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user