From 49570417340a0db63d00a6bbe36a124b0af0d0a0 Mon Sep 17 00:00:00 2001 From: Argolein <52103738+Argolein@users.noreply.github.com> Date: Mon, 9 Feb 2026 18:48:03 +0100 Subject: [PATCH] Add per-tool pressure advance snapshots with CAN support Switch M572 pressure advance handling to per-tool semantics and snapshot the active value per queued move so rapid adaptive updates do not require queue drains. Propagate the snapshot through local and CAN movement paths, including remote move validation and compatibility signaling for mixed firmware safety. --- src/CAN/CanInterface.cpp | 1 + src/CAN/CanMotion.cpp | 33 +++++++++- src/CAN/CanMotion.h | 2 +- src/CAN/CommandProcessor.cpp | 20 ++++-- src/CAN/ExpansionManager.cpp | 3 + src/CAN/ExpansionManager.h | 3 +- src/GCodes/GCodes.cpp | 3 + src/Movement/DDA.cpp | 12 ++-- src/Movement/DDA.h | 1 + src/Movement/Move.cpp | 6 +- src/Movement/Move.h | 2 +- src/Movement/Move2.cpp | 119 ++++++++++++++++++++++++----------- src/Movement/RawMove.cpp | 1 + src/Movement/RawMove.h | 1 + src/Tools/Tool.cpp | 16 ++++- src/Tools/Tool.h | 4 ++ 16 files changed, 174 insertions(+), 53 deletions(-) diff --git a/src/CAN/CanInterface.cpp b/src/CAN/CanInterface.cpp index 9c4f57a1bb..eb32ca5734 100644 --- a/src/CAN/CanInterface.cpp +++ b/src/CAN/CanInterface.cpp @@ -397,6 +397,7 @@ void CanInterface::SendAnnounce(CanMessageBuffer *buf) noexcept msg->timeSinceStarted = millis(); msg->numDrivers = NumDirectDrivers; msg->usesUf2Binary = BOARD_USES_UF2_BINARY; + msg->supportsMovementPaSnapshot = 1; msg->zero = 0; memcpy(msg->uniqueId, reprap.GetPlatform().GetUniqueId().GetRaw(), sizeof(msg->uniqueId)); // Note, board type name, firmware version, firmware date and firmware time are limited to 43 characters in the new diff --git a/src/CAN/CanMotion.cpp b/src/CAN/CanMotion.cpp index e571b66e0b..a908f735df 100644 --- a/src/CAN/CanMotion.cpp +++ b/src/CAN/CanMotion.cpp @@ -46,8 +46,10 @@ namespace CanMotion static volatile uint32_t whenRevertedAll; static Mutex stopListMutex; static uint8_t nextSeq[CanId::MaxCanAddress + 1] = { 0 }; + static bool warnedNoPaSnapshotSupport[CanId::MaxCanAddress + 1] = { false }; static CanMessageBuffer *_ecv_null GetBuffer(const PrepParams& params, DriverId canDriver) noexcept; + static bool BoardSupportsMovementPaSnapshot(CanAddress boardAddress) noexcept; static void FreeMovementBuffers() noexcept; } @@ -71,6 +73,16 @@ void CanMotion::FreeMovementBuffers() noexcept } } +bool CanMotion::BoardSupportsMovementPaSnapshot(CanAddress boardAddress) noexcept +{ + const ExpansionBoardData *const boardData = reprap.GetExpansion().GetBoardDetails(boardAddress); + if (boardData == nullptr) + { + return false; + } + return boardData->supportsMovementPaSnapshot; +} + // This is called by DDA::Prepare at the start of preparing a movement void CanMotion::StartMovement() noexcept { @@ -140,6 +152,7 @@ CanMessageBuffer *_ecv_null CanMotion::GetBuffer(const PrepParams& params, Drive move->acceleration = params.acceleration/params.totalDistance; // scale the acceleration to correspond to unit distance move->deceleration = -params.deceleration/params.totalDistance; // scale the deceleration to correspond to unit distance + move->pressureAdvanceClocks = 0.0; move->extruderDrives = 0; move->numDrivers = canDriver.localDriver + 1; move->zero1 = move->zero2 = 0; @@ -168,14 +181,15 @@ void CanMotion::AddAxisMovement(const PrepParams& params, DriverId canDriver, in } } -void CanMotion::AddExtruderMovement(const PrepParams& params, DriverId canDriver, float extrusion, bool usePressureAdvance) noexcept +void CanMotion::AddExtruderMovement(const PrepParams& params, DriverId canDriver, float extrusion, float pressureAdvanceClocks) noexcept { CanMessageBuffer * const buf = GetBuffer(params, canDriver); if (buf != nullptr) { buf->msg.moveLinearShaped.perDrive[canDriver.localDriver].extrusion = extrusion; buf->msg.moveLinearShaped.extruderDrives |= 1u << canDriver.localDriver; - buf->msg.moveLinearShaped.usePressureAdvance = usePressureAdvance; + buf->msg.moveLinearShaped.usePressureAdvance = (pressureAdvanceClocks > 0.0); + buf->msg.moveLinearShaped.pressureAdvanceClocks = pressureAdvanceClocks; } } @@ -199,6 +213,21 @@ uint32_t CanMotion::FinishMovement(const DDA& dda, uint32_t moveStartTime, bool CanMessageMovementLinearShaped& msg = buf->msg.moveLinearShaped; if (msg.HasMotion()) { + if (!BoardSupportsMovementPaSnapshot(buf->id.Dst())) + { + if (!warnedNoPaSnapshotSupport[buf->id.Dst()]) + { + warnedNoPaSnapshotSupport[buf->id.Dst()] = true; + reprap.GetPlatform().MessageF(ErrorMessage, + "CAN board %u is missing movement PA snapshot support; flash matching firmware on all boards\n", + buf->id.Dst()); + } + reprap.EmergencyStop(); + CanMessageBuffer::Free(buf); + buf = nextBuffer; + continue; + } + msg.whenToExecute = moveStartTime; uint8_t& seq = nextSeq[buf->id.Dst()]; msg.seq = seq; diff --git a/src/CAN/CanMotion.h b/src/CAN/CanMotion.h index ddd2898155..5b779b32c1 100644 --- a/src/CAN/CanMotion.h +++ b/src/CAN/CanMotion.h @@ -21,7 +21,7 @@ namespace CanMotion void Init() noexcept; void StartMovement() noexcept; void AddAxisMovement(const PrepParams& params, DriverId canDriver, int32_t steps) noexcept; - void AddExtruderMovement(const PrepParams& params, DriverId canDriver, float extrusion, bool usePressureAdvance) noexcept; + void AddExtruderMovement(const PrepParams& params, DriverId canDriver, float extrusion, float pressureAdvanceClocks) noexcept; uint32_t FinishMovement(const DDA& dda, uint32_t moveStartTime, bool simulating) noexcept; bool CanPrepareMove() noexcept; CanMessageBuffer *GetUrgentMessage() noexcept; diff --git a/src/CAN/CommandProcessor.cpp b/src/CAN/CommandProcessor.cpp index 17bff581ae..7fbce12ada 100644 --- a/src/CAN/CommandProcessor.cpp +++ b/src/CAN/CommandProcessor.cpp @@ -327,10 +327,22 @@ void CommandProcessor::ProcessReceivedMessage(CanMessageBuffer *buf) noexcept reprap.ScheduleReset(); return; // no reply needed - case CanMessageType::movementLinearShaped: - // Check for duplicate and out-of-sequence message - // We can get out-of-sequence messages because of a bug in the CAN hardware; so use only the sequence number to detect duplicates - { + case CanMessageType::movementLinearShaped: + { + const CanMessageMovementLinearShaped& msg = buf->msg.moveLinearShaped; + const size_t minLength = sizeof(msg) - sizeof(msg.perDrive); + const size_t expectedLength = minLength + (size_t)msg.numDrivers * sizeof(msg.perDrive[0]); + if (msg.numDrivers == 0 || msg.numDrivers > MaxLinearDriversPerCanSlave || buf->dataLength < expectedLength) + { + ++oosMessagesOther; + CanInterface::LogIgnoredMovementMessage(); + return; + } + } + + // Check for duplicate and out-of-sequence message + // We can get out-of-sequence messages because of a bug in the CAN hardware; so use only the sequence number to detect duplicates + { const int8_t seq = buf->msg.moveLinearShaped.seq; if (((seq + 1) & CanMessageMovementLinearShaped::SeqMask) == expectedSeq) { diff --git a/src/CAN/ExpansionManager.cpp b/src/CAN/ExpansionManager.cpp index ad69a3e751..2aa9fb1369 100644 --- a/src/CAN/ExpansionManager.cpp +++ b/src/CAN/ExpansionManager.cpp @@ -118,6 +118,7 @@ ExpansionBoardData::ExpansionBoardData() noexcept driverData(nullptr), accelerometerRuns(0), closedLoopRuns(0), hasMcuTemp(false), hasVin(false), hasV12(false), hasAccelerometer(false), + supportsMovementPaSnapshot(false), state(BoardState::unknown), numDrivers(0) { } @@ -183,10 +184,12 @@ void ExpansionManager::ProcessAnnouncement(CanMessageBuffer *buf, bool isNewForm if (isNewFormat) { boardTypeAndFirmwareVersion.copy(buf->msg.announceNew.boardTypeAndFirmwareVersion, CanMessageAnnounceNew::GetMaxTextLength(buf->dataLength)); + board.supportsMovementPaSnapshot = buf->msg.announceNew.supportsMovementPaSnapshot; } else { boardTypeAndFirmwareVersion.copy(buf->msg.announceOld.boardTypeAndFirmwareVersion, CanMessageAnnounceOld::GetMaxTextLength(buf->dataLength)); + board.supportsMovementPaSnapshot = false; } UpdateBoardState(src, BoardState::unknown); if (board.typeName == nullptr || strcmp(board.typeName, boardTypeAndFirmwareVersion.c_str()) != 0) diff --git a/src/CAN/ExpansionManager.h b/src/CAN/ExpansionManager.h index 29d65729fc..6cdc52277e 100644 --- a/src/CAN/ExpansionManager.h +++ b/src/CAN/ExpansionManager.h @@ -44,7 +44,8 @@ struct ExpansionBoardData hasClosedLoop : 1, hasInductiveSensor : 1, usesUf2Binary : 1, - spare : 9; + supportsMovementPaSnapshot : 1, + spare : 8; BoardState state; uint8_t numDrivers; uint8_t accelerometerOrientation = DefaultAccelerometerOrientation; diff --git a/src/GCodes/GCodes.cpp b/src/GCodes/GCodes.cpp index 4a909f0886..c4a988c89e 100644 --- a/src/GCodes/GCodes.cpp +++ b/src/GCodes/GCodes.cpp @@ -2100,6 +2100,7 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated) THROWS(GCodeExc ms.checkEndstops = false; ms.reduceAcceleration = false; ms.usePressureAdvance = false; + ms.pressureAdvance = 0.0; ms.linearAxesMentioned = false; ms.rotationalAxesMentioned = false; @@ -2564,6 +2565,7 @@ bool GCodes::DoStraightMove(GCodeBuffer& gb, bool isCoordinated) THROWS(GCodeExc AxesBitmap axesMentionedExceptZ = axesMentioned; axesMentionedExceptZ.ClearBit(Z_AXIS); ms.usePressureAdvance = axesMentionedExceptZ.IsNonEmpty(); + ms.pressureAdvance = (ms.usePressureAdvance && ms.movementTool != nullptr) ? ms.movementTool->GetPressureAdvance() : 0.0; } // Apply segmentation if necessary @@ -3026,6 +3028,7 @@ bool GCodes::DoArcMove(GCodeBuffer& gb, bool clockwise) THROWS(GCodeException) #endif ms.usePressureAdvance = ms.hasPositiveExtrusion; + ms.pressureAdvance = (ms.usePressureAdvance && ms.movementTool != nullptr) ? ms.movementTool->GetPressureAdvance() : 0.0; // Calculate the total angle moved, which depends on which way round we are going float totalArc; diff --git a/src/Movement/DDA.cpp b/src/Movement/DDA.cpp index 9452cc4682..d8a1a5fb12 100644 --- a/src/Movement/DDA.cpp +++ b/src/Movement/DDA.cpp @@ -170,6 +170,7 @@ DDA::DDA(DDA *_ecv_null n) noexcept : next(n), prev(nullptr) flags.all = 0; // in particular we need to set endCoordinatesValid, usePressureAdvance to false, stateBits to empty, also checkEndstops false for the ATE build SetState(empty); // should alrrady be covered by the above + pressureAdvanceClocks = 0.0; virtualExtruderPosition = 0.0; filePos = noFilePosition; @@ -377,7 +378,7 @@ MovementError DDA::InitStandardMove(DDARing& ring, const RawMove &nextMove, bool } if (flags.xyMoving && nextMove.usePressureAdvance) { - const float compensationClocks = move.GetPressureAdvanceClocksForLogicalDrive(drive); + const float compensationClocks = (float)nextMove.pressureAdvance * (float)StepClockRate; if (compensationClocks > 0.0) { // Compensation causes instant velocity changes equal to acceleration * k, so we may need to limit the acceleration @@ -417,6 +418,7 @@ MovementError DDA::InitStandardMove(DDARing& ring, const RawMove &nextMove, bool initialUserC0 = nextMove.initialUserC0; initialUserC1 = nextMove.initialUserC1; originalFeedRate = nextMove.originalFeedRate; + pressureAdvanceClocks = (nextMove.usePressureAdvance) ? (float)nextMove.pressureAdvance * (float)StepClockRate : 0.0; // These 4 or 5 bits can be copied in one go by the compiler generating a ubfx instruction flags.canPauseAfter = nextMove.canPauseAfter; @@ -1112,7 +1114,7 @@ void DDA::Prepare(DDARing& ring, uint32_t prepareAdvanceTime, SimulationMode sim else // we don't generate segments for leadscrew adjustment moves to remote drivers #endif { - move.AddLinearSegments(driver.localDriver + MaxAxesPlusExtruders, afterPrepare.moveStartTime, params, (motioncalc_t)delta, segFlags); + move.AddLinearSegments(driver.localDriver + MaxAxesPlusExtruders, afterPrepare.moveStartTime, params, (motioncalc_t)delta, segFlags, 0.0); } } } @@ -1146,7 +1148,7 @@ void DDA::Prepare(DDARing& ring, uint32_t prepareAdvanceTime, SimulationMode sim delta = move.ApplyBacklashCompensation(drive, delta); // We generate segments even for nonlocal drivers so that the final position is correct and to track the position in near real time - move.AddLinearSegments(drive, afterPrepare.moveStartTime, params, (motioncalc_t)delta, segFlags); + move.AddLinearSegments(drive, afterPrepare.moveStartTime, params, (motioncalc_t)delta, segFlags, 0.0); afterPrepare.drivesMoving.SetBit(drive); #if SUPPORT_CAN_EXPANSION @@ -1198,14 +1200,14 @@ void DDA::Prepare(DDARing& ring, uint32_t prepareAdvanceTime, SimulationMode sim const motioncalc_t delta = totalDistance * directionVector[drive] * move.DriveStepsPerMm(drive); // We generate segments even for nonlocal extruders in order to track extruder position - move.AddLinearSegments(drive, afterPrepare.moveStartTime, params, delta, segFlags.AddIsExtruder()); + move.AddLinearSegments(drive, afterPrepare.moveStartTime, params, delta, segFlags.AddIsExtruder(), pressureAdvanceClocks); #if SUPPORT_CAN_EXPANSION const DriverId driver = move.GetExtruderDriver(extruder); if (driver.IsRemote()) { // The MovementLinearShaped message requires the extrusion amount in steps to be passed as a float. The remote board adds the PA and handles fractional steps. - CanMotion::AddExtruderMovement(params, driver, delta, flags.usePressureAdvance); + CanMotion::AddExtruderMovement(params, driver, delta, flags.usePressureAdvance ? pressureAdvanceClocks : 0.0); } #endif afterPrepare.drivesMoving.SetBit(drive); diff --git a/src/Movement/DDA.h b/src/Movement/DDA.h index f6d8cefa8e..597ea024ca 100644 --- a/src/Movement/DDA.h +++ b/src/Movement/DDA.h @@ -245,6 +245,7 @@ class DDA final float totalDistance; // How long is the move in hypercuboid space float maxAcceleration, maxDeceleration; // The maximum acceleration and deceleration to use, always positive float requestedSpeed; // The speed that the user asked for + float pressureAdvanceClocks; // pressure advance to use for this move, in step clocks float virtualExtruderPosition; // the virtual extruder position at the end of this move, used for pause/resume // These vary depending on how we connect the move with its predecessor and successor, but remain constant while the move is being executed diff --git a/src/Movement/Move.cpp b/src/Movement/Move.cpp index 56c1bffbbd..441abe77d9 100644 --- a/src/Movement/Move.cpp +++ b/src/Movement/Move.cpp @@ -1814,7 +1814,7 @@ MoveSegment *Move::AddSegment(MoveSegment *list, uint32_t startTime, uint32_t du // Add some linear segments to be executed by a driver, taking account of possible input shaping. This is used by linear axes and by extruders. // We never add a segment that starts earlier than the earliest existing segment (if any). -void Move::AddLinearSegments(size_t logicalDrive, uint32_t startTime, const PrepParams& params, motioncalc_t steps, MovementFlags moveFlags) noexcept +void Move::AddLinearSegments(size_t logicalDrive, uint32_t startTime, const PrepParams& params, motioncalc_t steps, MovementFlags moveFlags, float pressureAdvanceClocks) noexcept { if (reprap.GetDebugFlags(Module::Move).IsBitSet(MoveDebugFlags::Segments)) { @@ -1899,7 +1899,7 @@ void Move::AddLinearSegments(size_t logicalDrive, uint32_t startTime, const Prep else { accelDistance = (params.decelClocks + params.steadyClocks == 0) ? totalDistance : (motioncalc_t)params.accelDistance; - accelPressureAdvance = (moveFlags.isExtruder && !moveFlags.nonPrintingMove) ? (motioncalc_t)(params.accelClocks * dm.extruderShaper.GetKclocks()) : (motioncalc_t)0.0; + accelPressureAdvance = (moveFlags.isExtruder && !moveFlags.nonPrintingMove) ? (motioncalc_t)(params.accelClocks * pressureAdvanceClocks) : (motioncalc_t)0.0; } motioncalc_t decelDistance, decelPressureAdvance; @@ -1911,7 +1911,7 @@ void Move::AddLinearSegments(size_t logicalDrive, uint32_t startTime, const Prep else { decelDistance = totalDistance - ((params.steadyClocks == 0) ? accelDistance : (motioncalc_t)params.decelStartDistance); - decelPressureAdvance = (moveFlags.isExtruder && !moveFlags.nonPrintingMove) ? (motioncalc_t)(params.decelClocks * dm.extruderShaper.GetKclocks()) : (motioncalc_t)0.0; + decelPressureAdvance = (moveFlags.isExtruder && !moveFlags.nonPrintingMove) ? (motioncalc_t)(params.decelClocks * pressureAdvanceClocks) : (motioncalc_t)0.0; } const motioncalc_t steadyDistance = (params.steadyClocks == 0) ? (motioncalc_t)0.0 : totalDistance - accelDistance - decelDistance; diff --git a/src/Movement/Move.h b/src/Movement/Move.h index 759e869199..63b6901620 100644 --- a/src/Movement/Move.h +++ b/src/Movement/Move.h @@ -346,7 +346,7 @@ class Move final INHERIT_OBJECT_MODEL AxisShaper& GetAxisShaper() noexcept { return axisShaper; } // Functions called by DDA::Prepare to generate segments for executing DDAs - void AddLinearSegments(size_t logicalDrive, uint32_t startTime, const PrepParams& params, motioncalc_t steps, MovementFlags moveFlags) noexcept; + void AddLinearSegments(size_t logicalDrive, uint32_t startTime, const PrepParams& params, motioncalc_t steps, MovementFlags moveFlags, float pressureAdvanceClocks) noexcept; bool AreDrivesStopped(LogicalDrivesBitmap drives) const noexcept; // return true if none of the drives passed has any movement pending diff --git a/src/Movement/Move2.cpp b/src/Movement/Move2.cpp index fadf67df72..c028ffdd4d 100644 --- a/src/Movement/Move2.cpp +++ b/src/Movement/Move2.cpp @@ -162,18 +162,16 @@ GCodeResult Move::ConfigurePressureAdvance(GCodeBuffer& gb, const StringRef& rep if (gb.Seen('S')) { const float advance = gb.GetNonNegativeFValue(); - if (!reprap.GetGCodes().LockCurrentMovementSystemAndWaitForStandstill(gb)) - { - return GCodeResult::notFinished; - } - GCodeResult rslt = GCodeResult::ok; - -#if SUPPORT_CAN_EXPANSION - CanDriversData canDriversToUpdate; -#endif + ToolNumbersBitmap toolsToUpdate; + toolsToUpdate.Clear(); if (gb.Seen('D')) { + bool targetAnyTools = false; + bool extruderSelected[MaxExtruders]; + bool extruderMatchedToTool[MaxExtruders]; + memset(extruderSelected, 0, sizeof(extruderSelected)); + memset(extruderMatchedToTool, 0, sizeof(extruderMatchedToTool)); uint32_t eDrive[MaxExtruders]; size_t eCount = MaxExtruders; gb.GetUnsignedArray(eDrive, eCount, false); @@ -183,17 +181,63 @@ GCodeResult Move::ConfigurePressureAdvance(GCodeBuffer& gb, const StringRef& rep if (extruder >= reprap.GetGCodes().GetNumExtruders()) { reply.printf("Invalid extruder number '%" PRIu32 "'", extruder); - rslt = GCodeResult::error; - break; + return GCodeResult::error; } - GetExtruderShaperForExtruder(extruder).SetKseconds(advance); -#if SUPPORT_CAN_EXPANSION - const DriverId did = GetExtruderDriver(extruder); - if (did.IsRemote()) + extruderSelected[extruder] = true; + } + + ReadLocker lock(Tool::toolListLock); + for (const Tool *_ecv_null tool = Tool::GetToolList(); tool != nullptr; tool = tool->Next()) + { + bool anySelected = false; + bool allSelected = true; + tool->IterateExtruders([&extruderSelected, &anySelected, &allSelected](unsigned int extruder) noexcept + { + if (extruderSelected[extruder]) + { + anySelected = true; + } + else + { + allSelected = false; + } + } + ); + if (anySelected) { - canDriversToUpdate.AddEntry(did, advance); + tool->IterateExtruders([&extruderSelected, &extruderMatchedToTool](unsigned int extruder) noexcept + { + if (extruderSelected[extruder]) + { + extruderMatchedToTool[extruder] = true; + } + } + ); + + if (!allSelected && tool->DriveCount() > 1) + { + reply.printf("Extruder list partially targets multi-extruder tool %d; set pressure advance per tool", tool->Number()); + return GCodeResult::error; + } + toolsToUpdate.SetBit(tool->Number()); + targetAnyTools = true; + } + } + + if (!targetAnyTools) + { + reply.copy("No tool found for specified extruder(s)"); + return GCodeResult::error; + } + + for (size_t i = 0; i < eCount; ++i) + { + const uint32_t extruder = eDrive[i]; + if (!extruderMatchedToTool[extruder]) + { + reply.printf("No tool found for specified extruder '%" PRIu32 "'", extruder); + return GCodeResult::error; } -#endif } } else @@ -202,23 +246,29 @@ GCodeResult Move::ConfigurePressureAdvance(GCodeBuffer& gb, const StringRef& rep if (ct == nullptr) { reply.copy("No tool selected"); - rslt = GCodeResult::error; + return GCodeResult::error; } - else + toolsToUpdate.SetBit(ct->Number()); + } + + { + WriteLocker lock(Tool::toolListLock); + for (Tool *_ecv_null tool = Tool::GetToolList(); tool != nullptr; tool = tool->Next()) { + if (!toolsToUpdate.IsBitSet(tool->Number())) + { + continue; + } + + tool->SetPressureAdvance(advance); #if SUPPORT_CAN_EXPANSION - ct->IterateExtruders([this, advance, &canDriversToUpdate](unsigned int extruder) + tool->IterateExtruders([this, advance](unsigned int extruder) { GetExtruderShaperForExtruder(extruder).SetKseconds(advance); - const DriverId did = GetExtruderDriver(extruder); - if (did.IsRemote()) - { - canDriversToUpdate.AddEntry(did, advance); - } } ); #else - ct->IterateExtruders([this, advance](unsigned int extruder) + tool->IterateExtruders([this, advance](unsigned int extruder) { GetExtruderShaperForExtruder(extruder).SetKseconds(advance); } @@ -228,19 +278,17 @@ GCodeResult Move::ConfigurePressureAdvance(GCodeBuffer& gb, const StringRef& rep } reprap.MoveUpdated(); + reprap.ToolsUpdated(); -#if SUPPORT_CAN_EXPANSION - return max(rslt, CanInterface::SetRemotePressureAdvance(canDriversToUpdate, reply)); -#else return rslt; -#endif } - reply.copy("Extruder pressure advance"); + reply.copy("Tool pressure advance"); char c = ':'; - for (size_t i = 0; i < reprap.GetGCodes().GetNumExtruders(); ++i) + ReadLocker lock(Tool::toolListLock); + for (const Tool *_ecv_null tool = Tool::GetToolList(); tool != nullptr; tool = tool->Next()) { - reply.catf("%c %.3f", c, (double)GetExtruderShaperForExtruder(i).GetKseconds()); + reply.catf("%c T%d %.4f", c, tool->Number(), (double)tool->GetPressureAdvance()); c = ','; } return GCodeResult::ok; @@ -1270,7 +1318,8 @@ void Move::AddMoveFromRemote(const CanMessageMovementLinearShaped& msg) noexcept if (extrusionRequested != 0.0) { EnableDrivers(drive, false); - AddLinearSegments(drive, msg.whenToExecute, params, extrusionRequested, segFlags.AddIsExtruder()); + const float pressureAdvanceClocks = (msg.usePressureAdvance) ? msg.pressureAdvanceClocks : 0.0; + AddLinearSegments(drive, msg.whenToExecute, params, extrusionRequested, segFlags.AddIsExtruder(), pressureAdvanceClocks); } } else @@ -1279,7 +1328,7 @@ void Move::AddMoveFromRemote(const CanMessageMovementLinearShaped& msg) noexcept if (delta != 0.0) { EnableDrivers(drive, false); - AddLinearSegments(drive, msg.whenToExecute, params, delta, segFlags); + AddLinearSegments(drive, msg.whenToExecute, params, delta, segFlags, 0.0); } } } diff --git a/src/Movement/RawMove.cpp b/src/Movement/RawMove.cpp index 58235c10bf..f1df1168c5 100644 --- a/src/Movement/RawMove.cpp +++ b/src/Movement/RawMove.cpp @@ -37,6 +37,7 @@ void MovementState::SetDefaults(size_t firstDriveToZero) noexcept #endif filePos = noFilePosition; movementTool = nullptr; + pressureAdvance = 0.0; moveFractionToSkip = 0.0; #if 0 // we don't use this yet cosXyAngle = 1.0; diff --git a/src/Movement/RawMove.h b/src/Movement/RawMove.h index 2bca6349cd..10a5d8e4be 100644 --- a/src/Movement/RawMove.h +++ b/src/Movement/RawMove.h @@ -27,6 +27,7 @@ struct RawMove float maxTravelAcceleration; const Tool *_ecv_null movementTool; // which tool (if any) is being used by this move + float16_t pressureAdvance; // pressure advance to use for this move, in seconds (per-tool snapshot) static constexpr LogicalDrivesBitmap allLogicalDrives = LogicalDrivesBitmap::MakeLowestNBits(MaxAxesPlusExtruders); diff --git a/src/Tools/Tool.cpp b/src/Tools/Tool.cpp index fb43c18ccb..a65d579d75 100644 --- a/src/Tools/Tool.cpp +++ b/src/Tools/Tool.cpp @@ -122,6 +122,7 @@ constexpr ObjectModelTableEntry Tool::objectModelTable[] = { "number", OBJECT_MODEL_FUNC((int32_t)self->myNumber), ObjectModelEntryFlags::none }, { "offsets", OBJECT_MODEL_FUNC_ARRAY(6), ObjectModelEntryFlags::none }, { "offsetsProbed", OBJECT_MODEL_FUNC((int32_t)self->axisOffsetsProbed.GetRaw()), ObjectModelEntryFlags::none }, + { "pressureAdvance", OBJECT_MODEL_FUNC(self->pressureAdvance, 4), ObjectModelEntryFlags::none }, { "retraction", OBJECT_MODEL_FUNC(self, 1), ObjectModelEntryFlags::none }, { "spindle", OBJECT_MODEL_FUNC((int32_t)self->spindleNumber), ObjectModelEntryFlags::none }, { "spindleRpm", OBJECT_MODEL_FUNC((int32_t)self->spindleRpm), ObjectModelEntryFlags::none }, @@ -136,7 +137,7 @@ constexpr ObjectModelTableEntry Tool::objectModelTable[] = { "zHop", OBJECT_MODEL_FUNC(self->configuredRetractHop, 2), ObjectModelEntryFlags::none }, }; -constexpr uint8_t Tool::objectModelTableDescriptor[] = { 2, 21, 5 }; +constexpr uint8_t Tool::objectModelTableDescriptor[] = { 2, 22, 5 }; DEFINE_GET_OBJECT_MODEL_TABLE(Tool) @@ -244,6 +245,7 @@ uint16_t Tool::numToolsToReport = 0; t->drives[drive] = d[drive]; t->mix[drive] = (drive == 0) ? 1.0 : 0.0; // initial mix ratio is 1:0:0 } + t->pressureAdvance = (t->driveCount != 0) ? reprap.GetMove().GetPressureAdvanceClocksForExtruder(t->drives[0])/(float)StepClockRate : 0.0; for (size_t heater = 0; heater < t->heaterCount; heater++) { @@ -888,6 +890,18 @@ bool Tool::UsesHeater(int8_t heater) const noexcept return false; } +bool Tool::UsesExtruder(unsigned int extruder) const noexcept +{ + for (size_t i = 0; i < driveCount; ++i) + { + if (drives[i] == extruder) + { + return true; + } + } + return false; +} + const char *_ecv_array Tool::GetFilamentName() const noexcept { return (filament == nullptr) ? "" : filament->GetName(); diff --git a/src/Tools/Tool.h b/src/Tools/Tool.h index 67c9de142f..2a1996938c 100644 --- a/src/Tools/Tool.h +++ b/src/Tools/Tool.h @@ -134,6 +134,7 @@ class Tool final INHERIT_OBJECT_MODEL void IterateExtruders(function_ref_noexcept f) const noexcept; void IterateHeaters(function_ref_noexcept f) const noexcept; bool UsesHeater(int8_t heater) const noexcept; + bool UsesExtruder(unsigned int extruder) const noexcept; void SetFansPwm(float f) const noexcept; @@ -144,6 +145,8 @@ class Tool final INHERIT_OBJECT_MODEL uint32_t GetFeedForwardAdvanceClocks() const noexcept { return feedForwardAdvanceClocks; } void ApplyExtrusionFeedForward(float extrusionSpeed) const noexcept; void StopExtrusionFeedForward() const noexcept; + float GetPressureAdvance() const noexcept { return pressureAdvance; } + void SetPressureAdvance(float pa) noexcept { pressureAdvance = pa; } void Activate() noexcept; void Standby() noexcept; @@ -187,6 +190,7 @@ class Tool final INHERIT_OBJECT_MODEL float heaterFeedForwardPwm[MaxHeatersPerTool]; float heaterFeedForwardTemp[MaxHeatersPerTool]; uint32_t feedForwardAdvanceClocks = 0; + float pressureAdvance = 0.0; // Firmware retraction settings float retractLength, retractExtra; // retraction length and extra length to un-retract