diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index b2e29a1c9473..234fc02fd802 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -2519,6 +2519,11 @@ // //#define INCH_MODE_SUPPORT +// +// G93/G94 Feedrate mode support +// +//#define FEEDRATE_MODE_SUPPORT + // // M149 Set temperature units support // diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 3aa430db20ec..06cfd41c7268 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -1231,7 +1231,7 @@ void setup() { #endif #endif - #if ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) + #if ENABLED(FREEZE_FEATURE) SETUP_LOG("FREEZE_PIN"); #if FREEZE_STATE SET_INPUT_PULLDOWN(FREEZE_PIN); @@ -1334,7 +1334,10 @@ void setup() { #endif SERIAL_ECHO_MSG(" Compiled: " __DATE__); SERIAL_ECHO_MSG(STR_FREE_MEMORY, hal.freeMemory(), STR_PLANNER_BUFFER_BYTES, sizeof(block_t) * (BLOCK_BUFFER_SIZE)); - + + #if HAS_ROTATIONAL_AXES + parser.cartes_move = true; + #endif // Some HAL need precise delay adjustment calibrate_delay_loop(); diff --git a/Marlin/src/feature/bedlevel/abl/bbl.cpp b/Marlin/src/feature/bedlevel/abl/bbl.cpp index 0e8c4a273ef9..545af97f8eab 100644 --- a/Marlin/src/feature/bedlevel/abl/bbl.cpp +++ b/Marlin/src/feature/bedlevel/abl/bbl.cpp @@ -78,7 +78,7 @@ void LevelingBilinear::extrapolate_one_point(const uint8_t x, const uint8_t y, c const float a = 2 * a1 - a2, b = 2 * b1 - b2, c = 2 * c1 - c2; // Take the average instead of the median - z_values[x][y] = (a + b + c) / 3.0; + z_values[x][y] = (a + b + c) / 3.0f; TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y])); // Median is robust (ignores outliers). diff --git a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp index 93e50b649857..90bb162849f1 100644 --- a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp +++ b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp @@ -71,14 +71,10 @@ void mesh_bed_leveling::line_to_destination(const feedRate_t scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) { // Get current and destination cells for this line xy_uint8_t scel = cell_indexes(motion.position), ecel = cell_indexes(motion.destination); - NOMORE(scel.x, GRID_MAX_CELLS_X - 1); - NOMORE(scel.y, GRID_MAX_CELLS_Y - 1); - NOMORE(ecel.x, GRID_MAX_CELLS_X - 1); - NOMORE(ecel.y, GRID_MAX_CELLS_Y - 1); // Start and end in the same cell? No split needed. if (scel == ecel) { - motion.position = motion.destination; + motion.position = motion.destination; motion.goto_current_position(scaled_fr_mm_s); return; } diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp index 40c308e39a7d..851ad4df99a9 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp @@ -357,23 +357,39 @@ const xyze_pos_t total = motion.destination - motion.position; - const float cart_xy_mm_2 = HYPOT2(total.x, total.y), - cart_xy_mm = SQRT(cart_xy_mm_2); // Total XY distance + // If the move is only in Z/E don't split up the move + if (!total.x && !total.y) { + planner.buffer_line(motion.destination, scaled_fr_mm_s); + return false; // caller will update current_position + } + #if HAS_ROTATIONAL_AXES + bool cartes_move = true; + #endif + float cartesian_mm = motion.get_move_distance(total OPTARG(HAS_ROTATIONAL_AXES, cartes_move)); + + // If the move is very short, check the E move distance + TERN_(HAS_EXTRUDERS, if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(total.e)); + + // No E move either? Game over. + if (UNEAR_ZERO(cartesian_mm)) return true; #if IS_KINEMATIC - const float seconds = cart_xy_mm / scaled_fr_mm_s; // Duration of XY move at requested rate + // Minimum number of seconds to move the given distance + const float seconds = cartesian_mm / scaled_fr_mm_s; + uint16_t segments = LROUND(segments_per_second * seconds), // Preferred number of segments for distance @ feedrate - seglimit = LROUND(cart_xy_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length + seglimit = LROUND(cartesian_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length + NOMORE(segments, seglimit); // Limit to minimum segment length (fewer segments) #else - uint16_t segments = LROUND(cart_xy_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Cartesian fixed segment length + uint16_t segments = LROUND(cartesian_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Cartesian fixed segment length #endif NOLESS(segments, 1U); // Must have at least one segment const float inv_segments = 1.0f / segments; // Reciprocal to save calculation // Add hints to help optimize the move - PlannerHints hints(SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments); // Length of each segment + PlannerHints hints(cartesian_mm * inv_segments); // Length of each segment #if ENABLED(FEEDRATE_SCALING) hints.inv_duration = scaled_fr_mm_s / hints.millimeters; #endif diff --git a/Marlin/src/gcode/bedlevel/G42.cpp b/Marlin/src/gcode/bedlevel/G42.cpp index 8383dd1a8de5..54ddd90f9212 100644 --- a/Marlin/src/gcode/bedlevel/G42.cpp +++ b/Marlin/src/gcode/bedlevel/G42.cpp @@ -54,6 +54,8 @@ void GcodeSuite::G42() { return; } + TERN_(FEEDRATE_MODE_SUPPORT, parser.linear_motion_gcode = true); + // Move to motion.position, as modified by I, J, P parameters motion.destination = motion.position; @@ -67,7 +69,7 @@ void GcodeSuite::G42() { } #endif - const feedRate_t fval = parser.linearval('F'), + const feedRate_t fval = parser.feedrateval('F'), fr_mm_s = MMM_TO_MMS(fval > 0 ? fval : 0.0f); // SCARA kinematic has "safe" XY raw moves @@ -76,6 +78,8 @@ void GcodeSuite::G42() { #else motion.prepare_internal_move_to_destination(fr_mm_s); #endif + + TERN_(FEEDRATE_MODE_SUPPORT, parser.linear_motion_gcode = false); } #endif // HAS_MESH diff --git a/Marlin/src/gcode/feature/camera/M240.cpp b/Marlin/src/gcode/feature/camera/M240.cpp index 7ce0a1934cac..bccae9268302 100644 --- a/Marlin/src/gcode/feature/camera/M240.cpp +++ b/Marlin/src/gcode/feature/camera/M240.cpp @@ -134,11 +134,11 @@ void GcodeSuite::M240() { #ifdef PHOTO_RETRACT_MM const float rval = parser.linearval('R', _PHOTO_RETRACT_MM); - const feedRate_t sval = parser.feedrateval('S', TERN(ADVANCED_PAUSE_FEATURE, PAUSE_PARK_RETRACT_FEEDRATE, TERN(FWRETRACT, RETRACT_FEEDRATE, 45))); + const feedRate_t sval = MMM_TO_MMS(parser.feedrateval('S', TERN(ADVANCED_PAUSE_FEATURE, PAUSE_PARK_RETRACT_FEEDRATE, TERN(FWRETRACT, RETRACT_FEEDRATE, 45)))); e_move_m240(-rval, sval); #endif - feedRate_t fr_mm_s = parser.feedrateval('F'); + feedRate_t fr_mm_s = MMM_TO_MMS(parser.feedrateval('F')); if (fr_mm_s) NOLESS(fr_mm_s, 10.0f); constexpr xyz_pos_t photo_position = PHOTO_POSITION; diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index e84b367e7af4..32ee47e3c0aa 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -199,6 +199,18 @@ void GcodeSuite::get_destination_from_command() { motion.destination.e = motion.position.e; #endif + #if HAS_ROTATIONAL_AXES || IS_KINEMATIC || HAS_LEVELING || ENABLED(FEEDRATE_MODE_SUPPORT) + const xyze_pos_t displacement = motion.destination - motion.position; + + parser.cartesian_mm = motion.get_move_distance(displacement OPTARG(HAS_ROTATIONAL_AXES, parser.cartes_move)); + + #if HAS_EXTRUDERS + if (NEAR_ZERO(parser.cartesian_mm)) { + parser.cartesian_mm = ABS(displacement.e); + } + #endif + #endif + #if ENABLED(POWER_LOSS_RECOVERY) && !PIN_EXISTS(POWER_LOSS) // Only update power loss recovery on moves with E if (recovery.enabled && card.isStillPrinting() && seen.e && (seen.x || seen.y)) @@ -206,7 +218,7 @@ void GcodeSuite::get_destination_from_command() { #endif if (parser.floatval('F') > 0) { - const float fr_mm_min = parser.value_linear_units(); + const float fr_mm_min = parser.value_feedrate(); motion.feedrate_mm_s = MMM_TO_MMS(fr_mm_min); // Update the cutter feed rate for use by M4 I set inline moves. TERN_(LASER_FEATURE, cutter.feedrate_mm_m = fr_mm_min); @@ -468,6 +480,11 @@ void GcodeSuite::process_parsed_command(bool no_ok/*=false*/) { case 92: G92(); break; // G92: Set current axis position(s) + #if ENABLED(FEEDRATE_MODE_SUPPORT) + case 93: G93(); break; // G93: Set feedrate mode to inverse time + case 94: G94(); break; // G94: Set feedrate mode to length units per minute + #endif + #if ENABLED(CALIBRATION_GCODE) case 425: G425(); break; // G425: Perform calibration with calibration cube #endif diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index d80305fb7556..638afe513167 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -649,6 +649,12 @@ class GcodeSuite { static void G92(); + + #if ENABLED(FEEDRATE_MODE_SUPPORT) + static void G93(); + static void G94(); + #endif + #if ENABLED(CALIBRATION_GCODE) static void G425(); #endif diff --git a/Marlin/src/gcode/motion/G0_G1.cpp b/Marlin/src/gcode/motion/G0_G1.cpp index 1106ce0acc13..8b7c8e647d93 100644 --- a/Marlin/src/gcode/motion/G0_G1.cpp +++ b/Marlin/src/gcode/motion/G0_G1.cpp @@ -54,17 +54,32 @@ void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) { #ifdef G0_FEEDRATE feedRate_t old_feedrate; #if ENABLED(VARIABLE_G0_FEEDRATE) + #if ENABLED(FEEDRATE_MODE_SUPPORT) + parser.linear_motion_gcode = true; + #endif if (fast_move) { old_feedrate = motion.feedrate_mm_s; // Back up the (old) motion mode feedrate motion.feedrate_mm_s = fast_move_feedrate; // Get G0 feedrate from last usage } + #elif ENABLED(FEEDRATE_MODE_SUPPORT) + if (fast_move) { + parser.linear_motion_gcode = false; + } + else { + parser.linear_motion_gcode = true; + } #endif + #elif ENABLED(FEEDRATE_MODE_SUPPORT) + parser.linear_motion_gcode = true; #endif get_destination_from_command(); // Get X Y [Z[I[J[K]]]] [E] F (and set cutter power) #ifdef G0_FEEDRATE if (fast_move) { + #if ENABLED(FEEDRATE_MODE_SUPPORT) + parser.linear_motion_gcode = false; + #endif #if ENABLED(VARIABLE_G0_FEEDRATE) fast_move_feedrate = motion.feedrate_mm_s; // Save feedrate for the next G0 #else @@ -104,6 +119,10 @@ void GcodeSuite::G0_G1(TERN_(HAS_FAST_MOVES, const bool fast_move/*=false*/)) { if (fast_move) motion.feedrate_mm_s = old_feedrate; #endif + #if ENABLED(FEEDRATE_MODE_SUPPORT) + parser.linear_motion_gcode = false; + #endif + #if ENABLED(NANODLP_Z_SYNC) #if ENABLED(NANODLP_ALL_AXIS) #define _MOVE_SYNC parser.seenval('X') || parser.seenval('Y') || parser.seenval('Z') // For any move wait and output sync message diff --git a/Marlin/src/gcode/motion/G2_G3.cpp b/Marlin/src/gcode/motion/G2_G3.cpp index 384a90fe6d09..af0812d2806e 100644 --- a/Marlin/src/gcode/motion/G2_G3.cpp +++ b/Marlin/src/gcode/motion/G2_G3.cpp @@ -235,7 +235,9 @@ void plan_arc( // Add hints to help optimize the move PlannerHints hints; - #if ENABLED(FEEDRATE_SCALING) + #if ENABLED(FEEDRATE_MODE_SUPPORT) + hints.inv_duration = segments * (parser.inverse_time_enabled ? scaled_fr_mm_s : (scaled_fr_mm_s / flat_mm)); + #elif ENABLED(FEEDRATE_SCALING) hints.inv_duration = (scaled_fr_mm_s / flat_mm) * segments; #endif @@ -426,7 +428,10 @@ void GcodeSuite::G2_G3(const bool clockwise) { if (motion.gcode_motion_ignored()) return; TERN_(FULL_REPORT_TO_HOST_FEATURE, motion.set_and_report_grblstate(M_RUNNING)); - + #if HAS_ROTATIONAL_AXES || IS_KINEMATIC || HAS_LEVELING || ENABLED(FEEDRATE_MODE_SUPPORT) + parser.linear_motion_gcode = false; + #endif + #if ENABLED(SF_ARC_FIX) const bool relative_mode_backup = motion.relative_mode; motion.relative_mode = true; diff --git a/Marlin/src/gcode/parser.cpp b/Marlin/src/gcode/parser.cpp index b692c818aae6..760aa9324d03 100644 --- a/Marlin/src/gcode/parser.cpp +++ b/Marlin/src/gcode/parser.cpp @@ -37,6 +37,18 @@ bool GCodeParser::volumetric_enabled; float GCodeParser::linear_unit_factor, GCodeParser::volumetric_unit_factor; #endif +#if HAS_ROTATIONAL_AXES || IS_KINEMATIC || HAS_LEVELING || ENABLED(FEEDRATE_MODE_SUPPORT) + bool GCodeParser::linear_motion_gcode; + float GCodeParser::cartesian_mm; + #if ENABLED(FEEDRATE_MODE_SUPPORT) + bool GCodeParser::inverse_time_enabled; + #endif + #if HAS_ROTATIONAL_AXES + bool GCodeParser::cartes_move; + #endif + +#endif + #if ENABLED(TEMPERATURE_UNITS_SUPPORT) TempUnit GCodeParser::input_temp_units = TEMPUNIT_C; #endif diff --git a/Marlin/src/gcode/parser.h b/Marlin/src/gcode/parser.h index 71cad6857dce..440d8bf38063 100644 --- a/Marlin/src/gcode/parser.h +++ b/Marlin/src/gcode/parser.h @@ -76,6 +76,17 @@ class GCodeParser { static float linear_unit_factor, volumetric_unit_factor; #endif + #if HAS_ROTATIONAL_AXES || IS_KINEMATIC || HAS_LEVELING || ENABLED(FEEDRATE_MODE_SUPPORT) + static float cartesian_mm; + static bool linear_motion_gcode; + #if ENABLED(FEEDRATE_MODE_SUPPORT) + static bool inverse_time_enabled; + #endif + #if HAS_ROTATIONAL_AXES + static bool cartes_move; + #endif + #endif + #if ENABLED(TEMPERATURE_UNITS_SUPPORT) static TempUnit input_temp_units; #endif @@ -415,7 +426,18 @@ class GCodeParser { #endif // !TEMPERATURE_UNITS_SUPPORT - static feedRate_t value_feedrate() { return MMM_TO_MMS(value_linear_units()); } + static feedRate_t value_feedrate() { + #if HAS_ROTATIONAL_AXES || ENABLED(FEEDRATE_MODE_SUPPORT) + float fr = ((TERN0(FEEDRATE_MODE_SUPPORT, inverse_time_enabled && linear_motion_gcode)) || TERN0(HAS_ROTATIONAL_AXES, (!cartes_move))) ? value_float() : value_linear_units(); + #if ENABLED(FEEDRATE_MODE_SUPPORT) + if (inverse_time_enabled && linear_motion_gcode) + fr *= cartesian_mm; + #endif + return fr; + #else + return value_linear_units(); + #endif + } void unknown_command_warning(); diff --git a/Marlin/src/gcode/units/G93_G94.cpp b/Marlin/src/gcode/units/G93_G94.cpp new file mode 100644 index 000000000000..ee98be274dde --- /dev/null +++ b/Marlin/src/gcode/units/G93_G94.cpp @@ -0,0 +1,47 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * @file G93_G94.cpp + * @author DerAndere + * @brief G93 (inverse time mode) and G94 (units per minute feedrate mode). + * + * Copyright 2025 DerAndere + */ + +#include "../../inc/MarlinConfig.h" + +#if ENABLED(FEEDRATE_MODE_SUPPORT) + +#include "../gcode.h" + +/** + * G93: Set feedrate mode to inverse time + */ +void GcodeSuite::G93() { parser.inverse_time_enabled = true; } + +/** + * G94: Set feedrate mode to length units per minute + */ +void GcodeSuite::G94() { parser.inverse_time_enabled = false; } + +#endif // FEEDRATE_MODE_SUPPORT diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index 0e96c355f5a0..4611c3953195 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -581,10 +581,8 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L */ #if ENABLED(SOFT_FEED_HOLD) && !defined(FREEZE_JERK) #error "SOFT_FEED_HOLD requires FREEZE_JERK." -#elif ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) && !(defined(FREEZE_PIN) && defined(FREEZE_STATE)) +#elif ENABLED(FREEZE_FEATURE) && !(defined(FREEZE_PIN) && defined(FREEZE_STATE)) #error "FREEZE_FEATURE requires FREEZE_PIN and FREEZE_STATE." -#elif ENABLED(NO_FREEZE_PIN) && !(defined(REALTIME_REPORTING_COMMANDS)) - #error "NO_FREEZE_PIN requires REALTIME_REPORTING_COMMANDS." #endif /** @@ -875,6 +873,13 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L #endif #endif +/** + * Feedrate mode requirements + */ +#if ALL(FEEDRATE_MODE_SUPPORT, BEZIER_CURVE_SUPPORT) + #error "FEEDRATE_MODE_SUPPORT is currently incompatible with BEZIER_CURVE_SUPPORT." +#endif + /** * Special tool-changing options */ diff --git a/Marlin/src/lcd/marlinui.cpp b/Marlin/src/lcd/marlinui.cpp index f475b52efc31..78718bce9ad9 100644 --- a/Marlin/src/lcd/marlinui.cpp +++ b/Marlin/src/lcd/marlinui.cpp @@ -857,13 +857,6 @@ void MarlinUI::init() { const feedRate_t fr_mm_s = (axis < LOGICAL_AXES) ? manual_feedrate_mm_s[axis] : PLANNER_XY_FEEDRATE_MM_S; - /** - * For a rotational axis apply the "inch" to "mm" conversion factor. This mimics behaviour of the G-code G1 - * (see get_destination_from_command). For moves involving only rotational axes, the planner will convert - * back to the feedrate in degrees-per-time unit. - */ - const feedRate_t fr = parser.axis_is_rotational(axis) && parser.using_inch_units() ? IN_TO_MM(fr_mm_s) : fr_mm_s; - #if IS_KINEMATIC #if HAS_MULTI_EXTRUDER @@ -890,13 +883,13 @@ void MarlinUI::init() { // previous invocation is being blocked. Modifications to offset shouldn't be made while // processing is true or the planner will get out of sync. processing = true; - motion.prepare_internal_move_to_destination(fr); // will set motion.position from destination + motion.prepare_internal_move_to_destination(fr_mm_s); // will set current_position from destination processing = false; #else - // For Cartesian / Core motion simply move to the motion.position - planner.buffer_line(motion.position, fr, + // For Cartesian / Core motion simply move to the current_position + planner.buffer_line(motion.position, fr_mm_s, TERN_(MULTI_E_MANUAL, axis == E_AXIS ? e_index :) motion.extruder ); diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index b401090d8722..519d0712514b 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1544,16 +1544,23 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE #endif #if HAS_ROTATIONAL_AXES + if (UNEAR_ZERO(distance_sqr)) { // Move involves no linear axes. Calculate angular distance in accordance with LinuxCNC distance_sqr = ROTATIONAL_AXIS_GANG(sq(diff.i), + sq(diff.j), + sq(diff.k), + sq(diff.u), + sq(diff.v), + sq(diff.w)); + if (!UNEAR_ZERO(distance_sqr)) { + // Move involves rotational axes, not just the extruder + is_cartesian_move = false; + } + else { + // Move involves just the extruder + is_cartesian_move = true; + } } - if (!UNEAR_ZERO(distance_sqr)) { - // Move involves rotational axes, not just the extruder - is_cartesian_move = false; + else { + is_cartesian_move = true; } #endif - #endif return SQRT(distance_sqr); @@ -1618,12 +1625,12 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE // Fail if attempting move outside printable radius if (!can_reach(destination)) return true; - - // Get the linear distance in XYZ #if HAS_ROTATIONAL_AXES - bool cartes_move = true; + bool cartesian_move = true; #endif - float cartesian_mm = get_move_distance(diff OPTARG(HAS_ROTATIONAL_AXES, cartes_move)); + float cartesian_mm = parser.cartesian_mm; + if (!parser.linear_motion_gcode) + cartesian_mm = motion.get_move_distance(diff OPTARG(HAS_ROTATIONAL_AXES, cartesian_move)); // If the move is very short, check the E move distance TERN_(HAS_EXTRUDERS, if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(diff.e)); @@ -1632,13 +1639,7 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE if (UNEAR_ZERO(cartesian_mm)) return true; // Minimum number of seconds to move the given distance - const float seconds = cartesian_mm / ( - #if ALL(HAS_ROTATIONAL_AXES, INCH_MODE_SUPPORT) - cartes_move ? scaled_fr_mm_s : LINEAR_UNIT(scaled_fr_mm_s) - #else - scaled_fr_mm_s - #endif - ); + const float seconds = cartesian_mm / scaled_fr_mm_s; // The number of segments-per-second times the duration // gives the number of segments @@ -1660,9 +1661,9 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE // Add hints to help optimize the move PlannerHints hints(cartesian_mm * inv_segments); - TERN_(HAS_ROTATIONAL_AXES, hints.cartesian_move = cartes_move); - TERN_(FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters); - + #if ENABLED(FEEDRATE_SCALING) + hints.inv_duration = scaled_fr_mm_s / hints.millimeters; + #endif /* SERIAL_ECHOPGM("mm=", cartesian_mm); SERIAL_ECHOPGM(" seconds=", seconds); @@ -1710,11 +1711,11 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE return; } - // Get the linear distance in XYZ #if HAS_ROTATIONAL_AXES - bool cartes_move = true; + bool cartesian_move = true; #endif - float cartesian_mm = get_move_distance(diff OPTARG(HAS_ROTATIONAL_AXES, cartes_move)); + // Get the move distance + float cartesian_mm = get_move_distance(diff OPTARG(HAS_ROTATIONAL_AXES, cartesian_move)); // If the move is very short, check the E move distance TERN_(HAS_EXTRUDERS, if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(diff.e)); @@ -1733,7 +1734,6 @@ float Motion::get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXE // Add hints to help optimize the move PlannerHints hints(cartesian_mm * inv_segments); - TERN_(HAS_ROTATIONAL_AXES, hints.cartesian_move = cartes_move); TERN_(FEEDRATE_SCALING, hints.inv_duration = scaled_fr_mm_s / hints.millimeters); //SERIAL_ECHOPGM("mm=", cartesian_mm); diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 8f3815a3fcbc..a7021955517e 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -2022,7 +2022,7 @@ bool Planner::_populate_block( TERN_(FT_MOTION, block->ext_distance_mm = dist_mm); // Store the distance for all axes in mm for this block #if HAS_ROTATIONAL_AXES - bool cartesian_move = hints.cartesian_move; + bool cartesian_move = true; #endif // Determine linear distance for block->millimeters @@ -2172,11 +2172,12 @@ bool Planner::_populate_block( } #endif // HAS_EXTRUDERS - if (esteps) - NOLESS(fr_mm_s, settings.min_feedrate_mm_s); - else - NOLESS(fr_mm_s, settings.min_travel_feedrate_mm_s); - + if (esteps) { + NOLESS(fr_mm_s, settings.min_feedrate_mm_s); + } + else { + NOLESS(fr_mm_s, settings.min_travel_feedrate_mm_s); + } const float inverse_millimeters = 1.0f / block->millimeters; // Inverse millimeters to remove multiple divides /** @@ -2184,15 +2185,12 @@ bool Planner::_populate_block( * EXAMPLE: At 120mm/s a 60mm move involving XYZ axes takes 0.5s. So this will give 2.0. * EXAMPLE: At 120°/s a 60° move involving only rotational axes takes 0.5s. So this will give 2.0. */ - float inverse_secs = inverse_millimeters * ( - #if ALL(HAS_ROTATIONAL_AXES, INCH_MODE_SUPPORT) - /** - * Workaround for premature feedrate conversion - * from in/s to mm/s by get_distance_from_command. - */ - cartesian_move ? fr_mm_s : LINEAR_UNIT(fr_mm_s) + + float inverse_secs = ( + #if ANY(FEEDRATE_SCALING, FEEDRATE_MODE_SUPPORT) + hints.inv_duration ? : inverse_millimeters * fr_mm_s #else - fr_mm_s + inverse_millimeters * fr_mm_s #endif ); @@ -2202,7 +2200,7 @@ bool Planner::_populate_block( // Slow down when the buffer starts to empty, rather than wait at the corner for a buffer refill #if ANY(SLOWDOWN, HAS_WIRED_LCD) || defined(XY_FREQUENCY_LIMIT) // Segment time in microseconds - int32_t segment_time_us = LROUND(1000000.0f / inverse_secs); + int32_t segment_time_us = LROUND(1000000.0f * RECIPROCAL(inverse_secs)); #endif #if ENABLED(SLOWDOWN) @@ -2981,10 +2979,13 @@ bool Planner::buffer_line(const xyze_pos_t &cart, const feedRate_t fr_mm_s // Cartesian XYZ to kinematic ABC, stored in global 'delta' inverse_kinematics(machine); + #if HAS_ROTATIONAL_AXES + bool cartesian_move = true; + #endif // Provide known Cartesian length in the hints structure PlannerHints ph = hints; if (!hints.millimeters) - ph.millimeters = motion.get_move_distance(xyze_pos_t(cart_dist_mm) OPTARG(HAS_ROTATIONAL_AXES, ph.cartesian_move)); + ph.millimeters = motion.get_move_distance(xyze_pos_t(cart_dist_mm) OPTARG(HAS_ROTATIONAL_AXES, cartesian_move)); #if DISABLED(FEEDRATE_SCALING) diff --git a/Marlin/src/module/planner.h b/Marlin/src/module/planner.h index a9d4c37afacb..0af227778090 100644 --- a/Marlin/src/module/planner.h +++ b/Marlin/src/module/planner.h @@ -469,7 +469,7 @@ typedef struct PlannerSettings { struct PlannerHints { float millimeters = 0.0; // Move Length, if known, else 0. - #if ENABLED(FEEDRATE_SCALING) + #if ANY(FEEDRATE_SCALING, FEEDRATE_MODE_SUPPORT) float inv_duration = 0.0; // Reciprocal of the move duration, if known #endif #if ENABLED(HINTS_CURVE_RADIUS) @@ -483,11 +483,6 @@ struct PlannerHints { // would calculate if it knew the as-yet-unbuffered path #endif - #if HAS_ROTATIONAL_AXES - bool cartesian_move = true; // True if linear motion of the tool centerpoint relative to the workpiece occurs. - // False if no movement of the tool center point relative to the work piece occurs - // (i.e. the tool rotates around the tool centerpoint) - #endif PlannerHints(const float mm=0.0f) : millimeters(mm) {} }; diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index b92d8981577b..c9c2e4e71bdb 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -3921,9 +3921,7 @@ void Stepper::report_positions() { cutter.apply_power(frozen_last_laser_power); // Restore frozen laser power #endif - #if ENABLED(REALTIME_REPORTING_COMMANDS) - set_and_report_grblstate(state ? M_HOLD : M_RUNNING); - #endif + TERN_(FULL_REPORT_TO_HOST_FEATURE, motion.set_and_report_grblstate(state ? M_HOLD : M_RUNNING)); } void Stepper::check_frozen_time(uint32_t &step_rate) { diff --git a/ini/features.ini b/ini/features.ini index 03a9bdbba8d1..fbbeacd7bb2d 100644 --- a/ini/features.ini +++ b/ini/features.ini @@ -368,6 +368,7 @@ HAS_TEMP_PROBE = build_src_filter=+ MPCTEMP = build_src_filter=+ INCH_MODE_SUPPORT = build_src_filter=+ +FEEDRATE_MODE_SUPPORT = build_src_filter=+ TEMPERATURE_UNITS_SUPPORT = build_src_filter=+ NEED_HEX_PRINT = build_src_filter=+ NEED_LSF = build_src_filter=+