🐛 Fix FT Motion motion+dir for endstops (#28326)

Co-Authored-By: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
ellensp 2026-02-10 10:30:24 +13:00 committed by Scott Lahteine
parent 5b71e64a77
commit 097fc30201
13 changed files with 276 additions and 186 deletions

View file

@ -366,17 +366,17 @@
#define STR_Z2 STR_C "2"
#define STR_Z3 STR_C "3"
#define STR_Z4 STR_C "4"
#if CORE_IS_XY || CORE_IS_XZ
#if ANY(HAS_REAL_X, IS_SCARA, DELTA)
#define STEPPER_A_NAME 'A'
#else
#define STEPPER_A_NAME 'X'
#endif
#if CORE_IS_XY || CORE_IS_YZ
#if ANY(HAS_REAL_Y, IS_SCARA, DELTA, POLAR)
#define STEPPER_B_NAME 'B'
#else
#define STEPPER_B_NAME 'Y'
#endif
#if CORE_IS_XZ || CORE_IS_YZ
#if ANY(HAS_REAL_Z, DELTA)
#define STEPPER_C_NAME 'C'
#else
#define STEPPER_C_NAME 'Z'

View file

@ -352,7 +352,7 @@ typedef struct {
//
// - X_AXIS, Y_AXIS, and Z_AXIS should be used for axes in Cartesian space
// - A_AXIS, B_AXIS, and C_AXIS should be used for Steppers, corresponding to XYZ on Cartesians
// - X_HEAD, Y_HEAD, and Z_HEAD should be used for axes on Core kinematics
// - X_REAL, Y_REAL, and Z_REAL should be used for axes on Core kinematics
//
enum AxisEnum : uint8_t {
@ -364,8 +364,14 @@ enum AxisEnum : uint8_t {
#undef _EN_ITEM
// Core also keeps toolhead directions
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
X_HEAD, Y_HEAD, Z_HEAD,
#if HAS_REAL_X
X_REAL,
#endif
#if HAS_REAL_Y
Y_REAL,
#endif
#if HAS_REAL_Z
Z_REAL,
#endif
// Distinct axes, including all E and Core
@ -374,6 +380,10 @@ enum AxisEnum : uint8_t {
// Most of the time we refer only to the single E_AXIS
#if HAS_EXTRUDERS
E_AXIS = E0_AXIS,
E_REAL = E_AXIS,
#define _EN_REAL(N) E##N##_REAL = E##N##_AXIS,
REPEAT(EXTRUDERS, _EN_REAL)
#undef _EN_REAL
#endif
// A, B, and C are for DELTA, SCARA, etc.
@ -387,6 +397,35 @@ enum AxisEnum : uint8_t {
C_AXIS = Z_AXIS,
#endif
// Aliases to distinguish tool axes from stepper indexes
#if HAS_X_AXIS && !HAS_REAL_X
X_REAL = X_AXIS,
#endif
#if HAS_Y_AXIS && !HAS_REAL_Y
Y_REAL = Y_AXIS,
#endif
#if HAS_Z_AXIS && !HAS_REAL_Z
Z_REAL = Z_AXIS,
#endif
#if HAS_I_AXIS
I_REAL = I_AXIS,
#endif
#if HAS_J_AXIS
J_REAL = J_AXIS,
#endif
#if HAS_K_AXIS
K_REAL = K_AXIS,
#endif
#if HAS_U_AXIS
U_REAL = U_AXIS,
#endif
#if HAS_V_AXIS
V_REAL = V_AXIS,
#endif
#if HAS_W_AXIS
W_REAL = W_AXIS,
#endif
// To refer to all or none
ALL_AXES_ENUM = 0xFE, NO_AXIS_ENUM = 0xFF
};
@ -1153,8 +1192,14 @@ public:
#define _EN_ITEM(N) bool e##N:1;
REPEAT(EXTRUDERS,_EN_ITEM)
#undef _EN_ITEM
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
bool rx:1, ry:1, rz:1;
#if HAS_REAL_X
bool rx:1;
#endif
#if HAS_REAL_Y
bool ry:1;
#endif
#if HAS_REAL_Z
bool rz:1;
#endif
};
// Axes X, Y, Z ... E0, E1, E2 ... RX, RY, RZ
@ -1165,8 +1210,14 @@ public:
#define _EN_ITEM(N) bool E##N:1;
REPEAT(EXTRUDERS,_EN_ITEM)
#undef _EN_ITEM
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
bool RX:1, RY:1, RZ:1;
#if HAS_REAL_X
bool RX:1;
#endif
#if HAS_REAL_Y
bool RY:1;
#endif
#if HAS_REAL_Z
bool RZ:1;
#endif
};
// a, b, c, e ... ra, rb, rc
@ -1177,8 +1228,14 @@ public:
REPEAT_S(1,EXTRUDERS,_EN_ITEM)
#undef _EN_ITEM
#endif
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
bool ra:1, rb:1, rc:1;
#if HAS_REAL_X
bool ra:1;
#endif
#if HAS_REAL_Y
bool rb:1;
#endif
#if HAS_REAL_Z
bool rc:1;
#endif
};
// A, B, C, E ... RA, RB, RC
@ -1189,8 +1246,14 @@ public:
REPEAT_S(1,EXTRUDERS,_EN_ITEM)
#undef _EN_ITEM
#endif
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
bool RA:1, RB:1, RC:1;
#if HAS_REAL_X
bool RA:1;
#endif
#if HAS_REAL_Y
bool RB:1;
#endif
#if HAS_REAL_Z
bool RC:1;
#endif
};
};

