MarkForged kinematics (#19235)

This commit is contained in:
Victor Sokolov 2020-09-04 05:12:53 +04:00 committed by Scott Lahteine
parent 94809feac7
commit 47753d8a01
9 changed files with 100 additions and 34 deletions

View file

@ -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 ===========================

View file

@ -60,6 +60,7 @@ void safe_delay(millis_t ms) {
TERN_(DELTA, "Delta")
TERN_(IS_SCARA, "SCARA")
TERN_(IS_CORE, "Core")
TERN_(MARKFORGED_XY, "MarkForged")
TERN_(IS_CARTESIAN, "Cartesian")
);

View file

@ -148,6 +148,7 @@ void GcodeSuite::M360() {
TERN_(DELTA, "Delta")
TERN_(IS_SCARA, "SCARA")
TERN_(IS_CORE, "Core")
TERN_(MARKFORGED_XY, "MarkForged")
TERN_(IS_CARTESIAN, "Cartesian")
);

View file

@ -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

View file

@ -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],

View file

@ -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()

View file

@ -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

View file

@ -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);
#else
#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))

View file

@ -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);