Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better support for "infinite" axes: Enable G92.9 (directly set current position) for rotational axes #26174

Merged
merged 17 commits into from
Mar 2, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 24 additions & 8 deletions Marlin/src/core/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -937,23 +937,19 @@ class AxisBits {
typedef bits_t(NUM_AXIS_ENUMS) el;
union {
el bits;
// x, y, z ... e0, e1, e2 ... hx, hy, hz
// Axes x, y, z ... e0, e1, e2 ... hx, hy, hz
struct {
#if NUM_AXES
bool NUM_AXIS_LIST(x:1, y:1, z:1, i:1, j:1, k:1, u:1, v:1, w:1);
#endif
bool NUM_AXIS_LIST(x:1, y:1, z:1, i:1, j:1, k:1, u:1, v:1, w:1);
#define _EN_ITEM(N) bool e##N:1;
REPEAT(EXTRUDERS,_EN_ITEM)
#undef _EN_ITEM
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
bool hx:1, hy:1, hz:1;
#endif
};
// X, Y, Z ... E0, E1, E2 ... HX, HY, HZ
// Axes X, Y, Z ... E0, E1, E2 ... HX, HY, HZ
struct {
#if NUM_AXES
bool NUM_AXIS_LIST(X:1, Y:1, Z:1, I:1, J:1, K:1, U:1, V:1, W:1);
#endif
bool NUM_AXIS_LIST(X:1, Y:1, Z:1, I:1, J:1, K:1, U:1, V:1, W:1);
#define _EN_ITEM(N) bool E##N:1;
REPEAT(EXTRUDERS,_EN_ITEM)
#undef _EN_ITEM
Expand Down Expand Up @@ -985,6 +981,26 @@ class AxisBits {
bool HA:1, HB:1, HC:1;
#endif
};
// Joints ja, jb, jc ... je0, je1, je2 ... jha, jhb, jhc
struct {
bool NUM_AXIS_LIST(ja:1, jb:1, jc:1, ji:1, jj:1, jk:1, ju:1, jv:1, jw:1);
#define _EN_ITEM(N) bool je##N:1;
REPEAT(EXTRUDERS,_EN_ITEM)
#undef _EN_ITEM
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
bool jha:1, jhb:1, jhc:1;
#endif
};
// Joints JA, JB, JC ... JE0, JE1, JE2 ... JHA, JHB, JHC
struct {
bool NUM_AXIS_LIST(JA:1, JB:1, JC:1, JI:1, JJ:1, JK:1, JU:1, JV:1, JW:1);
#define _EN_ITEM(N) bool JE##N:1;
REPEAT(EXTRUDERS,_EN_ITEM)
#undef _EN_ITEM
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
bool JHA:1, JHB:1, JHC:1;
#endif
};
};

AxisBits() { reset(); }
Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/gcode/geometry/G92.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
* G92 : Modify Workspace Offsets so the reported position shows the given X [Y [Z [A [B [C [U [V [W ]]]]]]]] [E].
* G92.1 : Zero XYZ Workspace Offsets (so the reported position = the native position).
*
* With POWER_LOSS_RECOVERY:
* With POWER_LOSS_RECOVERY or with AXISn_ROTATES:
* G92.9 : Set NATIVE Current Position to the given X [Y [Z [A [B [C [U [V [W ]]]]]]]] [E].
*/
void GcodeSuite::G92() {
Expand All @@ -67,7 +67,7 @@ void GcodeSuite::G92() {
break;
#endif

#if ENABLED(POWER_LOSS_RECOVERY)
#if ANY(POWER_LOSS_RECOVERY, HAS_ROTATIONAL_AXES)
case 9: // G92.9 - Set Current Position directly (like Marlin 1.0)
LOOP_LOGICAL_AXES(i) {
if (parser.seenval(AXIS_CHAR(i))) {
Expand Down
32 changes: 18 additions & 14 deletions Marlin/src/gcode/parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,17 @@ class GCodeParser {
// Bool is true with no value or non-zero
static bool value_bool() { return !has_value() || !!value_byte(); }

static constexpr bool axis_is_rotational(const AxisEnum axis) {
return (false
|| TERN0(AXIS4_ROTATES, axis == I_AXIS)
|| TERN0(AXIS5_ROTATES, axis == J_AXIS)
|| TERN0(AXIS6_ROTATES, axis == K_AXIS)
|| TERN0(AXIS7_ROTATES, axis == U_AXIS)
|| TERN0(AXIS8_ROTATES, axis == V_AXIS)
|| TERN0(AXIS9_ROTATES, axis == W_AXIS)
);
}

// Units modes: Inches, Fahrenheit, Kelvin

#if ENABLED(INCH_MODE_SUPPORT)
Expand All @@ -307,14 +318,7 @@ class GCodeParser {
}

static float axis_unit_factor(const AxisEnum axis) {
if (false
|| TERN0(AXIS4_ROTATES, axis == I_AXIS)
|| TERN0(AXIS5_ROTATES, axis == J_AXIS)
|| TERN0(AXIS6_ROTATES, axis == K_AXIS)
|| TERN0(AXIS7_ROTATES, axis == U_AXIS)
|| TERN0(AXIS8_ROTATES, axis == V_AXIS)
|| TERN0(AXIS9_ROTATES, axis == W_AXIS)
) return 1.0f;
if (axis_is_rotational(axis)) return 1.0f;
#if HAS_EXTRUDERS
if (axis >= E_AXIS && volumetric_enabled) return volumetric_unit_factor;
#endif
Expand All @@ -327,12 +331,12 @@ class GCodeParser {

#else

static float mm_to_linear_unit(const_float_t mm) { return mm; }
static float mm_to_volumetric_unit(const_float_t mm) { return mm; }
static constexpr float mm_to_linear_unit(const_float_t mm) { return mm; }
static constexpr float mm_to_volumetric_unit(const_float_t mm) { return mm; }

static float linear_value_to_mm(const_float_t v) { return v; }
static float axis_value_to_mm(const AxisEnum, const float v) { return v; }
static float per_axis_value(const AxisEnum, const float v) { return v; }
static constexpr float linear_value_to_mm(const_float_t v) { return v; }
static constexpr float axis_value_to_mm(const AxisEnum, const float v) { return v; }
static constexpr float per_axis_value(const AxisEnum, const float v) { return v; }

#endif

Expand Down Expand Up @@ -402,7 +406,7 @@ class GCodeParser {

#else // !TEMPERATURE_UNITS_SUPPORT

static float to_temp_units(int16_t c) { return (float)c; }
static constexpr float to_temp_units(int16_t c) { return (float)c; }

static celsius_t value_celsius() { return value_int(); }
static celsius_t value_celsius_diff() { return value_int(); }
Expand Down
4 changes: 4 additions & 0 deletions Marlin/src/inc/Conditionals_LCD.h
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,10 @@
#define ROTATIONAL_AXES 0
#endif

#if ROTATIONAL_AXES
#define HAS_ROTATIONAL_AXES 1
#endif

/**
* Number of Secondary Linear Axes (e.g., UVW)
* All secondary axes for which AXIS*_ROTATES is not defined.
Expand Down
5 changes: 5 additions & 0 deletions Marlin/src/inc/Conditionals_adv.h
Original file line number Diff line number Diff line change
Expand Up @@ -1351,6 +1351,11 @@
#undef MANUAL_MOVE_DISTANCE_IN
#endif

// Clean up if no rotational axes exist
#if !HAS_ROTATIONAL_AXES
#undef MANUAL_MOVE_DISTANCE_DEG
#endif

// Power-Loss Recovery
#if ENABLED(POWER_LOSS_RECOVERY) && defined(PLR_BED_THRESHOLD)
#define HAS_PLR_BED_THRESHOLD 1
Expand Down
8 changes: 1 addition & 7 deletions Marlin/src/inc/Conditionals_post.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,6 @@
#define AXIS9_NAME 'W'
#endif

#if ANY(AXIS4_ROTATES, AXIS5_ROTATES, AXIS6_ROTATES, AXIS7_ROTATES, AXIS8_ROTATES, AXIS9_ROTATES)
#define HAS_ROTATIONAL_AXES 1
#else
#undef MANUAL_MOVE_DISTANCE_DEG
#endif

#if HAS_X_AXIS
#define X_MAX_LENGTH (X_MAX_POS - (X_MIN_POS))
#endif
Expand Down Expand Up @@ -3282,7 +3276,7 @@
#endif

// Add commands that need sub-codes to this list
#if ANY(G38_PROBE_TARGET, CNC_COORDINATE_SYSTEMS, POWER_LOSS_RECOVERY)
#if ANY(G38_PROBE_TARGET, CNC_COORDINATE_SYSTEMS, POWER_LOSS_RECOVERY, HAS_ROTATIONAL_AXES)
#define USE_GCODE_SUBCODES 1
#endif

Expand Down
75 changes: 41 additions & 34 deletions Marlin/src/lcd/marlinui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "../inc/MarlinConfig.h"

#include "../MarlinCore.h" // for printingIsPaused
#include "../gcode/parser.h" // for axis_is_rotational, using_inch_units

#if LED_POWEROFF_TIMEOUT > 0 || ALL(HAS_WIRED_LCD, PRINTER_EVENT_LEDS) || (HAS_BACKLIGHT_TIMEOUT && defined(NEOPIXEL_BKGD_INDEX_FIRST))
#include "../feature/leds/leds.h"
Expand Down Expand Up @@ -847,52 +848,58 @@ void MarlinUI::init() {
if (processing) return; // Prevent re-entry from idle() calls

// Add a manual move to the queue?
if (axis != NO_AXIS_ENUM && ELAPSED(millis(), start_time) && !planner.is_full()) {
if (axis == NO_AXIS_ENUM || PENDING(millis(), start_time) || planner.is_full()) return;

const feedRate_t fr_mm_s = (axis < LOGICAL_AXES) ? manual_feedrate_mm_s[axis] : XY_PROBE_FEEDRATE_MM_S;
const feedRate_t fr_mm_s = (axis < LOGICAL_AXES) ? manual_feedrate_mm_s[axis] : XY_PROBE_FEEDRATE_MM_S;

#if IS_KINEMATIC
/**
* 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 HAS_MULTI_EXTRUDER
REMEMBER(ae, active_extruder);
#if MULTI_E_MANUAL
if (axis == E_AXIS) active_extruder = e_index;
#endif
#if IS_KINEMATIC

#if HAS_MULTI_EXTRUDER
REMEMBER(ae, active_extruder);
#if MULTI_E_MANUAL
if (axis == E_AXIS) active_extruder = e_index;
#endif
#endif

// Apply a linear offset to a single axis
if (axis == ALL_AXES_ENUM)
destination = all_axes_destination;
else if (axis <= LOGICAL_AXES) {
destination = current_position;
destination[axis] += offset;
}
// Apply a linear offset to a single axis
if (axis == ALL_AXES_ENUM)
destination = all_axes_destination;
else if (axis <= LOGICAL_AXES) {
destination = current_position;
destination[axis] += offset;
}

// Reset for the next move
offset = 0;
axis = NO_AXIS_ENUM;
// Reset for the next move
offset = 0;
axis = NO_AXIS_ENUM;

// DELTA and SCARA machines use segmented moves, which could fill the planner during the call to
// move_to_destination. This will cause idle() to be called, which can then call this function while the
// 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;
prepare_internal_move_to_destination(fr_mm_s); // will set current_position from destination
processing = false;
// DELTA and SCARA machines use segmented moves, which could fill the planner during the call to
// move_to_destination. This will cause idle() to be called, which can then call this function while the
// 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;
prepare_internal_move_to_destination(fr); // will set current_position from destination
processing = false;

#else
#else

// For Cartesian / Core motion simply move to the current_position
planner.buffer_line(current_position, fr_mm_s,
TERN_(MULTI_E_MANUAL, axis == E_AXIS ? e_index :) active_extruder
);
// For Cartesian / Core motion simply move to the current_position
planner.buffer_line(current_position, fr,
TERN_(MULTI_E_MANUAL, axis == E_AXIS ? e_index :) active_extruder
);

//SERIAL_ECHOLNPGM("Add planner.move with Axis ", C(AXIS_CHAR(axis)), " at FR ", fr_mm_s);
//SERIAL_ECHOLNPGM("Add planner.move with Axis ", C(AXIS_CHAR(axis)), " at FR ", fr_mm_s);

axis = NO_AXIS_ENUM;
axis = NO_AXIS_ENUM;

#endif
}
#endif
}

//
Expand Down
4 changes: 2 additions & 2 deletions Marlin/src/lcd/menu/menu_motion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@ void lcd_move_axis(const AxisEnum axis) {
if (ui.should_draw()) {
MenuEditItemBase::itemIndex = axis;
const float pos = ui.manual_move.axis_value(axis);
if (parser.using_inch_units()) {
const float imp_pos = LINEAR_UNIT(pos);
if (parser.using_inch_units() && !parser.axis_is_rotational(axis)) {
const float imp_pos = parser.per_axis_value(axis, pos);
MenuEditItemBase::draw_edit_screen(GET_TEXT_F(MSG_MOVE_N), ftostr63(imp_pos));
}
else
Expand Down
10 changes: 7 additions & 3 deletions Marlin/src/module/motion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1154,10 +1154,13 @@ float get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXES, bool

#if HAS_ROTATIONAL_AXES
if (UNEAR_ZERO(distance_sqr)) {
// Move involves only rotational axes. Calculate angular distance in accordance with LinuxCNC
is_cartesian_move = false;
// 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;
}
#endif

#endif
Expand Down Expand Up @@ -1318,8 +1321,9 @@ float get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXES, bool
float cartesian_mm = get_move_distance(diff OPTARG(HAS_ROTATIONAL_AXES, cartes_move));

// If the move is very short, check the E move distance
// No E move either? Game over.
TERN_(HAS_EXTRUDERS, if (UNEAR_ZERO(cartesian_mm)) cartesian_mm = ABS(diff.e));

// No E move either? Game over.
if (UNEAR_ZERO(cartesian_mm)) return;

// The length divided by the segment size
Expand Down
Loading