View file

@ -584,6 +584,15 @@
#if ANY(COREYZ, COREZY)
#define CORE_IS_YZ 1
#endif
#if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY)
#define HAS_REAL_X 1
#endif
#if ANY(CORE_IS_XY, CORE_IS_YZ, MARKFORGED_YX)
#define HAS_REAL_Y 1
#endif
#if CORE_IS_XZ || CORE_IS_YZ
#define HAS_REAL_Z 1
#endif
#if CORE_IS_XY || CORE_IS_XZ || CORE_IS_YZ
#define IS_CORE 1
#if CORE_IS_XY

View file

@ -1943,7 +1943,7 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#if ENABLED(DUAL_X_CARRIAGE)
#if EXTRUDERS < 2
#error "DUAL_X_CARRIAGE requires 2 (or more) extruders."
#elif ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY, MARKFORGED_YX)
#elif HAS_REAL_X
#error "DUAL_X_CARRIAGE cannot be used with COREXY, COREYX, COREXZ, COREZX, MARKFORGED_YX, or MARKFORGED_XY."
#elif !GOOD_AXIS_PINS(X2)
#error "DUAL_X_CARRIAGE requires X2 stepper pins to be defined."

View file

@ -462,30 +462,6 @@ void Endstops::update() {
#define X_MIN_TEST() TERN1(DUAL_X_CARRIAGE, stepper.last_moved_extruder == 0) // Check min for the left carriage
#define X_MAX_TEST() TERN1(DUAL_X_CARRIAGE, stepper.last_moved_extruder != 0) // Check max for the right carriage
// Use HEAD for core axes, AXIS for others
#if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY, MARKFORGED_YX)
#define X_AXIS_HEAD X_HEAD
#else
#define X_AXIS_HEAD X_AXIS
#endif
#if ANY(CORE_IS_XY, CORE_IS_YZ, MARKFORGED_XY, MARKFORGED_YX)
#define Y_AXIS_HEAD Y_HEAD
#else
#define Y_AXIS_HEAD Y_AXIS
#endif
#if CORE_IS_XZ || CORE_IS_YZ
#define Z_AXIS_HEAD Z_HEAD
#else
#define Z_AXIS_HEAD Z_AXIS
#endif
#define I_AXIS_HEAD I_AXIS
#define J_AXIS_HEAD J_AXIS
#define K_AXIS_HEAD K_AXIS
#define U_AXIS_HEAD U_AXIS
#define V_AXIS_HEAD V_AXIS
#define W_AXIS_HEAD W_AXIS
/**
* Check and update endstops
*/
@ -680,8 +656,8 @@ void Endstops::update() {
#define PROCESS_ENDSTOP_Z(MINMAX) PROCESS_DUAL_ENDSTOP(Z, MINMAX)
#endif
#define AXIS_IS_MOVING(A) TERN(FT_MOTION, ftMotion, stepper).axis_is_moving(_AXIS(A))
#define AXIS_DIR_REV(A) !TERN(FT_MOTION, ftMotion, stepper).motor_direction(A)
#define AXIS_IS_MOVING(A) TERN(FT_MOTION, ftMotion, stepper).axis_is_moving(A##_REAL)
#define AXIS_DIR_REV(A) !TERN(FT_MOTION, ftMotion, stepper).axis_direction(A##_REAL)
#if ENABLED(G38_PROBE_TARGET)
// For G38 moves check the probe's pin for ALL movement
@ -704,8 +680,7 @@ void Endstops::update() {
#if HAS_X_AXIS
if (AXIS_IS_MOVING(X)) {
const AxisEnum x_head = TERN0(FT_MOTION, ftMotion.cfg.active) ? X_AXIS : X_AXIS_HEAD;
if (AXIS_DIR_REV(x_head)) {
if (AXIS_DIR_REV(X)) {
#if HAS_X_MIN_STATE
PROCESS_ENDSTOP_X(MIN);
#if CORE_DIAG(XY, Y, MIN)
@ -738,8 +713,7 @@ void Endstops::update() {
#if HAS_Y_AXIS
if (AXIS_IS_MOVING(Y)) {
const AxisEnum y_head = TERN0(FT_MOTION, ftMotion.cfg.active) ? Y_AXIS : Y_AXIS_HEAD;
if (AXIS_DIR_REV(y_head)) {
if (AXIS_DIR_REV(Y)) {
#if HAS_Y_MIN_STATE
PROCESS_ENDSTOP_Y(MIN);
#if CORE_DIAG(XY, X, MIN)
@ -772,8 +746,7 @@ void Endstops::update() {
#if HAS_Z_AXIS
if (AXIS_IS_MOVING(Z)) {
const AxisEnum z_head = TERN0(FT_MOTION, ftMotion.cfg.active) ? Z_AXIS : Z_AXIS_HEAD;
if (AXIS_DIR_REV(z_head)) {
if (AXIS_DIR_REV(Z)) {
// Z- : Gantry down, bed up
#if HAS_Z_MIN_STATE
// If the Z_MIN_PIN is being used for the probe there's no
@ -820,7 +793,7 @@ void Endstops::update() {
#if HAS_I_AXIS && HAS_I_STATE
if (AXIS_IS_MOVING(I)) {
if (AXIS_DIR_REV(I_AXIS_HEAD)) {
if (AXIS_DIR_REV(I)) {
#if HAS_I_MIN_STATE
PROCESS_ENDSTOP(I, MIN);
#endif
@ -835,7 +808,7 @@ void Endstops::update() {
#if HAS_J_AXIS && HAS_J_STATE
if (AXIS_IS_MOVING(J)) {
if (AXIS_DIR_REV(J_AXIS_HEAD)) {
if (AXIS_DIR_REV(J)) {
#if HAS_J_MIN_STATE
PROCESS_ENDSTOP(J, MIN);
#endif
@ -850,7 +823,7 @@ void Endstops::update() {
#if HAS_K_AXIS && HAS_K_STATE
if (AXIS_IS_MOVING(K)) {
if (AXIS_DIR_REV(K_AXIS_HEAD)) {
if (AXIS_DIR_REV(K)) {
#if HAS_K_MIN_STATE
PROCESS_ENDSTOP(K, MIN);
#endif
@ -865,7 +838,7 @@ void Endstops::update() {
#if HAS_U_AXIS && HAS_U_STATE
if (AXIS_IS_MOVING(U)) {
if (AXIS_DIR_REV(U_AXIS_HEAD)) {
if (AXIS_DIR_REV(U)) {
#if HAS_U_MIN_STATE
PROCESS_ENDSTOP(U, MIN);
#endif
@ -880,7 +853,7 @@ void Endstops::update() {
#if HAS_V_AXIS && HAS_V_STATE
if (AXIS_IS_MOVING(V)) {
if (AXIS_DIR_REV(V_AXIS_HEAD)) {
if (AXIS_DIR_REV(V)) {
#if HAS_V_MIN_STATE
PROCESS_ENDSTOP(V, MIN);
#endif
@ -895,7 +868,7 @@ void Endstops::update() {
#if HAS_W_AXIS && HAS_W_STATE
if (AXIS_IS_MOVING(W)) {
if (AXIS_DIR_REV(W_AXIS_HEAD)) {
if (AXIS_DIR_REV(W)) {
#if HAS_W_MIN_STATE
PROCESS_ENDSTOP(W, MIN);
#endif

View file

@ -390,15 +390,9 @@ bool FTMotion::plan_next_block() {
#endif
// Some kinematics track axis motion in RX, RY, RZ
#if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY, MARKFORGED_YX)
stepper.last_direction_bits.rx = current_block->direction_bits.rx;
#endif
#if ANY(CORE_IS_XY, CORE_IS_YZ, MARKFORGED_XY, MARKFORGED_YX)
stepper.last_direction_bits.ry = current_block->direction_bits.ry;
#endif
#if ANY(CORE_IS_XZ, CORE_IS_YZ)
stepper.last_direction_bits.rz = current_block->direction_bits.rz;
#endif
TERN_(HAS_REAL_X, stepper.last_direction_bits.rx = current_block->direction_bits.rx);
TERN_(HAS_REAL_Y, stepper.last_direction_bits.ry = current_block->direction_bits.ry);
TERN_(HAS_REAL_Z, stepper.last_direction_bits.rz = current_block->direction_bits.rz);
// Cache the extruder index / axis for this block
#if ANY(HAS_MULTI_EXTRUDER, MIXING_EXTRUDER)
@ -411,7 +405,7 @@ bool FTMotion::plan_next_block() {
const float totalLength = current_block->millimeters;
startPos = endPos_prevBlock;
const xyze_pos_t &moveDist = current_block->distance_mm;
const ext_distance_t &moveDist = current_block->ext_distance_mm;
ratio = moveDist / totalLength;
// Plan the trajectory using the trajectory generator
@ -422,9 +416,15 @@ bool FTMotion::plan_next_block() {
TERN_(FTM_HAS_LIN_ADVANCE, use_advance_lead = current_block->use_advance_lead);
axis_move_dir = current_block->direction_bits;
#define _SET_MOVE_END(A) moving_axis_flags.A = bool(moveDist.A);
// Set moving flags for axes that have movement in this block
// For CORE kinematics: moveDist.x/.y/.z contain motor distances (a/b/c)
// HEAD movement flags need to be inferred: if either motor moves, the head moves
#define _SET_MOVE_END(A) moving_axis_flags.A = bool(moveDist.A);
LOGICAL_AXIS_MAP(_SET_MOVE_END);
TERN_(HAS_REAL_X, moving_axis_flags.rx = bool(moveDist.real.x));
TERN_(HAS_REAL_Y, moving_axis_flags.ry = bool(moveDist.real.y));
TERN_(HAS_REAL_Z, moving_axis_flags.rz = bool(moveDist.real.z));
// If the endstop is already pressed, endstop interrupts won't invoke
// endstop_triggered and the move will grind. So check here for a

View file

@ -300,11 +300,11 @@ class FTMotion {
static TrajectoryType getTrajectoryType() { return TERN(FTM_POLYS, trajectoryType, TrajectoryType::TRAPEZOIDAL); }
static FSTR_P getTrajectoryName();
FORCE_INLINE static bool axis_is_moving(const AxisEnum axis) {
return cfg.active ? moving_axis_flags[axis] : TERN0(HAS_STANDARD_MOTION, stepper.axis_is_moving(axis));
FORCE_INLINE static bool axis_is_moving(const AxisEnum real) {
return cfg.active ? moving_axis_flags[real] : TERN0(HAS_STANDARD_MOTION, stepper.axis_is_moving(real));
}
FORCE_INLINE static bool motor_direction(const AxisEnum axis) {
return cfg.active ? axis_move_dir[axis] : stepper.last_direction_bits[axis];
FORCE_INLINE static bool axis_direction(const AxisEnum real) {
return cfg.active ? axis_move_dir[real] : stepper.last_direction_bits[real];
}
// A frame of the stepping plan

View file

@ -33,10 +33,16 @@ void AxisShaping::set_axis_shaping_A(
OPTARG(HAS_FTM_EI_SHAPING, const float vtol)
) {
const float K = exp(-zeta * M_PI / sqrt(1.f - sq(zeta))),
K2 = sq(K),
K3 = K2 * K,
K4 = K3 * K;
const float K = exp(-zeta * M_PI / sqrt(1.f - sq(zeta)));
#if ANY(FTM_SHAPER_ZVD, FTM_SHAPER_ZVDD, FTM_SHAPER_ZVDDD, FTM_SHAPER_EI, FTM_SHAPER_2HEI, FTM_SHAPER_3HEI, FTM_SHAPER_MZV)
const float K2 = sq(K);
#endif
#if ANY(FTM_SHAPER_ZVDD, FTM_SHAPER_ZVDDD, FTM_SHAPER_2HEI, FTM_SHAPER_3HEI)
const float K3 = K2 * K;
#endif
#if ANY(FTM_SHAPER_ZVDDD, FTM_SHAPER_3HEI)
const float K4 = K3 * K;
#endif
switch (shaper) {

View file

@ -1854,42 +1854,36 @@ bool Planner::_populate_block(
#endif // PREVENT_COLD_EXTRUSION || PREVENT_LENGTHY_EXTRUDE
// Compute direction bit-mask for this block
// In this context Core kinematics "real" bits pertain to Cartesian
// directions and others pertain directly to stepper directions.
AxisBits dm;
#if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY, MARKFORGED_YX)
dm.rx = (steps_dist.a > 0); // True direction in X
#endif
#if ANY(CORE_IS_XY, CORE_IS_YZ, MARKFORGED_XY, MARKFORGED_YX)
dm.ry = (steps_dist.b > 0); // True direction in Y
#endif
#if ANY(CORE_IS_XZ, CORE_IS_YZ)
dm.rz = (steps_dist.c > 0); // True direction in Z
#endif
TERN_(HAS_REAL_X, dm.rx = (steps_dist.x > 0)); // True direction in X
TERN_(HAS_REAL_Y, dm.ry = (steps_dist.y > 0)); // True direction in Y
TERN_(HAS_REAL_Z, dm.rz = (steps_dist.z > 0)); // True direction in Z
#if CORE_IS_XY
dm.a = (steps_dist.a + steps_dist.b > 0); // Motor A direction
dm.b = (CORESIGN(steps_dist.a - steps_dist.b) > 0); // Motor B direction
TERN_(HAS_Z_AXIS, dm.z = (steps_dist.c > 0)); // Axis Z direction
#elif CORE_IS_XZ
dm.a = (steps_dist.a + steps_dist.c > 0); // Motor A direction
dm.y = (steps_dist.b > 0); // Axis Y direction
dm.c = (CORESIGN(steps_dist.a - steps_dist.c) > 0); // Motor C direction
#elif CORE_IS_YZ
dm.x = (steps_dist.a > 0); // Axis X direction
dm.b = (steps_dist.b + steps_dist.c > 0); // Motor B direction
dm.c = (CORESIGN(steps_dist.b - steps_dist.c) > 0); // Motor C direction
#elif ENABLED(MARKFORGED_XY)
dm.a = (steps_dist.a TERN(MARKFORGED_INVERSE, -, +) steps_dist.b > 0); // Motor A direction
dm.b = (steps_dist.b > 0); // Motor B direction
TERN_(HAS_Z_AXIS, dm.z = (steps_dist.c > 0)); // Axis Z direction
#elif ENABLED(MARKFORGED_YX)
dm.a = (steps_dist.a > 0); // Motor A direction
dm.b = (steps_dist.b TERN(MARKFORGED_INVERSE, -, +) steps_dist.a > 0); // Motor B direction
TERN_(HAS_Z_AXIS, dm.z = (steps_dist.c > 0)); // Axis Z direction
#else
XYZ_CODE(
dm.x = (steps_dist.a > 0),
dm.y = (steps_dist.b > 0),
dm.z = (steps_dist.c > 0)
dm.a = (steps_dist.x + steps_dist.y) > 0, // Motor A direction
dm.b = CORESIGN(steps_dist.x - steps_dist.y) > 0, // Motor B direction
dm.z = (steps_dist.z > 0) // Axis Z direction
);
#elif CORE_IS_XZ
dm.a = (steps_dist.x + steps_dist.z) > 0; // Motor A direction
dm.y = (steps_dist.y > 0); // Axis Y direction
dm.c = (CORESIGN(steps_dist.x - steps_dist.z) > 0); // Motor C direction
#elif CORE_IS_YZ
dm.x = (steps_dist.x > 0); // Axis X direction
dm.b = (steps_dist.y + steps_dist.z > 0); // Motor B direction
dm.c = (CORESIGN(steps_dist.y - steps_dist.z) > 0); // Motor C direction
#elif ENABLED(MARKFORGED_XY)
dm.a = (steps_dist.x TERN(MARKFORGED_INVERSE, -, +) steps_dist.y > 0); // Motor A direction
dm.b = (steps_dist.y > 0); // Motor B direction
TERN_(HAS_Z_AXIS, dm.z = (steps_dist.z > 0)); // Axis Z direction
#elif ENABLED(MARKFORGED_YX)
dm.a = (steps_dist.x > 0); // Motor A direction
dm.b = (steps_dist.y TERN(MARKFORGED_INVERSE, -, +) steps_dist.x > 0); // Motor B direction
TERN_(HAS_Z_AXIS, dm.z = (steps_dist.z > 0)); // Axis Z direction
#else
XYZ_CODE(dm.x = (steps_dist.x > 0), dm.y = (steps_dist.y > 0), dm.z = (steps_dist.z > 0));
#endif
SECONDARY_AXIS_CODE(
@ -1965,82 +1959,86 @@ bool Planner::_populate_block(
/**
* This part of the code calculates the total length of the movement.
* For Cartesian bots, the distance in XY axes equals the X_AXIS/Y_AXIS joint displacement.
* But for geometries like CORE_XY that is not true. For these machines we need to create 2 additional variables, named X_HEAD and Y_HEAD, to store the displacent of the head along the X and Y axes in a cartesian coordinate system.
* The displacement of the head along the axes of the cartesian coordinate system has to be calculated from "X_AXIS" and "Y_AXIS" (should be renamed to A_JOINT and B_JOINT)
* For Core/H-bot geometries we use X_REAL and Y_REAL to store the real XY displacement of the head in Cartesian coordinates.
* The Cartesian head displacement is calculated from a combined ABC stepper positions.
* For the joint displacements use forward kinematics (A=X+Y and B=X-Y in the case of CORE_XY).
* Next we can calculate the total movement length and apply the desired speed.
*/
struct DistanceMM : abce_float_t {
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
struct { float x, y, z; } head;
#endif
} dist_mm;
ext_distance_t dist_mm;
#if ANY(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
dist_mm.head.x = steps_dist.a * mm_per_step[A_AXIS];
dist_mm.head.y = steps_dist.b * mm_per_step[B_AXIS];
TERN_(HAS_Z_AXIS, dist_mm.z = steps_dist.c * mm_per_step[Z_AXIS]);
#if HAS_X_AXIS
const float dx = steps_dist.x * mm_per_step[X_AXIS]; // Axis X or Tower A Steps
#endif
#if HAS_Y_AXIS
const float dy = steps_dist.y * mm_per_step[Y_AXIS]; // Axis Y or Tower B Steps
#endif
#if HAS_Z_AXIS
const float dz = steps_dist.z * mm_per_step[Z_AXIS]; // Axis Z or Tower C Steps
#endif
#if CORE_IS_XY
dist_mm.a = (steps_dist.a + steps_dist.b) * mm_per_step[A_AXIS];
dist_mm.b = CORESIGN(steps_dist.a - steps_dist.b) * mm_per_step[B_AXIS];
#elif CORE_IS_XZ
dist_mm.head.x = steps_dist.a * mm_per_step[A_AXIS];
dist_mm.y = steps_dist.b * mm_per_step[Y_AXIS];
dist_mm.head.z = steps_dist.c * mm_per_step[C_AXIS];
dist_mm.a = (steps_dist.a + steps_dist.c) * mm_per_step[A_AXIS];
dist_mm.c = CORESIGN(steps_dist.a - steps_dist.c) * mm_per_step[C_AXIS];
#elif CORE_IS_YZ
dist_mm.x = steps_dist.a * mm_per_step[X_AXIS];
dist_mm.head.y = steps_dist.b * mm_per_step[B_AXIS];
dist_mm.head.z = steps_dist.c * mm_per_step[C_AXIS];
dist_mm.b = (steps_dist.b + steps_dist.c) * mm_per_step[B_AXIS];
dist_mm.c = CORESIGN(steps_dist.b - steps_dist.c) * mm_per_step[C_AXIS];
#elif ENABLED(MARKFORGED_XY)
dist_mm.a = (steps_dist.a TERN(MARKFORGED_INVERSE, +, -) steps_dist.b) * mm_per_step[A_AXIS];
dist_mm.b = steps_dist.b * mm_per_step[B_AXIS];
#elif ENABLED(MARKFORGED_YX)
dist_mm.a = steps_dist.a * mm_per_step[A_AXIS];
dist_mm.b = (steps_dist.b TERN(MARKFORGED_INVERSE, +, -) steps_dist.a) * mm_per_step[B_AXIS];
#else
XYZ_CODE(
dist_mm.a = steps_dist.a * mm_per_step[A_AXIS],
dist_mm.b = steps_dist.b * mm_per_step[B_AXIS],
dist_mm.c = steps_dist.c * mm_per_step[C_AXIS]
dist_mm.a = dx + dy,
dist_mm.b = CORESIGN(dx - dy),
dist_mm.z = dz
);
#elif CORE_IS_XZ
dist_mm.a = dx + dz,
dist_mm.y = dy,
dist_mm.c = CORESIGN(dx - dz)
#elif CORE_IS_YZ
dist_mm.x = dx;
dist_mm.b = dy + dz;
dist_mm.c = CORESIGN(dy - dz);
#elif ENABLED(MARKFORGED_XY)
XYZ_CODE(
dist_mm.a = dx TERN(MARKFORGED_INVERSE, +, -) dy,
dist_mm.b = dy,
dist_mm.z = dz
);
#elif ENABLED(MARKFORGED_YX)
XYZ_CODE(
dist_mm.a = dx,
dist_mm.b = dy TERN(MARKFORGED_INVERSE, +, -) dx,
dist_mm.z = dz
);
#else
// Cartesian, Delta, SCARA, etc.
XYZ_CODE(dist_mm.a = dx, dist_mm.b = dy, dist_mm.c = dz);
#endif
SECONDARY_AXIS_CODE(
dist_mm.i = steps_dist.i * mm_per_step[I_AXIS], dist_mm.j = steps_dist.j * mm_per_step[J_AXIS], dist_mm.k = steps_dist.k * mm_per_step[K_AXIS],
dist_mm.u = steps_dist.u * mm_per_step[U_AXIS], dist_mm.v = steps_dist.v * mm_per_step[V_AXIS], dist_mm.w = steps_dist.w * mm_per_step[W_AXIS]
dist_mm.i = steps_dist.i * mm_per_step[I_AXIS], dist_mm.j = steps_dist.j * mm_per_step[J_AXIS],
dist_mm.k = steps_dist.k * mm_per_step[K_AXIS], dist_mm.u = steps_dist.u * mm_per_step[U_AXIS],
dist_mm.v = steps_dist.v * mm_per_step[V_AXIS], dist_mm.w = steps_dist.w * mm_per_step[W_AXIS]
);
TERN_(HAS_EXTRUDERS, dist_mm.e = esteps_float * mm_per_step[E_AXIS_N(extruder)]);
TERN_(HAS_REAL_X, dist_mm.real.x = dx);
TERN_(HAS_REAL_Y, dist_mm.real.y = dy);
TERN_(HAS_REAL_Z, dist_mm.real.z = dz);
TERN_(HAS_EXTRUDERS, dist_mm.e = esteps_float * mm_per_step[E_AXIS_N(extruder)]);
TERN_(LCD_SHOW_E_TOTAL, e_move_accumulator += dist_mm.e);
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;
#endif
// Determine linear distance for block->millimeters
if (!XYZ_HAS_ENOUGH_STEPS(block)) {
block->millimeters = TERN0(HAS_EXTRUDERS, ABS(dist_mm.e));
}
else {
if (hints.millimeters)
// Use a provided move length (e.g., always provided by Kinematic robots)
if (/* ANY(DELTA, IS_SCARA, TPARA) || */ hints.millimeters)
block->millimeters = hints.millimeters;
else {
// Calculate move length from axis positions
const xyze_pos_t displacement = LOGICAL_AXIS_ARRAY(
dist_mm.e,
#if ANY(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX)
dist_mm.head.x, dist_mm.head.y, dist_mm.z,
#elif CORE_IS_XZ
dist_mm.head.x, dist_mm.y, dist_mm.head.z,
#elif CORE_IS_YZ
dist_mm.x, dist_mm.head.y, dist_mm.head.z,
#else
dist_mm.x, dist_mm.y, dist_mm.z,
#endif
dx, dy, dz,
dist_mm.i, dist_mm.j, dist_mm.k,
dist_mm.u, dist_mm.v, dist_mm.w
);
@ -2051,19 +2049,19 @@ bool Planner::_populate_block(
/**
* At this point at least one of the axes has more steps than
* MIN_STEPS_PER_SEGMENT, ensuring the segment won't get dropped as
* zero-length. It's important to not apply corrections
* to blocks that would get dropped!
* zero-length. It's important to not apply corrections to blocks
* that would get dropped!
*
* A correction function is permitted to add steps to an axis, it
* should *never* remove steps!
* A correction function is permitted to add steps to an axis, but
* it should *never* remove steps!
*/
TERN_(BACKLASH_COMPENSATION, backlash.add_correction_steps(steps_dist, dm, block));
}
TERN_(FT_MOTION, block->distance_mm = dist_mm); // Store the distance for all axes in mm for this block
TERN_(HAS_EXTRUDERS, block->steps.e = esteps);
// Set the block's Step Event Count based from the axis with the most steps
block->step_event_count = (
#if NUM_AXES
_MAX(LOGICAL_AXIS_LIST(esteps,
@ -2076,9 +2074,11 @@ bool Planner::_populate_block(
#endif
);
// Bail if this is a zero-length block
// Bail if this is a "zero-length" block
if (block->step_event_count < MIN_STEPS_PER_SEGMENT) return false;
E_TERN_(block->extruder = extruder);
TERN_(MIXING_EXTRUDER, mixer.populate_block(block->b_color));
#if HAS_FAN
@ -2090,8 +2090,6 @@ bool Planner::_populate_block(
block->e_to_p_pressure = baricuda_e_to_p_pressure;
#endif
E_TERN_(block->extruder = extruder);
#if ENABLED(AUTO_POWER_CONTROL)
if (XYZ_HAS_STEPS(block)) powerManager.power_on();
#endif
@ -2983,6 +2981,7 @@ 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);
// Provide known Cartesian length in the hints structure
PlannerHints ph = hints;
if (!hints.millimeters)
ph.millimeters = get_move_distance(xyze_pos_t(cart_dist_mm) OPTARG(HAS_ROTATIONAL_AXES, ph.cartesian_move));

View file

@ -190,6 +190,52 @@ typedef struct {
#endif
typedef struct DistanceMM : abce_float_t {
#if ANY(HAS_REAL_X, HAS_REAL_Y, HAS_REAL_Z)
struct {
#if HAS_REAL_X
float x;
#endif
#if HAS_REAL_Y
float y;
#endif
#if HAS_REAL_Z
float z;
#endif
} real;
#endif
const float& operator[](const int n) const {
switch (n) {
#if HAS_REAL_X
case X_REAL: return real.x;
#endif
#if HAS_REAL_Y
case Y_REAL: return real.y;
#endif
#if HAS_REAL_Z
case Z_REAL: return real.z;
#endif
default: break;
}
return pos[n];
}
float& operator[](const int n) {
switch (n) {
#if HAS_REAL_X
case X_REAL: return real.x;
#endif
#if HAS_REAL_Y
case Y_REAL: return real.y;
#endif
#if HAS_REAL_Z
case Z_REAL: return real.z;
#endif
default: break;
}
return pos[n];
}
} ext_distance_t;
/**
* struct block_t
*
@ -262,7 +308,7 @@ typedef struct PlannerBlock {
AxisBits direction_bits; // Direction bits set for this block, where 1 is negative motion
#if ENABLED(FT_MOTION)
xyze_pos_t distance_mm; // The distance traveled in mm along each axis
ext_distance_t ext_distance_mm; // The distance traveled in mm along each axis
#endif
#if ANY(SMOOTH_LIN_ADVANCE, FTM_HAS_LIN_ADVANCE)

View file

@ -682,7 +682,7 @@ void Stepper::disable_all_steppers() {
// Set a single axis direction based on the last set flags.
// A direction bit of "1" indicates forward or positive motion.
#define SET_STEP_DIR(A) do{ \
const bool fwd = motor_direction(_AXIS(A)); \
const bool fwd = axis_direction(_AXIS(A)); \
A##_APPLY_DIR(fwd, false); \
count_direction[_AXIS(A)] = fwd ? 1 : -1; \
}while(0)
@ -2389,19 +2389,12 @@ void Stepper::isr() {
#endif
// Set flags for all axes that move in this block
// These are set per-axis, not per-stepper
AxisBits didmove;
NUM_AXIS_CODE(
if (X_MOVE_TEST) didmove.a = true, // Cartesian X or Kinematic A
if (Y_MOVE_TEST) didmove.b = true, // Cartesian Y or Kinematic B
if (Z_MOVE_TEST) didmove.c = true, // Cartesian Z or Kinematic C
if (!!current_block->steps.i) didmove.i = true,
if (!!current_block->steps.j) didmove.j = true,
if (!!current_block->steps.k) didmove.k = true,
if (!!current_block->steps.u) didmove.u = true,
if (!!current_block->steps.v) didmove.v = true,
if (!!current_block->steps.w) didmove.w = true
);
#define _DID_MOVE(A) didmove.A = bool(current_block->steps.A);
MAIN_AXIS_MAP(_DID_MOVE);
TERN_(HAS_REAL_X, didmove.rx = X_MOVE_TEST); // Cartesian X
TERN_(HAS_REAL_Y, didmove.ry = Y_MOVE_TEST); // ... Y
TERN_(HAS_REAL_Z, didmove.rz = Z_MOVE_TEST); // ... Z
axis_did_move = didmove;
}
@ -2563,7 +2556,7 @@ void Stepper::isr() {
const bool forward_e = la_step_rate < step_rate;
la_interval = calc_timer_interval((forward_e ? step_rate - la_step_rate : la_step_rate - step_rate) >> current_block->la_scaling);
if (forward_e != motor_direction(E_AXIS)) {
if (forward_e != axis_direction(E_AXIS)) {
last_direction_bits.toggle(E_AXIS);
count_direction.e *= -1;
@ -3009,7 +3002,7 @@ void Stepper::isr() {
#endif
la_interval = calc_timer_interval(uint32_t(ABS(step_rate)));
if (forward_e != motor_direction(E_AXIS)) {
if (forward_e != axis_direction(E_AXIS)) {
last_direction_bits.toggle(E_AXIS);
count_direction.e *= -1;
DIR_WAIT_BEFORE();
@ -3595,13 +3588,13 @@ int32_t Stepper::triggered_position(const AxisEnum axis) {
* Reporting
*/
#if ANY(CORE_IS_XY, CORE_IS_XZ, MARKFORGED_XY, MARKFORGED_YX, IS_SCARA, DELTA)
#if ANY(HAS_REAL_X, IS_SCARA, DELTA)
#define SAYS_A 1
#endif
#if ANY(CORE_IS_XY, CORE_IS_YZ, MARKFORGED_XY, MARKFORGED_YX, IS_SCARA, DELTA, POLAR)
#if ANY(HAS_REAL_Y, IS_SCARA, DELTA, POLAR)
#define SAYS_B 1
#endif
#if ANY(CORE_IS_XZ, CORE_IS_YZ, DELTA)
#if ANY(HAS_REAL_Z, DELTA)
#define SAYS_C 1
#endif

View file

@ -657,18 +657,18 @@ class Stepper {
// Quickly stop all steppers
FORCE_INLINE static void quick_stop() { abort_current_block = true; }
// The direction of a single motor. A true result indicates forward or positive motion.
FORCE_INLINE static bool motor_direction(const AxisEnum axis) { return last_direction_bits[axis]; }
// The direction of a single motor and/or real axis. A true result indicates forward or positive motion.
FORCE_INLINE static bool axis_direction(const AxisEnum real) { return last_direction_bits[real]; }
#if HAS_STANDARD_MOTION
// The last movement direction was not null on the specified axis. Note that motor direction is not necessarily the same.
FORCE_INLINE static bool axis_is_moving(const AxisEnum axis) { return axis_did_move[axis]; }
// The last segment moved on the specified motor and/or real axis.
FORCE_INLINE static bool axis_is_moving(const AxisEnum real) { return axis_did_move[real]; }
#endif
// Handle a triggered endstop
static void endstop_triggered(const AxisEnum axis);
// Triggered position of an axis in steps
// Triggered position of an axis in steps, converted as needed from Core kinematics
static int32_t triggered_position(const AxisEnum axis);
#if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM

View file

@ -1938,7 +1938,8 @@ void Temperature::mintemp_error(const heater_id_t heater_id OPTARG(ERR_INCLUDE_T
#endif
temp_hotend[e].soft_pwm_amount = (temp_hotend[e].celsius > temp_range[e].mintemp || is_hotend_preheating(e))
&& temp_hotend[e].celsius < temp_range[e].maxtemp ? (int)get_pid_output_hotend(e) >> 1 : 0;
&& (temp_hotend[e].celsius < temp_range[e].maxtemp)
? (int)get_pid_output_hotend(e) >> 1 : 0;
#if WATCH_HOTENDS
// Make sure temperature is increasing