diff options
author | David Crocker <dcrocker@eschertech.com> | 2017-10-26 00:36:48 +0300 |
---|---|---|
committer | David Crocker <dcrocker@eschertech.com> | 2017-10-26 00:37:05 +0300 |
commit | ad73549a01954de291cea135cde2527d1883596e (patch) | |
tree | 0fcd0f7b05c9e40e986851c5f6455c0f1f589108 /src/Platform.cpp | |
parent | dff15aadac70efe87d3ec952af8b6a3d9acbcf66 (diff) |
Version 1.20beta2
New features:
On SCARA printera arm position limits are applied as well as XY size
limits
Heater 0 values are sent to to PanelDue even if there is no heated bed
When logging is enabled, log entries are now written for when the
date/time/date is set
When logging is enabled, "Maximum open file count exceeded" messages are
logged
Loss of power is now handled much faster. The print is paused in the
middle of a move if necessary. The M991 parameters are changed to
facilitate this.
When resuming a print after loss of power, the head is now moved,
sideways and finally down when restoring position
Following a power failure, M916 can now be used to resume the print
instead of using M98 Presurrect.g
The heater control now switches to fast PID parameters when the
temperature is within 3C of target, instead of within 1C
The TMC2660 Stallguard detection and Coolstep parameters may now be
configured using M915. Currently, no action os performed when a stall is
signalled.
If a heater fault occurs, the print is paused instead of cancelled
All error messages relating to incorrect use of a G- or M-code now
include the G- or M-number of the command that caused them
Increased ADC oversample bits to 2
Duet WiFi: M122 diagnostics now include the wifi module sleep mode and
additional network diagnostics
You can now disable monitorng of TMC2660 drivers rthat are not in use by
using parameter R-1 in the corresponding M569 command
The M585 (probe tool) command is now implemented (thanks chrrishamm)
If axis lengths are adjusted by probing, a subsequent M500 command saves
them in config-override.g
If tool offsets are adjusted by probing, a subsequent M500 command saves
them in config-override.g
The layer counting mechanism has been modified to better handle GCode
files that use a different layer height when printing support
Debug messages sent to the USB port are truncated or thrown away if a
software watchdog reset is imminent
XY speed limiting is now done separate for each kinematics, in
particular for CoreXY printers
Support for Polar kinematics has been added but not tested (see M669
command)
The TMC2660 drivers are configured to detect short-to-ground conditions
faster
The parameters in rr_ http commands are now all order-independent
Bug fixes
An error in computing the time taken to execute moves that were not yet
frozen caused the first movement on a SCARA printer following homing to
be jerky
An extra space in the output from the M114 command confused Printerface,
causing it to print exception messages
When tuning a heater, any previous maximum PWM that was set is now
ignored
Diffstat (limited to 'src/Platform.cpp')
-rw-r--r-- | src/Platform.cpp | 533 |
1 files changed, 376 insertions, 157 deletions
diff --git a/src/Platform.cpp b/src/Platform.cpp index d1b72425..b9a79ad4 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -31,14 +31,18 @@ #include "Scanner.h" #include "Version.h" #include "SoftTimer.h" +#include "Logger.h" #include "Libraries/Math/Isqrt.h" #include "sam/drivers/tc/tc.h" #include "sam/drivers/hsmci/hsmci.h" #include "sd_mmc.h" -#ifdef DUET_NG +#if HAS_SMART_DRIVERS # include "TMC2660.h" +#endif + +#ifdef DUET_NG # include "FirmwareUpdater.h" #endif @@ -52,7 +56,7 @@ extern "C" char *sbrk(int i); # error Missing feature definition #endif -#ifdef DUET_NG +#if HAS_VOLTAGE_MONITOR inline constexpr float AdcReadingToPowerVoltage(uint16_t adcVal) { @@ -254,10 +258,12 @@ bool ZProbeParameters::WriteParameters(FileStore *f, unsigned int probeType) con //************************************************************************************************* // Platform class +uint8_t Platform::softwareResetDebugInfo = 0; // extra info for debugging + Platform::Platform() : - logger(nullptr), lastLogFlushTime(0), board(DEFAULT_BOARD_TYPE), active(false), errorCodeBits(0), lastFanCheckTime(0), + logger(nullptr), board(DEFAULT_BOARD_TYPE), active(false), errorCodeBits(0), lastFanCheckTime(0), auxGCodeReply(nullptr), fileStructureInitialised(false), tickState(0), debugCode(0) -#ifdef DUET_NG +#if HAS_SMART_DRIVERS , lastWarningMillis(0), nextDriveToPoll(0), onBoardDriversFanRunning(false), offBoardDriversFanRunning(false), onBoardDriversFanStartMillis(0), offBoardDriversFanStartMillis(0) #endif @@ -411,6 +417,7 @@ void Platform::Init() // AXES ARRAY_INIT(axisMaxima, AXIS_MAXIMA); ARRAY_INIT(axisMinima, AXIS_MINIMA); + axisMaximaProbed = axisMinimaProbed = 0; idleCurrentFactor = DefaultIdleCurrentFactor; @@ -436,7 +443,7 @@ void Platform::Init() for (size_t drive = 0; drive < DRIVES; drive++) { - enableValues[drive] = false; // assume active low enable signal + enableValues[drive] = 0; // assume active low enable signal directions[drive] = true; // drive moves forwards by default // Map axes and extruders straight through @@ -501,22 +508,23 @@ void Platform::Init() switch (expansionBoard) { case ExpansionBoardType::DueX2: - numTMC2660Drivers = 7; + numSmartDrivers = 7; break; case ExpansionBoardType::DueX5: - numTMC2660Drivers = 10; + numSmartDrivers = 10; break; case ExpansionBoardType::none: case ExpansionBoardType::DueX0: default: - numTMC2660Drivers = 5; // assume that additional drivers are dumb enable/step/dir ones + numSmartDrivers = 5; // assume that any additional drivers are dumb enable/step/dir ones break; } DuetExpansion::AdditionalOutputInit(); // Initialise TMC2660 driver module driversPowered = false; - TMC2660::Init(ENABLE_PINS, numTMC2660Drivers); + SmartDrivers::Init(ENABLE_PINS, numSmartDrivers); + pauseOnStallDrivers = rehomeOnStallDrivers = 0; // Set up the VSSA sense pin. Older Duet WiFis don't have it connected, so we enable the pulldown resistor to keep it inactive. { @@ -559,7 +567,13 @@ void Platform::Init() { if (heatOnPins[heater] != NoPin) { - pinMode(heatOnPins[heater], (HEAT_ON) ? OUTPUT_LOW : OUTPUT_HIGH); + pinMode(heatOnPins[heater], +#if ACTIVE_LOW_HEAT_ON + OUTPUT_LOW +#else + OUTPUT_HIGH +#endif + ); } AnalogChannelNumber chan = PinToAdcChannel(tempSensePins[heater]); // translate the Arduino Due Analog pin number to the SAM ADC channel number pinMode(tempSensePins[heater], AIN); @@ -611,10 +625,10 @@ void Platform::Init() AnalogInEnableChannel(temperatureAdcChannel, true); highestMcuTemperature = 0; // the highest output we have seen from the ADC filter lowestMcuTemperature = 4095 * ThermistorAverageReadings; // the lowest output we have seen from the ADC filter -#endif mcuTemperatureAdjust = 0.0; +#endif -#ifdef DUET_NG +#if HAS_VOLTAGE_MONITOR // Power monitoring vInMonitorAdcChannel = PinToAdcChannel(PowerMonitorVinDetectPin); pinMode(PowerMonitorVinDetectPin, AIN); @@ -1329,7 +1343,7 @@ void Platform::Spin() for (;;) {} } -#ifdef DUET_NG +#if HAS_SMART_DRIVERS // Check whether the TMC drivers need to be initialised. // The tick ISR also looks for over-voltage events, but it just disables the driver without changing driversPowerd or numOverVoltageEvents if (driversPowered) @@ -1347,44 +1361,46 @@ void Platform::Spin() else { // Poll one TMC2660 for temperature warning or temperature shutdown - const uint32_t stat = TMC2660::GetStatus(nextDriveToPoll); - const uint16_t mask = 1 << nextDriveToPoll; - if (stat & TMC_RR_OT) - { - temperatureShutdownDrivers |= mask; - temperatureWarningDrivers &= ~mask; // no point in setting warning as well as error - } - else + if (enableValues[nextDriveToPoll] >= 0) // don't poll driver if it is flagged "no poll" { - temperatureShutdownDrivers &= ~mask; - if (stat & TMC_RR_OTPW) + const uint32_t stat = SmartDrivers::GetStatus(nextDriveToPoll); + const DriversBitmap mask = MakeBitmap<DriversBitmap>(nextDriveToPoll); + if (stat & TMC_RR_OT) { - temperatureWarningDrivers |= mask; + temperatureShutdownDrivers |= mask; + temperatureWarningDrivers &= ~mask; // no point in setting warning as well as error } else { - temperatureWarningDrivers &= ~mask; + temperatureShutdownDrivers &= ~mask; + if (stat & TMC_RR_OTPW) + { + temperatureWarningDrivers |= mask; + } + else + { + temperatureWarningDrivers &= ~mask; + } + } + if (stat & TMC_RR_S2G) + { + shortToGroundDrivers |= mask; + } + else + { + shortToGroundDrivers &= ~mask; + } + if ((stat & (TMC_RR_OLA | TMC_RR_OLB)) != 0 && (stat & TMC_RR_STST) == 0) + { + openLoadDrivers |= mask; + } + else + { + openLoadDrivers &= ~mask; } } - if (stat & TMC_RR_S2G) - { - shortToGroundDrivers |= mask; - } - else - { - shortToGroundDrivers &= ~mask; - } - if ((stat & (TMC_RR_OLA | TMC_RR_OLB)) != 0 && (stat & TMC_RR_STST) == 0) - { - openLoadDrivers |= mask; - } - else - { - openLoadDrivers &= ~mask; - } - ++nextDriveToPoll; - if (nextDriveToPoll > numTMC2660Drivers) + if (nextDriveToPoll > numSmartDrivers) { nextDriveToPoll = 0; } @@ -1394,7 +1410,7 @@ void Platform::Spin() { driversPowered = true; } - TMC2660::SetDriversPowered(driversPowered); + SmartDrivers::SetDriversPowered(driversPowered); #endif const uint32_t now = millis(); @@ -1418,7 +1434,7 @@ void Platform::Spin() fans[fan].Check(); } -#ifdef DUET_NG +#if HAS_SMART_DRIVERS // Check whether it is time to report any faults (do this after checking fans in case driver cooling fans are turned on) if (now - lastWarningMillis > MinimumWarningInterval) { @@ -1457,7 +1473,7 @@ void Platform::Spin() #endif } -#ifdef DUET_NG +#if HAS_VOLTAGE_MONITOR // Check for auto-pause, shutdown or resume if (autoSaveEnabled) { @@ -1473,7 +1489,7 @@ void Platform::Spin() case AutoSaveState::normal: if (currentVin < autoPauseReading) { - if (reprap.GetGCodes().AutoPause()) + if (reprap.GetGCodes().LowVoltagePause()) { autoSaveState = AutoSaveState::autoPaused; } @@ -1481,26 +1497,9 @@ void Platform::Spin() break; case AutoSaveState::autoPaused: - if (currentVin < autoShutdownReading) - { - if (reprap.GetGCodes().AutoShutdown()) - { - autoSaveState = AutoSaveState::autoShutdown; - } - } - else if (currentVin >= autoResumeReading) - { - if (reprap.GetGCodes().AutoResume()) - { - autoSaveState = AutoSaveState::normal; - } - } - break; - - case AutoSaveState::autoShutdown: if (currentVin >= autoResumeReading) { - if (reprap.GetGCodes().AutoResumeAfterShutdown()) + if (reprap.GetGCodes().LowVoltageResume()) { autoSaveState = AutoSaveState::normal; } @@ -1516,21 +1515,17 @@ void Platform::Spin() // Flush the log file it it is time. This may take some time, so do it last. if (logger != nullptr) { - if (now - lastLogFlushTime >= LogFlushInterval) - { - logger->Flush(); - lastLogFlushTime = now; - } + logger->Flush(); } ClassReport(longWait); } -#ifdef DUET_NG +#if HAS_SMART_DRIVERS // Report driver status conditions that require attention. // Sets 'reported' if we reported anything, else leaves 'reported' alone. -void Platform::ReportDrivers(uint16_t whichDrivers, const char* text, bool& reported) +void Platform::ReportDrivers(DriversBitmap whichDrivers, const char* text, bool& reported) { if (whichDrivers != 0) { @@ -1548,36 +1543,35 @@ void Platform::ReportDrivers(uint16_t whichDrivers, const char* text, bool& repo } } -// Configure auto save on power fail -bool Platform::ConfigureAutoSave(GCodeBuffer& gb, StringRef& reply) +#endif + +#if HAS_VOLTAGE_MONITOR + +void Platform::DisableAutoSave() { - bool seen = false; - float autoSaveVoltages[3]; - if (gb.TryGetFloatArray('S', 3, autoSaveVoltages, reply, seen)) - { - return true; - } + autoSaveEnabled = false; +} - if (seen) - { - autoSaveEnabled = (autoSaveVoltages[0] >= 5.0 && autoSaveVoltages[1] > autoSaveVoltages[0] && autoSaveVoltages[2] > autoSaveVoltages[1]); - if (autoSaveEnabled) - { - autoShutdownReading = PowerVoltageToAdcReading(autoSaveVoltages[0]); - autoPauseReading = PowerVoltageToAdcReading(autoSaveVoltages[1]); - autoResumeReading = PowerVoltageToAdcReading(autoSaveVoltages[2]); - } - } - else if (!autoSaveEnabled) - { - reply.copy("Auto save is disabled"); - } - else +bool Platform::IsPowerOk() const +{ + return currentVin > autoPauseReading; +} + +void Platform::EnableAutoSave(float saveVoltage, float resumeVoltage) +{ + autoPauseReading = PowerVoltageToAdcReading(saveVoltage); + autoResumeReading = PowerVoltageToAdcReading(resumeVoltage); + autoSaveEnabled = true; +} + +bool Platform::GetAutoSaveSettings(float& saveVoltage, float&resumeVoltage) +{ + if (autoSaveEnabled) { - reply.printf(" Auto shutdown at %.1fV, save/pause at %.1fV, resume at %.1fV", - (double)AdcReadingToPowerVoltage(autoShutdownReading), (double)AdcReadingToPowerVoltage(autoPauseReading), (double)AdcReadingToPowerVoltage(autoResumeReading)); + saveVoltage = AdcReadingToPowerVoltage(autoPauseReading); + resumeVoltage = AdcReadingToPowerVoltage(autoResumeReading); } - return false; + return autoSaveEnabled; } #endif @@ -1593,16 +1587,22 @@ bool Platform::WriteFanSettings(FileStore *f) const return ok; } +#if HAS_CPU_TEMP_SENSOR + float Platform::AdcReadingToCpuTemperature(uint32_t adcVal) const { - float voltage = (float)adcVal * (3.3/(float)(4096 * ThermistorAverageReadings)); -#if SAM4E + const float voltage = (float)adcVal * (3.3/(float)(4096 * ThermistorAverageReadings)); +#if SAM4E || SAM4S return (voltage - 1.44) * (1000.0/4.7) + 27.0 + mcuTemperatureAdjust; // accuracy at 27C is +/-13C #elif SAM3XA return (voltage - 0.8) * (1000.0/2.65) + 27.0 + mcuTemperatureAdjust; // accuracy at 27C is +/-45C +#else +# error undefined CPU temp conversion #endif } +#endif + // Perform a software reset. 'stk' points to the program counter on the stack if the cause is an exception, otherwise it is nullptr. void Platform::SoftwareReset(uint16_t reason, const uint32_t *stk) { @@ -1635,17 +1635,20 @@ void Platform::SoftwareReset(uint16_t reason, const uint32_t *stk) reason |= (uint16_t)SoftwareResetReason::inAuxOutput; // if we are resetting because we are stuck in a Spin function, record whether we are trying to send to aux } } - reason |= (uint16_t)reprap.GetSpinningModule(); + reason |= (uint8_t)reprap.GetSpinningModule(); + reason |= (softwareResetDebugInfo & 0x07) << 5; // Record the reason for the software reset // First find a free slot (wear levelling) size_t slot = SoftwareResetData::numberOfSlots; SoftwareResetData srdBuf[SoftwareResetData::numberOfSlots]; -#if SAM4E +#if SAM4E || SAM4S if (flash_read_user_signature(reinterpret_cast<uint32_t*>(srdBuf), sizeof(srdBuf)/sizeof(uint32_t)) == FLASH_RC_OK) -#else +#elif SAM3XA DueFlashStorage::read(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf)); +#else +# error #endif { while (slot != 0 && srdBuf[slot - 1].isVacant()) @@ -1657,7 +1660,7 @@ void Platform::SoftwareReset(uint16_t reason, const uint32_t *stk) if (slot == SoftwareResetData::numberOfSlots) { // No free slots, so erase the area -#if SAM4E +#if SAM4E || SAM4S flash_erase_user_signature(); #endif memset(srdBuf, 0xFF, sizeof(srdBuf)); @@ -1680,7 +1683,7 @@ void Platform::SoftwareReset(uint16_t reason, const uint32_t *stk) } // Save diagnostics data to Flash -#if SAM4E +#if SAM4E || SAM4S flash_write_user_signature(srdBuf, sizeof(srdBuf)/sizeof(uint32_t)); #else DueFlashStorage::write(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf)); @@ -1696,11 +1699,13 @@ void Platform::SoftwareReset(uint16_t reason, const uint32_t *stk) // Interrupts #if HAS_LWIP_NETWORKING + void NETWORK_TC_HANDLER() { tc_get_status(NETWORK_TC, NETWORK_TC_CHAN); reprap.GetNetwork().Interrupt(); } + #endif static void FanInterrupt(void*) @@ -1720,7 +1725,7 @@ void Platform::InitialiseInterrupts() // Set the tick interrupt to the highest priority. We need to to monitor the heaters and kick the watchdog. NVIC_SetPriority(SysTick_IRQn, NvicPrioritySystick); // set priority for tick interrupts -#if SAM4E +#if SAM4E || SAM4S NVIC_SetPriority(UART0_IRQn, NvicPriorityUart); // set priority for UART interrupt - must be higher than step interrupt #else NVIC_SetPriority(UART_IRQn, NvicPriorityUart); // set priority for UART interrupt - must be higher than step interrupt @@ -1763,11 +1768,12 @@ void Platform::InitialiseInterrupts() NVIC_SetPriority(PIOE_IRQn, NvicPriorityPins); #endif -#if SAM3XA - NVIC_SetPriority(UOTGHS_IRQn, NvicPriorityUSB); -#endif -#if SAM4E +#if SAM4E || SAM4S NVIC_SetPriority(UDP_IRQn, NvicPriorityUSB); +#elif SAM3XA + NVIC_SetPriority(UOTGHS_IRQn, NvicPriorityUSB); +#else +# error #endif NVIC_SetPriority(TWI1_IRQn, NvicPriorityTwi); @@ -1804,7 +1810,7 @@ void Platform::Diagnostics(MessageType mtype) MessageF(mtype, "%s version %s running on %s", FIRMWARE_NAME, VERSION, GetElectronicsString()); #ifdef DUET_NG - const char* expansionName = DuetExpansion::GetExpansionBoardName(); + const char* const expansionName = DuetExpansion::GetExpansionBoardName(); if (expansionName != nullptr) { MessageF(mtype, " + %s", expansionName); @@ -1813,7 +1819,7 @@ void Platform::Diagnostics(MessageType mtype) Message(mtype, "\n"); -#ifdef DUET_NG +#if SAM4E || SAM4S // Print the unique ID { uint32_t idBuf[5]; @@ -1883,7 +1889,7 @@ void Platform::Diagnostics(MessageType mtype) // Print memory stats and error codes to USB and copy them to the current webserver reply const char *ramstart = -#if SAM4E +#if SAM4E || SAM4S (char *) 0x20000000; #elif SAM3XA (char *) 0x20070000; @@ -1902,7 +1908,7 @@ void Platform::Diagnostics(MessageType mtype) // Show the up time and reason for the last reset const uint32_t now = (uint32_t)(millis64()/1000u); // get up time in seconds const char* resetReasons[8] = { "power up", "backup", "watchdog", "software", -#if SAM4E +#if SAM4E || SAM4S // On the SAM4E a watchdog reset may be reported as a user reset because of the capacitor on the NRST pin "reset button or watchdog", #else @@ -1919,7 +1925,7 @@ void Platform::Diagnostics(MessageType mtype) memset(srdBuf, 0, sizeof(srdBuf)); int slot = -1; -#if SAM4E +#if SAM4E || SAM4S // Work around bug in ASF flash library: flash_read_user_signature calls a RAMFUNC without disabling interrupts first. // This caused a crash (watchdog timeout) sometimes if we run M122 while a print is in progress const irqflags_t flags = cpu_irq_save(); @@ -2002,7 +2008,7 @@ void Platform::Diagnostics(MessageType mtype) lowestMcuTemperature = highestMcuTemperature = currentMcuTemperature; #endif -#ifdef DUET_NG +#if HAS_VOLTAGE_MONITOR // Show the supply voltage MessageF(mtype, "Supply voltage: min %.1f, current %.1f, max %.1f, under voltage events: %" PRIu32 ", over voltage events: %" PRIu32 "\n", (double)AdcReadingToPowerVoltage(lowestVin), (double)AdcReadingToPowerVoltage(currentVin), (double)AdcReadingToPowerVoltage(highestVin), @@ -2016,9 +2022,9 @@ void Platform::Diagnostics(MessageType mtype) MessageF(mtype, "Expansion motor(s) stall indication: %s\n", (stalled) ? "yes" : "no"); } - for (size_t drive = 0; drive < numTMC2660Drivers; ++drive) + for (size_t drive = 0; drive < numSmartDrivers; ++drive) { - const uint32_t stat = TMC2660::GetStatus(drive); + const uint32_t stat = SmartDrivers::GetStatus(drive); MessageF(mtype, "Driver %d:%s%s%s%s%s%s\n", drive, (stat & TMC_RR_SG) ? " stalled" : "", (stat & TMC_RR_OT) ? " temperature-shutdown!" @@ -2097,10 +2103,12 @@ void Platform::DiagnosticTest(int d) case (int)DiagnosticTestType::BusFault: // Read from the "Undefined (Abort)" area -#ifdef DUET_NG +#if SAM4E || SAM4S (void)RepRap::ReadDword(reinterpret_cast<const char*>(0x20800000)); -#else +#elif SAM3XA (void)RepRap::ReadDword(reinterpret_cast<const char*>(0x20200000)); +#else +# error #endif break; @@ -2168,7 +2176,8 @@ void Platform::ClassReport(uint32_t &lastTime) } } -#ifdef DUET_NG +#if HAS_SMART_DRIVERS + // This is called when a fan that monitors driver temperatures is turned on when it was off void Platform::DriverCoolingFansOn(uint32_t driverChannelsMonitored) { @@ -2183,6 +2192,7 @@ void Platform::DriverCoolingFansOn(uint32_t driverChannelsMonitored) offBoardDriversFanRunning = true; } } + #endif // Power is a fraction in [0,1] @@ -2191,7 +2201,13 @@ void Platform::SetHeater(size_t heater, float power) if (heatOnPins[heater] != NoPin) { const uint16_t freq = (reprap.GetHeat().UseSlowPwm(heater)) ? SlowHeaterPwmFreq : NormalHeaterPwmFreq; - IoPort::WriteAnalog(heatOnPins[heater], (HEAT_ON) ? power : 1.0 - power, freq); + const float pwm = +#if ACTIVE_LOW_HEAT_ON + 1.0 - power; +#else + power; +#endif + IoPort::WriteAnalog(heatOnPins[heater], pwm, freq); } } @@ -2277,10 +2293,20 @@ EndStopHit Platform::GetZProbeResult() const : EndStopHit::noStop; } -// Write the Z probe parameters to file -bool Platform::WriteZProbeParameters(FileStore *f) const +// Write the platform parameters to file +bool Platform::WritePlatformParameters(FileStore *f) const { - bool ok = f->Write("; Z probe parameters\n"); + bool ok = WriteAxisLimits(f, axisMinimaProbed, axisMinima, 1); + if (ok) + { + ok = WriteAxisLimits(f, axisMaximaProbed, axisMaxima, 0); + } + +#if 0 // From version 1.20 we no longer write the Z probe parameters, but keep the code for now in case too many users complain + if (ok) + { + ok = f->Write("; Z probe parameters\n"); + } if (ok) { ok = irZProbeParameters.WriteParameters(f, 1); @@ -2293,9 +2319,27 @@ bool Platform::WriteZProbeParameters(FileStore *f) const { ok = switchZProbeParameters.WriteParameters(f, 4); } +#endif + return ok; } +bool Platform::WriteAxisLimits(FileStore *f, AxesBitmap axesProbed, const float limits[MaxAxes], int sParam) +{ + if (axesProbed == 0) + { + return true; + } + + scratchString.printf("M208 S%d", sParam); + for (size_t axis = 0; axis < MaxAxes && IsBitSet(axesProbed, axis); ++axis) + { + scratchString.catf(" %c%.2f", GCodes::axisLetters[axis], (double)limits[axis]); + } + scratchString.cat('\n'); + return f->Write(scratchString.Pointer()); +} + // This is called from the step ISR as well as other places, so keep it fast // If drive >= DRIVES then we are setting an individual motor direction void Platform::SetDirection(size_t drive, bool direction) @@ -2326,16 +2370,16 @@ void Platform::EnableDriver(size_t driver) driverState[driver] = DriverStatus::enabled; UpdateMotorCurrent(driver); // the current may have been reduced by the idle timeout -#if defined(DUET_NG) - if (driver < numTMC2660Drivers) +#if HAS_SMART_DRIVERS + if (driver < numSmartDrivers) { - TMC2660::EnableDrive(driver, true); + SmartDrivers::EnableDrive(driver, true); } else { #endif - digitalWrite(ENABLE_PINS[driver], enableValues[driver]); -#if defined(DUET_NG) + digitalWrite(ENABLE_PINS[driver], enableValues[driver] > 0); +#if HAS_SMART_DRIVERS } #endif } @@ -2346,16 +2390,16 @@ void Platform::DisableDriver(size_t driver) { if (driver < DRIVES) { -#if defined(DUET_NG) - if (driver < numTMC2660Drivers) +#if HAS_SMART_DRIVERS + if (driver < numSmartDrivers) { - TMC2660::EnableDrive(driver, false); + SmartDrivers::EnableDrive(driver, false); } else { #endif - digitalWrite(ENABLE_PINS[driver], !enableValues[driver]); -#if defined(DUET_NG) + digitalWrite(ENABLE_PINS[driver], enableValues[driver] <= 0); +#if HAS_SMART_DRIVERS } #endif driverState[driver] = DriverStatus::disabled; @@ -2470,13 +2514,13 @@ void Platform::UpdateMotorCurrent(size_t driver) current *= motorCurrentFraction[driver]; } -#if defined(DUET_NG) - if (driver < numTMC2660Drivers) +#if HAS_SMART_DRIVERS + if (driver < numSmartDrivers) { - TMC2660::SetCurrent(driver, current); + SmartDrivers::SetCurrent(driver, current); } #elif defined (DUET_06_085) - unsigned short pot = (unsigned short)((0.256*current*8.0*senseResistor + maxStepperDigipotVoltage/2)/maxStepperDigipotVoltage); + const uint16_t pot = (unsigned short)((0.256*current*8.0*senseResistor + maxStepperDigipotVoltage/2)/maxStepperDigipotVoltage); if (driver < 4) { mcpDuet.setNonVolatileWiper(potWipes[driver], pot); @@ -2555,10 +2599,10 @@ bool Platform::SetDriverMicrostepping(size_t driver, int microsteps, int mode) { if (driver < DRIVES) { -#if defined(DUET_NG) - if (driver < numTMC2660Drivers) +#if HAS_SMART_DRIVERS + if (driver < numSmartDrivers) { - return TMC2660::SetMicrostepping(driver, microsteps, mode); + return SmartDrivers::SetMicrostepping(driver, microsteps, mode); } else { @@ -2599,10 +2643,10 @@ bool Platform::SetMicrostepping(size_t drive, int microsteps, int mode) // Get the microstepping for a driver unsigned int Platform::GetDriverMicrostepping(size_t driver, int mode, bool& interpolation) const { -#if defined(DUET_NG) - if (driver < numTMC2660Drivers) +#if HAS_SMART_DRIVERS + if (driver < numSmartDrivers) { - return TMC2660::GetMicrostepping(driver, mode, interpolation); + return SmartDrivers::GetMicrostepping(driver, mode, interpolation); } // On-board drivers only support x16 microstepping without interpolation interpolation = false; @@ -2635,6 +2679,23 @@ unsigned int Platform::GetMicrostepping(size_t drive, int mode, bool& interpolat } } +void Platform::SetEnableValue(size_t driver, int8_t eVal) +{ + enableValues[driver] = eVal; + DisableDriver(driver); // disable the drive, because the enable polarity may have been wrong before +#if HAS_SMART_DRIVERS + if (eVal == -1) + { + // User has asked to disable status monitoring for this driver, so clear its error bits + DriversBitmap mask = ~MakeBitmap<DriversBitmap>(driver); + temperatureShutdownDrivers &= mask; + temperatureWarningDrivers &= mask; + shortToGroundDrivers &= mask; + openLoadDrivers &= mask; + } +#endif +} + void Platform::SetAxisDriversConfig(size_t drive, const AxisDriversConfig& config) { axisDrivers[drive] = config; @@ -2914,9 +2975,16 @@ void Platform::RawMessage(MessageType type, const char *message) if ((type & BlockingUsbMessage) != 0) { - // Debug messages in blocking mode - potentially DANGEROUS, use with care! - SERIAL_MAIN_DEVICE.write(message); - SERIAL_MAIN_DEVICE.flush(); + // Debug output sends messages in blocking mode. We now give up sending if we are close to software watchdog timeout. + const char *p = message; + size_t len = strlen(p); + while (SERIAL_MAIN_DEVICE && len != 0 && !reprap.SpinTimeoutImminent()) + { + const size_t written = SERIAL_MAIN_DEVICE.write(p, len); + len -= written; + p += written; + } + // We no longer flush afterwards } else if ((type & UsbMessage) != 0) { @@ -3153,7 +3221,7 @@ bool Platform::ConfigureLogging(GCodeBuffer& gb, StringRef& reply) return false; } -// This is called form EmergencyStop. It closes the log file and stops logging. +// This is called from EmergencyStop. It closes the log file and stops logging. void Platform::StopLogging() { if (logger != nullptr) @@ -3352,7 +3420,11 @@ bool Platform::GetFirmwarePin(LogicalPin logicalPin, PinAccess access, Pin& firm if (!reprap.GetHeat().IsHeaterEnabled(logicalPin - Heater0LogicalPin)) { firmwarePin = heatOnPins[logicalPin - Heater0LogicalPin]; - invert = !HEAT_ON; +#if ACTIVE_LOW_HEAT_ON + invert = true; +#else + invert = false; +#endif } } else if (logicalPin >= Fan0LogicalPin && logicalPin < Fan0LogicalPin + (int)NUM_FANS) // pins 20- correspond to fan channels @@ -3497,6 +3569,25 @@ Pin Platform::GetEndstopPin(int endstop) const return (endstop >= 0 && endstop < (int)ARRAY_SIZE(endStopPins)) ? endStopPins[endstop] : NoPin; } +// Axis limits +void Platform::SetAxisMaximum(size_t axis, float value, bool byProbing) +{ + axisMaxima[axis] = value; + if (byProbing) + { + SetBit(axisMaximaProbed, axis); + } +} + +void Platform::SetAxisMinimum(size_t axis, float value, bool byProbing) +{ + axisMinima[axis] = value; + if (byProbing) + { + SetBit(axisMinimaProbed, axis); + } +} + #if SUPPORT_INKJET // Fire the inkjet (if any) in the given pattern @@ -3552,7 +3643,7 @@ void Platform::GetMcuTemperatures(float& minT, float& currT, float& maxT) const } #endif -#ifdef DUET_NG +#if HAS_VOLTAGE_MONITOR // Power in voltage void Platform::GetPowerVoltages(float& minV, float& currV, float& maxV) const @@ -3562,6 +3653,10 @@ void Platform::GetPowerVoltages(float& minV, float& currV, float& maxV) const maxV = AdcReadingToPowerVoltage(highestVin); } +#endif + +#if HAS_SMART_DRIVERS + // TMC driver temperatures float Platform::GetTmcDriversTemperature(unsigned int board) const { @@ -3571,6 +3666,127 @@ float Platform::GetTmcDriversTemperature(unsigned int board) const : 0.0; } +// Configure the motor stall detection, returning true if an error was encountered +bool Platform::ConfigureStallDetection(GCodeBuffer& gb, StringRef& reply) +{ + // Build a bitmap of all the drivers referenced + // First looks for explicit driver numbers + DriversBitmap drivers = 0; + if (gb.Seen('P')) + { + long int drives[DRIVES]; + size_t dCount = DRIVES; + gb.GetLongArray(drives, dCount); + for (size_t i = 0; i < dCount; i++) + { + if (drives[i] < 0 || (size_t)drives[i] >= DRIVES) + { + reply.printf("Invalid drive number '%ld'", drives[i]); + return true; + } + SetBit(drivers, i); + } + } + + // Now look for axes + for (size_t i = 0; i < reprap.GetGCodes().GetTotalAxes(); ++i) + { + if (gb.Seen(GCodes::axisLetters[i])) + { + for (size_t j = 0; j < axisDrivers[i].numDrivers; ++j) + { + SetBit(drivers, axisDrivers[i].driverNumbers[j]); + } + } + } + + // Now check for values to change + bool seen = false; + if (gb.Seen('S')) + { + seen = true; + const int sgThreshold = gb.GetIValue(); + for (size_t drive = 0; drive < numSmartDrivers; ++drive) + { + if (IsBitSet(drivers, drive)) + { + SmartDrivers::SetStallThreshold(drive, sgThreshold); + } + } + } + if (gb.Seen('F')) + { + seen = true; + const int sgFilter = (gb.GetIValue() == 1); + for (size_t drive = 0; drive < numSmartDrivers; ++drive) + { + if (IsBitSet(drivers, drive)) + { + SmartDrivers::SetStallFilter(drive, sgFilter); + } + } + } + if (gb.Seen('T')) + { + seen = true; + const uint16_t coolStepConfig = (uint16_t)gb.GetUIValue(); + for (size_t drive = 0; drive < numSmartDrivers; ++drive) + { + if (IsBitSet(drivers, drive)) + { + SmartDrivers::SetCoolStep(drive, coolStepConfig); + } + } + } + if (gb.Seen('R')) + { + seen = true; + const int action = gb.GetIValue(); + switch (action) + { + case 0: + default: + pauseOnStallDrivers &= ~drivers; + rehomeOnStallDrivers &= ~drivers; + break; + + case 1: + rehomeOnStallDrivers &= ~drivers; + pauseOnStallDrivers |= drivers; + break; + + case 2: + pauseOnStallDrivers &= ~drivers; + rehomeOnStallDrivers |= drivers; + break; + } + } + + if (!seen) + { + if (drivers == 0) + { + drivers = LowestNBits<DriversBitmap>(numSmartDrivers); + } + bool printed = false; + for (size_t drive = 0; drive < numSmartDrivers; ++drive) + { + if (IsBitSet(drivers, drive)) + { + if (printed) + { + reply.cat('\n'); + } + reply.catf("Driver %u: ", drive); + SmartDrivers::AppendStallConfig(drive, reply); + printed = true; + } + } + + } + return false; +} + #endif // Real-time clock @@ -3591,8 +3807,11 @@ bool Platform::SetDateTime(time_t time) const bool ok = (gmtime_r(&time, &brokenDateTime) != nullptr); if (ok) { - Message(LogMessage, "Time and date set\n"); - realTime = time; + realTime = time; // set the date and time + + // Write a log message, giving the time since power up in same format as the logger does + const uint32_t timeSincePowerUp = (uint32_t)(millis64()/1000u); + MessageF(LogMessage, "Date and time set at power up + %02" PRIu32 ":%02" PRIu32 ":%02" PRIu32 "\n", timeSincePowerUp/3600u, (timeSincePowerUp % 3600u)/60u, timeSincePowerUp % 60u); timeLastUpdatedMillis = millis(); } return ok; @@ -3628,7 +3847,7 @@ void STEP_TC_HANDLER() { const irqflags_t flags = cpu_irq_save(); const int32_t diff = (int32_t)(tim - GetInterruptClocks()); // see how long we have to go - if (diff < (int32_t)DDA::minInterruptInterval) // if less than about 2us or already passed + if (diff < (int32_t)DDA::minInterruptInterval) // if less than about 6us or already passed { cpu_irq_restore(flags); return true; // tell the caller to simulate an interrupt instead @@ -3661,7 +3880,7 @@ void STEP_TC_HANDLER() { const irqflags_t flags = cpu_irq_save(); const int32_t diff = (int32_t)(tim - GetInterruptClocks()); // see how long we have to go - if (diff < (int32_t)DDA::minInterruptInterval) // if less than about 2us or already passed + if (diff < (int32_t)DDA::minInterruptInterval) // if less than about 6us or already passed { cpu_irq_restore(flags); return true; // tell the caller to simulate an interrupt instead @@ -3700,7 +3919,7 @@ void Platform::Tick() { if (tickState != 0) { -#ifdef DUET_NG +#if HAS_VOLTAGE_MONITOR // Read the power input voltage currentVin = AnalogInReadChannel(vInMonitorAdcChannel); if (currentVin > highestVin) @@ -3713,7 +3932,7 @@ void Platform::Tick() } if (driversPowered && currentVin > driverOverVoltageAdcReading) { - TMC2660::SetDriversPowered(false); + SmartDrivers::SetDriversPowered(false); // We deliberately do not clear driversPowered here or increase the over voltage event count - we let the spin loop handle that } #endif |