NO_STANDARD_MOTION (#28212)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
Harald Wagener
2025-12-11 01:09:05 +01:00
committed by GitHub
parent 6b18b1bb8a
commit 9c29c634b7
14 changed files with 1398 additions and 1297 deletions
+5 -1
View File
@@ -1160,7 +1160,11 @@
#if ENABLED(FT_MOTION)
//#define FTM_IS_DEFAULT_MOTION // Use FT Motion as the factory default?
//#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 and M494 parameters
//#define FTM_HOME_AND_PROBE // Use FT Motion for homing / probing. Disable if FT Motion breaks these functions.
//#define NO_STANDARD_MOTION // Disable the standard motion system entirely to save Flash and RAM
#if DISABLED(NO_STANDARD_MOTION)
//#define FTM_HOME_AND_PROBE // Use FT Motion for homing / probing. Disable if FT Motion breaks these functions.
#endif
//#define FTM_DYNAMIC_FREQ // Enable for linear adjustment of XY shaping frequency according to Z or E
#if ENABLED(FTM_DYNAMIC_FREQ)
+28 -23
View File
@@ -156,38 +156,43 @@ void stepperTask(void *parameter) {
while (dma.rw_pos < DMA_SAMPLE_COUNT) {
#if ENABLED(FT_MOTION)
if (using_ftMotion) {
if (using_ftMotion) {
#if ENABLED(FT_MOTION)
if (!nextMainISR) stepper.ftMotion_stepper();
nextMainISR = 0;
}
#endif
#endif
}
else {
if (!using_ftMotion) {
if (!nextMainISR) {
stepper.pulse_phase_isr();
nextMainISR = stepper.block_phase_isr();
}
#if ENABLED(LIN_ADVANCE)
else if (!nextAdvanceISR) {
stepper.advance_isr();
nextAdvanceISR = stepper.la_interval;
#if HAS_STANDARD_MOTION
if (!nextMainISR) {
stepper.pulse_phase_isr();
nextMainISR = stepper.block_phase_isr();
}
#endif
else
i2s_push_sample();
#if ENABLED(LIN_ADVANCE)
else if (!nextAdvanceISR) {
stepper.advance_isr();
nextAdvanceISR = stepper.la_interval;
}
#endif
else
i2s_push_sample();
nextMainISR--;
nextMainISR--;
#if ENABLED(LIN_ADVANCE)
if (nextAdvanceISR == stepper.LA_ADV_NEVER)
nextAdvanceISR = stepper.la_interval;
#if ENABLED(LIN_ADVANCE)
if (nextAdvanceISR == stepper.LA_ADV_NEVER)
nextAdvanceISR = stepper.la_interval;
if (nextAdvanceISR && nextAdvanceISR != stepper.LA_ADV_NEVER)
nextAdvanceISR--;
#endif
#endif // HAS_STANDARD_MOTION
if (nextAdvanceISR && nextAdvanceISR != stepper.LA_ADV_NEVER)
nextAdvanceISR--;
#endif
}
}
}
+11 -9
View File
@@ -151,7 +151,7 @@ void GcodeSuite::M493_report(const bool forReplay/*=true*/) {
/**
* M493: Set Fixed-time Motion Control parameters
*
* S<bool> Set Fixed-Time motion mode on or off.
* S<bool> Set Fixed-Time motion mode on or off. Ignored with NO_STANDARD_MOTION.
* 0: Fixed-Time Motion OFF (Standard Motion)
* 1: Fixed-Time Motion ON
*
@@ -210,15 +210,17 @@ void GcodeSuite::M493() {
ft_config_t &c = ftMotion.cfg;
// Parse 'S' mode parameter.
if (parser.seen('S')) {
const bool active = parser.value_bool();
if (active != c.active) {
stepper.ftMotion_syncPosition();
c.active = active;
flag.report = true;
#if HAS_STANDARD_MOTION
// Parse 'S' mode parameter.
if (parser.seen('S')) {
const bool active = parser.value_bool();
if (active != c.active) {
stepper.ftMotion_syncPosition();
c.active = active;
flag.report = true;
}
}
}
#endif
#if NUM_AXES_SHAPED > 0
+39 -25
View File
@@ -346,10 +346,46 @@
#define HAS_CLASSIC_E_JERK 1
#endif
// Linear advance uses Jerk since E is an isolated axis
#if ALL(FT_MOTION, HAS_EXTRUDERS)
#define FTM_HAS_LIN_ADVANCE 1
// Fixed-Time Motion
#if ENABLED(FT_MOTION)
#if HAS_X_AXIS
#define HAS_FTM_SHAPING 1
#define FTM_SHAPER_X
#endif
#if HAS_Y_AXIS
#define FTM_SHAPER_Y
#endif
#if !HAS_Z_AXIS
#undef FTM_SHAPER_Z
#endif
#if HAS_EXTRUDERS
#define FTM_HAS_LIN_ADVANCE 1
#else
#undef FTM_SHAPER_E
#endif
#if ENABLED(NO_STANDARD_MOTION)
#define FTM_HOME_AND_PROBE
#undef LIN_ADVANCE
#undef SMOOTH_LIN_ADVANCE
#undef S_CURVE_ACCELERATION
#undef ADAPTIVE_STEP_SMOOTHING
#undef INPUT_SHAPING_X
#undef INPUT_SHAPING_Y
#undef INPUT_SHAPING_E_SYNC
#undef MULTISTEPPING_LIMIT
#define MULTISTEPPING_LIMIT 1
#endif
#endif
#if DISABLED(NO_STANDARD_MOTION)
#define HAS_STANDARD_MOTION 1
#endif
// ZV Input shaping
#if ANY(INPUT_SHAPING_X, INPUT_SHAPING_Y, INPUT_SHAPING_Z)
#define HAS_ZV_SHAPING 1
#endif
// Linear advance uses Jerk since E is an isolated axis
#if ANY(FTM_HAS_LIN_ADVANCE, LIN_ADVANCE)
#define HAS_LIN_ADVANCE_K 1
#endif
@@ -1521,28 +1557,6 @@
#define CANNOT_EMBED_CONFIGURATION defined(__AVR__)
#endif
// Input shaping
#if ANY(INPUT_SHAPING_X, INPUT_SHAPING_Y, INPUT_SHAPING_Z)
#define HAS_ZV_SHAPING 1
#endif
// FT Motion: Shapers
#if ENABLED(FT_MOTION)
#if HAS_X_AXIS
#define HAS_FTM_SHAPING 1
#define FTM_SHAPER_X
#endif
#if HAS_Y_AXIS
#define FTM_SHAPER_Y
#endif
#if !HAS_Z_AXIS
#undef FTM_SHAPER_Z
#endif
#if !HAS_EXTRUDERS
#undef FTM_SHAPER_E
#endif
#endif
// Multi-Stepping Limit
#ifndef MULTISTEPPING_LIMIT
#define MULTISTEPPING_LIMIT 128
+19
View File
@@ -4500,6 +4500,25 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
#if ENABLED(FTM_RESONANCE_TEST) && DISABLED(EMERGENCY_PARSER)
#error "EMERGENCY_PARSER is required with FTM_RESONANCE_TEST (to cancel the test)."
#endif
#if !HAS_STANDARD_MOTION
#if ENABLED(NONLINEAR_EXTRUSION)
#error "NONLINEAR_EXTRUSION is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(SMOOTH_LIN_ADVANCE)
#error "SMOOTH_LIN_ADVANCE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(MIXING_EXTRUDER)
#error "MIXING_EXTRUDER is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(FREEZE_FEATURE)
#error "FREEZE_FEATURE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(DIRECT_STEPPING)
#error "DIRECT_STEPPING is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(DIFFERENTIAL_EXTRUDER)
#error "DIFFERENTIAL_EXTRUDER is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(LASER_FEATURE)
#error "LASER_FEATURE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(Z_LATE_ENABLE)
#error "Z_LATE_ENABLE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#endif
#endif
#endif
// Multi-Stepping Limit
+4 -2
View File
@@ -499,8 +499,10 @@ void menu_move() {
START_MENU();
BACK_ITEM(MSG_MOTION);
bool show_state = c.active;
EDIT_ITEM(bool, MSG_FIXED_TIME_MOTION, &show_state, []{ (void)ftMotion.toggle(); });
#if HAS_STANDARD_MOTION
bool show_state = c.active;
EDIT_ITEM(bool, MSG_FIXED_TIME_MOTION, &show_state, []{ (void)ftMotion.toggle(); });
#endif
// Show only when FT Motion is active (or optionally always show)
if (c.active || ENABLED(FT_MOTION_NO_MENU_TOGGLE)) {
+1 -5
View File
@@ -375,12 +375,8 @@ bool FTMotion::plan_next_block() {
const xyze_pos_t& moveDist = current_block->dist_mm;
ratio = moveDist / totalLength;
const float mmps = totalLength / current_block->step_event_count, // (mm/step) Distance for each step
initial_speed = mmps * current_block->initial_rate, // (mm/s) Start feedrate
final_speed = mmps * current_block->final_rate; // (mm/s) End feedrate
// Plan the trajectory using the trajectory generator
currentGenerator->plan(initial_speed, final_speed, current_block->acceleration, current_block->nominal_speed, totalLength);
currentGenerator->plan(current_block->entry_speed, current_block->exit_speed, current_block->acceleration, current_block->nominal_speed, totalLength);
endPos_prevBlock += moveDist;
+44 -36
View File
@@ -58,7 +58,11 @@
* FTConfig - The active configured state of FT Motion
*/
typedef struct FTConfig {
bool active = ENABLED(FTM_IS_DEFAULT_MOTION); // Active (else standard motion)
#if HAS_STANDARD_MOTION
bool active = ENABLED(FTM_IS_DEFAULT_MOTION); // Active (else Standard Motion)
#else
static constexpr bool active = true; // Always active with NO_STANDARD_MOTION
#endif
bool axis_sync_enabled = true; // Axis synchronization enabled
#if HAS_FTM_SHAPING
@@ -104,6 +108,33 @@ typedef struct FTConfig {
constexpr bool goodBaseFreq(const float f) { return WITHIN(f, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2); }
void set_defaults() {
#if HAS_STANDARD_MOTION
active = ENABLED(FTM_IS_DEFAULT_MOTION);
#endif
#if HAS_FTM_SHAPING
#define _SET_CFG_DEFAULTS(A) do{ \
shaper.A = FTM_DEFAULT_SHAPER_##A; \
baseFreq.A = FTM_SHAPING_DEFAULT_FREQ_##A; \
zeta.A = FTM_SHAPING_ZETA_##A; \
vtol.A = FTM_SHAPING_V_TOL_##A; \
}while(0);
SHAPED_MAP(_SET_CFG_DEFAULTS);
#undef _SET_CFG_DEFAULTS
#if HAS_DYNAMIC_FREQ
dynFreqMode = FTM_DEFAULT_DYNFREQ_MODE;
dynFreqK.reset();
#endif
#endif // HAS_FTM_SHAPING
TERN_(FTM_POLYS, poly6_acceleration_overshoot = FTM_POLY6_ACCELERATION_OVERSHOOT);
}
} ft_config_t;
/**
@@ -122,31 +153,9 @@ class FTMotion {
static bool busy;
static void set_defaults() {
cfg.active = ENABLED(FTM_IS_DEFAULT_MOTION);
cfg.set_defaults();
#if HAS_FTM_SHAPING
#define _SET_CFG_DEFAULTS(A) do{ \
cfg.shaper.A = FTM_DEFAULT_SHAPER_##A; \
cfg.baseFreq.A = FTM_SHAPING_DEFAULT_FREQ_##A; \
cfg.zeta.A = FTM_SHAPING_ZETA_##A; \
cfg.vtol.A = FTM_SHAPING_V_TOL_##A; \
}while(0);
SHAPED_MAP(_SET_CFG_DEFAULTS);
#undef _SET_CFG_DEFAULTS
#if HAS_DYNAMIC_FREQ
cfg.dynFreqMode = FTM_DEFAULT_DYNFREQ_MODE;
//ZERO(cfg.dynFreqK);
#define _DYN_RESET(A) cfg.dynFreqK.A = 0.0f;
SHAPED_MAP(_DYN_RESET);
#undef _DYN_RESET
#endif
update_shaping_params();
#endif // HAS_FTM_SHAPING
TERN_(HAS_FTM_SHAPING, update_shaping_params());
#if ENABLED(FTM_SMOOTHING)
#define _SET_SMOOTH(A) set_smoothing_time(_AXIS(A), FTM_SMOOTHING_TIME_##A);
@@ -154,10 +163,7 @@ class FTMotion {
#undef _SET_SMOOTH
#endif
#if ENABLED(FTM_POLYS)
cfg.poly6_acceleration_overshoot = FTM_POLY6_ACCELERATION_OVERSHOOT;
setTrajectoryType(TrajectoryType::FTM_TRAJECTORY_TYPE);
#endif
TERN_(FTM_POLYS, setTrajectoryType(TrajectoryType::FTM_TRAJECTORY_TYPE));
reset();
}
@@ -188,12 +194,14 @@ class FTMotion {
static void reset(); // Reset all states of the fixed time conversion to defaults.
// Safely toggle the active state of FT Motion
static bool toggle() {
stepper.ftMotion_syncPosition();
FLIP(cfg.active);
update_shaping_params();
return cfg.active;
}
#if ALL(FT_MOTION, HAS_STANDARD_MOTION)
static bool toggle() {
stepper.ftMotion_syncPosition();
FLIP(cfg.active);
update_shaping_params();
return cfg.active;
}
#endif
// Trajectory generator selection
static void setTrajectoryType(const TrajectoryType type);
@@ -201,7 +209,7 @@ class FTMotion {
static FSTR_P getTrajectoryName();
FORCE_INLINE static bool axis_is_moving(const AxisEnum axis) {
return cfg.active ? moving_axis_flags[axis] : stepper.axis_is_moving(axis);
return cfg.active ? moving_axis_flags[axis] : TERN0(HAS_STANDARD_MOTION, stepper.axis_is_moving(axis));
}
FORCE_INLINE static bool motor_direction(const AxisEnum axis) {
return cfg.active ? axis_move_dir[axis] : stepper.last_direction_bits[axis];
+1
View File
@@ -73,6 +73,7 @@ struct FTShapedAxes {
T& operator[](const int axis) {
return val[axis_to_index(axis)];
}
void reset() { ZERO(val); }
private:
static constexpr int axis_to_index(const int axis) {
+159 -138
View File
@@ -797,141 +797,150 @@ block_t* Planner::get_future_block(const uint8_t offset) {
*/
void Planner::calculate_trapezoid_for_block(block_t * const block, const float entry_speed, const float exit_speed) {
const float spmm = block->steps_per_mm;
uint32_t initial_rate = entry_speed ? LROUND(entry_speed * spmm) : block->initial_rate,
final_rate = LROUND(exit_speed * spmm);
NOLESS(initial_rate, stepper.minimal_step_rate);
NOLESS(final_rate, stepper.minimal_step_rate);
NOLESS(block->nominal_rate, stepper.minimal_step_rate);
#if ANY(S_CURVE_ACCELERATION, LIN_ADVANCE)
// If we have some plateau time, the cruise rate will be the nominal rate
uint32_t cruise_rate = block->nominal_rate;
#if ENABLED(FT_MOTION)
block->entry_speed = entry_speed;
block->exit_speed = exit_speed;
#endif
// Steps for acceleration, plateau and deceleration
int32_t plateau_steps = block->step_event_count,
accelerate_steps = 0,
decelerate_steps = 0;
#if HAS_STANDARD_MOTION
const int32_t accel = block->acceleration_steps_per_s2;
float inverse_accel = 0.0f;
if (accel != 0) {
inverse_accel = 1.0f / accel;
const float half_inverse_accel = 0.5f * inverse_accel,
nominal_rate_sq = FLOAT_SQ(block->nominal_rate),
// Steps required for acceleration, deceleration to/from nominal rate
decelerate_steps_float = half_inverse_accel * (nominal_rate_sq - FLOAT_SQ(final_rate)),
accelerate_steps_float = half_inverse_accel * (nominal_rate_sq - FLOAT_SQ(initial_rate));
// Aims to fully reach nominal and final rates
accelerate_steps = CEIL(accelerate_steps_float);
decelerate_steps = CEIL(decelerate_steps_float);
const float spmm = block->steps_per_mm;
uint32_t initial_rate = entry_speed ? LROUND(entry_speed * spmm) : block->initial_rate,
final_rate = LROUND(exit_speed * spmm);
// Steps between acceleration and deceleration, if any
plateau_steps -= accelerate_steps + decelerate_steps;
NOLESS(initial_rate, stepper.minimal_step_rate);
NOLESS(final_rate, stepper.minimal_step_rate);
NOLESS(block->nominal_rate, stepper.minimal_step_rate);
// Does accelerate_steps + decelerate_steps exceed step_event_count?
// Then we can't possibly reach the nominal rate, there will be no cruising.
// Calculate accel / braking time in order to reach the final_rate exactly
// at the end of this block.
if (plateau_steps < 0) {
accelerate_steps = LROUND((block->step_event_count + accelerate_steps_float - decelerate_steps_float) * 0.5f);
LIMIT(accelerate_steps, 0, int32_t(block->step_event_count));
decelerate_steps = block->step_event_count - accelerate_steps;
#if ANY(S_CURVE_ACCELERATION, LIN_ADVANCE)
// If we have some plateau time, the cruise rate will be the nominal rate
uint32_t cruise_rate = block->nominal_rate;
#endif
#if ANY(S_CURVE_ACCELERATION, LIN_ADVANCE)
// We won't reach the cruising rate. Let's calculate the speed we will reach
NOMORE(cruise_rate, final_speed(initial_rate, accel, accelerate_steps));
#endif
}
}
// Steps for acceleration, plateau and deceleration
int32_t plateau_steps = block->step_event_count,
accelerate_steps = 0,
decelerate_steps = 0;
#if ANY(S_CURVE_ACCELERATION, SMOOTH_LIN_ADVANCE)
const float rate_factor = inverse_accel * (STEPPER_TIMER_RATE);
// Jerk controlled speed requires to express speed versus time, NOT steps
uint32_t acceleration_time = rate_factor * float(cruise_rate - initial_rate),
deceleration_time = rate_factor * float(cruise_rate - final_rate);
#endif
#if ENABLED(S_CURVE_ACCELERATION)
// And to offload calculations from the ISR, we also calculate the inverse of those times here
uint32_t acceleration_time_inverse = get_period_inverse(acceleration_time),
deceleration_time_inverse = get_period_inverse(deceleration_time);
#endif
const int32_t accel = block->acceleration_steps_per_s2;
float inverse_accel = 0.0f;
if (accel != 0) {
inverse_accel = 1.0f / accel;
const float half_inverse_accel = 0.5f * inverse_accel,
nominal_rate_sq = FLOAT_SQ(block->nominal_rate),
// Steps required for acceleration, deceleration to/from nominal rate
decelerate_steps_float = half_inverse_accel * (nominal_rate_sq - FLOAT_SQ(final_rate)),
accelerate_steps_float = half_inverse_accel * (nominal_rate_sq - FLOAT_SQ(initial_rate));
// Aims to fully reach nominal and final rates
accelerate_steps = CEIL(accelerate_steps_float);
decelerate_steps = CEIL(decelerate_steps_float);
// Store new block parameters
block->accelerate_before = accelerate_steps;
block->decelerate_start = block->step_event_count - decelerate_steps;
block->initial_rate = initial_rate;
block->final_rate = final_rate;
// Steps between acceleration and deceleration, if any
plateau_steps -= accelerate_steps + decelerate_steps;
#if ANY(S_CURVE_ACCELERATION, SMOOTH_LIN_ADVANCE)
block->acceleration_time = acceleration_time;
block->deceleration_time = deceleration_time;
block->cruise_rate = cruise_rate;
#endif
#if ENABLED(S_CURVE_ACCELERATION)
block->acceleration_time_inverse = acceleration_time_inverse;
block->deceleration_time_inverse = deceleration_time_inverse;
#endif
// Does accelerate_steps + decelerate_steps exceed step_event_count?
// Then we can't possibly reach the nominal rate, there will be no cruising.
// Calculate accel / braking time in order to reach the final_rate exactly
// at the end of this block.
if (plateau_steps < 0) {
accelerate_steps = LROUND((block->step_event_count + accelerate_steps_float - decelerate_steps_float) * 0.5f);
LIMIT(accelerate_steps, 0, int32_t(block->step_event_count));
decelerate_steps = block->step_event_count - accelerate_steps;
#if ENABLED(SMOOTH_LIN_ADVANCE)
block->cruise_time = plateau_steps > 0 ? float(plateau_steps) * float(STEPPER_TIMER_RATE) / float(cruise_rate) : 0;
#elif HAS_ROUGH_LIN_ADVANCE
if (block->la_advance_rate) {
const float comp = get_advance_k(block->extruder) * block->steps.e / block->step_event_count;
block->max_adv_steps = cruise_rate * comp;
block->final_adv_steps = final_rate * comp;
}
#endif
#if ENABLED(LASER_POWER_TRAP)
/**
* Laser Trapezoid Calculations
*
* Approximate the trapezoid with the laser, incrementing the power every `trap_ramp_entry_incr`
* steps while accelerating, and decrementing the power every `trap_ramp_exit_decr` while decelerating,
* to keep power proportional to feedrate. Laser power trap will reduce the initial power to no less
* than the laser_power_floor value. Based on the number of calculated accel/decel steps the power is
* distributed over the trapezoid entry- and exit-ramp steps.
*
* trap_ramp_active_pwr - The active power is initially set at a reduced level factor of initial
* power / accel steps and will be additively incremented using a trap_ramp_entry_incr value for each
* accel step processed later in the stepper code. The trap_ramp_exit_decr value is calculated as
* power / decel steps and is also adjusted to no less than the power floor.
*
* If the power == 0 the inline mode variables need to be set to zero to prevent stepper processing.
* The method allows for simpler non-powered moves like G0 or G28.
*
* Laser Trap Power works for all Jerk and Curve modes; however Arc-based moves will have issues since
* the segments are usually too small.
*/
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS
&& planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled
) {
if (block->laser.power > 0) {
NOLESS(block->laser.power, laser_power_floor);
block->laser.trap_ramp_active_pwr = (block->laser.power - laser_power_floor) * (initial_rate / float(block->nominal_rate)) + laser_power_floor;
block->laser.trap_ramp_entry_incr = (block->laser.power - block->laser.trap_ramp_active_pwr) / accelerate_steps;
float laser_pwr = block->laser.power * (final_rate / float(block->nominal_rate));
NOLESS(laser_pwr, laser_power_floor);
block->laser.trap_ramp_exit_decr = (block->laser.power - laser_pwr) / decelerate_steps;
#if ENABLED(DEBUG_LASER_TRAP)
SERIAL_ECHO_MSG("lp:", block->laser.power);
SERIAL_ECHO_MSG("as:", accelerate_steps);
SERIAL_ECHO_MSG("ds:", decelerate_steps);
SERIAL_ECHO_MSG("p.trap:", block->laser.trap_ramp_active_pwr);
SERIAL_ECHO_MSG("p.incr:", block->laser.trap_ramp_entry_incr);
SERIAL_ECHO_MSG("p.decr:", block->laser.trap_ramp_exit_decr);
#if ANY(S_CURVE_ACCELERATION, LIN_ADVANCE)
// We won't reach the cruising rate. Let's calculate the speed we will reach
NOMORE(cruise_rate, final_speed(initial_rate, accel, accelerate_steps));
#endif
}
else {
block->laser.trap_ramp_active_pwr = 0;
block->laser.trap_ramp_entry_incr = 0;
block->laser.trap_ramp_exit_decr = 0;
}
}
#endif // LASER_POWER_TRAP
#if ANY(S_CURVE_ACCELERATION, SMOOTH_LIN_ADVANCE)
const float rate_factor = inverse_accel * (STEPPER_TIMER_RATE);
// Jerk controlled speed requires to express speed versus time, NOT steps
uint32_t acceleration_time = rate_factor * float(cruise_rate - initial_rate),
deceleration_time = rate_factor * float(cruise_rate - final_rate);
#endif
#if ENABLED(S_CURVE_ACCELERATION)
// And to offload calculations from the ISR, we also calculate the inverse of those times here
uint32_t acceleration_time_inverse = get_period_inverse(acceleration_time),
deceleration_time_inverse = get_period_inverse(deceleration_time);
#endif
// Store new block parameters
block->accelerate_before = accelerate_steps;
block->decelerate_start = block->step_event_count - decelerate_steps;
block->initial_rate = initial_rate;
block->final_rate = final_rate;
#if ANY(S_CURVE_ACCELERATION, SMOOTH_LIN_ADVANCE)
block->acceleration_time = acceleration_time;
block->deceleration_time = deceleration_time;
block->cruise_rate = cruise_rate;
#endif
#if ENABLED(S_CURVE_ACCELERATION)
block->acceleration_time_inverse = acceleration_time_inverse;
block->deceleration_time_inverse = deceleration_time_inverse;
#endif
#if ENABLED(SMOOTH_LIN_ADVANCE)
block->cruise_time = plateau_steps > 0 ? float(plateau_steps) * float(STEPPER_TIMER_RATE) / float(cruise_rate) : 0;
#elif HAS_ROUGH_LIN_ADVANCE
if (block->la_advance_rate) {
const float comp = get_advance_k(block->extruder) * block->steps.e / block->step_event_count;
block->max_adv_steps = cruise_rate * comp;
block->final_adv_steps = final_rate * comp;
}
#endif
#if ENABLED(LASER_POWER_TRAP)
/**
* Laser Trapezoid Calculations
*
* Approximate the trapezoid with the laser, incrementing the power every `trap_ramp_entry_incr`
* steps while accelerating, and decrementing the power every `trap_ramp_exit_decr` while decelerating,
* to keep power proportional to feedrate. Laser power trap will reduce the initial power to no less
* than the laser_power_floor value. Based on the number of calculated accel/decel steps the power is
* distributed over the trapezoid entry- and exit-ramp steps.
*
* trap_ramp_active_pwr - The active power is initially set at a reduced level factor of initial
* power / accel steps and will be additively incremented using a trap_ramp_entry_incr value for each
* accel step processed later in the stepper code. The trap_ramp_exit_decr value is calculated as
* power / decel steps and is also adjusted to no less than the power floor.
*
* If the power == 0 the inline mode variables need to be set to zero to prevent stepper processing.
* The method allows for simpler non-powered moves like G0 or G28.
*
* Laser Trap Power works for all Jerk and Curve modes; however Arc-based moves will have issues since
* the segments are usually too small.
*/
if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS
&& planner.laser_inline.status.isPowered && planner.laser_inline.status.isEnabled
) {
if (block->laser.power > 0) {
NOLESS(block->laser.power, laser_power_floor);
block->laser.trap_ramp_active_pwr = (block->laser.power - laser_power_floor) * (initial_rate / float(block->nominal_rate)) + laser_power_floor;
block->laser.trap_ramp_entry_incr = (block->laser.power - block->laser.trap_ramp_active_pwr) / accelerate_steps;
float laser_pwr = block->laser.power * (final_rate / float(block->nominal_rate));
NOLESS(laser_pwr, laser_power_floor);
block->laser.trap_ramp_exit_decr = (block->laser.power - laser_pwr) / decelerate_steps;
#if ENABLED(DEBUG_LASER_TRAP)
SERIAL_ECHO_MSG("lp:", block->laser.power);
SERIAL_ECHO_MSG("as:", accelerate_steps);
SERIAL_ECHO_MSG("ds:", decelerate_steps);
SERIAL_ECHO_MSG("p.trap:", block->laser.trap_ramp_active_pwr);
SERIAL_ECHO_MSG("p.incr:", block->laser.trap_ramp_entry_incr);
SERIAL_ECHO_MSG("p.decr:", block->laser.trap_ramp_exit_decr);
#endif
}
else {
block->laser.trap_ramp_active_pwr = 0;
block->laser.trap_ramp_entry_incr = 0;
block->laser.trap_ramp_exit_decr = 0;
}
}
#endif // LASER_POWER_TRAP
#endif // HAS_STANDARD_MOTION
}
/**
@@ -1131,12 +1140,18 @@ void Planner::recalculate_trapezoids(const float safe_exit_speed_sqr) {
if (stepper.is_block_busy(block)) {
// Block is BUSY so we can't change the exit speed. Revert any reverse pass change.
next->entry_speed_sqr = next->min_entry_speed_sqr;
if (!next->initial_rate) {
// 'next' was never calculated. Planner is falling behind so for maximum efficiency
// set next's stepping speed directly and forgo checking against min_entry_speed_sqr.
// calculate_trapezoid_for_block() can handle it, albeit sub-optimally.
next->initial_rate = block->final_rate;
}
// If 'next' was never calculated the Planner is falling behind, so for maximum efficiency
// set next's stepping speed directly and forego checking against min_entry_speed_sqr.
// calculate_trapezoid_for_block() can handle it, albeit sub-optimally.
#if HAS_STANDARD_MOTION
if (!next->initial_rate) next->initial_rate = block->final_rate;
#endif
#if ENABLED(FT_MOTION)
if (!next->entry_speed) next->entry_speed = block->exit_speed;
#endif
// Note that at this point next_entry_speed is (still) 0.
}
else {
@@ -2108,7 +2123,7 @@ bool Planner::_populate_block(
NUM_AXIS_CODE(
if (block->steps.x) stepper.enable_axis(X_AXIS),
if (block->steps.y) stepper.enable_axis(Y_AXIS),
if (TERN(Z_LATE_ENABLE, 0, block->steps.z)) stepper.enable_axis(Z_AXIS),
if (TERN(Z_LATE_ENABLE, false, block->steps.z)) stepper.enable_axis(Z_AXIS),
if (block->steps.i) stepper.enable_axis(I_AXIS),
if (block->steps.j) stepper.enable_axis(J_AXIS),
if (block->steps.k) stepper.enable_axis(K_AXIS),
@@ -2224,8 +2239,10 @@ bool Planner::_populate_block(
if (was_enabled) stepper.wake_up();
#endif
block->nominal_speed = block->millimeters * inverse_secs; // (mm/sec) Always > 0
block->nominal_rate = CEIL(block->step_event_count * inverse_secs); // (step/sec) Always > 0
block->nominal_speed = block->millimeters * inverse_secs; // (mm/sec) Always > 0
#if HAS_STANDARD_MOTION
block->nominal_rate = CEIL(block->step_event_count * inverse_secs); // (step/sec) Always > 0
#endif
#if ENABLED(FILAMENT_WIDTH_SENSOR)
if (extruder == FILAMENT_SENSOR_EXTRUDER_NUM) // Only for extruder with filament sensor
@@ -2317,7 +2334,7 @@ bool Planner::_populate_block(
// Correct the speed
if (speed_factor < 1.0f) {
current_speed *= speed_factor;
block->nominal_rate *= speed_factor;
TERN_(HAS_STANDARD_MOTION, block->nominal_rate *= speed_factor);
block->nominal_speed *= speed_factor;
}
@@ -2409,11 +2426,13 @@ bool Planner::_populate_block(
);
}
}
block->acceleration_steps_per_s2 = accel;
block->acceleration = accel / steps_per_mm;
#if DISABLED(S_CURVE_ACCELERATION)
block->acceleration_rate = uint32_t(accel * (float(_BV32(24)) / (STEPPER_TIMER_RATE)));
#if HAS_STANDARD_MOTION
block->acceleration_steps_per_s2 = accel;
#if DISABLED(S_CURVE_ACCELERATION)
block->acceleration_rate = uint32_t(accel * (float(_BV32(24)) / (STEPPER_TIMER_RATE)));
#endif
#endif
block->acceleration = accel / steps_per_mm;
#if HAS_ROUGH_LIN_ADVANCE
block->la_advance_rate = 0;
@@ -2724,8 +2743,10 @@ bool Planner::_populate_block(
block->entry_speed_sqr = minimum_planner_speed_sqr;
// Set min entry speed. Rarely it could be higher than the previous nominal speed but that's ok.
block->min_entry_speed_sqr = minimum_planner_speed_sqr;
// Zero the initial_rate to indicate that calculate_trapezoid_for_block() hasn't been called yet.
block->initial_rate = 0;
// Zero initial_rate and/or entry_speed to indicate calculate_trapezoid_for_block() needs a call.
TERN_(HAS_STANDARD_MOTION, block->initial_rate = 0);
TERN_(FT_MOTION, block->entry_speed = 0);
block->flag.recalculate = true;
+12 -5
View File
@@ -254,7 +254,7 @@ typedef struct PlannerBlock {
#if ENABLED(S_CURVE_ACCELERATION)
uint32_t acceleration_time_inverse, // Inverse of acceleration and deceleration periods, expressed as integer. Scale depends on CPU being used
deceleration_time_inverse;
#else
#elif HAS_STANDARD_MOTION
uint32_t acceleration_rate; // Acceleration rate in (2^24 steps)/timer_ticks*s
#endif
@@ -277,10 +277,17 @@ typedef struct PlannerBlock {
#endif
#endif
uint32_t nominal_rate, // The nominal step rate for this block in step_events/sec
initial_rate, // The jerk-adjusted step rate at start of block
final_rate, // The minimal rate at exit
acceleration_steps_per_s2; // acceleration steps/sec^2
#if ENABLED(FT_MOTION)
float entry_speed, // Block entry speed in steps units
exit_speed; // Block exit speed in steps units
#endif
#if HAS_STANDARD_MOTION
uint32_t nominal_rate, // The nominal step rate for this block in step_events/sec
initial_rate, // The jerk-adjusted step rate at start of block
final_rate, // The minimal rate at exit
acceleration_steps_per_s2; // acceleration steps/sec^2
#endif
#if ENABLED(DIRECT_STEPPING)
page_idx_t page_idx; // Page index used for direct stepping
+1049 -1035
View File
File diff suppressed because it is too large Load Diff
+24 -17
View File
@@ -400,8 +400,11 @@ class Stepper {
static block_t* current_block; // A pointer to the block currently being traced
static AxisBits last_direction_bits, // The next stepping-bits to be output
axis_did_move; // Last Movement in the given direction is not null, as computed when the last movement was fetched from planner
static AxisBits last_direction_bits; // The last set of directions applied to all axes
#if HAS_STANDARD_MOTION
static AxisBits axis_did_move; // Last Movement in the given direction is not null, as computed when the last movement was fetched from planner
#endif
static bool abort_current_block; // Signals to the stepper that current block should be aborted
@@ -542,11 +545,13 @@ class Stepper {
// The ISR scheduler
static void isr();
// The stepper pulse ISR phase
static void pulse_phase_isr();
#if HAS_STANDARD_MOTION
// The stepper pulse ISR phase
static void pulse_phase_isr();
// The stepper block processing ISR phase
static hal_timer_t block_phase_isr();
// The stepper block processing ISR phase
static hal_timer_t block_phase_isr();
#endif
#if HAS_ZV_SHAPING
static void shaping_isr();
@@ -618,7 +623,7 @@ class Stepper {
if (current_block->is_page()) page_manager.free_page(current_block->page_idx);
#endif
current_block = nullptr;
axis_did_move.reset();
TERN_(HAS_STANDARD_MOTION, axis_did_move.reset());
planner.release_current_block();
TERN_(HAS_ROUGH_LIN_ADVANCE, la_interval = nextAdvanceISR = LA_ADV_NEVER);
}
@@ -629,8 +634,10 @@ class Stepper {
// 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 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]; }
#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]; }
#endif
// Handle a triggered endstop
static void endstop_triggered(const AxisEnum axis);
@@ -761,14 +768,14 @@ class Stepper {
// Set the current position in steps
static void _set_position(const abce_long_t &spos);
// Calculate the timing interval for the given step rate
static hal_timer_t calc_timer_interval(uint32_t step_rate);
// Calculate timing interval and steps-per-ISR for the given step rate
static hal_timer_t calc_multistep_timer_interval(uint32_t step_rate);
// Evaluate axis motions and set bits in axis_did_move
static void set_axis_moved_for_current_block();
#if HAS_STANDARD_MOTION
// Calculate the timing interval for the given step rate
static hal_timer_t calc_timer_interval(uint32_t step_rate);
// Calculate timing interval and steps-per-ISR for the given step rate
static hal_timer_t calc_multistep_timer_interval(uint32_t step_rate);
// Evaluate axis motions and set bits in axis_did_move
static void set_axis_moved_for_current_block();
#endif
#if NONLINEAR_EXTRUSION_Q24
static void calc_nonlinear_e(const uint32_t step_rate);
+2 -1
View File
@@ -73,7 +73,8 @@ opt_set MOTHERBOARD BOARD_RAMBO EXTRUDERS 0 TEMP_SENSOR_BED 1 TEMP_SENSOR_PROBE
DEFAULT_MAX_ACCELERATION '{ 3000, 3000, 100 }' \
MANUAL_FEEDRATE '{ 50*60, 50*60, 4*60 }' \
AXIS_RELATIVE_MODES '{ false, false, false }'
opt_enable REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER FIX_MOUNTED_PROBE Z_SAFE_HOMING FT_MOTION FTM_SMOOTHING FTM_HOME_AND_PROBE
opt_enable REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER FIX_MOUNTED_PROBE Z_SAFE_HOMING \
FT_MOTION FTM_SMOOTHING FTM_HOME_AND_PROBE NO_STANDARD_MOTION
exec_test $1 $2 "Rambo with ZERO EXTRUDERS, heated bed, FT_MOTION" "$3"
#