diff options
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; } |