Skip to content

Commit 65d27fa

Browse files
committed
FEEDRATE_MODE_SUPPORT. Adds G93 inverse time mode and G94 units per minute mode
1 parent 02e1199 commit 65d27fa

File tree

19 files changed

+302
-57
lines changed

19 files changed

+302
-57
lines changed

Marlin/Configuration.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2522,6 +2522,11 @@
25222522
//
25232523
//#define INCH_MODE_SUPPORT
25242524

2525+
//
2526+
// G93/G94 Feedrate mode support
2527+
//
2528+
//#define FEEDRATE_MODE_SUPPORT
2529+
25252530
//
25262531
// M149 Set temperature units support
25272532
//

Marlin/src/core/language.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@
148148
#define STR_ERR_MATERIAL_INDEX "M145 S<index> out of range (0-1)"
149149
#define STR_ERR_M421_PARAMETERS "M421 incorrect parameter usage"
150150
#define STR_ERR_BAD_PLANE_MODE "G5 requires XY plane mode"
151+
#define STR_ERR_BAD_FEEDRATE_MODE "G5 currently requires units/min feedrate mode"
151152
#define STR_ERR_MESH_XY "Mesh point out of range"
152153
#define STR_ERR_ARC_ARGS "G2/G3 bad parameters"
153154
#define STR_ERR_PROTECTED_PIN "Protected Pin"

Marlin/src/feature/bedlevel/abl/bbl.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ void LevelingBilinear::extrapolate_one_point(const uint8_t x, const uint8_t y, c
7878
const float a = 2 * a1 - a2, b = 2 * b1 - b2, c = 2 * c1 - c2;
7979

8080
// Take the average instead of the median
81-
z_values[x][y] = (a + b + c) / 3.0;
81+
z_values[x][y] = (a + b + c) / 3.0f;
8282
TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y]));
8383

