const Error *Amp::UpdateEvents( uint16 stat, uint32 events, uint16 inputs ) { cml.Debug( "Amp %d status 0x%08x 0x%04x 0x%04x\n", GetNodeID(), events, stat, inputs ); uint32 mask = 0; if( stat & DRIVESTAT_SPACK ) mask |= AMPEVENT_SPACK; if( stat & DRIVESTAT_MOVEDONE ) mask |= AMPEVENT_MOVEDONE; if( ~stat & DRIVESTAT_TRJ ) mask |= AMPEVENT_TRJDONE; if( ~stat & DRIVESTAT_QSTOP ) mask |= AMPEVENT_QUICKSTOP; if( stat & DRIVESTAT_ABORT ) mask |= AMPEVENT_ABORT; if( stat & DRIVESTAT_HOMECAP ) mask |= AMPEVENT_HOME_CAPTURE; if( events & ERROR_EVENTS ) mask |= AMPEVENT_ERROR; if( events & ESTAT_FAULT ) mask |= AMPEVENT_FAULT; if( events & ESTAT_TRK_WARN ) mask |= AMPEVENT_POSWARN; if( events & ESTAT_TRK_WIN ) mask |= AMPEVENT_POSWIN; if( events & ESTAT_VEL_WIN ) mask |= AMPEVENT_VELWIN; if( events & ESTAT_PWM_DISABLE ) mask |= AMPEVENT_DISABLED; if( events & ESTAT_POSLIM ) mask |= AMPEVENT_POSLIM; if( events & ESTAT_NEGLIM ) mask |= AMPEVENT_NEGLIM; if( events & ESTAT_SOFTLIM_POS ) mask |= AMPEVENT_SOFTLIM_POS; if( events & ESTAT_SOFTLIM_NEG ) mask |= AMPEVENT_SOFTLIM_NEG; if( events & ESTAT_SOFT_DISABLE) mask |= AMPEVENT_SOFTDISABLE; if( events & ESTAT_PHASE_INIT ) mask |= AMPEVENT_PHASE_INIT; // On new move aborts, do some clean up. if( mask & AMPEVENT_ABORT ) { uint32 old = eventMap.getMask(); if( !(old & AMPEVENT_ABORT) ) MoveAborted(); } // Do the same thing if the amplifier is disabled while // a trajectory was in progress if( mask & AMPEVENT_DISABLED ) { uint32 old = eventMap.getMask(); if( !(old & AMPEVENT_TRJDONE) ) MoveAborted(); } // Change the bits that this function is responsible for. // for now, that's everything but the node guarding // and PVT buffer empty bits. eventMap.changeBits( ~(AMPEVENT_PVT_EMPTY|AMPEVENT_NODEGUARD), mask ); // Update the input pins state inputStateMap.setMask( (uint32)inputs ); return 0; }
// This is called by the interrupt service routine to execute steps. // It returns true if it needs to be called again on the DDA of the new current move, otherwise false. // This must be as fast as possible, because it determines the maximum movement speed. bool DDA::Step() { bool repeat; uint32_t numReps = 0; do { // Keep this loop as fast as possible, in the case that there are no endstops to check! // Check endstop switches and Z probe if asked if (endStopsToCheck != 0) // if any homing switches or the Z probe is enabled in this move { if ((endStopsToCheck & ZProbeActive) != 0) // if the Z probe is enabled in this move { // Check whether the Z probe has been triggered. On a delta at least, this must be done separately from endstop checks, // because we have both a high endstop and a Z probe, and the Z motor is not the same thing as the Z axis. switch (reprap.GetPlatform()->GetZProbeResult()) { case EndStopHit::lowHit: MoveAborted(); // set the state to completed and recalculate the endpoints reprap.GetMove()->ZProbeTriggered(this); break; case EndStopHit::lowNear: ReduceHomingSpeed(); break; default: break; } } for (size_t drive = 0; drive < AXES; ++drive) { if ((endStopsToCheck & (1 << drive)) != 0) { switch(reprap.GetPlatform()->Stopped(drive)) { case EndStopHit::lowHit: endStopsToCheck &= ~(1 << drive); // clear this check so that we can check for more if (endStopsToCheck == 0 || reprap.GetMove()->IsCoreXYAxis(drive)) // if no more endstops to check, or this axis uses shared motors { MoveAborted(); } else { StopDrive(drive); } reprap.GetMove()->HitLowStop(drive, this); break; case EndStopHit::highHit: endStopsToCheck &= ~(1 << drive); // clear this check so that we can check for more if (endStopsToCheck == 0 || reprap.GetMove()->IsCoreXYAxis(drive)) // if no more endstops to check, or this axis uses shared motors { MoveAborted(); } else { StopDrive(drive); } reprap.GetMove()->HitHighStop(drive, this); break; case EndStopHit::lowNear: // Only reduce homing speed if there are no more axes to be homed. // This allows us to home X and Y simultaneously. if (endStopsToCheck == (1 << drive)) { ReduceHomingSpeed(); } break; default: break; } } } if (state == completed) // we may have completed the move due to triggering an endstop switch or Z probe { break; } } // Generate any steps that are now due, overdue, or will be due very shortly DriveMovement* dm = firstDM; if (dm == nullptr) // I don't think this should happen, but best to be sure { state = completed; break; } const uint32_t elapsedTime = (Platform::GetInterruptClocks() - moveStartTime) + minInterruptInterval; while (elapsedTime >= dm->nextStepTime) // if the next step is due { size_t drive = dm->drive; ++numReps; reprap.GetPlatform()->StepHigh(drive); firstDM = dm->nextDM; bool moreSteps = (isDeltaMovement && drive < AXES) ? dm->CalcNextStepTimeDelta(*this, drive, true) : dm->CalcNextStepTimeCartesian(*this, drive, true); if (moreSteps) { InsertDM(dm); } else if (firstDM == nullptr) { state = completed; reprap.GetPlatform()->StepLow(drive); goto quit; // yukky multi-level break, but saves us another test in this time-critical code } reprap.GetPlatform()->StepLow(drive); dm = firstDM; //uint32_t t3 = Platform::GetInterruptClocks() - t2; //if (t3 > maxCalcTime) maxCalcTime = t3; //if (t3 < minCalcTime) minCalcTime = t3; } repeat = reprap.GetPlatform()->ScheduleInterrupt(firstDM->nextStepTime + moveStartTime); } while (repeat); quit: if (numReps > maxReps) { maxReps = numReps; } if (state == completed) { uint32_t finishTime = moveStartTime + clocksNeeded; // calculate how long this move should take Move *move = reprap.GetMove(); move->CurrentMoveCompleted(); // tell Move that the current move is complete return move->StartNextMove(finishTime); // schedule the next move } return false; }