diff --git a/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.17dev7.bin b/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.17dev8.bin similarity index 56% rename from Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.17dev7.bin rename to Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.17dev8.bin index 73e5a4f..2263075 100644 Binary files a/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.17dev7.bin and b/Release/Duet-0.6-0.8.5/Edge/RepRapFirmware-1.17dev8.bin differ diff --git a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.17dev7.bin b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.17dev8.bin similarity index 55% rename from Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.17dev7.bin rename to Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.17dev8.bin index e9388c7..afdf372 100644 Binary files a/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.17dev7.bin and b/Release/Duet-WiFi/Edge/DuetWiFiFirmware-1.17dev8.bin differ diff --git a/src/Configuration.h b/src/Configuration.h index 924e163..728a115 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -28,11 +28,11 @@ Licence: GPL // Firmware name is now defined in the Pins file #ifndef VERSION -# define VERSION "1.17dev7" +# define VERSION "1.17dev8" #endif #ifndef DATE -# define DATE "2016-12-07" +# define DATE "2016-12-09" #endif #define AUTHORS "reprappro, dc42, zpl, t3p3, dnewman" @@ -118,7 +118,7 @@ const unsigned int FirstRtdChannel = 200; // Temperature sensor channels 200.. const unsigned int SlowHeaterPwmFreq = 10; // slow PWM frequency for bed and chamber heaters, compatible with DC/AC SSRs const unsigned int NormalHeaterPwmFreq = 250; // normal PWM frequency used for hot ends const unsigned int DefaultFanPwmFreq = 250; // increase to 25kHz using M106 command to meet Intel 4-wire PWM fan specification -const unsigned int DefaultPinWritePwmFreq = 500; // default PWM frequency for M42 pin writes +const unsigned int DefaultPinWritePwmFreq = 500; // default PWM frequency for M42 pin writes and extrusion ancilliary PWM // Default Z probe values diff --git a/src/Fan.cpp b/src/Fan.cpp index 89e2f97..83c56e4 100644 --- a/src/Fan.cpp +++ b/src/Fan.cpp @@ -132,4 +132,14 @@ void Fan::Check() } } +void Fan::Disable() +{ + if (pin != NoPin) + { + inverted = false; + SetHardwarePwm(0.0); + } + pin = NoPin; +} + // End diff --git a/src/Fan.h b/src/Fan.h index 9e31feb..1c277bf 100644 --- a/src/Fan.h +++ b/src/Fan.h @@ -48,7 +48,7 @@ public: void SetTriggerTemperature(float t) { triggerTemperature = t; } void SetHeatersMonitored(uint16_t h); void Check(); - void Disable() { pin = NoPin; } + void Disable(); }; #endif /* SRC_FAN_H_ */ diff --git a/src/GCodes/GCodes1.cpp b/src/GCodes/GCodes.cpp similarity index 97% rename from src/GCodes/GCodes1.cpp rename to src/GCodes/GCodes.cpp index e562245..3fb2911 100644 --- a/src/GCodes/GCodes1.cpp +++ b/src/GCodes/GCodes.cpp @@ -148,7 +148,7 @@ void GCodes::Reset() simulationMode = 0; simulationTime = 0.0; isPaused = false; - filePos = moveBuffer.filePos = noFilePosition; + moveBuffer.filePos = noFilePosition; lastEndstopStates = platform->GetAllEndstopStates(); firmwareUpdateModuleMap = 0; @@ -699,11 +699,6 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply) char b; if (fd.Read(b)) { - if (gb.StartingNewCode() && &gb == fileGCode && gb.MachineState().previous == nullptr) - { - filePos = fd.GetPosition() - 1; - //debugPrintf("Set file pos %u\n", filePos); - } if (gb.Put(b)) { gb.SetFinished(ActOnCode(gb, reply)); @@ -732,6 +727,7 @@ void GCodes::DoFilePrint(GCodeBuffer& gb, StringRef& reply) if (gb.MachineState().previous == nullptr) { // Finished printing SD card file + UnlockAll(gb); reprap.GetPrintMonitor()->StoppedPrint(); if (platform->Emulating() == marlin) { @@ -947,7 +943,7 @@ void GCodes::Pop(GCodeBuffer& gb) // Move expects all axis movements to be absolute, and all extruder drive moves to be relative. This function serves that. // 'moveType' is the S parameter in the G0 or G1 command, or -1 if we are doing G92. // For regular (type 0) moves, we apply limits and do X axis mapping. -// Returns the number of segments if we have a legal move (or 1 if we are doing G92), or zero if this gcode should be discarded +// Returns the number of segments if we have a legal move, 1 if we are doing G92, or zero if this gcode should be discarded unsigned int GCodes::LoadMoveBufferFromGCode(GCodeBuffer& gb, int moveType) { // Zero every extruder drive as some drives may not be changed @@ -1214,7 +1210,7 @@ int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply) } } - // Load the last position and feed rate into moveBuffer + // Load the last position into moveBuffer #if SUPPORT_ROLAND if (reprap.GetRoland()->Active()) { @@ -1227,24 +1223,25 @@ int GCodes::SetUpMove(GCodeBuffer& gb, StringRef& reply) } // Load the move buffer with either the absolute movement required or the relative movement required - float oldCoords[MAX_AXES]; - memcpy(oldCoords, moveBuffer.coords, sizeof(oldCoords)); + memcpy(moveBuffer.initialCoords, moveBuffer.coords, numAxes * sizeof(moveBuffer.initialCoords[0])); segmentsLeft = LoadMoveBufferFromGCode(gb, moveBuffer.moveType); + if (segmentsLeft != 0) { // Flag whether we should use pressure advance, if there is any extrusion in this move. - // We assume it is a normal printing move needing pressure advance if there is forward extrusion and XY movement. - // The movement code will only apply pressure advance if there is forward extrusion, so we only need to check for XY movement here. + // We assume it is a normal printing move needing pressure advance if there is forward extrusion and XYU.. movement. + // The movement code will only apply pressure advance if there is forward extrusion, so we only need to check for XYU.. movement here. moveBuffer.usePressureAdvance = false; for (size_t axis = 0; axis < numAxes; ++axis) { - if (axis != Z_AXIS && moveBuffer.coords[axis] != oldCoords[axis]) + if (axis != Z_AXIS && moveBuffer.coords[axis] != moveBuffer.initialCoords[axis]) { moveBuffer.usePressureAdvance = true; break; } } - moveBuffer.filePos = (&gb == fileGCode) ? filePos : noFilePosition; + moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() : noFilePosition; + moveBuffer.canPauseAfter = (moveBuffer.endStopsToCheck == 0); //debugPrintf("Queue move pos %u\n", moveFilePos); } return (moveBuffer.moveType != 0 || moveBuffer.endStopsToCheck != 0) ? 2 : 1; @@ -1266,7 +1263,9 @@ bool GCodes::ReadMove(RawMove& m) } else { - // This move needs to be divided into 2 or more segments + // This move needs to be divided into 2 or more segments. We can only pause after the final segment. + m.canPauseAfter = false; + // Do the axes for (size_t drive = 0; drive < numAxes; ++drive) { @@ -1375,52 +1374,65 @@ bool GCodes::DoCannedCycleMove(GCodeBuffer& gb, EndstopChecks ce) return false; } -// This handles G92 +// This handles G92. Return true if completed, false if it needs to be called again. bool GCodes::SetPositions(GCodeBuffer& gb) { - // Don't pause the machine if only extruder drives are being reset (DC, 2015-09-06). - // This avoids blobs and seams when the gcode uses absolute E coordinates and periodically includes G92 E0. - bool includingAxes = false; - for (size_t drive = 0; drive < numAxes; ++drive) + if (gb.Seen('R') && gb.GetIValue() == 1) { - if (gb.Seen(axisLetters[drive])) + // Babystepping command. All coordinates except Z are ignored. + if (gb.Seen('Z')) { - includingAxes = true; - break; + const float babystepAmount = gb.GetFValue() * distanceScale; + if (fabs(babystepAmount) <= 1.0) // limit babystepping to 1mm + { + reprap.GetMove()->Babystep(babystepAmount); + } } } - - if (includingAxes) + else { - if (!LockMovementAndWaitForStandstill(gb)) + // Don't pause the machine if only extruder drives are being reset (DC, 2015-09-06). + // This avoids blobs and seams when the gcode uses absolute E coordinates and periodically includes G92 E0. + bool includingAxes = false; + for (size_t drive = 0; drive < numAxes; ++drive) + { + if (gb.Seen(axisLetters[drive])) + { + includingAxes = true; + break; + } + } + + if (includingAxes) + { + if (!LockMovementAndWaitForStandstill(gb)) // lock movement and get current coordinates + { + return false; + } + } + else if (segmentsLeft != 0) // wait for previous move to be taken so that GetCurrentUserPosition returns the correct value { return false; } - } - else if (segmentsLeft != 0) // wait for previous move to be taken so that GetCurrentUserPosition returns the correct value - { - return false; - } - reprap.GetMove()->GetCurrentUserPosition(moveBuffer.coords, 0, reprap.GetCurrentXAxes()); // make sure move buffer is up to date - bool ok = LoadMoveBufferFromGCode(gb, -1); - if (ok && includingAxes) - { -#if SUPPORT_ROLAND - if (reprap.GetRoland()->Active()) + const bool ok = LoadMoveBufferFromGCode(gb, -1); + if (ok && includingAxes) { - for(size_t axis = 0; axis < AXES; axis++) +#if SUPPORT_ROLAND + if (reprap.GetRoland()->Active()) { - if (!reprap.GetRoland()->ProcessG92(moveBuffer[axis], axis)) + for(size_t axis = 0; axis < AXES; axis++) { - return false; + if (!reprap.GetRoland()->ProcessG92(moveBuffer[axis], axis)) + { + return false; + } } } - } #endif - SetPositions(moveBuffer.coords); + SetPositions(moveBuffer.coords); + } } - return true; } @@ -2974,7 +2986,7 @@ void GCodes::StartToolChange(GCodeBuffer& gb, bool inM109) } // Retract or un-retract filament, returning true if movement has been queued, false if this needs to be called again -bool GCodes::RetractFilament(bool retract) +bool GCodes::RetractFilament(GCodeBuffer& gb, bool retract) { if (retractLength != 0.0 || retractHop != 0.0 || (!retract && retractExtra != 0.0)) { @@ -3008,7 +3020,8 @@ bool GCodes::RetractFilament(bool retract) moveBuffer.isFirmwareRetraction = true; moveBuffer.usePressureAdvance = false; - moveBuffer.filePos = filePos; + moveBuffer.filePos = (&gb == fileGCode) ? gb.MachineState().fileState.GetPosition() : noFilePosition; + moveBuffer.canPauseAfter = !retract; // don't pause after a retraction because that could cause too much retraction moveBuffer.xAxes = reprap.GetCurrentXAxes(); segmentsLeft = 1; } diff --git a/src/GCodes/GCodes.h b/src/GCodes/GCodes.h index eee3cd7..4c67738 100644 --- a/src/GCodes/GCodes.h +++ b/src/GCodes/GCodes.h @@ -65,12 +65,13 @@ public: float coords[DRIVES]; // new positions for the axes, amount of movement for the extruders float initialCoords[MAX_AXES]; // the initial positions of the axes float feedRate; // feed rate of this move - FilePosition filePos; // offset in the file being printed that this move was read from + FilePosition filePos; // offset in the file being printed at the end of reading this move uint32_t xAxes; // axes that X is mapped to EndstopChecks endStopsToCheck; // endstops to check uint8_t moveType; // the S parameter from the G0 or G1 command, 0 for a normal move bool isFirmwareRetraction; // true if this is a firmware retraction/un-retraction move bool usePressureAdvance; // true if we want to us extruder pressure advance, if there is any extrusion + bool canPauseAfter; // true if we can pause just after this move and successfully restart }; GCodes(Platform* p, Webserver* w); @@ -197,7 +198,7 @@ private: void SetAllAxesNotHomed(); // Flag all axes as not homed void SetPositions(float positionNow[DRIVES]); // Set the current position to be this const char *TranslateEndStopResult(EndStopHit es); // Translate end stop result to text - bool RetractFilament(bool retract); // Retract or un-retract filaments + bool RetractFilament(GCodeBuffer& gb, bool retract); // Retract or un-retract filaments bool ChangeMicrostepping(size_t drive, int microsteps, int mode) const; // Change microstepping on the specified drive void ListTriggers(StringRef reply, TriggerMask mask); // Append a list of trigger endstops to a message void CheckTriggers(); // Check for and execute triggers @@ -280,7 +281,6 @@ private: float simulationTime; // Accumulated simulation time uint8_t simulationMode; // 0 = not simulating, 1 = simulating, >1 are simulation modes for debugging - FilePosition filePos; // The position we got up to in the file being printed // Firmware retraction settings float retractLength, retractExtra; // retraction length and extra length to un-retract diff --git a/src/GCodes/GCodes2.cpp b/src/GCodes/GCodes2.cpp index 5c7ef14..3dd0c2d 100644 --- a/src/GCodes/GCodes2.cpp +++ b/src/GCodes/GCodes2.cpp @@ -133,7 +133,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) { return false; } - result = RetractFilament(true); + result = RetractFilament(gb, true); } break; @@ -142,7 +142,7 @@ bool GCodes::HandleGcode(GCodeBuffer& gb, StringRef& reply) { return false; } - result = RetractFilament(false); + result = RetractFilament(gb, false); break; case 20: // Inches (which century are we living in, here?) @@ -913,121 +913,127 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } else { - bool seen = false; Fan& fan = platform->GetFan(fanNum); - - if (gb.Seen('I')) // Invert cooling + if (!fan.IsEnabled()) { - const int invert = gb.GetIValue(); - if (invert < 0) + reply.printf("Fan number %d is disabled", fanNum); + } + else + { + bool seen = false; + if (gb.Seen('I')) // Invert cooling { - fan.Disable(); - } - else - { - fan.SetInverted(invert > 0); - } - seen = true; - } - - if (gb.Seen('F')) // Set PWM frequency - { - fan.SetPwmFrequency(gb.GetFValue()); - seen = true; - } - - if (gb.Seen('T')) // Set thermostatic trigger temperature - { - seen = true; - fan.SetTriggerTemperature(gb.GetFValue()); - } - - if (gb.Seen('B')) // Set blip time - { - seen = true; - fan.SetBlipTime(gb.GetFValue()); - } - - if (gb.Seen('L')) // Set minimum speed - { - seen = true; - fan.SetMinValue(gb.GetFValue()); - } - - if (gb.Seen('H')) // Set thermostatically-controller heaters - { - seen = true; - long heaters[HEATERS]; - size_t numH = HEATERS; - gb.GetLongArray(heaters, numH); - // Note that M106 H-1 disables thermostatic mode. The following code implements that automatically. - uint16_t hh = 0; - for (size_t h = 0; h < numH; ++h) - { - const int hnum = heaters[h]; - if (hnum >= 0 && hnum < HEATERS) + const int invert = gb.GetIValue(); + if (invert < 0) { - hh |= (1u << (unsigned int)hnum); + fan.Disable(); + } + else + { + fan.SetInverted(invert > 0); + } + seen = true; + } + + if (gb.Seen('F')) // Set PWM frequency + { + fan.SetPwmFrequency(gb.GetFValue()); + seen = true; + } + + if (gb.Seen('T')) // Set thermostatic trigger temperature + { + seen = true; + fan.SetTriggerTemperature(gb.GetFValue()); + } + + if (gb.Seen('B')) // Set blip time + { + seen = true; + fan.SetBlipTime(gb.GetFValue()); + } + + if (gb.Seen('L')) // Set minimum speed + { + seen = true; + fan.SetMinValue(gb.GetFValue()); + } + + if (gb.Seen('H')) // Set thermostatically-controller heaters + { + seen = true; + long heaters[HEATERS]; + size_t numH = HEATERS; + gb.GetLongArray(heaters, numH); + // Note that M106 H-1 disables thermostatic mode. The following code implements that automatically. + uint16_t hh = 0; + for (size_t h = 0; h < numH; ++h) + { + const int hnum = heaters[h]; + if (hnum >= 0 && hnum < HEATERS) + { + hh |= (1u << (unsigned int)hnum); + } + } + if (hh != 0) + { + platform->SetFanValue(fanNum, 1.0); // default the fan speed to full for safety + } + fan.SetHeatersMonitored(hh); + } + + if (gb.Seen('S')) // Set new fan value - process this after processing 'H' or it may not be acted on + { + const float f = constrain(gb.GetFValue(), 0.0, 255.0); + if (seen || seenFanNum) + { + platform->SetFanValue(fanNum, f); + } + else + { + // We are processing an M106 S### command with no other recognised parameters and we have a tool selected. + // Apply the fan speed setting to the fans in the fan mapping for the current tool. + lastDefaultFanSpeed = f; + SetMappedFanSpeed(); } } - if (hh != 0) + else if (gb.Seen('R')) { - platform->SetFanValue(fanNum, 1.0); // default the fan speed to full for safety - } - fan.SetHeatersMonitored(hh); - } - - if (gb.Seen('S')) // Set new fan value - process this after processing 'H' or it may not be acted on - { - const float f = constrain(gb.GetFValue(), 0.0, 255.0); - if (seen || seenFanNum) - { - platform->SetFanValue(fanNum, f); - } - else - { - // We are processing an M106 S### command with no other recognised parameters and we have a tool selected. - // Apply the fan speed setting to the fans in the fan mapping for the current tool. - lastDefaultFanSpeed = f; - SetMappedFanSpeed(); - } - } - else if (gb.Seen('R')) - { - const int i = gb.GetIValue(); - switch(i) - { - case 0: - case 1: - // Restore fan speed to value when print was paused - platform->SetFanValue(fanNum, pausedFanValues[fanNum]); - break; - case 2: - // Set the speeds of mapped fans to the last known value. Fan number is ignored. - SetMappedFanSpeed(); - break; - default: - break; - } - } - else if (!seen) - { - reply.printf("Fan%i frequency: %dHz, speed: %d%%, min: %d%%, blip: %.2f, inverted: %s", - fanNum, - (int)(fan.GetPwmFrequency()), - (int)(fan.GetValue() * 100.0), - (int)(fan.GetMinValue() * 100.0), - fan.GetBlipTime(), - (fan.GetInverted()) ? "yes" : "no"); - uint16_t hh = fan.GetHeatersMonitored(); - if (hh != 0) - { - reply.catf(", trigger: %dC, heaters:", (int)fan.GetTriggerTemperature()); - for (unsigned int i = 0; i < HEATERS; ++i) + const int i = gb.GetIValue(); + switch(i) { - if ((hh & (1u << i)) != 0) + case 0: + case 1: + // Restore fan speed to value when print was paused + platform->SetFanValue(fanNum, pausedFanValues[fanNum]); + break; + case 2: + // Set the speeds of mapped fans to the last known value. Fan number is ignored. + SetMappedFanSpeed(); + break; + default: + break; + } + } + else if (!seen) + { + reply.printf("Fan%i frequency: %dHz, speed: %d%%, min: %d%%, blip: %.2f, inverted: %s", + fanNum, + (int)(fan.GetPwmFrequency()), + (int)(fan.GetValue() * 100.0), + (int)(fan.GetMinValue() * 100.0), + fan.GetBlipTime(), + (fan.GetInverted()) ? "yes" : "no"); + uint16_t hh = fan.GetHeatersMonitored(); + if (hh != 0) + { + reply.catf(", trigger: %dC, heaters:", (int)fan.GetTriggerTemperature()); + for (unsigned int i = 0; i < HEATERS; ++i) { - reply.catf(" %u", i); + if ((hh & (1u << i)) != 0) + { + reply.catf(" %u", i); + } } } } @@ -1491,13 +1497,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } } - if (gb.Seen('P')) - { - // Set max average printing acceleration - platform->SetMaxAverageAcceleration(gb.GetFValue() * distanceScale); - seen = true; - } - if (!seen) { reply.printf("Accelerations: "); @@ -1512,7 +1511,6 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) reply.catf("%c%.1f", sep, platform->Acceleration(extruder + numAxes) / distanceScale); sep = ':'; } - reply.catf(", avg. printing: %.1f", platform->GetMaxAverageAcceleration()); } } break; @@ -2591,6 +2589,10 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } seen = true; } + if (gb.Seen('F')) + { + platform->SetExtrusionAncilliaryPwmFrequency(gb.GetFValue()); + } if (gb.Seen('S')) { platform->SetExtrusionAncilliaryPwmValue(gb.GetFValue()); @@ -2598,8 +2600,10 @@ bool GCodes::HandleMcode(GCodeBuffer& gb, StringRef& reply) } if (!seen) { - reply.printf("Extrusion ancillary PWM %.3f on pin %u", - platform->GetExtrusionAncilliaryPwmValue(), platform->GetExtrusionAncilliaryPwmPin()); + reply.printf("Extrusion ancillary PWM %.3f at %.1fHz on pin %u", + platform->GetExtrusionAncilliaryPwmValue(), + platform->GetExtrusionAncilliaryPwmFrequency(), + platform->GetExtrusionAncilliaryPwmPin()); } } break; diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp index a42ac64..6456d6e 100644 --- a/src/Movement/DDA.cpp +++ b/src/Movement/DDA.cpp @@ -240,6 +240,7 @@ bool DDA::Init(const GCodes::RawMove &nextMove, bool doMotorMapping) // 3. Store some values endStopsToCheck = nextMove.endStopsToCheck; + canPauseAfter = nextMove.canPauseAfter; filePos = nextMove.filePos; usePressureAdvance = nextMove.usePressureAdvance; hadLookaheadUnderrun = false; @@ -564,15 +565,14 @@ void DDA::RecalculateMove() topSpeed = requestedSpeed; } - canPause = (endStopsToCheck == 0); - if (canPause && endSpeed != 0.0) + if (canPauseAfter && endSpeed != 0.0) { const Platform * const p = reprap.GetPlatform(); for (size_t drive = 0; drive < DRIVES; ++drive) { if (ddm[drive].state == DMState::moving && endSpeed * fabsf(directionVector[drive]) > p->ActualInstantDv(drive)) { - canPause = false; + canPauseAfter = false; break; } } @@ -724,44 +724,6 @@ void DDA::Prepare() float decelStartTime = accelStopTime + (params.decelStartDistance - accelDistance)/topSpeed; float totalTime = decelStartTime + (topSpeed - endSpeed)/acceleration; - // Enforce the maximum average acceleration - if (isPrintingMove && topSpeed > startSpeed && topSpeed > endSpeed) - { - const float maxAverageAcceleration = reprap.GetPlatform()->GetMaxAverageAcceleration(); - if (2 * topSpeed - startSpeed - endSpeed > totalTime * maxAverageAcceleration) - { - // Reduce the top speed to comply with the maximum average acceleration - const float a2 = fsquare(acceleration); - const float am2 = fsquare(maxAverageAcceleration); - const float aam = acceleration * maxAverageAcceleration; - const float discriminant = (a2 + (2 * aam) - am2) * (fsquare(startSpeed) + fsquare(endSpeed)) - + (2 * a2 + 2 * am2 - 4 * aam) * startSpeed * endSpeed - + (8 * a2 * maxAverageAcceleration - 4 * acceleration * am2) * totalDistance; - const float oldTopSpeed = topSpeed; - if (discriminant < 0.0) - { - topSpeed = max(startSpeed, endSpeed); - } - else - { - const float temp = (sqrtf(discriminant) + (acceleration - maxAverageAcceleration) * (startSpeed + endSpeed)) - /(4 * acceleration - 2 * maxAverageAcceleration); - topSpeed = max(max(temp, startSpeed), endSpeed); - } - - //DEBUG - debugPrintf("ots %f nts %f ss %f es %f\n", oldTopSpeed, topSpeed, startSpeed, endSpeed); - - // Recalculate parameters - accelDistance = (fsquare(topSpeed) - fsquare(startSpeed))/(2 * acceleration); - decelDistance = (fsquare(topSpeed) - fsquare(endSpeed))/(2 * acceleration); - params.decelStartDistance = totalDistance - decelDistance; - accelStopTime = (topSpeed - startSpeed)/acceleration; - decelStartTime = accelStopTime + (params.decelStartDistance - accelDistance)/topSpeed; - totalTime = decelStartTime + (topSpeed - endSpeed)/acceleration; - } - } - clocksNeeded = (uint32_t)(totalTime * stepClockRate); params.startSpeedTimesCdivA = (uint32_t)((startSpeed * stepClockRate)/acceleration); diff --git a/src/Movement/DDA.h b/src/Movement/DDA.h index 642a152..e0ceb1d 100644 --- a/src/Movement/DDA.h +++ b/src/Movement/DDA.h @@ -47,7 +47,7 @@ public: void Prepare(); // Calculate all the values and freeze this DDA float CalcTime() const; // Calculate the time needed for this move (used for simulation) bool HasStepError() const; - bool CanPause() const { return canPause; } + bool CanPauseAfter() const { return canPauseAfter; } bool IsPrintingMove() const { return isPrintingMove; } // Return true if this involves both XY movement and extrusion DDAState GetState() const { return state; } @@ -117,7 +117,7 @@ private: volatile DDAState state; // what state this DDA is in uint8_t endCoordinatesValid : 1; // True if endCoordinates can be relied on uint8_t isDeltaMovement : 1; // True if this is a delta printer movement - uint8_t canPause : 1; // True if we can pause at the end of this move + uint8_t canPauseAfter : 1; // True if we can pause at the end of this move uint8_t goingSlow : 1; // True if we have reduced speed during homing uint8_t isPrintingMove : 1; // True if this move includes XY movement and extrusion uint8_t usePressureAdvance : 1; // True if pressure advance should be applied to any forward extrusion diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp index e9ed842..278ecc8 100644 --- a/src/Movement/Move.cpp +++ b/src/Movement/Move.cpp @@ -46,6 +46,7 @@ void Move::Init() currentDda = nullptr; addNoMoreMoves = false; + babysteppingLeft = 0.0; stepErrors = 0; numLookaheadUnderruns = numPrepareUnderruns = 0; @@ -311,11 +312,13 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui const DDA *savedDdaRingAddPointer = ddaRingAddPointer; cpu_irq_disable(); DDA *dda = currentDda; + FilePosition fPos = noFilePosition; if (dda != nullptr) { // A move is being executed. See if we can safely pause at the end of it. - if (dda->CanPause()) + if (dda->CanPauseAfter()) { + fPos = dda->GetFilePosition(); ddaRingAddPointer = dda->GetNext(); } else @@ -325,8 +328,9 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui dda = ddaRingGetPointer; while (dda != ddaRingAddPointer) { - if (dda->CanPause()) + if (dda->CanPauseAfter()) { + fPos = dda->GetFilePosition(); ddaRingAddPointer = dda->GetNext(); if (ddaRingAddPointer->GetState() == DDA::frozen) { @@ -347,8 +351,6 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui cpu_irq_enable(); - FilePosition fPos = noFilePosition; - if (ddaRingAddPointer != savedDdaRingAddPointer) { const size_t numAxes = reprap.GetGCodes()->GetNumAxes(); @@ -372,10 +374,6 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui { positions[drive] += dda->GetEndCoordinate(drive, true); // update the amount of extrusion we are going to skip } - if (fPos == noFilePosition) - { - fPos = dda->GetFilePosition(); - } (void)dda->Free(); dda = dda->GetNext(); } @@ -389,6 +387,13 @@ FilePosition Move::PausePrint(float positions[DRIVES], float& pausedFeedRate, ui return fPos; } +// Request babystepping +void Move::Babystep(float zMovement) +{ + babysteppingLeft += zMovement; + // TODO use this value somewhere +} + uint32_t maxReps = 0; #if 0 @@ -402,7 +407,7 @@ void Move::Diagnostics(MessageType mtype) Platform * const p = reprap.GetPlatform(); p->Message(mtype, "=== Move ===\n"); p->MessageF(mtype, "MaxReps: %u, StepErrors: %u, MaxWait: %ums, Underruns: %u, %u\n", - maxReps, stepErrors, longestGcodeWaitInterval, numLookaheadUnderruns, numPrepareUnderruns); + maxReps, stepErrors, longestGcodeWaitInterval, numLookaheadUnderruns, numPrepareUnderruns); maxReps = 0; numLookaheadUnderruns = numPrepareUnderruns = 0; longestGcodeWaitInterval = 0; diff --git a/src/Movement/Move.h b/src/Movement/Move.h index 698fae2..ad94626 100644 --- a/src/Movement/Move.h +++ b/src/Movement/Move.h @@ -117,6 +117,7 @@ public: void UseHeightMap(bool b) { useGridHeights = b; } // Start or stop using the height map bool UsingHeightMap() const { return useGridHeights; } // Are we doing grid bed compensation? + void Babystep(float zMovement); // Request babystepping private: @@ -192,6 +193,8 @@ private: int coreXYMode; // 0 = Cartesian, 1 = CoreXY, 2 = CoreXZ, 3 = CoreYZ float axisFactors[MAX_AXES]; // How much further the motors need to move for each axis movement, on a CoreXY/CoreXZ/CoreYZ machine unsigned int stepErrors; // count of step errors, for diagnostics + + float babysteppingLeft; // the amount of Z babystepping left to do }; //****************************************************************************************************** diff --git a/src/Platform.cpp b/src/Platform.cpp index 3fb43cf..95ca1ed 100644 --- a/src/Platform.cpp +++ b/src/Platform.cpp @@ -227,8 +227,6 @@ void Platform::Init() stepperDacVoltageOffset = STEPPER_DAC_VOLTAGE_OFFSET; #endif - maxAverageAcceleration = 10000.0; // high enough to have no effect until it is changed - // Z PROBE zProbePin = Z_PROBE_PIN; zProbeAdcChannel = PinToAdcChannel(zProbePin); @@ -326,12 +324,17 @@ void Platform::Init() TMC2660::Init(ENABLE_PINS, numTMC2660Drivers); #endif + // Allow extrusion ancilliary PWM to use FAN0 even if FAN0 has not been disabled, for backwards compatibility extrusionAncilliaryPwmValue = 0.0; - extrusionAncilliaryPwmLogicalPin = -1; - extrusionAncilliaryPwmFirmwarePin = NoPin; - extrusionAncilliaryPwmInvert = false; - SetExtrusionAncilliaryPwmPin(Fan0LogicalPin); - + extrusionAncilliaryPwmFrequency = DefaultPinWritePwmFreq; + extrusionAncilliaryPwmLogicalPin = Fan0LogicalPin; + extrusionAncilliaryPwmFirmwarePin = COOLING_FAN_PINS[0]; + extrusionAncilliaryPwmInvert = +#if defined(DUET_NG) || defined(__RADDS__) + false; +#else + (board == BoardType::Duet_06 || board == BoardType::Duet_07); +#endif ARRAY_INIT(tempSensePins, TEMP_SENSE_PINS); ARRAY_INIT(heatOnPins, HEAT_ON_PINS); ARRAY_INIT(spiTempSenseCsPins, SpiTempSensorCsPins); @@ -1243,14 +1246,41 @@ void Platform::SoftwareReset(uint16_t reason) reason |= (uint16_t)reprap.GetSpinningModule(); // Record the reason for the software reset - SoftwareResetData temp; - temp.magic = SoftwareResetData::magicValue; - temp.version = SoftwareResetData::versionValue; - temp.resetReason = reason; - GetStackUsage(NULL, NULL, &temp.neverUsedRam); + // First find a free slot (wear levelling) + size_t slot = SoftwareResetData::numberOfSlots; + SoftwareResetData srdBuf[SoftwareResetData::numberOfSlots]; - // Save diagnostics data to Flash and reset the software - DueFlashStorage::write(SoftwareResetData::nvAddress, &temp, sizeof(SoftwareResetData)); +#ifdef DUET_NG + if (flash_read_user_signature(reinterpret_cast(srdBuf), sizeof(srdBuf)/sizeof(uint32_t)) == FLASH_RC_OK) +#else + DueFlashStorage::read(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf)); +#endif + { + while (slot != 0 && srdBuf[slot - 1].isVacant()) + { + --slot; + } + } + + if (slot == SoftwareResetData::numberOfSlots) + { + // No free slots, so erase the area +#ifdef DUET_NG + flash_erase_user_signature(); +#endif + memset(srdBuf, 0xFF, sizeof(srdBuf)); + slot = 0; + } + srdBuf[slot].magic = SoftwareResetData::magicValue; + srdBuf[slot].resetReason = reason; + GetStackUsage(NULL, NULL, &srdBuf[slot].neverUsedRam); + + // Save diagnostics data to Flash +#ifdef DUET_NG + flash_write_user_signature(srdBuf, sizeof(srdBuf)/sizeof(uint32_t)); +#else + DueFlashStorage::write(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf)); +#endif } Reset(); for(;;) {} @@ -1365,7 +1395,7 @@ void Platform::Diagnostics(MessageType mtype) MessageF(mtype, "Program static ram used: %d\n", &_end - ramstart); MessageF(mtype, "Dynamic ram used: %d\n", mi.uordblks); MessageF(mtype, "Recycled dynamic ram: %d\n", mi.fordblks); - size_t currentStack, maxStack, neverUsed; + uint32_t currentStack, maxStack, neverUsed; GetStackUsage(¤tStack, &maxStack, &neverUsed); MessageF(mtype, "Current stack ram used: %d\n", currentStack); MessageF(mtype, "Maximum stack ram used: %d\n", maxStack); @@ -1373,20 +1403,40 @@ void Platform::Diagnostics(MessageType mtype) // Show the up time and reason for the last reset const uint32_t now = (uint32_t)Time(); // get up time in seconds - const char* resetReasons[8] = { "power up", "backup", "watchdog", "software", "external", "?", "?", "?" }; + const char* resetReasons[8] = { "power up", "backup", "watchdog", "software", "reset button", "?", "?", "?" }; MessageF(mtype, "Last reset %02d:%02d:%02d ago, cause: %s\n", (unsigned int)(now/3600), (unsigned int)((now % 3600)/60), (unsigned int)(now % 60), resetReasons[(REG_RSTC_SR & RSTC_SR_RSTTYP_Msk) >> RSTC_SR_RSTTYP_Pos]); // Show the reset code stored at the last software reset + Message(mtype, "Last software reset code & available RAM: "); { - SoftwareResetData temp; - temp.magic = 0; - DueFlashStorage::read(SoftwareResetData::nvAddress, &temp, sizeof(SoftwareResetData)); - if (temp.magic == SoftwareResetData::magicValue && temp.version == SoftwareResetData::versionValue) + SoftwareResetData srdBuf[SoftwareResetData::numberOfSlots]; + memset(srdBuf, 0, sizeof(srdBuf)); + int slot = -1; + +#ifdef DUET_NG + if (flash_read_user_signature(reinterpret_cast(srdBuf), sizeof(srdBuf)/sizeof(uint32_t)) == FLASH_RC_OK) +#else + DueFlashStorage::read(SoftwareResetData::nvAddress, srdBuf, sizeof(srdBuf)); +#endif { - MessageF(mtype, "Last software reset code & available RAM: 0x%04x, %u\n", temp.resetReason, temp.neverUsedRam); - MessageF(mtype, "Spinning module during software reset: %s\n", moduleName[temp.resetReason & 0x0F]); + // Find the last slot written + slot = SoftwareResetData::numberOfSlots - 1; + while (slot >= 0 && srdBuf[slot].magic == 0xFFFF) + { + --slot; + } + } + + if (slot >= 0 && srdBuf[slot].magic == SoftwareResetData::magicValue) + { + MessageF(mtype, "0x%04x, %u (slot %d)\n", srdBuf[slot].resetReason, srdBuf[slot].neverUsedRam, slot); + MessageF(mtype, "Spinning module during software reset: %s\n", moduleName[srdBuf[slot].resetReason & 0x0F]); + } + else + { + Message(mtype, "not available\n"); } } @@ -1447,13 +1497,18 @@ void Platform::Diagnostics(MessageType mtype) #endif // Show current RTC time + Message(mtype, "Current date and time: "); struct tm timeInfo; if (gmtime_r(&realTime, &timeInfo) != nullptr) { - MessageF(mtype, "Current date and time: %04u-%02u-%02u %02u:%02u:%02u\n", + MessageF(mtype, "%04u-%02u-%02u %02u:%02u:%02u\n", timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday, timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec); } + else + { + Message(mtype, "clock not set\n"); + } // Debug //MessageF(mtype, "TC_FMR = %08x, PWM_FPE = %08x, PWM_FSR = %08x\n", TC2->TC_FMR, PWM->PWM_FPE, PWM->PWM_FSR); @@ -1503,24 +1558,24 @@ void Platform::DiagnosticTest(int d) } // Return the stack usage and amount of memory that has never been used, in bytes -void Platform::GetStackUsage(size_t* currentStack, size_t* maxStack, size_t* neverUsed) const +void Platform::GetStackUsage(uint32_t* currentStack, uint32_t* maxStack, uint32_t* neverUsed) const { - const char *ramend = (const char *) -#ifdef DUET_NG - 0x20020000; // 0x20000000 + 128Kb + const char * const ramend = (const char *) +#if SAM4E + IRAM_ADDR + IRAM_SIZE; #else - 0x20088000; // 0x20070000 + 96Kb + IRAM0_ADDR + IRAM_SIZE; #endif register const char * stack_ptr asm ("sp"); - const char *heapend = sbrk(0); - const char* stack_lwm = heapend; + const char * const heapend = sbrk(0); + const char * stack_lwm = heapend; while (stack_lwm < stack_ptr && *stack_lwm == memPattern) { ++stack_lwm; } - if (currentStack) { *currentStack = ramend - stack_ptr; } - if (maxStack) { *maxStack = ramend - stack_lwm; } - if (neverUsed) { *neverUsed = stack_lwm - heapend; } + if (currentStack != nullptr) { *currentStack = ramend - stack_ptr; } + if (maxStack != nullptr) { *maxStack = ramend - stack_lwm; } + if (neverUsed != nullptr) { *neverUsed = stack_lwm - heapend; } } void Platform::ClassReport(float &lastTime) @@ -2541,11 +2596,11 @@ void Platform::SetBoardType(BoardType bt) #else // Determine whether this is a Duet 0.6 or a Duet 0.8.5 board. // If it is a 0.85 board then DAC0 (AKA digital pin 67) is connected to ground via a diode and a 2.15K resistor. - // So we enable the pullup (value 150K-150K) on pin 67 and read it, expecting a LOW on a 0.8.5 board and a HIGH on a 0.6 board. + // So we enable the pullup (value 100K-150K) on pin 67 and read it, expecting a LOW on a 0.8.5 board and a HIGH on a 0.6 board. // This may fail if anyone connects a load to the DAC0 pin on a Duet 0.6, hence we implement board selection in M115 as well. pinMode(Dac0DigitalPin, INPUT_PULLUP); board = (digitalRead(Dac0DigitalPin)) ? BoardType::Duet_06 : BoardType::Duet_085; - pinMode(Dac0DigitalPin, INPUT); // turn pullup off + pinMode(Dac0DigitalPin, INPUT); // turn pullup off #endif } else @@ -2628,6 +2683,9 @@ bool Platform::GetFirmwarePin(int logicalPin, PinAccess access, Pin& firmwarePin ) { firmwarePin = COOLING_FAN_PINS[logicalPin - Fan0LogicalPin]; +#if !defined(DUET_NG) && !defined(__RADDS__) + invert = (board == BoardType::Duet_06 || board == BoardType::Duet_07); +#endif } } else if (logicalPin >= EndstopXLogicalPin && logicalPin < EndstopXLogicalPin + (int)ARRAY_SIZE(endStopPins)) // pins 40-49 correspond to endstop pins diff --git a/src/Platform.h b/src/Platform.h index 8c9af3c..86668c7 100644 --- a/src/Platform.h +++ b/src/Platform.h @@ -509,10 +509,6 @@ public: float Acceleration(size_t drive) const; const float* Accelerations() const; void SetAcceleration(size_t drive, float value); - const float GetMaxAverageAcceleration() const - { return maxAverageAcceleration; } - void SetMaxAverageAcceleration(float f) - { maxAverageAcceleration = f; } float MaxFeedrate(size_t drive) const; const float* MaxFeedrates() const; void SetMaxFeedrate(size_t drive, float value); @@ -567,6 +563,8 @@ public: void SetExtrusionAncilliaryPwmValue(float v); float GetExtrusionAncilliaryPwmValue() const; + void SetExtrusionAncilliaryPwmFrequency(float f); + float GetExtrusionAncilliaryPwmFrequency() const; bool SetExtrusionAncilliaryPwmPin(int logicalPin); int GetExtrusionAncilliaryPwmPin() const { return extrusionAncilliaryPwmLogicalPin; } void ExtrudeOn(); @@ -681,31 +679,41 @@ private: static float AdcReadingToPowerVoltage(uint16_t reading); #endif - // These are the structures used to hold out non-volatile data. - // The SAM3X doesn't have EEPROM so we save the data to flash. This unfortunately means that it gets cleared + // These are the structures used to hold our non-volatile data. + // The SAM3X and SAM4E don't have EEPROM so we save the data to flash. This unfortunately means that it gets cleared // every time we reprogram the firmware via bossa, but it can be retained when firmware updates are performed // via the web interface. That's why it's a good idea to implement versioning here - increase these values // whenever the fields of the following structs have changed. - + // + // The SAM4E has a large page erase size (8K). For this reason we store the software reset data in the 512-byte user signature area + // instead, which doesn't get cleared when the Erase button is pressed. The SoftareResetData struct must have at least one 32-bit + // field to guarantee that values of this type will be 32-bit aligned. It must have no virtual members because it is read/written + // directly form/to flash memory. struct SoftwareResetData { - static const uint16_t magicValue = 0x7C5F; // value we use to recognise that all the flash data has been written - static const uint16_t versionValue = 1; // increment this whenever this struct changes + static const uint16_t versionValue = 2; // increment this whenever this struct changes + static const uint16_t magicValue = 0x7D00 | versionValue; // value we use to recognise that all the flash data has been written static const uint32_t nvAddress = 0; // must be 4-byte aligned + static const size_t numberOfSlots = 8; // number of storage slots used to implement wear levelling - uint16_t magic; - uint16_t version; - + uint16_t magic; // the magic number, including the version uint16_t resetReason; // this records why we did a software reset, for diagnostic purposes - uint16_t dummy; // padding to align the next field (should happen automatically I think) - size_t neverUsedRam; // the amount of never used RAM at the last abnormal software reset + uint32_t neverUsedRam; // the amount of never used RAM at the last abnormal software reset + + bool isVacant() const // return true if this struct can be written without erasing it first + { + return magic == 0xFFFF && resetReason == 0xFFFF && neverUsedRam == 0xFFFFFFFF; + } }; + static_assert(SoftwareResetData::numberOfSlots * sizeof(SoftwareResetData) <= 512, "Can't fit software reset data in SAM4E user signature area"); + + // The following struct is due to be replaced by the config-override.g file. struct FlashData { static const uint16_t magicValue = 0xE6C4; // value we use to recognise that the flash data has been written static const uint16_t versionValue = 5; // increment this whenever this struct changes - static const uint32_t nvAddress = (SoftwareResetData::nvAddress + sizeof(SoftwareResetData) + 3) & (~3); + static const uint32_t nvAddress = SoftwareResetData::nvAddress + (SoftwareResetData::numberOfSlots * sizeof(SoftwareResetData)); uint16_t magic; uint16_t version; @@ -742,7 +750,7 @@ private: uint32_t errorCodeBits; void InitialiseInterrupts(); - void GetStackUsage(size_t* currentStack, size_t* maxStack, size_t* neverUsed) const; + void GetStackUsage(uint32_t* currentStack, uint32_t* maxStack, uint32_t* neverUsed) const; // DRIVES @@ -770,7 +778,6 @@ private: uint32_t slowDriverStepPulseClocks; // minimum high and low step pulse widths, in processor clocks uint32_t slowDrivers; // bitmap of driver port bits that need extended step pulse timing float idleCurrentFactor; - float maxAverageAcceleration; #if defined(DUET_NG) size_t numTMC2660Drivers; // the number of TMC2660 drivers we have, the remaining are simple enable/step/dir drivers @@ -793,6 +800,7 @@ private: volatile ThermistorAveragingFilter thermistorFilters[HEATERS]; // bed and extruder thermistor readings float extrusionAncilliaryPwmValue; + float extrusionAncilliaryPwmFrequency; int extrusionAncilliaryPwmLogicalPin; Pin extrusionAncilliaryPwmFirmwarePin; bool extrusionAncilliaryPwmInvert; @@ -1125,6 +1133,16 @@ inline float Platform::GetExtrusionAncilliaryPwmValue() const return extrusionAncilliaryPwmValue; } +inline void Platform::SetExtrusionAncilliaryPwmFrequency(float f) +{ + extrusionAncilliaryPwmFrequency = f; +} + +inline float Platform::GetExtrusionAncilliaryPwmFrequency() const +{ + return extrusionAncilliaryPwmFrequency; +} + // For the Duet we use the fan output for this // DC 2015-03-21: To allow users to control the cooling fan via gcodes generated by slic3r etc., // only turn the fan on/off if the extruder ancilliary PWM has been set nonzero. @@ -1133,7 +1151,8 @@ inline void Platform::ExtrudeOn() { if (extrusionAncilliaryPwmValue > 0.0) { - WriteAnalog(extrusionAncilliaryPwmFirmwarePin, extrusionAncilliaryPwmValue, DefaultPinWritePwmFreq); + WriteAnalog(extrusionAncilliaryPwmFirmwarePin, + (extrusionAncilliaryPwmInvert) ? 1.0 - extrusionAncilliaryPwmValue : extrusionAncilliaryPwmValue, extrusionAncilliaryPwmFrequency); } } @@ -1144,7 +1163,8 @@ inline void Platform::ExtrudeOff() { if (extrusionAncilliaryPwmValue > 0.0) { - WriteAnalog(extrusionAncilliaryPwmFirmwarePin, 0.0, DefaultPinWritePwmFreq); + WriteAnalog(extrusionAncilliaryPwmFirmwarePin, + (extrusionAncilliaryPwmInvert) ? 1.0 : 0.0, extrusionAncilliaryPwmFrequency); } } diff --git a/src/Tool.cpp b/src/Tool.cpp index 5c6dd0b..e5e4bcb 100644 --- a/src/Tool.cpp +++ b/src/Tool.cpp @@ -306,15 +306,22 @@ void Tool::SetVariables(const float* standby, const float* active) else { const float temperatureLimit = reprap.GetHeat()->GetTemperatureLimit(heaters[heater]); + const Tool * const currentTool = reprap.GetCurrentTool(); if (active[heater] < temperatureLimit) { activeTemperatures[heater] = active[heater]; - reprap.GetHeat()->SetActiveTemperature(heaters[heater], activeTemperatures[heater]); + if (currentTool == nullptr || currentTool == this) + { + reprap.GetHeat()->SetActiveTemperature(heaters[heater], activeTemperatures[heater]); + } } if (standby[heater] < temperatureLimit) { standbyTemperatures[heater] = standby[heater]; - reprap.GetHeat()->SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]); + if (currentTool == nullptr || currentTool == this) + { + reprap.GetHeat()->SetStandbyTemperature(heaters[heater], standbyTemperatures[heater]); + } } } }