✨ NO_STANDARD_MOTION (#28212)
Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+24
-17
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
#
|
||||
|
||||
Reference in New Issue
Block a user