diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 8cd2faf7f8..52af1375a5 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -608,7 +608,7 @@ // @section machine -// Uncomment one of these options to enable CoreXY, CoreXZ, or CoreYZ kinematics +// Enable one of the options below for CoreXY, CoreXZ, or CoreYZ kinematics, // either in the usual order or reversed //#define COREXY //#define COREXZ @@ -616,6 +616,7 @@ //#define COREYX //#define COREZX //#define COREZY +//#define MARKFORGED_XY // MarkForged. See https://reprap.org/forum/read.php?152,504042 //=========================================================================== //============================== Endstop Settings =========================== diff --git a/Marlin/src/core/utility.cpp b/Marlin/src/core/utility.cpp index 295657fa64..f999568167 100644 --- a/Marlin/src/core/utility.cpp +++ b/Marlin/src/core/utility.cpp @@ -57,10 +57,11 @@ void safe_delay(millis_t ms) { void log_machine_info() { SERIAL_ECHOLNPGM("Machine Type: " - TERN_(DELTA, "Delta") - TERN_(IS_SCARA, "SCARA") - TERN_(IS_CORE, "Core") - TERN_(IS_CARTESIAN, "Cartesian") + TERN_(DELTA, "Delta") + TERN_(IS_SCARA, "SCARA") + TERN_(IS_CORE, "Core") + TERN_(MARKFORGED_XY, "MarkForged") + TERN_(IS_CARTESIAN, "Cartesian") ); SERIAL_ECHOLNPGM("Probe: " diff --git a/Marlin/src/gcode/host/M360.cpp b/Marlin/src/gcode/host/M360.cpp index 87ca23becd..9970dc4df9 100644 --- a/Marlin/src/gcode/host/M360.cpp +++ b/Marlin/src/gcode/host/M360.cpp @@ -145,10 +145,11 @@ void GcodeSuite::M360() { config_prefix(PSTR("PrinterType")); SERIAL_ECHOLNPGM( - TERN_(DELTA, "Delta") - TERN_(IS_SCARA, "SCARA") - TERN_(IS_CORE, "Core") - TERN_(IS_CARTESIAN, "Cartesian") + TERN_(DELTA, "Delta") + TERN_(IS_SCARA, "SCARA") + TERN_(IS_CORE, "Core") + TERN_(MARKFORGED_XY, "MarkForged") + TERN_(IS_CARTESIAN, "Cartesian") ); // diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h index f566643bdb..71ae4bb285 100644 --- a/Marlin/src/inc/Conditionals_post.h +++ b/Marlin/src/inc/Conditionals_post.h @@ -149,11 +149,16 @@ #define CORE_AXIS_2 C_AXIS #endif #define CORESIGN(n) (ANY(COREYX, COREZX, COREZY) ? (-(n)) : (n)) +#elif ENABLED(MARKFORGED_XY) + // Markforged kinematics + #define CORE_AXIS_1 A_AXIS + #define CORE_AXIS_2 B_AXIS + #define NORMAL_AXIS Z_AXIS #endif // Calibration codes only for non-core axes #if EITHER(BACKLASH_GCODE, CALIBRATION_GCODE) - #if IS_CORE + #if EITHER(IS_CORE, MARKFORGED_XY) #define X_AXIS_INDEX 0 #define Y_AXIS_INDEX 1 #define Z_AXIS_INDEX 2 diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index da5ddc1c2d..dcd6e4f18b 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -764,6 +764,8 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS #if ENABLED(BABYSTEPPING) #if ENABLED(SCARA) #error "BABYSTEPPING is not implemented for SCARA yet." + #elif BOTH(MARKFORGED_XY, BABYSTEP_XY) + #error "BABYSTEPPING only implemented for Z axis on MarkForged." #elif BOTH(DELTA, BABYSTEP_XY) #error "BABYSTEPPING only implemented for Z axis on deltabots." #elif BOTH(BABYSTEP_ZPROBE_OFFSET, MESH_BED_LEVELING) @@ -1155,8 +1157,9 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS + ENABLED(COREYZ) \ + ENABLED(COREYX) \ + ENABLED(COREZX) \ - + ENABLED(COREZY) - #error "Please enable only one of DELTA, MORGAN_SCARA, COREXY, COREYX, COREXZ, COREZX, COREYZ, or COREZY." + + ENABLED(COREZY) \ + + ENABLED(MARKFORGED_XY) + #error "Please enable only one of DELTA, MORGAN_SCARA, COREXY, COREYX, COREXZ, COREZX, COREYZ, COREZY, or MARKFORGED_XY." #endif /** @@ -1576,8 +1579,8 @@ static_assert(hbm[Z_AXIS] >= 0, "HOMING_BUMP_MM.Z must be greater than or equal #if ENABLED(DUAL_X_CARRIAGE) #if EXTRUDERS < 2 #error "DUAL_X_CARRIAGE requires 2 (or more) extruders." - #elif CORE_IS_XY || CORE_IS_XZ - #error "DUAL_X_CARRIAGE cannot be used with COREXY, COREYX, COREXZ, or COREZX." + #elif ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY) + #error "DUAL_X_CARRIAGE cannot be used with COREXY, COREYX, COREXZ, COREZX, or MARKFORGED_XY." #elif !GOOD_AXIS_PINS(X2) #error "DUAL_X_CARRIAGE requires X2 stepper pins to be defined." #elif !HAS_X_MAX @@ -2533,6 +2536,8 @@ static_assert(hbm[Z_AXIS] >= 0, "HOMING_BUMP_MM.Z must be greater than or equal #error "CoreXZ requires both X and Z to use sensorless homing if either one does." #elif CORE_IS_YZ && Y_SENSORLESS != Z_SENSORLESS && !HOMING_Z_WITH_PROBE #error "CoreYZ requires both Y and Z to use sensorless homing if either one does." +#elif ENABLED(MARKFORGED_XY) && X_SENSORLESS != Y_SENSORLESS + #error "MARKFORGED_XY requires both X and Y to use sensorless homing if either one does." #endif // Other TMC feature requirements @@ -2848,6 +2853,10 @@ static_assert( _ARR_TEST(3,0) && _ARR_TEST(3,1) && _ARR_TEST(3,2) #error "BACKLASH_COMPENSATION requires BACKLASH_DISTANCE_MM." #elif !defined(BACKLASH_CORRECTION) #error "BACKLASH_COMPENSATION requires BACKLASH_CORRECTION." + #elif ENABLED(MARKFORGED_XY) + constexpr float backlash_arr[] = BACKLASH_DISTANCE_MM; + static_assert(!backlash_arr[CORE_AXIS_1] && !backlash_arr[CORE_AXIS_2], + "BACKLASH_COMPENSATION can only apply to " STRINGIFY(NORMAL_AXIS) " on a MarkForged system."); #elif IS_CORE constexpr float backlash_arr[] = BACKLASH_DISTANCE_MM; static_assert(!backlash_arr[CORE_AXIS_1] && !backlash_arr[CORE_AXIS_2], diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index 289270072d..1169ede2dc 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -498,7 +498,7 @@ void Endstops::update() { #define UPDATE_ENDSTOP_BIT(AXIS, MINMAX) SET_BIT_TO(live_state, _ENDSTOP(AXIS, MINMAX), (READ(_ENDSTOP_PIN(AXIS, MINMAX)) != _ENDSTOP_INVERTING(AXIS, MINMAX))) #define COPY_LIVE_STATE(SRC_BIT, DST_BIT) SET_BIT_TO(live_state, DST_BIT, TEST(live_state, SRC_BIT)) - #if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && !(CORE_IS_XY || CORE_IS_XZ) + #if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && NONE(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY) // If G38 command is active check Z_MIN_PROBE for ALL movement if (G38_move) UPDATE_ENDSTOP_BIT(Z, MIN_PROBE); #endif @@ -514,12 +514,12 @@ void Endstops::update() { #endif // Use HEAD for core axes, AXIS for others - #if CORE_IS_XY || CORE_IS_XZ + #if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY) #define X_AXIS_HEAD X_HEAD #else #define X_AXIS_HEAD X_AXIS #endif - #if CORE_IS_XY || CORE_IS_YZ + #if ANY(CORE_IS_XY, CORE_IS_YZ, MARKFORGED_XY) #define Y_AXIS_HEAD Y_HEAD #else #define Y_AXIS_HEAD Y_AXIS @@ -736,7 +736,7 @@ void Endstops::update() { #define PROCESS_ENDSTOP_Z(MINMAX) PROCESS_DUAL_ENDSTOP(Z, MINMAX) #endif - #if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && !(CORE_IS_XY || CORE_IS_XZ) + #if ENABLED(G38_PROBE_TARGET) && PIN_EXISTS(Z_MIN_PROBE) && NONE(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY) #if ENABLED(G38_PROBE_AWAY) #define _G38_OPEN_STATE (G38_move >= 4) #else @@ -865,7 +865,7 @@ void Endstops::update() { bool hit = false; #if X_SPI_SENSORLESS if (tmc_spi_homing.x && (stepperX.test_stall_status() - #if CORE_IS_XY && Y_SPI_SENSORLESS + #if ANY(CORE_IS_XY, MARKFORGED_XY) && Y_SPI_SENSORLESS || stepperY.test_stall_status() #elif CORE_IS_XZ && Z_SPI_SENSORLESS || stepperZ.test_stall_status() @@ -877,7 +877,7 @@ void Endstops::update() { #endif #if Y_SPI_SENSORLESS if (tmc_spi_homing.y && (stepperY.test_stall_status() - #if CORE_IS_XY && X_SPI_SENSORLESS + #if ANY(CORE_IS_XY, MARKFORGED_XY) && X_SPI_SENSORLESS || stepperX.test_stall_status() #elif CORE_IS_YZ && Z_SPI_SENSORLESS || stepperZ.test_stall_status() diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 401721140b..2f12302e59 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1152,7 +1152,7 @@ feedRate_t get_homing_bump_feedrate(const AxisEnum axis) { #if AXIS_HAS_STALLGUARD(X2) stealth_states.x2 = tmc_enable_stallguard(stepperX2); #endif - #if CORE_IS_XY && Y_SENSORLESS + #if EITHER(CORE_IS_XY, MARKFORGED_XY) && Y_SENSORLESS stealth_states.y = tmc_enable_stallguard(stepperY); #elif CORE_IS_XZ && Z_SENSORLESS stealth_states.z = tmc_enable_stallguard(stepperZ); @@ -1165,7 +1165,7 @@ feedRate_t get_homing_bump_feedrate(const AxisEnum axis) { #if AXIS_HAS_STALLGUARD(Y2) stealth_states.y2 = tmc_enable_stallguard(stepperY2); #endif - #if CORE_IS_XY && X_SENSORLESS + #if EITHER(CORE_IS_XY, MARKFORGED_XY) && X_SENSORLESS stealth_states.x = tmc_enable_stallguard(stepperX); #elif CORE_IS_YZ && Z_SENSORLESS stealth_states.z = tmc_enable_stallguard(stepperZ); @@ -1216,7 +1216,7 @@ feedRate_t get_homing_bump_feedrate(const AxisEnum axis) { #if AXIS_HAS_STALLGUARD(X2) tmc_disable_stallguard(stepperX2, enable_stealth.x2); #endif - #if CORE_IS_XY && Y_SENSORLESS + #if EITHER(CORE_IS_XY, MARKFORGED_XY) && Y_SENSORLESS tmc_disable_stallguard(stepperY, enable_stealth.y); #elif CORE_IS_XZ && Z_SENSORLESS tmc_disable_stallguard(stepperZ, enable_stealth.z); @@ -1229,7 +1229,7 @@ feedRate_t get_homing_bump_feedrate(const AxisEnum axis) { #if AXIS_HAS_STALLGUARD(Y2) tmc_disable_stallguard(stepperY2, enable_stealth.y2); #endif - #if CORE_IS_XY && X_SENSORLESS + #if EITHER(CORE_IS_XY, MARKFORGED_XY) && X_SENSORLESS tmc_disable_stallguard(stepperX, enable_stealth.x); #elif CORE_IS_YZ && Z_SENSORLESS tmc_disable_stallguard(stepperZ, enable_stealth.z); @@ -1789,7 +1789,7 @@ void homeaxis(const AxisEnum axis) { do_homing_move(axis, adjDistance, get_homing_bump_feedrate(axis)); } - #else // CARTESIAN / CORE + #else // CARTESIAN / CORE / MARKFORGED_XY set_axis_is_at_home(axis); sync_plan_position(); @@ -1818,8 +1818,11 @@ void homeaxis(const AxisEnum axis) { #if ENABLED(SENSORLESS_HOMING) planner.synchronize(); - if (TERN0(IS_CORE, axis != NORMAL_AXIS)) - safe_delay(200); // Short delay to allow belts to spring back + if (false + #if EITHER(IS_CORE, MARKFORGED_XY) + || axis != NORMAL_AXIS + #endif + ) safe_delay(200); // Short delay to allow belts to spring back #endif } #endif diff --git a/Marlin/src/module/planner.cpp b/Marlin/src/module/planner.cpp index 931daa3322..82c62ed579 100644 --- a/Marlin/src/module/planner.cpp +++ b/Marlin/src/module/planner.cpp @@ -1614,6 +1614,7 @@ void Planner::finish_and_disable() { float Planner::get_axis_position_mm(const AxisEnum axis) { float axis_steps; #if IS_CORE + // Requesting one of the "core" axes? if (axis == CORE_AXIS_1 || axis == CORE_AXIS_2) { @@ -1631,9 +1632,30 @@ float Planner::get_axis_position_mm(const AxisEnum axis) { } else axis_steps = stepper.position(axis); + + #elif ENABLED(MARKFORGED_XY) + + // Requesting one of the joined axes? + if (axis == CORE_AXIS_1 || axis == CORE_AXIS_2) { + // Protect the access to the position. + const bool was_enabled = stepper.suspend(); + + const int32_t p1 = stepper.position(CORE_AXIS_1), + p2 = stepper.position(CORE_AXIS_2); + + if (was_enabled) stepper.wake_up(); + + axis_steps = ((axis == CORE_AXIS_1) ? p1 - p2 : p2); + } + else + axis_steps = stepper.position(axis); + #else + axis_steps = stepper.position(axis); + #endif + return axis_steps * steps_to_mm[axis]; } @@ -1808,6 +1830,12 @@ bool Planner::_populate_block(block_t * const block, bool split_move, if (dc < 0) SBI(dm, Z_HEAD); // ...and Z if (db + dc < 0) SBI(dm, B_AXIS); // Motor B direction if (CORESIGN(db - dc) < 0) SBI(dm, C_AXIS); // Motor C direction + #elif ENABLED(MARKFORGED_XY) + if (da < 0) SBI(dm, X_HEAD); // Save the real Extruder (head) direction in X Axis + if (db < 0) SBI(dm, Y_HEAD); // ...and Y + if (dc < 0) SBI(dm, Z_AXIS); + if (da + db < 0) SBI(dm, A_AXIS); // Motor A direction + if (db < 0) SBI(dm, B_AXIS); // Motor B direction #else if (da < 0) SBI(dm, X_AXIS); if (db < 0) SBI(dm, Y_AXIS); @@ -1843,6 +1871,8 @@ bool Planner::_populate_block(block_t * const block, bool split_move, block->steps.set(ABS(da + dc), ABS(db), ABS(da - dc)); #elif CORE_IS_YZ block->steps.set(ABS(da), ABS(db + dc), ABS(db - dc)); + #elif ENABLED(MARKFORGED_XY) + block->steps.set(ABS(da + db), ABS(db), ABS(dc)); #elif IS_SCARA block->steps.set(ABS(da), ABS(db), ABS(dc)); #else @@ -1859,7 +1889,9 @@ bool Planner::_populate_block(block_t * const block, bool split_move, * Having the real displacement of the head, we can calculate the total movement length and apply the desired speed. */ struct DistanceMM : abce_float_t { - TERN_(IS_CORE, xyz_pos_t head); + #if EITHER(IS_CORE, MARKFORGED_XY) + xyz_pos_t head; + #endif } steps_dist_mm; #if IS_CORE #if CORE_IS_XY @@ -1881,6 +1913,12 @@ bool Planner::_populate_block(block_t * const block, bool split_move, steps_dist_mm.b = (db + dc) * steps_to_mm[B_AXIS]; steps_dist_mm.c = CORESIGN(db - dc) * steps_to_mm[C_AXIS]; #endif + #elif ENABLED(MARKFORGED_XY) + steps_dist_mm.head.x = da * steps_to_mm[A_AXIS]; + steps_dist_mm.head.y = db * steps_to_mm[B_AXIS]; + steps_dist_mm.z = dc * steps_to_mm[Z_AXIS]; + steps_dist_mm.a = (da - db) * steps_to_mm[A_AXIS]; + steps_dist_mm.b = db * steps_to_mm[B_AXIS]; #else steps_dist_mm.a = da * steps_to_mm[A_AXIS]; steps_dist_mm.b = db * steps_to_mm[B_AXIS]; @@ -1907,7 +1945,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, block->millimeters = millimeters; else block->millimeters = SQRT( - #if CORE_IS_XY + #if EITHER(CORE_IS_XY, MARKFORGED_XY) sq(steps_dist_mm.head.x) + sq(steps_dist_mm.head.y) + sq(steps_dist_mm.z) #elif CORE_IS_XZ sq(steps_dist_mm.head.x) + sq(steps_dist_mm.y) + sq(steps_dist_mm.head.z) @@ -1964,7 +2002,7 @@ bool Planner::_populate_block(block_t * const block, bool split_move, #endif // Enable active axes - #if CORE_IS_XY + #if EITHER(CORE_IS_XY, MARKFORGED_XY) if (block->steps.a || block->steps.b) { ENABLE_AXIS_X(); ENABLE_AXIS_Y(); @@ -2325,9 +2363,9 @@ bool Planner::_populate_block(block_t * const block, bool split_move, * On CoreXY the length of the vector [A,B] is SQRT(2) times the length of the head movement vector [X,Y]. * So taking Z and E into account, we cannot scale to a unit vector with "inverse_millimeters". * => normalize the complete junction vector. - * Elsewise, when needed JD factors in the E component + * Elsewise, when needed JD will factor-in the E component */ - if (ENABLED(IS_CORE) || esteps > 0) + if (EITHER(IS_CORE, MARKFORGED_XY) || esteps > 0) normalize_junction_vector(unit_vec); // Normalize with XYZE components else unit_vec *= inverse_millimeters; // Use pre-calculated (1 / SQRT(x^2 + y^2 + z^2)) diff --git a/Marlin/src/module/stepper.cpp b/Marlin/src/module/stepper.cpp index 0e70fd370c..fab1a61548 100644 --- a/Marlin/src/module/stepper.cpp +++ b/Marlin/src/module/stepper.cpp @@ -2041,6 +2041,8 @@ uint32_t Stepper::block_phase_isr() { #define X_CMP(A,B) ((A)!=(B)) #endif #define X_MOVE_TEST ( S_(1) != S_(2) || (S_(1) > 0 && X_CMP(D_(1),D_(2))) ) + #elif ENABLED(MARKFORGED_XY) + #define X_MOVE_TEST (current_block->steps.a != current_block->steps.b) #else #define X_MOVE_TEST !!current_block->steps.a #endif @@ -2614,6 +2616,8 @@ void Stepper::_set_position(const int32_t &a, const int32_t &b, const int32_t &c #elif CORE_IS_YZ // coreyz planning count_position.set(a, b + c, CORESIGN(b - c)); + #elif ENABLED(MARKFORGED_XY) + count_position.set(a - b, b, c); #else // default non-h-bot planning count_position.set(a, b, c); @@ -2680,6 +2684,10 @@ void Stepper::endstop_triggered(const AxisEnum axis) { ? CORESIGN(count_position[CORE_AXIS_1] - count_position[CORE_AXIS_2]) : count_position[CORE_AXIS_1] + count_position[CORE_AXIS_2] ) * double(0.5) + #elif ENABLED(MARKFORGED_XY) + axis == CORE_AXIS_1 + ? count_position[CORE_AXIS_1] - count_position[CORE_AXIS_2] + : count_position[CORE_AXIS_2] #else // !IS_CORE count_position[axis] #endif @@ -2709,12 +2717,12 @@ int32_t Stepper::triggered_position(const AxisEnum axis) { } void Stepper::report_a_position(const xyz_long_t &pos) { - #if CORE_IS_XY || CORE_IS_XZ || ENABLED(DELTA) || IS_SCARA + #if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY, DELTA, IS_SCARA) SERIAL_ECHOPAIR(STR_COUNT_A, pos.x, " B:", pos.y); #else SERIAL_ECHOPAIR_P(PSTR(STR_COUNT_X), pos.x, SP_Y_LBL, pos.y); #endif - #if CORE_IS_XZ || CORE_IS_YZ || ENABLED(DELTA) + #if ANY(CORE_IS_XZ, CORE_IS_YZ, DELTA) SERIAL_ECHOLNPAIR(" C:", pos.z); #else SERIAL_ECHOLNPAIR_P(SP_Z_LBL, pos.z);