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/Movement/Move.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/Movement/Move.cpp')
-rw-r--r-- | src/Movement/Move.cpp | 283 |
1 files changed, 181 insertions, 102 deletions
diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp index 8096ae79..4304db5d 100644 --- a/src/Movement/Move.cpp +++ b/src/Movement/Move.cpp @@ -7,13 +7,12 @@ #include "Move.h" #include "Platform.h" -#include "RepRap.h" -#include "Kinematics/LinearDeltaKinematics.h" // temporary -Move::Move() : currentDda(nullptr), scheduledMoves(0), completedMoves(0) -{ - active = false; +static constexpr uint32_t UsualMinimumPreparedTime = DDA::stepClockRate/10; // 100ms +static constexpr uint32_t AbsoluteMinimumPreparedTime = DDA::stepClockRate/20; // 50ms +Move::Move() : currentDda(nullptr), active(false), scheduledMoves(0), completedMoves(0) +{ kinematics = Kinematics::Create(KinematicsType::cartesian); // default to Cartesian // Build the DDA ring @@ -21,7 +20,7 @@ Move::Move() : currentDda(nullptr), scheduledMoves(0), completedMoves(0) ddaRingGetPointer = ddaRingAddPointer = dda; for (size_t i = 1; i < DdaRingLength; i++) { - DDA *oldDda = dda; + DDA * const oldDda = dda; dda = new DDA(dda); oldDda->SetPrevious(dda); } @@ -74,13 +73,13 @@ void Move::Init() longWait = millis(); idleTimeout = DefaultIdleTimeout; - iState = IdleState::idle; + moveState = MoveState::idle; idleCount = 0; simulationMode = 0; simulationTime = 0.0; longestGcodeWaitInterval = 0; - waitingForMove = specialMoveAvailable = false; + specialMoveAvailable = false; active = true; } @@ -119,9 +118,10 @@ void Move::Spin() ++idleCount; } - // Check for DDA errors to print if Move debug is enabled + // Recycle the DDAs for completed moves, checking for DDA errors to print if Move debug is enabled while (ddaRingCheckPointer->GetState() == DDA::completed) { + // Check for step errors and record/print them if we have any, before we lose the DMs if (ddaRingCheckPointer->HasStepError()) { if (reprap.Debug(moduleMove)) @@ -131,6 +131,8 @@ void Move::Spin() ++stepErrors; reprap.GetPlatform().LogError(ErrorCode::BadMove); } + + // Now release the DMs and check for underrun if (ddaRingCheckPointer->Free()) { ++numLookaheadUnderruns; @@ -139,21 +141,21 @@ void Move::Spin() } // See if we can add another move to the ring - if ( + bool canAddMove = ( #if SUPPORT_ROLAND - !reprap.GetRoland()->Active() && + !reprap.GetRoland()->Active() && #endif - ddaRingAddPointer->GetState() == DDA::empty - && ddaRingAddPointer->GetNext()->GetState() != DDA::provisional // function Prepare needs to access the endpoints in the previous move, so don't change them - && DriveMovement::NumFree() >= (int)DRIVES // check that we won't run out of DMs - ) + ddaRingAddPointer->GetState() == DDA::empty + && ddaRingAddPointer->GetNext()->GetState() != DDA::provisional // function Prepare needs to access the endpoints in the previous move, so don't change them + && DriveMovement::NumFree() >= (int)DRIVES // check that we won't run out of DMs + ); + if (canAddMove) { // In order to react faster to speed and extrusion rate changes, only add more moves if the total duration of - // all un-frozen moves is less than 2 seconds, or the total duration of all but the first un-frozen move is - // less than 0.5 seconds. + // all un-frozen moves is less than 2 seconds, or the total duration of all but the first un-frozen move is less than 0.5 seconds. const DDA *dda = ddaRingAddPointer; - float unPreparedTime = 0.0; - float prevMoveTime = 0.0; + uint32_t unPreparedTime = 0; + uint32_t prevMoveTime = 0; for(;;) { dda = dda->GetPrevious(); @@ -162,119 +164,124 @@ void Move::Spin() break; } unPreparedTime += prevMoveTime; - prevMoveTime = dda->CalcTime(); + prevMoveTime = dda->GetClocksNeeded(); } - if (unPreparedTime < 0.5 || unPreparedTime + prevMoveTime < 2.0) + canAddMove = (unPreparedTime < DDA::stepClockRate/2 || unPreparedTime + prevMoveTime < 2 * DDA::stepClockRate); + } + + if (canAddMove) + { + // OK to add another move. First check if a special move is available. + if (specialMoveAvailable) { - // First check for a special move - if (specialMoveAvailable) + if (simulationMode < 2) { - if (simulationMode < 2) + if (ddaRingAddPointer->Init(specialMoveCoords)) { - if (ddaRingAddPointer->Init(specialMoveCoords)) + ddaRingAddPointer = ddaRingAddPointer->GetNext(); + if (moveState == MoveState::idle || moveState == MoveState::timing) { - ddaRingAddPointer = ddaRingAddPointer->GetNext(); - idleCount = 0; + // We were previously idle, so we have a state change + moveState = MoveState::collecting; + const uint32_t now = millis(); + const uint32_t timeWaiting = now - lastStateChangeTime; + if (timeWaiting > longestGcodeWaitInterval) + { + longestGcodeWaitInterval = timeWaiting; + } + lastStateChangeTime = now; } } - specialMoveAvailable = false; } - else + specialMoveAvailable = false; + } + else + { + // If there's a G Code move available, add it to the DDA ring for processing. + GCodes::RawMove nextMove; + if (reprap.GetGCodes().ReadMove(nextMove)) // if we have a new move { - // If there's a G Code move available, add it to the DDA ring for processing. - GCodes::RawMove nextMove; - if (reprap.GetGCodes().ReadMove(nextMove)) // if we have a new move + if (simulationMode < 2) // in simulation mode 2 and higher, we don't process incoming moves beyond this point { - if (waitingForMove) +#if 0 // disabled this because it causes jerky movements on the SCARA printer + // Add on the extrusion left over from last time. + const size_t numAxes = reprap.GetGCodes().GetTotalAxes(); + for (size_t drive = numAxes; drive < DRIVES; ++drive) { - waitingForMove = false; - const uint32_t timeWaiting = millis() - gcodeWaitStartTime; - if (timeWaiting > longestGcodeWaitInterval) - { - longestGcodeWaitInterval = timeWaiting; - } + nextMove.coords[drive] += extrusionPending[drive - numAxes]; } - if (simulationMode < 2) // in simulation mode 2 and higher, we don't process incoming moves beyond this point - { -#if 0 // disabled this because it causes jerky movements on the SCARA printer - // Add on the extrusion left over from last time. - const size_t numAxes = reprap.GetGCodes().GetTotalAxes(); - for (size_t drive = numAxes; drive < DRIVES; ++drive) - { - nextMove.coords[drive] += extrusionPending[drive - numAxes]; - } #endif - if (nextMove.moveType == 0) - { - AxisAndBedTransform(nextMove.coords, nextMove.xAxes, nextMove.yAxes, true); - } - if (ddaRingAddPointer->Init(nextMove, !IsRawMotorMove(nextMove.moveType))) - { - ddaRingAddPointer = ddaRingAddPointer->GetNext(); - idleCount = 0; - scheduledMoves++; - } -#if 0 // see above - // Save the amount of extrusion not done - for (size_t drive = numAxes; drive < DRIVES; ++drive) + if (nextMove.moveType == 0) + { + AxisAndBedTransform(nextMove.coords, nextMove.xAxes, nextMove.yAxes, true); + } + if (ddaRingAddPointer->Init(nextMove, !IsRawMotorMove(nextMove.moveType))) + { + ddaRingAddPointer = ddaRingAddPointer->GetNext(); + idleCount = 0; + scheduledMoves++; + if (moveState == MoveState::idle || moveState == MoveState::timing) { - extrusionPending[drive - numAxes] = nextMove.coords[drive]; + moveState = MoveState::collecting; + const uint32_t now = millis(); + const uint32_t timeWaiting = now - lastStateChangeTime; + if (timeWaiting > longestGcodeWaitInterval) + { + longestGcodeWaitInterval = timeWaiting; + } + lastStateChangeTime = now; } -#endif } - } - else - { - // We wanted another move, but none was available - if (currentDda != nullptr && !waitingForMove) +#if 0 // see above + // Save the amount of extrusion not done + for (size_t drive = numAxes; drive < DRIVES; ++drive) { - gcodeWaitStartTime = millis(); - waitingForMove = true; + extrusionPending[drive - numAxes] = nextMove.coords[drive]; } +#endif } } } } + // See whether we need to kick off a move if (currentDda == nullptr) { // No DDA is executing, so start executing a new one if possible - if (idleCount > 10) // better to have a few moves in the queue so that we can do lookahead + if (!canAddMove || idleCount > 10) // better to have a few moves in the queue so that we can do lookahead { - DDA *dda = ddaRingGetPointer; + // Prepare one move and execute it. We assume that we will enter the next if-block before it completes, giving us time to prepare more moves. + Platform::DisableStepInterrupt(); // should be disabled already because we weren't executing a move, but make sure + DDA * const dda = ddaRingGetPointer; // capture volatile variable if (dda->GetState() == DDA::provisional) { dda->Prepare(simulationMode); - } - if (dda->GetState() == DDA::frozen) - { if (simulationMode != 0) { currentDda = dda; // pretend we are executing this move } else { - Platform::DisableStepInterrupt(); // should be disabled already because we weren't executing a move, but make sure if (StartNextMove(Platform::GetInterruptClocks())) // start the next move { Interrupt(); } } - iState = IdleState::busy; + moveState = MoveState::executing; } else if (simulationMode != 0) { - if (iState == IdleState::busy && !reprap.GetGCodes().IsPaused()) + if (moveState == MoveState::executing && !reprap.GetGCodes().IsPaused()) { - lastMoveTime = millis(); // record when we first noticed that the machine was idle - iState = IdleState::timing; + lastStateChangeTime = millis(); // record when we first noticed that the machine was idle + moveState = MoveState::timing; } - else if (iState == IdleState::timing && millis() - lastMoveTime >= idleTimeout) + else if (moveState == MoveState::timing && millis() - lastStateChangeTime >= idleTimeout) { reprap.GetPlatform().SetDriversIdle(); // put all drives in idle hold - iState = IdleState::idle; + moveState = MoveState::idle; } } } @@ -283,7 +290,7 @@ void Move::Spin() DDA *cdda = currentDda; // currentDda is volatile, so copy it if (cdda != nullptr) { - // See whether we need to prepare any moves + // See whether we need to prepare any moves. First count how many prepared or executing moves we have and how long they will take. int32_t preparedTime = 0; uint32_t preparedCount = 0; DDA::DDAState st; @@ -298,13 +305,17 @@ void Move::Spin() } } - // If the number of prepared moves will execute in less than the minimum time, prepare another move + // If the number of prepared moves will execute in less than the minimum time, prepare another move. + // Try to avoid preparing deceleration-only moves while (st == DDA::provisional - && preparedTime < (int32_t)(DDA::stepClockRate/8) // prepare moves one eighth of a second ahead of when they will be needed - && preparedCount < DdaRingLength/2 // but don't prepare more than half the ring + && preparedTime < (int32_t)UsualMinimumPreparedTime // prepare moves one eighth of a second ahead of when they will be needed + && preparedCount < DdaRingLength/2 - 1 // but don't prepare as much as half the ring ) { - cdda->Prepare(simulationMode); + if (cdda->IsGoodToPrepare() || preparedTime < (int32_t)AbsoluteMinimumPreparedTime) + { + cdda->Prepare(simulationMode); + } preparedTime += cdda->GetTimeLeft(); ++preparedCount; cdda = cdda->GetNext(); @@ -317,7 +328,7 @@ void Move::Spin() // Simulate completion of the current move //DEBUG //currentDda->DebugPrint(); - simulationTime += currentDda->CalcTime(); + simulationTime += (float)currentDda->GetClocksNeeded()/DDA::stepClockRate; currentDda->Complete(); CurrentMoveCompleted(); } @@ -361,13 +372,10 @@ bool Move::IsRawMotorMove(uint8_t moveType) const bool Move::IsAccessibleProbePoint(float x, float y) const { const ZProbeParameters& params = reprap.GetPlatform().GetCurrentZProbeParameters(); - return kinematics->IsReachable(x - params.xOffset, y - params.yOffset); + return kinematics->IsReachable(x - params.xOffset, y - params.yOffset, false); } -// Pause the print as soon as we can, returning true if we are able to. -// Returns the file position of the first queue move we are going to skip, or noFilePosition we we are not skipping any moves. -// We update 'positions' to the positions and feed rate expected for the next move, and the amount of extrusion in the moves we skipped. -// If we are not skipping any moves then the feed rate, virtual extruder position and iobits are left alone, therefore the caller should set them up first. +// Pause the print as soon as we can, returning true if we are able to skip any moves and updating 'rp' to the first move we skipped. bool Move::PausePrint(RestorePoint& rp) { // Find a move we can pause after. @@ -393,7 +401,7 @@ bool Move::PausePrint(RestorePoint& rp) // In general, we can pause after a move if it is the last segment and its end speed is slow enough. // We can pause before a move if it is the first segment in that move. - const DDA *savedDdaRingAddPointer = ddaRingAddPointer; + const DDA * const savedDdaRingAddPointer = ddaRingAddPointer; bool pauseOkHere; cpu_irq_disable(); @@ -414,7 +422,6 @@ bool Move::PausePrint(RestorePoint& rp) if (pauseOkHere && dda->CanPauseBefore()) { // We can pause before executing this move - (void)dda->Free(); // free the DDA we are going to pause before it so that it won't get executed after we enable interrupts ddaRingAddPointer = dda; break; } @@ -460,7 +467,79 @@ bool Move::PausePrint(RestorePoint& rp) return true; } -uint32_t maxReps = 0; +// Pause the print immediately, returning true if we were able to skip or abort any moves and setting up to the move we aborted +bool Move::LowPowerPause(RestorePoint& rp) +{ + const DDA * const savedDdaRingAddPointer = ddaRingAddPointer; + bool abortedMove = false; + + cpu_irq_disable(); + DDA *dda = currentDda; + if (dda != nullptr && dda->GetFilePosition() != noFilePosition) + { + // We are executing a move that has a file address, so we can interrupt it + Platform::DisableStepInterrupt(); + dda->MoveAborted(); + CurrentMoveCompleted(); // updates live endpoints, extrusion, ddaRingGetPointer, currentDda etc. + --completedMoves; // this move wasn't really completed + abortedMove = true; + } + else + { + if (dda == nullptr) + { + // No move is being executed + dda = ddaRingGetPointer; + } + while (dda != savedDdaRingAddPointer) + { + if (dda->GetFilePosition() != noFilePosition) + { + break; // we can pause before executing this move + } + dda = dda->GetNext(); + } + } + + cpu_irq_enable(); + + if (dda == savedDdaRingAddPointer) + { + return false; // we can't skip any moves + } + + // We are going to skip some moves, or part of a move. + // Store the parameters of the first move we are going to execute when we resume + rp.feedRate = dda->GetRequestedSpeed(); + rp.virtualExtruderPosition = dda->GetVirtualExtruderPosition(); + rp.filePos = dda->GetFilePosition(); + rp.proportionDone = dda->GetProportionDone(); // store how much of the complete multi-segment move's extrusion has been done + +#if SUPPORT_IOBITS + rp.ioBits = dda->GetIoBits(); +#endif + + ddaRingAddPointer = (abortedMove) ? dda->GetNext() : dda; + + // Get the end coordinates of the last move that was or will be completed, or the coordinates of the current move when we aborted it. + DDA * const prevDda = ddaRingAddPointer->GetPrevious(); + const size_t numVisibleAxes = reprap.GetGCodes().GetVisibleAxes(); + for (size_t axis = 0; axis < numVisibleAxes; ++axis) + { + rp.moveCoords[axis] = prevDda->GetEndCoordinate(axis, false); + } + + InverseAxisAndBedTransform(rp.moveCoords, prevDda->GetXAxes(), prevDda->GetYAxes()); // we assume that xAxes and yAxes have't changed between the moves + + // Free the DDAs for the moves we are going to skip + for (dda = ddaRingAddPointer; dda != savedDdaRingAddPointer; dda = dda->GetNext()) + { + (void)dda->Free(); + scheduledMoves--; + } + + return true; +} #if 0 // For debugging @@ -473,8 +552,8 @@ void Move::Diagnostics(MessageType mtype) Platform& p = reprap.GetPlatform(); p.Message(mtype, "=== Move ===\n"); p.MessageF(mtype, "MaxReps: %" PRIu32 ", StepErrors: %u, FreeDm: %d, MinFreeDm %d, MaxWait: %" PRIu32 "ms, Underruns: %u, %u\n", - maxReps, stepErrors, DriveMovement::NumFree(), DriveMovement::MinFree(), longestGcodeWaitInterval, numLookaheadUnderruns, numPrepareUnderruns); - maxReps = 0; + DDA::maxReps, stepErrors, DriveMovement::NumFree(), DriveMovement::MinFree(), longestGcodeWaitInterval, numLookaheadUnderruns, numPrepareUnderruns); + DDA::maxReps = 0; numLookaheadUnderruns = numPrepareUnderruns = 0; longestGcodeWaitInterval = 0; DriveMovement::ResetMinFree(); @@ -584,10 +663,10 @@ void Move::MotorStepsToCartesian(const int32_t motorPos[], size_t numVisibleAxes // Convert Cartesian coordinates to motor steps, axes only, returning true if successful. // Used to perform movement and G92 commands. -bool Move::CartesianToMotorSteps(const float machinePos[MaxAxes], int32_t motorPos[MaxAxes], bool allowModeChange) const +bool Move::CartesianToMotorSteps(const float machinePos[MaxAxes], int32_t motorPos[MaxAxes], bool isCoordinated) const { const bool b = kinematics->CartesianToMotorSteps(machinePos, reprap.GetPlatform().GetDriveStepsPerUnit(), - reprap.GetGCodes().GetVisibleAxes(), reprap.GetGCodes().GetTotalAxes(), motorPos, allowModeChange); + reprap.GetGCodes().GetVisibleAxes(), reprap.GetGCodes().GetTotalAxes(), motorPos, isCoordinated); if (reprap.Debug(moduleMove) && reprap.Debug(moduleDda)) { if (b) @@ -861,9 +940,9 @@ void Move::AdjustMotorPositions(const float_t adjustment[], size_t numMotors) const int32_t * const endCoordinates = lastQueuedMove->DriveCoordinates(); const float * const driveStepsPerUnit = reprap.GetPlatform().GetDriveStepsPerUnit(); - for (size_t drive = 0; drive < DELTA_AXES; ++drive) + for (size_t drive = 0; drive < numMotors; ++drive) { - const int32_t ep = endCoordinates[drive] + (int32_t)(adjustment[drive] * driveStepsPerUnit[drive]); + const int32_t ep = endCoordinates[drive] + lrintf(adjustment[drive] * driveStepsPerUnit[drive]); lastQueuedMove->SetDriveCoordinate(ep, drive); liveEndPoints[drive] = ep; } |