8484
// Median is robust (ignores outliers).
@@ -369,7 +369,7 @@ float LevelingBilinear::get_z_correction(const xy_pos_t &raw) {
369369
* Prepare a bilinear-leveled linear move on Cartesian,
370370
* splitting the move where it crosses grid borders.
371371
*/
372-
void LevelingBilinear::line_to_destination(const feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) {
372+
void LevelingBilinear::line_to_destination(const feedRate_t scaled_fr, uint16_t x_splits, uint16_t y_splits) {
373373
// Get current and destination cells for this line
374374
xy_int_t c1 { CELL_INDEX(x, current_position.x), CELL_INDEX(y, current_position.y) },
375375
c2 { CELL_INDEX(x, destination.x), CELL_INDEX(y, destination.y) };
@@ -385,6 +385,28 @@ float LevelingBilinear::get_z_correction(const xy_pos_t &raw) {
385385
return;
386386
}
387387

388+
389+
const xyze_pos_t total = destination - current_position;
390+
391+
#if ENABLED(FEEDRATE_MODE_SUPPORT)
392+
// Get the linear distance in XYZ
393+
#if HAS_ROTATIONAL_AXES
394+
bool cartes_move = true;
395+
#endif
396+
float cartesian_mm = get_move_distance(total OPTARG(HAS_ROTATIONAL_AXES, cartes_move));
397+
398+
// If the move is very short, check the E move distance
399+
TERN_(HAS_EXTRUDERS, if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(total.e));
400+
401+
const bool old_inverse_time_enabled = parser.inverse_time_enabled;
402+
403+
const feedrate_t scaled_fr_mm_s = (old_inverse_time_enabled && parser.print_move) ? cartesian_mm * scaled_fr : scaled_fr;
404+
parser.inverse_time_enabled = false;
405+
406+
#else
407+
const feedrate_t scaled_fr_mm_s = scaled_fr;
408+
#endif
409+
388410
#define LINE_SEGMENT_END(A) (current_position.A + (destination.A - current_position.A) * normalized_dist)
389411

390412
float normalized_dist;
@@ -415,6 +437,7 @@ float LevelingBilinear::get_z_correction(const xy_pos_t &raw) {
415437
// This should be a rare case.
416438
current_position = destination;
417439
line_to_current_position(scaled_fr_mm_s);
440+
parser.inverse_time_enabled = old_inverse_time_enabled;
418441
return;
419442
}
420443

@@ -427,6 +450,7 @@ float LevelingBilinear::get_z_correction(const xy_pos_t &raw) {
427450
// Restore destination from stack
428451
destination = end;
429452
line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
453+
parser.inverse_time_enabled = old_inverse_time_enabled;
430454
}
431455

432456
#endif // IS_CARTESIAN && !SEGMENT_LEVELED_MOVES

Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,16 @@
6464
void mesh_bed_leveling::line_to_destination(const feedRate_t scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) {
6565
// Get current and destination cells for this line
6666
xy_uint8_t scel = cell_indexes(current_position), ecel = cell_indexes(destination);
67-
NOMORE(scel.x, GRID_MAX_CELLS_X - 1);
68-
NOMORE(scel.y, GRID_MAX_CELLS_Y - 1);
69-
NOMORE(ecel.x, GRID_MAX_CELLS_X - 1);
70-
NOMORE(ecel.y, GRID_MAX_CELLS_Y - 1);
67+
68+
segments = ecel - scel;
69+
NOLESS(segments, 1);
70+
71+
segment_fr = (parser.inverse_time_enabled && parser.print_move) ? scaled_fr_mm_s * segments : scaled_fr_mm_s;
7172

7273
// Start and end in the same cell? No split needed.
7374
if (scel == ecel) {
7475
current_position = destination;
75-
line_to_current_position(scaled_fr_mm_s);
76+
line_to_current_position(segment_fr);
7677
return;
7778
}
7879

@@ -105,19 +106,19 @@
105106
// Must already have been split on these border(s)
106107
// This should be a rare case.
107108
current_position = destination;
108-
line_to_current_position(scaled_fr_mm_s);
109+
line_to_current_position(segment_fr);
109110
return;
110111
}
111112

112113
destination.z = MBL_SEGMENT_END(z);
113114
destination.e = MBL_SEGMENT_END(e);
114115

115116
// Do the split and look for more borders
116-
line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
117+
line_to_destination(segment_fr, x_splits, y_splits);
117118

118119
// Restore destination from stack
119120
destination = dest;
120-
line_to_destination(scaled_fr_mm_s, x_splits, y_splits);
121+
line_to_destination(segment_fr, x_splits, y_splits);
121122
}
122123

123124
#endif // IS_CARTESIAN && !SEGMENT_LEVELED_MOVES

Marlin/src/feature/bedlevel/ubl/ubl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ class unified_bed_leveling {
297297
#if UBL_SEGMENTED
298298
static bool line_to_destination_segmented(const feedRate_t scaled_fr_mm_s);
299299
#else
300-
static void line_to_destination_cartesian(const feedRate_t scaled_fr_mm_s, const uint8_t e);
300+
static void line_to_destination_cartesian(const feedRate_t scaled_fr, const uint8_t e);
301301
#endif
302302

303303
static bool mesh_is_valid() {

Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
// corners of cells. To fix the issue, simply check if the start/end of the line
4747
// is very close to a cell boundary in advance and don't split the line there.
4848

49-
void unified_bed_leveling::line_to_destination_cartesian(const feedRate_t scaled_fr_mm_s, const uint8_t extruder) {
49+
void unified_bed_leveling::line_to_destination_cartesian(const feedRate_t scaled_fr, const uint8_t extruder) {
5050
/**
5151
* Much of the nozzle movement will be within the same cell. So we will do as little computation
5252
* as possible to determine if this is the case. If this move is within the same cell, we will
@@ -60,6 +60,27 @@
6060
const xyze_pos_t &start = current_position, &end = destination;
6161
#endif
6262

63+
const xyze_pos_t total = end - start;
64+
65+
#if ENABLED(FEEDRATE_MODE_SUPPORT)
66+
// Get the linear distance in XYZ
67+
#if HAS_ROTATIONAL_AXES
68+
bool cartes_move = true;
69+
#endif
70+
float cartesian_mm = get_move_distance(total OPTARG(HAS_ROTATIONAL_AXES, cartes_move));
71+
72+
// If the move is very short, check the E move distance
73+
TERN_(HAS_EXTRUDERS, if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(total.e));
74+
75+
const bool old_inverse_time_enabled = parser.inverse_time_enabled;
76+
77+
const feedRate_t scaled_fr_mm_s = (old_inverse_time_enabled && parser.print_move) ? cartesian_mm * scaled_fr : scaled_fr;
78+
parser.inverse_time_enabled = false;
79+
80+
#else
81+
const feedRate_t scaled_fr_mm_s = scaled_fr;
82+
#endif
83+
6384
const xy_uint8_t istart = cell_indexes(start), iend = cell_indexes(end);
6485

6586
// A move within the same cell needs no splitting
@@ -79,6 +100,7 @@
79100
end.z += UBL_Z_RAISE_WHEN_OFF_MESH;
80101
planner.buffer_segment(end, scaled_fr_mm_s, extruder);
81102
current_position = destination;
103+
TERN_(FEEDRATE_MODE_SUPPORT, parser.inverse_time_enabled = old_inverse_time_enabled);
82104
return;
83105
}
84106
#endif
@@ -97,6 +119,7 @@
97119
if (!isnan(z0)) end.z += z0;
98120
planner.buffer_segment(end, scaled_fr_mm_s, extruder);
99121
current_position = destination;
122+
TERN_(FEEDRATE_MODE_SUPPORT, parser.inverse_time_enabled = old_inverse_time_enabled);
100123
return;
101124
}
102125

@@ -105,7 +128,7 @@
105128
* case - crossing only one X or Y line - after details are worked out to reduce computation.
106129
*/
107130

108-
const xy_float_t dist = end - start;
131+
const xy_float_t dist = xy_float:t(total);
109132
const xy_bool_t neg { dist.x < 0, dist.y < 0 };
110133
const xy_uint8_t ineg { uint8_t(neg.x), uint8_t(neg.y) };
111134
const xy_float_t sign { neg.x ? -1.0f : 1.0f, neg.y ? -1.0f : 1.0f };
@@ -194,6 +217,7 @@
194217
goto FINAL_MOVE;
195218

196219
current_position = destination;
220+
TERN_(FEEDRATE_MODE_SUPPORT, parser.inverse_time_enabled = old_inverse_time_enabled);
197221
return;
198222
}
199223

@@ -357,24 +381,58 @@
357381

358382
const xyze_pos_t total = destination - current_position;
359383

360-
const float cart_xy_mm_2 = HYPOT2(total.x, total.y),
361-
cart_xy_mm = SQRT(cart_xy_mm_2); // Total XY distance
384+
// If the move is only in Z/E don't split up the move
385+
if (!total.x && !total.y) {
386+
planner.buffer_line(destination, scaled_fr_mm_s);
387+
return false; // caller will update current_position
388+
}
389+
390+
#if HAS_ROTATIONAL_AXES
391+
bool cartes_move = true;
392+
#endif
393+
float cartesian_mm = get_move_distance(total OPTARG(HAS_ROTATIONAL_AXES, cartes_move));
394+
395+
// If the move is very short, check the E move distance
396+
TERN_(HAS_EXTRUDERS, if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(total.e));
397+
398+
// No E move either? Game over.
399+
if (UNEAR_ZERO(cartesian_mm)) return true;
362400

363401
#if IS_KINEMATIC
364-
const float seconds = cart_xy_mm / scaled_fr_mm_s; // Duration of XY move at requested rate
402+
// Minimum number of seconds to move the given distance
403+
#if ENABLED(FEEDRATE_MODE_SUPPORT)
404+
const float seconds = (parser.print_move && parser.inverse_time_enabled) ? RECIPROCAL(scaled_fr_mm_s) : cartesian_mm / (
405+
#if ALL(HAS_ROTATIONAL_AXES, INCH_MODE_SUPPORT)
406+
cartes_move ? scaled_fr_mm_s : LINEAR_UNIT(scaled_fr_mm_s)
407+
#else
408+
scaled_fr_mm_s
409+
#endif
410+
);
411+
#else
412+
const float seconds = cartesian_mm / (
413+
#if ALL(HAS_ROTATIONAL_AXES, INCH_MODE_SUPPORT)
414+
cartes_move ? scaled_fr_mm_s : LINEAR_UNIT(scaled_fr_mm_s)
415+
#else
416+
scaled_fr_mm_s
417+
#endif
418+
);
419+
#endif
365420
uint16_t segments = LROUND(segments_per_second * seconds), // Preferred number of segments for distance @ feedrate
366-
seglimit = LROUND(cart_xy_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length
421+
seglimit = LROUND(cartesian_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Number of segments at minimum segment length
422+
367423
NOMORE(segments, seglimit); // Limit to minimum segment length (fewer segments)
368424
#else
369-
uint16_t segments = LROUND(cart_xy_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Cartesian fixed segment length
425+
uint16_t segments = LROUND(cartesian_mm * RECIPROCAL(SEGMENT_MIN_LENGTH)); // Cartesian fixed segment length
370426
#endif
371427

372428
NOLESS(segments, 1U); // Must have at least one segment
373429
const float inv_segments = 1.0f / segments; // Reciprocal to save calculation
374430

375431
// Add hints to help optimize the move
376-
PlannerHints hints(SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments); // Length of each segment
377-
#if ENABLED(FEEDRATE_SCALING)
432+
PlannerHints hints(cartesian_mm * inv_segments); // Length of each segment
433+
#if IS_KINEMATIC && ENABLED(FEEDRATE_MODE_SUPPORT)
434+
hints.inv_duration = segments / seconds;
435+
#elif ENABLED(FEEDRATE_SCALING)
378436
hints.inv_duration = scaled_fr_mm_s / hints.millimeters;
379437
#endif
380438

Marlin/src/gcode/bedlevel/G42.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ void GcodeSuite::G42() {
5454
return;
5555
}
5656

57+
TERN_(FEEDRATE_MODE_SUPPORT, parser.print_move = true);
58+
5759
// Move to current_position, as modified by I, J, P parameters
5860
destination = current_position;
5961

@@ -67,15 +69,17 @@ void GcodeSuite::G42() {
6769
}
6870
#endif
6971

70-
const feedRate_t fval = parser.linearval('F'),
71-
fr_mm_s = MMM_TO_MMS(fval > 0 ? fval : 0.0f);
72+
const feedRate_t fval = parser.feedrateval('F'),
73+
fr_mm_s = MMM_TO_MMS(fval);
7274

7375
// SCARA kinematic has "safe" XY raw moves
7476
#if IS_SCARA
7577
prepare_internal_fast_move_to_destination(fr_mm_s);
7678
#else
7779
prepare_internal_move_to_destination(fr_mm_s);
7880
#endif
81+
82+
TERN_(FEEDRATE_MODE_SUPPORT, parser.print_move = false);
7983
}
8084

8185
#endif // HAS_MESH

Marlin/src/gcode/feature/camera/M240.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,12 @@ void GcodeSuite::M240() {
134134

135135
#ifdef PHOTO_RETRACT_MM
136136
const float rval = parser.linearval('R', _PHOTO_RETRACT_MM);
137-
const feedRate_t sval = parser.feedrateval('S', TERN(ADVANCED_PAUSE_FEATURE, PAUSE_PARK_RETRACT_FEEDRATE, TERN(FWRETRACT, RETRACT_FEEDRATE, 45)));
137+
const feedRate_t sval = MMM_TO_MMS(parser.feedrateval('S', TERN(ADVANCED_PAUSE_FEATURE, PAUSE_PARK_RETRACT_FEEDRATE, TERN(FWRETRACT, RETRACT_FEEDRATE, 45))));
138138
e_move_m240(-rval, sval);
139139
#endif
140140

141-
feedRate_t fr_mm_s = parser.feedrateval('F');
142-
if (fr_mm_s) NOLESS(fr_mm_s, 10.0f);
141+
feedRate_t fr_mm_s = MMM_TO_MMS(parser.feedrateval('F'));
142+
if (fr_mm_s) NOLESS(fr_mm_s, TERN(FEEDRATE_MODE_SUPPORT, 0.01f, 10.0f));
143143

144144
constexpr xyz_pos_t photo_position = PHOTO_POSITION;
145145
xyz_pos_t raw = {

Marlin/src/gcode/gcode.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ void GcodeSuite::get_destination_from_command() {
206206
#endif
207207

208208
if (parser.floatval('F') > 0) {
209-
const float fr_mm_min = parser.value_linear_units();
209+
const float fr_mm_min = parser.value_feedrate();
210210
feedrate_mm_s = MMM_TO_MMS(fr_mm_min);
211211
// Update the cutter feed rate for use by M4 I set inline moves.
212212
TERN_(LASER_FEATURE, cutter.feedrate_mm_m = fr_mm_min);
@@ -468,6 +468,11 @@ void GcodeSuite::process_parsed_command(bool no_ok/*=false*/) {
468468

469469
case 92: G92(); break; // G92: Set current axis position(s)
470470

471+
#if ENABLED(FEEDRATE_MODE_SUPPORT)
472+
case 93: G93(); break; // G93: Set feedrate mode to inverse time
473+
case 94: G94(); break; // G94: Set feedrate mode to length units per minute
474+
#endif
475+
471476
#if ENABLED(CALIBRATION_GCODE)
472477
case 425: G425(); break; // G425: Perform calibration with calibration cube
473478
#endif

Marlin/src/gcode/gcode.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,12 @@ class GcodeSuite {
649649

650650
static void G92();
651651

652+
653+
#if ENABLED(FEEDRATE_MODE_SUPPORT)
654+
static void G93();
655+
static void G94();
656+
#endif
657+
652658
#if ENABLED(CALIBRATION_GCODE)
653659
static void G425();
654660
#endif

0 commit comments

Comments
 (0)