Merge upstream changes from Marlin 2.1.2
This commit is contained in:
@@ -341,7 +341,6 @@ void report_current_position_projected() {
|
||||
can_reach = (
|
||||
a < polargraph_max_belt_len + 1
|
||||
&& b < polargraph_max_belt_len + 1
|
||||
&& (a + b) > _MIN(draw_area_size.x, draw_area_size.y)
|
||||
);
|
||||
|
||||
#endif
|
||||
@@ -562,7 +561,8 @@ void do_blocking_move_to(NUM_AXIS_ARGS(const float), const_feedRate_t fr_mm_s/*=
|
||||
const feedRate_t w_feedrate = fr_mm_s ?: homing_feedrate(W_AXIS)
|
||||
);
|
||||
|
||||
#if IS_KINEMATIC
|
||||
#if IS_KINEMATIC && DISABLED(POLARGRAPH)
|
||||
// kinematic machines are expected to home to a point 1.5x their range? never reachable.
|
||||
if (!position_is_reachable(x, y)) return;
|
||||
destination = current_position; // sync destination at the start
|
||||
#endif
|
||||
@@ -919,11 +919,16 @@ void restore_feedrate_and_scaling() {
|
||||
constexpr xy_pos_t offs{0};
|
||||
#endif
|
||||
|
||||
if (TERN1(IS_SCARA, axis_was_homed(X_AXIS) && axis_was_homed(Y_AXIS))) {
|
||||
const float dist_2 = HYPOT2(target.x - offs.x, target.y - offs.y);
|
||||
if (dist_2 > delta_max_radius_2)
|
||||
target *= float(delta_max_radius / SQRT(dist_2)); // 200 / 300 = 0.66
|
||||
}
|
||||
#if ENABLED(POLARGRAPH)
|
||||
LIMIT(target.x, draw_area_min.x, draw_area_max.x);
|
||||
LIMIT(target.y, draw_area_min.y, draw_area_max.y);
|
||||
#else
|
||||
if (TERN1(IS_SCARA, axis_was_homed(X_AXIS) && axis_was_homed(Y_AXIS))) {
|
||||
const float dist_2 = HYPOT2(target.x - offs.x, target.y - offs.y);
|
||||
if (dist_2 > delta_max_radius_2)
|
||||
target *= float(delta_max_radius / SQRT(dist_2)); // 200 / 300 = 0.66
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
@@ -1994,6 +1999,17 @@ void prepare_line_to_destination() {
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Back away to prevent opposite endstop damage
|
||||
//
|
||||
#if !defined(SENSORLESS_BACKOFF_MM) && XY_COUNTERPART_BACKOFF_MM
|
||||
if (!(axis_was_homed(X_AXIS) || axis_was_homed(Y_AXIS)) && (axis == X_AXIS || axis == Y_AXIS)) {
|
||||
const AxisEnum opposite_axis = axis == X_AXIS ? Y_AXIS : X_AXIS;
|
||||
const float backoff_length = -ABS(XY_COUNTERPART_BACKOFF_MM) * home_dir(opposite_axis);
|
||||
do_homing_move(opposite_axis, backoff_length, homing_feedrate(opposite_axis));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Determine if a homing bump will be done and the bumps distance
|
||||
// When homing Z with probe respect probe clearance
|
||||
const bool use_probe_bump = TERN0(HOMING_Z_WITH_PROBE, axis == Z_AXIS && home_bump_mm(axis));
|
||||
|
||||
@@ -227,7 +227,7 @@ float Planner::previous_nominal_speed;
|
||||
#endif
|
||||
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
float Planner::extruder_advance_K[EXTRUDERS]; // Initialized by settings.load()
|
||||
float Planner::extruder_advance_K[DISTINCT_E]; // Initialized by settings.load()
|
||||
#endif
|
||||
|
||||
#if HAS_POSITION_FLOAT
|
||||
@@ -793,19 +793,21 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
|
||||
uint32_t cruise_rate = block->nominal_rate;
|
||||
#endif
|
||||
|
||||
const int32_t accel = block->acceleration_steps_per_s2;
|
||||
|
||||
// Steps for acceleration, plateau and deceleration
|
||||
int32_t plateau_steps = block->step_event_count;
|
||||
uint32_t accelerate_steps = 0,
|
||||
decelerate_steps = 0;
|
||||
|
||||
const int32_t accel = block->acceleration_steps_per_s2;
|
||||
float inverse_accel = 0.0f;
|
||||
if (accel != 0) {
|
||||
// Steps required for acceleration, deceleration to/from nominal rate
|
||||
const float nominal_rate_sq = sq(float(block->nominal_rate));
|
||||
float accelerate_steps_float = (nominal_rate_sq - sq(float(initial_rate))) * (0.5f / accel);
|
||||
inverse_accel = 1.0f / accel;
|
||||
const float half_inverse_accel = 0.5f * inverse_accel,
|
||||
nominal_rate_sq = sq(float(block->nominal_rate)),
|
||||
// Steps required for acceleration, deceleration to/from nominal rate
|
||||
decelerate_steps_float = half_inverse_accel * (nominal_rate_sq - sq(float(final_rate)));
|
||||
float accelerate_steps_float = half_inverse_accel * (nominal_rate_sq - sq(float(initial_rate)));
|
||||
accelerate_steps = CEIL(accelerate_steps_float);
|
||||
const float decelerate_steps_float = (nominal_rate_sq - sq(float(final_rate))) * (0.5f / accel);
|
||||
decelerate_steps = FLOOR(decelerate_steps_float);
|
||||
|
||||
// Steps between acceleration and deceleration, if any
|
||||
@@ -828,9 +830,10 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
|
||||
}
|
||||
|
||||
#if ENABLED(S_CURVE_ACCELERATION)
|
||||
const float rate_factor = inverse_accel * (STEPPER_TIMER_RATE);
|
||||
// Jerk controlled speed requires to express speed versus time, NOT steps
|
||||
uint32_t acceleration_time = (float(cruise_rate - initial_rate) / accel) * (STEPPER_TIMER_RATE),
|
||||
deceleration_time = (float(cruise_rate - final_rate) / accel) * (STEPPER_TIMER_RATE),
|
||||
uint32_t acceleration_time = rate_factor * float(cruise_rate - initial_rate),
|
||||
deceleration_time = rate_factor * float(cruise_rate - final_rate),
|
||||
// And to offload calculations from the ISR, we also calculate the inverse of those times here
|
||||
acceleration_time_inverse = get_period_inverse(acceleration_time),
|
||||
deceleration_time_inverse = get_period_inverse(deceleration_time);
|
||||
@@ -851,7 +854,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
|
||||
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
if (block->la_advance_rate) {
|
||||
const float comp = extruder_advance_K[block->extruder] * block->steps.e / block->step_event_count;
|
||||
const float comp = extruder_advance_K[E_INDEX_N(block->extruder)] * block->steps.e / block->step_event_count;
|
||||
block->max_adv_steps = cruise_rate * comp;
|
||||
block->final_adv_steps = final_rate * comp;
|
||||
}
|
||||
@@ -1279,16 +1282,10 @@ void Planner::recalculate(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_s
|
||||
|
||||
void Planner::sync_fan_speeds(uint8_t (&fan_speed)[FAN_COUNT]) {
|
||||
|
||||
#if FAN_MIN_PWM != 0 || FAN_MAX_PWM != 255
|
||||
#define CALC_FAN_SPEED(f) (fan_speed[f] ? map(fan_speed[f], 1, 255, FAN_MIN_PWM, FAN_MAX_PWM) : FAN_OFF_PWM)
|
||||
#else
|
||||
#define CALC_FAN_SPEED(f) (fan_speed[f] ?: FAN_OFF_PWM)
|
||||
#endif
|
||||
|
||||
#if ENABLED(FAN_SOFT_PWM)
|
||||
#define _FAN_SET(F) thermalManager.soft_pwm_amount_fan[F] = CALC_FAN_SPEED(F);
|
||||
#define _FAN_SET(F) thermalManager.soft_pwm_amount_fan[F] = CALC_FAN_SPEED(fan_speed[F]);
|
||||
#else
|
||||
#define _FAN_SET(F) hal.set_pwm_duty(pin_t(FAN##F##_PIN), CALC_FAN_SPEED(F));
|
||||
#define _FAN_SET(F) hal.set_pwm_duty(pin_t(FAN##F##_PIN), CALC_FAN_SPEED(fan_speed[F]));
|
||||
#endif
|
||||
#define FAN_SET(F) do{ kickstart_fan(fan_speed, ms, F); _FAN_SET(F); }while(0)
|
||||
|
||||
@@ -1303,13 +1300,13 @@ void Planner::recalculate(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t safe_exit_s
|
||||
|
||||
void Planner::kickstart_fan(uint8_t (&fan_speed)[FAN_COUNT], const millis_t &ms, const uint8_t f) {
|
||||
static millis_t fan_kick_end[FAN_COUNT] = { 0 };
|
||||
if (fan_speed[f]) {
|
||||
if (fan_speed[f] > FAN_OFF_PWM) {
|
||||
if (fan_kick_end[f] == 0) {
|
||||
fan_kick_end[f] = ms + FAN_KICKSTART_TIME;
|
||||
fan_speed[f] = 255;
|
||||
fan_speed[f] = FAN_KICKSTART_POWER;
|
||||
}
|
||||
else if (PENDING(ms, fan_kick_end[f]))
|
||||
fan_speed[f] = 255;
|
||||
fan_speed[f] = FAN_KICKSTART_POWER;
|
||||
}
|
||||
else
|
||||
fan_kick_end[f] = 0;
|
||||
@@ -1727,6 +1724,13 @@ float Planner::triggered_position_mm(const AxisEnum axis) {
|
||||
return result * mm_per_step[axis];
|
||||
}
|
||||
|
||||
bool Planner::busy() {
|
||||
return (has_blocks_queued() || cleaning_buffer_counter
|
||||
|| TERN0(EXTERNAL_CLOSED_LOOP_CONTROLLER, CLOSED_LOOP_WAITING())
|
||||
|| TERN0(HAS_SHAPING, stepper.input_shaping_busy())
|
||||
);
|
||||
}
|
||||
|
||||
void Planner::finish_and_disable() {
|
||||
while (has_blocks_queued() || cleaning_buffer_counter) idle();
|
||||
stepper.disable_all_steppers();
|
||||
@@ -2170,7 +2174,7 @@ bool Planner::_populate_block(
|
||||
sq(steps_dist_mm.x), + sq(steps_dist_mm.y), + sq(steps_dist_mm.z),
|
||||
+ sq(steps_dist_mm.i), + sq(steps_dist_mm.j), + sq(steps_dist_mm.k),
|
||||
+ sq(steps_dist_mm.u), + sq(steps_dist_mm.v), + sq(steps_dist_mm.w)
|
||||
);
|
||||
)
|
||||
#elif ENABLED(FOAMCUTTER_XYUV)
|
||||
#if HAS_J_AXIS
|
||||
// Special 5 axis kinematics. Return the largest distance move from either X/Y or I/J plane
|
||||
@@ -2241,7 +2245,6 @@ bool Planner::_populate_block(
|
||||
|
||||
TERN_(MIXING_EXTRUDER, mixer.populate_block(block->b_color));
|
||||
|
||||
|
||||
#if HAS_FAN
|
||||
FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
|
||||
#endif
|
||||
@@ -2538,7 +2541,7 @@ bool Planner::_populate_block(
|
||||
*
|
||||
* de > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
|
||||
*/
|
||||
use_advance_lead = esteps && extruder_advance_K[extruder] && de > 0;
|
||||
use_advance_lead = esteps && extruder_advance_K[E_INDEX_N(extruder)] && de > 0;
|
||||
|
||||
if (use_advance_lead) {
|
||||
float e_D_ratio = (target_float.e - position_float.e) /
|
||||
@@ -2554,7 +2557,7 @@ bool Planner::_populate_block(
|
||||
use_advance_lead = false;
|
||||
else {
|
||||
// Scale E acceleration so that it will be possible to jump to the advance speed.
|
||||
const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[extruder] * e_D_ratio) * steps_per_mm;
|
||||
const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[E_INDEX_N(extruder)] * e_D_ratio) * steps_per_mm;
|
||||
if (TERN0(LA_DEBUG, accel > max_accel_steps_per_s2))
|
||||
SERIAL_ECHOLNPGM("Acceleration limited.");
|
||||
NOMORE(accel, max_accel_steps_per_s2);
|
||||
@@ -2591,7 +2594,7 @@ bool Planner::_populate_block(
|
||||
|
||||
if (use_advance_lead) {
|
||||
// the Bresenham algorithm will convert this step rate into extruder steps
|
||||
block->la_advance_rate = extruder_advance_K[extruder] * block->acceleration_steps_per_s2;
|
||||
block->la_advance_rate = extruder_advance_K[E_INDEX_N(extruder)] * block->acceleration_steps_per_s2;
|
||||
|
||||
// reduce LA ISR frequency by calling it only often enough to ensure that there will
|
||||
// never be more than four extruder steps per call
|
||||
|
||||
@@ -192,11 +192,11 @@ typedef struct PlannerBlock {
|
||||
|
||||
volatile block_flags_t flag; // Block flags
|
||||
|
||||
volatile bool is_fan_sync() { return TERN0(LASER_SYNCHRONOUS_M106_M107, flag.sync_fans); }
|
||||
volatile bool is_pwr_sync() { return TERN0(LASER_POWER_SYNC, flag.sync_laser_pwr); }
|
||||
volatile bool is_sync() { return flag.sync_position || is_fan_sync() || is_pwr_sync(); }
|
||||
volatile bool is_page() { return TERN0(DIRECT_STEPPING, flag.page); }
|
||||
volatile bool is_move() { return !(is_sync() || is_page()); }
|
||||
bool is_fan_sync() { return TERN0(LASER_SYNCHRONOUS_M106_M107, flag.sync_fans); }
|
||||
bool is_pwr_sync() { return TERN0(LASER_POWER_SYNC, flag.sync_laser_pwr); }
|
||||
bool is_sync() { return flag.sync_position || is_fan_sync() || is_pwr_sync(); }
|
||||
bool is_page() { return TERN0(DIRECT_STEPPING, flag.page); }
|
||||
bool is_move() { return !(is_sync() || is_page()); }
|
||||
|
||||
// Fields used by the motion planner to manage acceleration
|
||||
float nominal_speed, // The nominal speed for this block in (mm/sec)
|
||||
@@ -459,7 +459,7 @@ class Planner {
|
||||
#endif
|
||||
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
static float extruder_advance_K[EXTRUDERS];
|
||||
static float extruder_advance_K[DISTINCT_E];
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -930,11 +930,7 @@ class Planner {
|
||||
static float triggered_position_mm(const AxisEnum axis);
|
||||
|
||||
// Blocks are queued, or we're running out moves, or the closed loop controller is waiting
|
||||
static bool busy() {
|
||||
return (has_blocks_queued() || cleaning_buffer_counter
|
||||
|| TERN0(EXTERNAL_CLOSED_LOOP_CONTROLLER, CLOSED_LOOP_WAITING())
|
||||
);
|
||||
}
|
||||
static bool busy();
|
||||
|
||||
// Block until all buffered steps are executed / cleaned
|
||||
static void synchronize();
|
||||
@@ -988,7 +984,7 @@ class Planner {
|
||||
FORCE_INLINE static void recalculate_max_e_jerk() {
|
||||
const float prop = junction_deviation_mm * SQRT(0.5) / (1.0f - SQRT(0.5));
|
||||
EXTRUDER_LOOP()
|
||||
max_e_jerk[E_INDEX_N(e)] = SQRT(prop * settings.max_acceleration_mm_per_s2[E_INDEX_N(e)]);
|
||||
max_e_jerk[E_INDEX_N(e)] = SQRT(prop * settings.max_acceleration_mm_per_s2[E_AXIS_N(e)]);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -37,17 +37,12 @@
|
||||
#include "../lcd/marlinui.h"
|
||||
#include "../MarlinCore.h"
|
||||
|
||||
float segments_per_second; // Initialized by settings.load()
|
||||
|
||||
xy_pos_t draw_area_min = { X_MIN_POS, Y_MIN_POS },
|
||||
draw_area_max = { X_MAX_POS, Y_MAX_POS };
|
||||
|
||||
xy_float_t draw_area_size = { X_MAX_POS - X_MIN_POS, Y_MAX_POS - Y_MIN_POS };
|
||||
|
||||
float polargraph_max_belt_len = HYPOT(draw_area_size.x, draw_area_size.y);
|
||||
// Initialized by settings.load()
|
||||
float segments_per_second, polargraph_max_belt_len;
|
||||
xy_pos_t draw_area_min, draw_area_max;
|
||||
|
||||
void inverse_kinematics(const xyz_pos_t &raw) {
|
||||
const float x1 = raw.x - (draw_area_min.x), x2 = (draw_area_max.x) - raw.x, y = raw.y - (draw_area_max.y);
|
||||
const float x1 = raw.x - draw_area_min.x, x2 = draw_area_max.x - raw.x, y = raw.y - draw_area_max.y;
|
||||
delta.set(HYPOT(x1, y), HYPOT(x2, y), raw.z);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
|
||||
extern float segments_per_second;
|
||||
extern xy_pos_t draw_area_min, draw_area_max;
|
||||
extern xy_float_t draw_area_size;
|
||||
extern float polargraph_max_belt_len;
|
||||
|
||||
void inverse_kinematics(const xyz_pos_t &raw);
|
||||
|
||||
@@ -314,13 +314,13 @@ void PrintCounter::reset() {
|
||||
void PrintCounter::resetServiceInterval(const int index) {
|
||||
switch (index) {
|
||||
#if SERVICE_INTERVAL_1 > 0
|
||||
case 1: data.nextService1 = SERVICE_INTERVAL_SEC_1;
|
||||
case 1: data.nextService1 = SERVICE_INTERVAL_SEC_1; break;
|
||||
#endif
|
||||
#if SERVICE_INTERVAL_2 > 0
|
||||
case 2: data.nextService2 = SERVICE_INTERVAL_SEC_2;
|
||||
case 2: data.nextService2 = SERVICE_INTERVAL_SEC_2; break;
|
||||
#endif
|
||||
#if SERVICE_INTERVAL_3 > 0
|
||||
case 3: data.nextService3 = SERVICE_INTERVAL_SEC_3;
|
||||
case 3: data.nextService3 = SERVICE_INTERVAL_SEC_3; break;
|
||||
#endif
|
||||
}
|
||||
saveStats();
|
||||
|
||||
@@ -882,7 +882,9 @@ float Probe::probe_at_point(const_float_t rx, const_float_t ry, const ProbePtRai
|
||||
// Move the probe to the starting XYZ
|
||||
do_blocking_move_to(npos, feedRate_t(XY_PROBE_FEEDRATE_MM_S));
|
||||
|
||||
TERN_(BD_SENSOR, return bdl.read());
|
||||
#if ENABLED(BD_SENSOR)
|
||||
return current_position.z - bdl.read(); // Difference between Z-home-relative Z and sensor reading
|
||||
#endif
|
||||
|
||||
float measured_z = NAN;
|
||||
if (!deploy()) {
|
||||
|
||||
@@ -146,7 +146,7 @@ public:
|
||||
|
||||
#else
|
||||
|
||||
static constexpr xyz_pos_t offset = xyz_pos_t(NUM_AXIS_ARRAY(0, 0, 0, 0, 0, 0)); // See #16767
|
||||
static constexpr xyz_pos_t offset = xyz_pos_t(NUM_AXIS_ARRAY_1(0)); // See #16767
|
||||
|
||||
static bool set_deployed(const bool) { return false; }
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include "../MarlinCore.h"
|
||||
#endif
|
||||
|
||||
float segments_per_second = TERN(AXEL_TPARA, TPARA_SEGMENTS_PER_SECOND, SCARA_SEGMENTS_PER_SECOND);
|
||||
float segments_per_second = DEFAULT_SEGMENTS_PER_SECOND;
|
||||
|
||||
#if EITHER(MORGAN_SCARA, MP_SCARA)
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
*/
|
||||
|
||||
// Change EEPROM version if the structure changes
|
||||
#define EEPROM_VERSION "V86"
|
||||
#define EEPROM_VERSION "V87"
|
||||
#define EEPROM_OFFSET 100
|
||||
|
||||
// Check the integrity of data offsets.
|
||||
@@ -118,8 +118,8 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(EXTRA_LIN_ADVANCE_K)
|
||||
extern float other_extruder_advance_K[EXTRUDERS];
|
||||
#if ENABLED(ADVANCE_K_EXTRA)
|
||||
extern float other_extruder_advance_K[DISTINCT_E];
|
||||
#endif
|
||||
|
||||
#if HAS_MULTI_EXTRUDER
|
||||
@@ -257,7 +257,7 @@ typedef struct SettingsDataStruct {
|
||||
// HAS_BED_PROBE
|
||||
//
|
||||
|
||||
xyz_pos_t probe_offset;
|
||||
xyz_pos_t probe_offset; // M851 X Y Z
|
||||
|
||||
//
|
||||
// ABL_PLANAR
|
||||
@@ -319,7 +319,7 @@ typedef struct SettingsDataStruct {
|
||||
#endif
|
||||
|
||||
//
|
||||
// Kinematic Settings
|
||||
// Kinematic Settings (Delta, SCARA, TPARA, Polargraph...)
|
||||
//
|
||||
#if IS_KINEMATIC
|
||||
float segments_per_second; // M665 S
|
||||
@@ -330,7 +330,11 @@ typedef struct SettingsDataStruct {
|
||||
delta_diagonal_rod; // M665 L
|
||||
abc_float_t delta_tower_angle_trim, // M665 X Y Z
|
||||
delta_diagonal_rod_trim; // M665 A B C
|
||||
#elif ENABLED(POLARGRAPH)
|
||||
xy_pos_t draw_area_min, draw_area_max; // M665 L R T B
|
||||
float polargraph_max_belt_len; // M665 H
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//
|
||||
@@ -364,18 +368,18 @@ typedef struct SettingsDataStruct {
|
||||
//
|
||||
// PIDTEMP
|
||||
//
|
||||
PIDCF_t hotendPID[HOTENDS]; // M301 En PIDCF / M303 En U
|
||||
raw_pidcf_t hotendPID[HOTENDS]; // M301 En PIDCF / M303 En U
|
||||
int16_t lpq_len; // M301 L
|
||||
|
||||
//
|
||||
// PIDTEMPBED
|
||||
//
|
||||
PID_t bedPID; // M304 PID / M303 E-1 U
|
||||
raw_pid_t bedPID; // M304 PID / M303 E-1 U
|
||||
|
||||
//
|
||||
// PIDTEMPCHAMBER
|
||||
//
|
||||
PID_t chamberPID; // M309 PID / M303 E-2 U
|
||||
raw_pid_t chamberPID; // M309 PID / M303 E-2 U
|
||||
|
||||
//
|
||||
// User-defined Thermistors
|
||||
@@ -402,8 +406,8 @@ typedef struct SettingsDataStruct {
|
||||
//
|
||||
// Display Sleep
|
||||
//
|
||||
#if LCD_BACKLIGHT_TIMEOUT
|
||||
uint16_t lcd_backlight_timeout; // M255 S
|
||||
#if LCD_BACKLIGHT_TIMEOUT_MINS
|
||||
uint8_t backlight_timeout_minutes; // M255 S
|
||||
#elif HAS_DISPLAY_SLEEP
|
||||
uint8_t sleep_timeout_minutes; // M255 S
|
||||
#endif
|
||||
@@ -442,7 +446,7 @@ typedef struct SettingsDataStruct {
|
||||
//
|
||||
// LIN_ADVANCE
|
||||
//
|
||||
float planner_extruder_advance_K[_MAX(EXTRUDERS, 1)]; // M900 K planner.extruder_advance_K
|
||||
float planner_extruder_advance_K[DISTINCT_E]; // M900 K planner.extruder_advance_K
|
||||
|
||||
//
|
||||
// HAS_MOTOR_CURRENT_PWM
|
||||
@@ -468,7 +472,7 @@ typedef struct SettingsDataStruct {
|
||||
//
|
||||
// SKEW_CORRECTION
|
||||
//
|
||||
skew_factor_t planner_skew_factor; // M852 I J K planner.skew_factor
|
||||
skew_factor_t planner_skew_factor; // M852 I J K
|
||||
|
||||
//
|
||||
// ADVANCED_PAUSE_FEATURE
|
||||
@@ -573,6 +577,18 @@ typedef struct SettingsDataStruct {
|
||||
MPC_t mpc_constants[HOTENDS]; // M306
|
||||
#endif
|
||||
|
||||
//
|
||||
// Input Shaping
|
||||
//
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
float shaping_x_frequency, // M593 X F
|
||||
shaping_x_zeta; // M593 X D
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
float shaping_y_frequency, // M593 Y F
|
||||
shaping_y_zeta; // M593 Y D
|
||||
#endif
|
||||
|
||||
} SettingsData;
|
||||
|
||||
//static_assert(sizeof(SettingsData) <= MARLIN_EEPROM_SIZE, "EEPROM too small to contain SettingsData!");
|
||||
@@ -640,7 +656,7 @@ void MarlinSettings::postprocess() {
|
||||
TERN_(HAS_LCD_CONTRAST, ui.refresh_contrast());
|
||||
TERN_(HAS_LCD_BRIGHTNESS, ui.refresh_brightness());
|
||||
|
||||
#if LCD_BACKLIGHT_TIMEOUT
|
||||
#if LCD_BACKLIGHT_TIMEOUT_MINS
|
||||
ui.refresh_backlight_timeout();
|
||||
#elif HAS_DISPLAY_SLEEP
|
||||
ui.refresh_screen_timeout();
|
||||
@@ -988,7 +1004,7 @@ void MarlinSettings::postprocess() {
|
||||
}
|
||||
|
||||
//
|
||||
// Kinematic Settings
|
||||
// Kinematic Settings (Delta, SCARA, TPARA, Polargraph...)
|
||||
//
|
||||
#if IS_KINEMATIC
|
||||
{
|
||||
@@ -1001,6 +1017,11 @@ void MarlinSettings::postprocess() {
|
||||
EEPROM_WRITE(delta_diagonal_rod); // 1 float
|
||||
EEPROM_WRITE(delta_tower_angle_trim); // 3 floats
|
||||
EEPROM_WRITE(delta_diagonal_rod_trim); // 3 floats
|
||||
#elif ENABLED(POLARGRAPH)
|
||||
_FIELD_TEST(draw_area_min);
|
||||
EEPROM_WRITE(draw_area_min); // 2 floats
|
||||
EEPROM_WRITE(draw_area_max); // 2 floats
|
||||
EEPROM_WRITE(polargraph_max_belt_len); // 1 float
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -1052,27 +1073,20 @@ void MarlinSettings::postprocess() {
|
||||
//
|
||||
{
|
||||
_FIELD_TEST(hotendPID);
|
||||
#if DISABLED(PIDTEMP)
|
||||
raw_pidcf_t pidcf = { NAN, NAN, NAN, NAN, NAN };
|
||||
#endif
|
||||
HOTEND_LOOP() {
|
||||
PIDCF_t pidcf = {
|
||||
#if DISABLED(PIDTEMP)
|
||||
NAN, NAN, NAN,
|
||||
NAN, NAN
|
||||
#else
|
||||
PID_PARAM(Kp, e),
|
||||
unscalePID_i(PID_PARAM(Ki, e)),
|
||||
unscalePID_d(PID_PARAM(Kd, e)),
|
||||
PID_PARAM(Kc, e),
|
||||
PID_PARAM(Kf, e)
|
||||
#endif
|
||||
};
|
||||
#if ENABLED(PIDTEMP)
|
||||
const hotend_pid_t &pid = thermalManager.temp_hotend[e].pid;
|
||||
raw_pidcf_t pidcf = { pid.p(), pid.i(), pid.d(), pid.c(), pid.f() };
|
||||
#endif
|
||||
EEPROM_WRITE(pidcf);
|
||||
}
|
||||
|
||||
_FIELD_TEST(lpq_len);
|
||||
#if DISABLED(PID_EXTRUSION_SCALING)
|
||||
const int16_t lpq_len = 20;
|
||||
#endif
|
||||
EEPROM_WRITE(TERN(PID_EXTRUSION_SCALING, thermalManager.lpq_len, lpq_len));
|
||||
const int16_t lpq_len = TERN(PID_EXTRUSION_SCALING, thermalManager.lpq_len, 20);
|
||||
EEPROM_WRITE(lpq_len);
|
||||
}
|
||||
|
||||
//
|
||||
@@ -1080,17 +1094,12 @@ void MarlinSettings::postprocess() {
|
||||
//
|
||||
{
|
||||
_FIELD_TEST(bedPID);
|
||||
|
||||
const PID_t bed_pid = {
|
||||
#if DISABLED(PIDTEMPBED)
|
||||
NAN, NAN, NAN
|
||||
#else
|
||||
// Store the unscaled PID values
|
||||
thermalManager.temp_bed.pid.Kp,
|
||||
unscalePID_i(thermalManager.temp_bed.pid.Ki),
|
||||
unscalePID_d(thermalManager.temp_bed.pid.Kd)
|
||||
#endif
|
||||
};
|
||||
#if ENABLED(PIDTEMPBED)
|
||||
const PID_t &pid = thermalManager.temp_bed.pid;
|
||||
const raw_pid_t bed_pid = { pid.p(), pid.i(), pid.d() };
|
||||
#else
|
||||
const raw_pid_t bed_pid = { NAN, NAN, NAN };
|
||||
#endif
|
||||
EEPROM_WRITE(bed_pid);
|
||||
}
|
||||
|
||||
@@ -1099,17 +1108,12 @@ void MarlinSettings::postprocess() {
|
||||
//
|
||||
{
|
||||
_FIELD_TEST(chamberPID);
|
||||
|
||||
const PID_t chamber_pid = {
|
||||
#if DISABLED(PIDTEMPCHAMBER)
|
||||
NAN, NAN, NAN
|
||||
#else
|
||||
// Store the unscaled PID values
|
||||
thermalManager.temp_chamber.pid.Kp,
|
||||
unscalePID_i(thermalManager.temp_chamber.pid.Ki),
|
||||
unscalePID_d(thermalManager.temp_chamber.pid.Kd)
|
||||
#endif
|
||||
};
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
const PID_t &pid = thermalManager.temp_chamber.pid;
|
||||
const raw_pid_t chamber_pid = { pid.p(), pid.i(), pid.d() };
|
||||
#else
|
||||
const raw_pid_t chamber_pid = { NAN, NAN, NAN };
|
||||
#endif
|
||||
EEPROM_WRITE(chamber_pid);
|
||||
}
|
||||
|
||||
@@ -1117,10 +1121,8 @@ void MarlinSettings::postprocess() {
|
||||
// User-defined Thermistors
|
||||
//
|
||||
#if HAS_USER_THERMISTORS
|
||||
{
|
||||
_FIELD_TEST(user_thermistor);
|
||||
EEPROM_WRITE(thermalManager.user_thermistor);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
@@ -1157,8 +1159,8 @@ void MarlinSettings::postprocess() {
|
||||
//
|
||||
// LCD Backlight / Sleep Timeout
|
||||
//
|
||||
#if LCD_BACKLIGHT_TIMEOUT
|
||||
EEPROM_WRITE(ui.lcd_backlight_timeout);
|
||||
#if LCD_BACKLIGHT_TIMEOUT_MINS
|
||||
EEPROM_WRITE(ui.backlight_timeout_minutes);
|
||||
#elif HAS_DISPLAY_SLEEP
|
||||
EEPROM_WRITE(ui.sleep_timeout_minutes);
|
||||
#endif
|
||||
@@ -1419,7 +1421,7 @@ void MarlinSettings::postprocess() {
|
||||
EEPROM_WRITE(planner.extruder_advance_K);
|
||||
#else
|
||||
dummyf = 0;
|
||||
for (uint8_t q = _MAX(EXTRUDERS, 1); q--;) EEPROM_WRITE(dummyf);
|
||||
for (uint8_t q = DISTINCT_E; q--;) EEPROM_WRITE(dummyf);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1612,6 +1614,20 @@ void MarlinSettings::postprocess() {
|
||||
EEPROM_WRITE(thermalManager.temp_hotend[e].constants);
|
||||
#endif
|
||||
|
||||
//
|
||||
// Input Shaping
|
||||
///
|
||||
#if HAS_SHAPING
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
EEPROM_WRITE(stepper.get_shaping_frequency(X_AXIS));
|
||||
EEPROM_WRITE(stepper.get_shaping_damping_ratio(X_AXIS));
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
EEPROM_WRITE(stepper.get_shaping_frequency(Y_AXIS));
|
||||
EEPROM_WRITE(stepper.get_shaping_damping_ratio(Y_AXIS));
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//
|
||||
// Report final CRC and Data Size
|
||||
//
|
||||
@@ -1942,7 +1958,7 @@ void MarlinSettings::postprocess() {
|
||||
}
|
||||
|
||||
//
|
||||
// Kinematic Segments-per-second
|
||||
// Kinematic Settings (Delta, SCARA, TPARA, Polargraph...)
|
||||
//
|
||||
#if IS_KINEMATIC
|
||||
{
|
||||
@@ -1955,6 +1971,11 @@ void MarlinSettings::postprocess() {
|
||||
EEPROM_READ(delta_diagonal_rod); // 1 float
|
||||
EEPROM_READ(delta_tower_angle_trim); // 3 floats
|
||||
EEPROM_READ(delta_diagonal_rod_trim); // 3 floats
|
||||
#elif ENABLED(POLARGRAPH)
|
||||
_FIELD_TEST(draw_area_min);
|
||||
EEPROM_READ(draw_area_min); // 2 floats
|
||||
EEPROM_READ(draw_area_max); // 2 floats
|
||||
EEPROM_READ(polargraph_max_belt_len); // 1 float
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -2003,17 +2024,11 @@ void MarlinSettings::postprocess() {
|
||||
//
|
||||
{
|
||||
HOTEND_LOOP() {
|
||||
PIDCF_t pidcf;
|
||||
raw_pidcf_t pidcf;
|
||||
EEPROM_READ(pidcf);
|
||||
#if ENABLED(PIDTEMP)
|
||||
if (!validating && !isnan(pidcf.Kp)) {
|
||||
// Scale PID values since EEPROM values are unscaled
|
||||
PID_PARAM(Kp, e) = pidcf.Kp;
|
||||
PID_PARAM(Ki, e) = scalePID_i(pidcf.Ki);
|
||||
PID_PARAM(Kd, e) = scalePID_d(pidcf.Kd);
|
||||
TERN_(PID_EXTRUSION_SCALING, PID_PARAM(Kc, e) = pidcf.Kc);
|
||||
TERN_(PID_FAN_SCALING, PID_PARAM(Kf, e) = pidcf.Kf);
|
||||
}
|
||||
if (!validating && !isnan(pidcf.p))
|
||||
thermalManager.temp_hotend[e].pid.set(pidcf);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -2035,15 +2050,11 @@ void MarlinSettings::postprocess() {
|
||||
// Heated Bed PID
|
||||
//
|
||||
{
|
||||
PID_t pid;
|
||||
raw_pid_t pid;
|
||||
EEPROM_READ(pid);
|
||||
#if ENABLED(PIDTEMPBED)
|
||||
if (!validating && !isnan(pid.Kp)) {
|
||||
// Scale PID values since EEPROM values are unscaled
|
||||
thermalManager.temp_bed.pid.Kp = pid.Kp;
|
||||
thermalManager.temp_bed.pid.Ki = scalePID_i(pid.Ki);
|
||||
thermalManager.temp_bed.pid.Kd = scalePID_d(pid.Kd);
|
||||
}
|
||||
if (!validating && !isnan(pid.p))
|
||||
thermalManager.temp_bed.pid.set(pid);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2051,15 +2062,11 @@ void MarlinSettings::postprocess() {
|
||||
// Heated Chamber PID
|
||||
//
|
||||
{
|
||||
PID_t pid;
|
||||
raw_pid_t pid;
|
||||
EEPROM_READ(pid);
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
if (!validating && !isnan(pid.Kp)) {
|
||||
// Scale PID values since EEPROM values are unscaled
|
||||
thermalManager.temp_chamber.pid.Kp = pid.Kp;
|
||||
thermalManager.temp_chamber.pid.Ki = scalePID_i(pid.Ki);
|
||||
thermalManager.temp_chamber.pid.Kd = scalePID_d(pid.Kd);
|
||||
}
|
||||
if (!validating && !isnan(pid.p))
|
||||
thermalManager.temp_chamber.pid.set(pid);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -2108,8 +2115,8 @@ void MarlinSettings::postprocess() {
|
||||
//
|
||||
// LCD Backlight / Sleep Timeout
|
||||
//
|
||||
#if LCD_BACKLIGHT_TIMEOUT
|
||||
EEPROM_READ(ui.lcd_backlight_timeout);
|
||||
#if LCD_BACKLIGHT_TIMEOUT_MINS
|
||||
EEPROM_READ(ui.backlight_timeout_minutes);
|
||||
#elif HAS_DISPLAY_SLEEP
|
||||
EEPROM_READ(ui.sleep_timeout_minutes);
|
||||
#endif
|
||||
@@ -2367,7 +2374,7 @@ void MarlinSettings::postprocess() {
|
||||
// Linear Advance
|
||||
//
|
||||
{
|
||||
float extruder_advance_K[_MAX(EXTRUDERS, 1)];
|
||||
float extruder_advance_K[DISTINCT_E];
|
||||
_FIELD_TEST(planner_extruder_advance_K);
|
||||
EEPROM_READ(extruder_advance_K);
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
@@ -2592,6 +2599,27 @@ void MarlinSettings::postprocess() {
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Input Shaping
|
||||
//
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
{
|
||||
float _data[2];
|
||||
EEPROM_READ(_data);
|
||||
stepper.set_shaping_frequency(X_AXIS, _data[0]);
|
||||
stepper.set_shaping_damping_ratio(X_AXIS, _data[1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
{
|
||||
float _data[2];
|
||||
EEPROM_READ(_data);
|
||||
stepper.set_shaping_frequency(Y_AXIS, _data[0]);
|
||||
stepper.set_shaping_damping_ratio(Y_AXIS, _data[1]);
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Validate Final Size and CRC
|
||||
//
|
||||
@@ -2909,6 +2937,7 @@ void MarlinSettings::reset() {
|
||||
toolchange_settings.unretract_speed = TOOLCHANGE_FS_UNRETRACT_SPEED;
|
||||
toolchange_settings.extra_prime = TOOLCHANGE_FS_EXTRA_PRIME;
|
||||
toolchange_settings.prime_speed = TOOLCHANGE_FS_PRIME_SPEED;
|
||||
toolchange_settings.wipe_retract = TOOLCHANGE_FS_WIPE_RETRACT;
|
||||
toolchange_settings.fan_speed = TOOLCHANGE_FS_FAN_SPEED;
|
||||
toolchange_settings.fan_time = TOOLCHANGE_FS_FAN_TIME;
|
||||
#endif
|
||||
@@ -3011,15 +3040,11 @@ void MarlinSettings::reset() {
|
||||
#endif
|
||||
|
||||
//
|
||||
// Kinematic settings
|
||||
// Kinematic Settings (Delta, SCARA, TPARA, Polargraph...)
|
||||
//
|
||||
|
||||
#if IS_KINEMATIC
|
||||
segments_per_second = (
|
||||
TERN_(DELTA, DELTA_SEGMENTS_PER_SECOND)
|
||||
TERN_(IS_SCARA, SCARA_SEGMENTS_PER_SECOND)
|
||||
TERN_(POLARGRAPH, POLAR_SEGMENTS_PER_SECOND)
|
||||
);
|
||||
segments_per_second = DEFAULT_SEGMENTS_PER_SECOND;
|
||||
#if ENABLED(DELTA)
|
||||
const abc_float_t adj = DELTA_ENDSTOP_ADJ, dta = DELTA_TOWER_ANGLE_TRIM, ddr = DELTA_DIAGONAL_ROD_TRIM_TOWER;
|
||||
delta_height = DELTA_HEIGHT;
|
||||
@@ -3028,6 +3053,10 @@ void MarlinSettings::reset() {
|
||||
delta_diagonal_rod = DELTA_DIAGONAL_ROD;
|
||||
delta_tower_angle_trim = dta;
|
||||
delta_diagonal_rod_trim = ddr;
|
||||
#elif ENABLED(POLARGRAPH)
|
||||
draw_area_min.set(X_MIN_POS, Y_MIN_POS);
|
||||
draw_area_max.set(X_MAX_POS, Y_MAX_POS);
|
||||
polargraph_max_belt_len = POLARGRAPH_MAX_BELT_LEN;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -3142,11 +3171,13 @@ void MarlinSettings::reset() {
|
||||
#define PID_DEFAULT(N,E) DEFAULT_##N
|
||||
#endif
|
||||
HOTEND_LOOP() {
|
||||
PID_PARAM(Kp, e) = float(PID_DEFAULT(Kp, ALIM(e, defKp)));
|
||||
PID_PARAM(Ki, e) = scalePID_i(PID_DEFAULT(Ki, ALIM(e, defKi)));
|
||||
PID_PARAM(Kd, e) = scalePID_d(PID_DEFAULT(Kd, ALIM(e, defKd)));
|
||||
TERN_(PID_EXTRUSION_SCALING, PID_PARAM(Kc, e) = float(PID_DEFAULT(Kc, ALIM(e, defKc))));
|
||||
TERN_(PID_FAN_SCALING, PID_PARAM(Kf, e) = float(PID_DEFAULT(Kf, ALIM(e, defKf))));
|
||||
thermalManager.temp_hotend[e].pid.set(
|
||||
PID_DEFAULT(Kp, ALIM(e, defKp)),
|
||||
PID_DEFAULT(Ki, ALIM(e, defKi)),
|
||||
PID_DEFAULT(Kd, ALIM(e, defKd))
|
||||
OPTARG(PID_EXTRUSION_SCALING, PID_DEFAULT(Kc, ALIM(e, defKc)))
|
||||
OPTARG(PID_FAN_SCALING, PID_DEFAULT(Kf, ALIM(e, defKf)))
|
||||
);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -3160,9 +3191,7 @@ void MarlinSettings::reset() {
|
||||
//
|
||||
|
||||
#if ENABLED(PIDTEMPBED)
|
||||
thermalManager.temp_bed.pid.Kp = DEFAULT_bedKp;
|
||||
thermalManager.temp_bed.pid.Ki = scalePID_i(DEFAULT_bedKi);
|
||||
thermalManager.temp_bed.pid.Kd = scalePID_d(DEFAULT_bedKd);
|
||||
thermalManager.temp_bed.pid.set(DEFAULT_bedKp, DEFAULT_bedKi, DEFAULT_bedKd);
|
||||
#endif
|
||||
|
||||
//
|
||||
@@ -3170,9 +3199,7 @@ void MarlinSettings::reset() {
|
||||
//
|
||||
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
thermalManager.temp_chamber.pid.Kp = DEFAULT_chamberKp;
|
||||
thermalManager.temp_chamber.pid.Ki = scalePID_i(DEFAULT_chamberKi);
|
||||
thermalManager.temp_chamber.pid.Kd = scalePID_d(DEFAULT_chamberKd);
|
||||
thermalManager.temp_chamber.pid.set(DEFAULT_chamberKp, DEFAULT_chamberKi, DEFAULT_chamberKd);
|
||||
#endif
|
||||
|
||||
//
|
||||
@@ -3198,10 +3225,10 @@ void MarlinSettings::reset() {
|
||||
//
|
||||
// LCD Backlight / Sleep Timeout
|
||||
//
|
||||
#if LCD_BACKLIGHT_TIMEOUT
|
||||
ui.lcd_backlight_timeout = LCD_BACKLIGHT_TIMEOUT;
|
||||
#if LCD_BACKLIGHT_TIMEOUT_MINS
|
||||
ui.backlight_timeout_minutes = LCD_BACKLIGHT_TIMEOUT_MINS;
|
||||
#elif HAS_DISPLAY_SLEEP
|
||||
ui.sleep_timeout_minutes = DISPLAY_SLEEP_MINUTES;
|
||||
ui.sleep_timeout_minutes = TERN(TOUCH_SCREEN, TOUCH_IDLE_SLEEP_MINS, DISPLAY_SLEEP_MINUTES);
|
||||
#endif
|
||||
|
||||
//
|
||||
@@ -3240,12 +3267,17 @@ void MarlinSettings::reset() {
|
||||
//
|
||||
// Linear Advance
|
||||
//
|
||||
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
EXTRUDER_LOOP() {
|
||||
planner.extruder_advance_K[e] = LIN_ADVANCE_K;
|
||||
TERN_(EXTRA_LIN_ADVANCE_K, other_extruder_advance_K[e] = LIN_ADVANCE_K);
|
||||
}
|
||||
#if ENABLED(DISTINCT_E_FACTORS)
|
||||
constexpr float linAdvanceK[] = ADVANCE_K;
|
||||
EXTRUDER_LOOP() {
|
||||
const float a = linAdvanceK[_MAX(e, COUNT(linAdvanceK) - 1)];
|
||||
planner.extruder_advance_K[e] = a;
|
||||
TERN_(ADVANCE_K_EXTRA, other_extruder_advance_K[e] = a);
|
||||
}
|
||||
#else
|
||||
planner.extruder_advance_K[0] = ADVANCE_K;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
//
|
||||
@@ -3342,17 +3374,32 @@ void MarlinSettings::reset() {
|
||||
static_assert(COUNT(_filament_heat_capacity_permm) == HOTENDS, "FILAMENT_HEAT_CAPACITY_PERMM must have HOTENDS items.");
|
||||
|
||||
HOTEND_LOOP() {
|
||||
thermalManager.temp_hotend[e].constants.heater_power = _mpc_heater_power[e];
|
||||
thermalManager.temp_hotend[e].constants.block_heat_capacity = _mpc_block_heat_capacity[e];
|
||||
thermalManager.temp_hotend[e].constants.sensor_responsiveness = _mpc_sensor_responsiveness[e];
|
||||
thermalManager.temp_hotend[e].constants.ambient_xfer_coeff_fan0 = _mpc_ambient_xfer_coeff[e];
|
||||
MPC_t &constants = thermalManager.temp_hotend[e].constants;
|
||||
constants.heater_power = _mpc_heater_power[e];
|
||||
constants.block_heat_capacity = _mpc_block_heat_capacity[e];
|
||||
constants.sensor_responsiveness = _mpc_sensor_responsiveness[e];
|
||||
constants.ambient_xfer_coeff_fan0 = _mpc_ambient_xfer_coeff[e];
|
||||
#if ENABLED(MPC_INCLUDE_FAN)
|
||||
thermalManager.temp_hotend[e].constants.fan255_adjustment = _mpc_ambient_xfer_coeff_fan255[e] - _mpc_ambient_xfer_coeff[e];
|
||||
constants.fan255_adjustment = _mpc_ambient_xfer_coeff_fan255[e] - _mpc_ambient_xfer_coeff[e];
|
||||
#endif
|
||||
thermalManager.temp_hotend[e].constants.filament_heat_capacity_permm = _filament_heat_capacity_permm[e];
|
||||
constants.filament_heat_capacity_permm = _filament_heat_capacity_permm[e];
|
||||
}
|
||||
#endif
|
||||
|
||||
//
|
||||
// Input Shaping
|
||||
//
|
||||
#if HAS_SHAPING
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
stepper.set_shaping_frequency(X_AXIS, SHAPING_FREQ_X);
|
||||
stepper.set_shaping_damping_ratio(X_AXIS, SHAPING_ZETA_X);
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
stepper.set_shaping_frequency(Y_AXIS, SHAPING_FREQ_Y);
|
||||
stepper.set_shaping_damping_ratio(Y_AXIS, SHAPING_ZETA_Y);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
postprocess();
|
||||
|
||||
#if EITHER(EEPROM_CHITCHAT, DEBUG_LEVELING_FEATURE)
|
||||
@@ -3520,9 +3567,7 @@ void MarlinSettings::reset() {
|
||||
//
|
||||
// LCD Preheat Settings
|
||||
//
|
||||
#if HAS_PREHEAT
|
||||
gcode.M145_report(forReplay);
|
||||
#endif
|
||||
TERN_(HAS_PREHEAT, gcode.M145_report(forReplay));
|
||||
|
||||
//
|
||||
// PID
|
||||
@@ -3602,6 +3647,11 @@ void MarlinSettings::reset() {
|
||||
//
|
||||
TERN_(HAS_STEALTHCHOP, gcode.M569_report(forReplay));
|
||||
|
||||
//
|
||||
// Input Shaping
|
||||
//
|
||||
TERN_(HAS_SHAPING, gcode.M593_report(forReplay));
|
||||
|
||||
//
|
||||
// Linear Advance
|
||||
//
|
||||
|
||||
@@ -137,6 +137,10 @@ Stepper stepper; // Singleton
|
||||
#include "../lcd/extui/ui_api.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(I2S_STEPPER_STREAM)
|
||||
#include "../HAL/ESP32/i2s.h"
|
||||
#endif
|
||||
|
||||
// public:
|
||||
|
||||
#if EITHER(HAS_EXTRA_ENDSTOPS, Z_STEPPER_AUTO_ALIGN)
|
||||
@@ -195,7 +199,7 @@ IF_DISABLED(ADAPTIVE_STEP_SMOOTHING, constexpr) uint8_t Stepper::oversampling_fa
|
||||
|
||||
xyze_long_t Stepper::delta_error{0};
|
||||
|
||||
xyze_ulong_t Stepper::advance_dividend{0};
|
||||
xyze_long_t Stepper::advance_dividend{0};
|
||||
uint32_t Stepper::advance_divisor = 0,
|
||||
Stepper::step_events_completed = 0, // The number of step events executed in the current block
|
||||
Stepper::accelerate_until, // The count at which to stop accelerating
|
||||
@@ -228,6 +232,28 @@ uint32_t Stepper::advance_divisor = 0,
|
||||
Stepper::la_advance_steps = 0;
|
||||
#endif
|
||||
|
||||
#if HAS_SHAPING
|
||||
shaping_time_t ShapingQueue::now = 0;
|
||||
shaping_time_t ShapingQueue::times[shaping_echoes];
|
||||
shaping_echo_axis_t ShapingQueue::echo_axes[shaping_echoes];
|
||||
uint16_t ShapingQueue::tail = 0;
|
||||
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
shaping_time_t ShapingQueue::delay_x;
|
||||
shaping_time_t ShapingQueue::peek_x_val = shaping_time_t(-1);
|
||||
uint16_t ShapingQueue::head_x = 0;
|
||||
uint16_t ShapingQueue::_free_count_x = shaping_echoes - 1;
|
||||
ShapeParams Stepper::shaping_x;
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
shaping_time_t ShapingQueue::delay_y;
|
||||
shaping_time_t ShapingQueue::peek_y_val = shaping_time_t(-1);
|
||||
uint16_t ShapingQueue::head_y = 0;
|
||||
uint16_t ShapingQueue::_free_count_y = shaping_echoes - 1;
|
||||
ShapeParams Stepper::shaping_y;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(INTEGRATED_BABYSTEPPING)
|
||||
uint32_t Stepper::nextBabystepISR = BABYSTEP_NEVER;
|
||||
#endif
|
||||
@@ -454,12 +480,10 @@ xyze_int8_t Stepper::count_direction{0};
|
||||
#define PULSE_LOW_TICK_COUNT hal_timer_t(NS_TO_PULSE_TIMER_TICKS(_MIN_PULSE_LOW_NS - _MIN(_MIN_PULSE_LOW_NS, TIMER_SETUP_NS)))
|
||||
|
||||
#define USING_TIMED_PULSE() hal_timer_t start_pulse_count = 0
|
||||
#define START_TIMED_PULSE(DIR) (start_pulse_count = HAL_timer_get_count(MF_TIMER_PULSE))
|
||||
#define AWAIT_TIMED_PULSE(DIR) while (PULSE_##DIR##_TICK_COUNT > HAL_timer_get_count(MF_TIMER_PULSE) - start_pulse_count) { }
|
||||
#define START_HIGH_PULSE() START_TIMED_PULSE(HIGH)
|
||||
#define AWAIT_HIGH_PULSE() AWAIT_TIMED_PULSE(HIGH)
|
||||
#define START_LOW_PULSE() START_TIMED_PULSE(LOW)
|
||||
#define AWAIT_LOW_PULSE() AWAIT_TIMED_PULSE(LOW)
|
||||
#define START_TIMED_PULSE() (start_pulse_count = HAL_timer_get_count(MF_TIMER_PULSE))
|
||||
#define AWAIT_TIMED_PULSE(DIR) while (PULSE_##DIR##_TICK_COUNT > HAL_timer_get_count(MF_TIMER_PULSE) - start_pulse_count) { /* nada */ }
|
||||
#define AWAIT_HIGH_PULSE() AWAIT_TIMED_PULSE(HIGH)
|
||||
#define AWAIT_LOW_PULSE() AWAIT_TIMED_PULSE(LOW)
|
||||
|
||||
#if MINIMUM_STEPPER_PRE_DIR_DELAY > 0
|
||||
#define DIR_WAIT_BEFORE() DELAY_NS(MINIMUM_STEPPER_PRE_DIR_DELAY)
|
||||
@@ -555,6 +579,16 @@ void Stepper::disable_all_steppers() {
|
||||
TERN_(EXTENSIBLE_UI, ExtUI::onSteppersDisabled());
|
||||
}
|
||||
|
||||
#define SET_STEP_DIR(A) \
|
||||
if (motor_direction(_AXIS(A))) { \
|
||||
A##_APPLY_DIR(INVERT_##A##_DIR, false); \
|
||||
count_direction[_AXIS(A)] = -1; \
|
||||
} \
|
||||
else { \
|
||||
A##_APPLY_DIR(!INVERT_##A##_DIR, false); \
|
||||
count_direction[_AXIS(A)] = 1; \
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the stepper direction of each axis
|
||||
*
|
||||
@@ -566,16 +600,6 @@ void Stepper::set_directions() {
|
||||
|
||||
DIR_WAIT_BEFORE();
|
||||
|
||||
#define SET_STEP_DIR(A) \
|
||||
if (motor_direction(_AXIS(A))) { \
|
||||
A##_APPLY_DIR(INVERT_##A##_DIR, false); \
|
||||
count_direction[_AXIS(A)] = -1; \
|
||||
} \
|
||||
else { \
|
||||
A##_APPLY_DIR(!INVERT_##A##_DIR, false); \
|
||||
count_direction[_AXIS(A)] = 1; \
|
||||
}
|
||||
|
||||
TERN_(HAS_X_DIR, SET_STEP_DIR(X)); // A
|
||||
TERN_(HAS_Y_DIR, SET_STEP_DIR(Y)); // B
|
||||
TERN_(HAS_Z_DIR, SET_STEP_DIR(Z)); // C
|
||||
@@ -1463,6 +1487,8 @@ void Stepper::isr() {
|
||||
// Enable ISRs to reduce USART processing latency
|
||||
hal.isr_on();
|
||||
|
||||
TERN_(HAS_SHAPING, shaping_isr()); // Do Shaper stepping, if needed
|
||||
|
||||
if (!nextMainISR) pulse_phase_isr(); // 0 = Do coordinated axes Stepper pulses
|
||||
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
@@ -1493,10 +1519,12 @@ void Stepper::isr() {
|
||||
|
||||
// Get the interval to the next ISR call
|
||||
const uint32_t interval = _MIN(
|
||||
uint32_t(HAL_TIMER_TYPE_MAX), // Come back in a very long time
|
||||
nextMainISR // Time until the next Pulse / Block phase
|
||||
OPTARG(LIN_ADVANCE, nextAdvanceISR) // Come back early for Linear Advance?
|
||||
OPTARG(INTEGRATED_BABYSTEPPING, nextBabystepISR) // Come back early for Babystepping?
|
||||
uint32_t(HAL_TIMER_TYPE_MAX), // Come back in a very long time
|
||||
nextMainISR // Time until the next Pulse / Block phase
|
||||
OPTARG(INPUT_SHAPING_X, ShapingQueue::peek_x()) // Time until next input shaping echo for X
|
||||
OPTARG(INPUT_SHAPING_Y, ShapingQueue::peek_y()) // Time until next input shaping echo for Y
|
||||
OPTARG(LIN_ADVANCE, nextAdvanceISR) // Come back early for Linear Advance?
|
||||
OPTARG(INTEGRATED_BABYSTEPPING, nextBabystepISR) // Come back early for Babystepping?
|
||||
);
|
||||
|
||||
//
|
||||
@@ -1507,14 +1535,9 @@ void Stepper::isr() {
|
||||
//
|
||||
|
||||
nextMainISR -= interval;
|
||||
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
if (nextAdvanceISR != LA_ADV_NEVER) nextAdvanceISR -= interval;
|
||||
#endif
|
||||
|
||||
#if ENABLED(INTEGRATED_BABYSTEPPING)
|
||||
if (nextBabystepISR != BABYSTEP_NEVER) nextBabystepISR -= interval;
|
||||
#endif
|
||||
TERN_(HAS_SHAPING, ShapingQueue::decrement_delays(interval));
|
||||
TERN_(LIN_ADVANCE, if (nextAdvanceISR != LA_ADV_NEVER) nextAdvanceISR -= interval);
|
||||
TERN_(INTEGRATED_BABYSTEPPING, if (nextBabystepISR != BABYSTEP_NEVER) nextBabystepISR -= interval);
|
||||
|
||||
/**
|
||||
* This needs to avoid a race-condition caused by interleaving
|
||||
@@ -1558,14 +1581,7 @@ void Stepper::isr() {
|
||||
* On AVR the ISR epilogue+prologue is estimated at 100 instructions - Give 8µs as margin
|
||||
* On ARM the ISR epilogue+prologue is estimated at 20 instructions - Give 1µs as margin
|
||||
*/
|
||||
min_ticks = HAL_timer_get_count(MF_TIMER_STEP) + hal_timer_t(
|
||||
#ifdef __AVR__
|
||||
8
|
||||
#else
|
||||
1
|
||||
#endif
|
||||
* (STEPPER_TIMER_TICKS_PER_US)
|
||||
);
|
||||
min_ticks = HAL_timer_get_count(MF_TIMER_STEP) + hal_timer_t(TERN(__AVR__, 8, 1) * (STEPPER_TIMER_TICKS_PER_US));
|
||||
|
||||
/**
|
||||
* NB: If for some reason the stepper monopolizes the MPU, eventually the
|
||||
@@ -1607,11 +1623,24 @@ void Stepper::pulse_phase_isr() {
|
||||
// If we must abort the current block, do so!
|
||||
if (abort_current_block) {
|
||||
abort_current_block = false;
|
||||
if (current_block) discard_current_block();
|
||||
if (current_block) {
|
||||
discard_current_block();
|
||||
#if HAS_SHAPING
|
||||
ShapingQueue::purge();
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
shaping_x.delta_error = 0;
|
||||
shaping_x.last_block_end_pos = count_position.x;
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
shaping_y.delta_error = 0;
|
||||
shaping_y.last_block_end_pos = count_position.y;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// If there is no current block, do nothing
|
||||
if (!current_block) return;
|
||||
if (!current_block || step_events_completed >= step_event_count) return;
|
||||
|
||||
// Skipping step processing causes motion to freeze
|
||||
if (TERN0(FREEZE_FEATURE, frozen)) return;
|
||||
@@ -1630,6 +1659,9 @@ void Stepper::pulse_phase_isr() {
|
||||
#endif
|
||||
xyze_bool_t step_needed{0};
|
||||
|
||||
// Direct Stepping page?
|
||||
const bool is_page = current_block->is_page();
|
||||
|
||||
do {
|
||||
#define _APPLY_STEP(AXIS, INV, ALWAYS) AXIS ##_APPLY_STEP(INV, ALWAYS)
|
||||
#define _INVERT_STEP_PIN(AXIS) INVERT_## AXIS ##_STEP_PIN
|
||||
@@ -1638,15 +1670,50 @@ void Stepper::pulse_phase_isr() {
|
||||
#define PULSE_PREP(AXIS) do{ \
|
||||
delta_error[_AXIS(AXIS)] += advance_dividend[_AXIS(AXIS)]; \
|
||||
step_needed[_AXIS(AXIS)] = (delta_error[_AXIS(AXIS)] >= 0); \
|
||||
if (step_needed[_AXIS(AXIS)]) { \
|
||||
count_position[_AXIS(AXIS)] += count_direction[_AXIS(AXIS)]; \
|
||||
if (step_needed[_AXIS(AXIS)]) \
|
||||
delta_error[_AXIS(AXIS)] -= advance_divisor; \
|
||||
}while(0)
|
||||
|
||||
// With input shaping, direction changes can happen with almost only
|
||||
// AWAIT_LOW_PULSE() and DIR_WAIT_BEFORE() between steps. To work around
|
||||
// the TMC2208 / TMC2225 shutdown bug (#16076), add a half step hysteresis
|
||||
// in each direction. This results in the position being off by half an
|
||||
// average half step during travel but correct at the end of each segment.
|
||||
#if AXIS_DRIVER_TYPE_X(TMC2208) || AXIS_DRIVER_TYPE_X(TMC2208_STANDALONE) || \
|
||||
AXIS_DRIVER_TYPE_X(TMC5160) || AXIS_DRIVER_TYPE_X(TMC5160_STANDALONE)
|
||||
#define HYSTERESIS_X 64
|
||||
#else
|
||||
#define HYSTERESIS_X 0
|
||||
#endif
|
||||
#if AXIS_DRIVER_TYPE_Y(TMC2208) || AXIS_DRIVER_TYPE_Y(TMC2208_STANDALONE) || \
|
||||
AXIS_DRIVER_TYPE_Y(TMC5160) || AXIS_DRIVER_TYPE_Y(TMC5160_STANDALONE)
|
||||
#define HYSTERESIS_Y 64
|
||||
#else
|
||||
#define HYSTERESIS_Y 0
|
||||
#endif
|
||||
#define _HYSTERESIS(AXIS) HYSTERESIS_##AXIS
|
||||
#define HYSTERESIS(AXIS) _HYSTERESIS(AXIS)
|
||||
|
||||
#define PULSE_PREP_SHAPING(AXIS, DELTA_ERROR, DIVIDEND) do{ \
|
||||
if (step_needed[_AXIS(AXIS)]) { \
|
||||
DELTA_ERROR += (DIVIDEND); \
|
||||
if ((MAXDIR(AXIS) && DELTA_ERROR <= -(64 + HYSTERESIS(AXIS))) || (MINDIR(AXIS) && DELTA_ERROR >= (64 + HYSTERESIS(AXIS)))) { \
|
||||
{ USING_TIMED_PULSE(); START_TIMED_PULSE(); AWAIT_LOW_PULSE(); } \
|
||||
TBI(last_direction_bits, _AXIS(AXIS)); \
|
||||
DIR_WAIT_BEFORE(); \
|
||||
SET_STEP_DIR(AXIS); \
|
||||
DIR_WAIT_AFTER(); \
|
||||
} \
|
||||
step_needed[_AXIS(AXIS)] = DELTA_ERROR <= -(64 + HYSTERESIS(AXIS)) || DELTA_ERROR >= (64 + HYSTERESIS(AXIS)); \
|
||||
if (step_needed[_AXIS(AXIS)]) \
|
||||
DELTA_ERROR += MAXDIR(AXIS) ? -128 : 128; \
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
// Start an active pulse if needed
|
||||
#define PULSE_START(AXIS) do{ \
|
||||
if (step_needed[_AXIS(AXIS)]) { \
|
||||
count_position[_AXIS(AXIS)] += count_direction[_AXIS(AXIS)]; \
|
||||
_APPLY_STEP(AXIS, !_INVERT_STEP_PIN(AXIS), 0); \
|
||||
} \
|
||||
}while(0)
|
||||
@@ -1658,9 +1725,6 @@ void Stepper::pulse_phase_isr() {
|
||||
} \
|
||||
}while(0)
|
||||
|
||||
// Direct Stepping page?
|
||||
const bool is_page = current_block->is_page();
|
||||
|
||||
#if ENABLED(DIRECT_STEPPING)
|
||||
// Direct stepping is currently not ready for HAS_I_AXIS
|
||||
if (is_page) {
|
||||
@@ -1810,6 +1874,24 @@ void Stepper::pulse_phase_isr() {
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if HAS_SHAPING
|
||||
// record an echo if a step is needed in the primary bresenham
|
||||
const bool x_step = TERN0(INPUT_SHAPING_X, shaping_x.enabled && step_needed[X_AXIS]),
|
||||
y_step = TERN0(INPUT_SHAPING_Y, shaping_y.enabled && step_needed[Y_AXIS]);
|
||||
if (x_step || y_step)
|
||||
ShapingQueue::enqueue(x_step, TERN0(INPUT_SHAPING_X, shaping_x.forward), y_step, TERN0(INPUT_SHAPING_Y, shaping_y.forward));
|
||||
|
||||
// do the first part of the secondary bresenham
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
if (shaping_x.enabled)
|
||||
PULSE_PREP_SHAPING(X, shaping_x.delta_error, shaping_x.factor1 * (shaping_x.forward ? 1 : -1));
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
if (shaping_y.enabled)
|
||||
PULSE_PREP_SHAPING(Y, shaping_y.delta_error, shaping_y.factor1 * (shaping_y.forward ? 1 : -1));
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ISR_MULTI_STEPS
|
||||
@@ -1849,7 +1931,10 @@ void Stepper::pulse_phase_isr() {
|
||||
#endif
|
||||
|
||||
#if ENABLED(MIXING_EXTRUDER)
|
||||
if (step_needed.e) E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN);
|
||||
if (step_needed.e) {
|
||||
count_position[E_AXIS] += count_direction[E_AXIS];
|
||||
E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN);
|
||||
}
|
||||
#elif HAS_E0_STEP
|
||||
PULSE_START(E);
|
||||
#endif
|
||||
@@ -1858,7 +1943,7 @@ void Stepper::pulse_phase_isr() {
|
||||
|
||||
// TODO: need to deal with MINIMUM_STEPPER_PULSE over i2s
|
||||
#if ISR_MULTI_STEPS
|
||||
START_HIGH_PULSE();
|
||||
START_TIMED_PULSE();
|
||||
AWAIT_HIGH_PULSE();
|
||||
#endif
|
||||
|
||||
@@ -1898,12 +1983,66 @@ void Stepper::pulse_phase_isr() {
|
||||
#endif
|
||||
|
||||
#if ISR_MULTI_STEPS
|
||||
if (events_to_do) START_LOW_PULSE();
|
||||
if (events_to_do) START_TIMED_PULSE();
|
||||
#endif
|
||||
|
||||
} while (--events_to_do);
|
||||
}
|
||||
|
||||
#if HAS_SHAPING
|
||||
|
||||
void Stepper::shaping_isr() {
|
||||
xy_bool_t step_needed{0};
|
||||
|
||||
// Clear the echoes that are ready to process. If the buffers are too full and risk overflo, also apply echoes early.
|
||||
TERN_(INPUT_SHAPING_X, step_needed[X_AXIS] = !ShapingQueue::peek_x() || ShapingQueue::free_count_x() < steps_per_isr);
|
||||
TERN_(INPUT_SHAPING_Y, step_needed[Y_AXIS] = !ShapingQueue::peek_y() || ShapingQueue::free_count_y() < steps_per_isr);
|
||||
|
||||
if (bool(step_needed)) while (true) {
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
if (step_needed[X_AXIS]) {
|
||||
const bool forward = ShapingQueue::dequeue_x();
|
||||
PULSE_PREP_SHAPING(X, shaping_x.delta_error, shaping_x.factor2 * (forward ? 1 : -1));
|
||||
PULSE_START(X);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
if (step_needed[Y_AXIS]) {
|
||||
const bool forward = ShapingQueue::dequeue_y();
|
||||
PULSE_PREP_SHAPING(Y, shaping_y.delta_error, shaping_y.factor2 * (forward ? 1 : -1));
|
||||
PULSE_START(Y);
|
||||
}
|
||||
#endif
|
||||
|
||||
TERN_(I2S_STEPPER_STREAM, i2s_push_sample());
|
||||
|
||||
USING_TIMED_PULSE();
|
||||
if (bool(step_needed)) {
|
||||
#if ISR_MULTI_STEPS
|
||||
START_TIMED_PULSE();
|
||||
AWAIT_HIGH_PULSE();
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
PULSE_STOP(X);
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
PULSE_STOP(Y);
|
||||
#endif
|
||||
}
|
||||
|
||||
TERN_(INPUT_SHAPING_X, step_needed[X_AXIS] = !ShapingQueue::peek_x() || ShapingQueue::free_count_x() < steps_per_isr);
|
||||
TERN_(INPUT_SHAPING_Y, step_needed[Y_AXIS] = !ShapingQueue::peek_y() || ShapingQueue::free_count_y() < steps_per_isr);
|
||||
|
||||
if (!bool(step_needed)) break;
|
||||
|
||||
START_TIMED_PULSE();
|
||||
AWAIT_LOW_PULSE();
|
||||
}
|
||||
}
|
||||
|
||||
#endif // HAS_SHAPING
|
||||
|
||||
// Calculate timer interval, with all limits applied.
|
||||
uint32_t Stepper::calc_timer_interval(uint32_t step_rate) {
|
||||
#ifdef CPU_32_BIT
|
||||
@@ -2351,35 +2490,55 @@ uint32_t Stepper::block_phase_isr() {
|
||||
acceleration_time = deceleration_time = 0;
|
||||
|
||||
#if ENABLED(ADAPTIVE_STEP_SMOOTHING)
|
||||
uint8_t oversampling = 0; // Assume no axis smoothing (via oversampling)
|
||||
oversampling_factor = 0; // Assume no axis smoothing (via oversampling)
|
||||
// Decide if axis smoothing is possible
|
||||
uint32_t max_rate = current_block->nominal_rate; // Get the step event rate
|
||||
while (max_rate < MIN_STEP_ISR_FREQUENCY) { // As long as more ISRs are possible...
|
||||
max_rate <<= 1; // Try to double the rate
|
||||
if (max_rate < MIN_STEP_ISR_FREQUENCY) // Don't exceed the estimated ISR limit
|
||||
++oversampling; // Increase the oversampling (used for left-shift)
|
||||
++oversampling_factor; // Increase the oversampling (used for left-shift)
|
||||
}
|
||||
oversampling_factor = oversampling; // For all timer interval calculations
|
||||
#else
|
||||
constexpr uint8_t oversampling = 0;
|
||||
#endif
|
||||
|
||||
// Based on the oversampling factor, do the calculations
|
||||
step_event_count = current_block->step_event_count << oversampling;
|
||||
step_event_count = current_block->step_event_count << oversampling_factor;
|
||||
|
||||
// Initialize Bresenham delta errors to 1/2
|
||||
delta_error = TERN_(LIN_ADVANCE, la_delta_error =) -int32_t(step_event_count);
|
||||
|
||||
// Calculate Bresenham dividends and divisors
|
||||
advance_dividend = current_block->steps << 1;
|
||||
advance_dividend = (current_block->steps << 1).asLong();
|
||||
advance_divisor = step_event_count << 1;
|
||||
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
if (shaping_x.enabled) {
|
||||
const int64_t steps = TEST(current_block->direction_bits, X_AXIS) ? -int64_t(current_block->steps.x) : int64_t(current_block->steps.x);
|
||||
shaping_x.last_block_end_pos += steps;
|
||||
|
||||
// If there are any remaining echos unprocessed, then direction change must
|
||||
// be delayed and processed in PULSE_PREP_SHAPING. This will cause half a step
|
||||
// to be missed, which will need recovering and this can be done through shaping_x.remainder.
|
||||
shaping_x.forward = !TEST(current_block->direction_bits, X_AXIS);
|
||||
if (!ShapingQueue::empty_x()) SET_BIT_TO(current_block->direction_bits, X_AXIS, TEST(last_direction_bits, X_AXIS));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Y follows the same logic as X (but the comments aren't repeated)
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
if (shaping_y.enabled) {
|
||||
const int64_t steps = TEST(current_block->direction_bits, Y_AXIS) ? -int64_t(current_block->steps.y) : int64_t(current_block->steps.y);
|
||||
shaping_y.last_block_end_pos += steps;
|
||||
shaping_y.forward = !TEST(current_block->direction_bits, Y_AXIS);
|
||||
if (!ShapingQueue::empty_y()) SET_BIT_TO(current_block->direction_bits, Y_AXIS, TEST(last_direction_bits, Y_AXIS));
|
||||
}
|
||||
#endif
|
||||
|
||||
// No step events completed so far
|
||||
step_events_completed = 0;
|
||||
|
||||
// Compute the acceleration and deceleration points
|
||||
accelerate_until = current_block->accelerate_until << oversampling;
|
||||
decelerate_after = current_block->decelerate_after << oversampling;
|
||||
accelerate_until = current_block->accelerate_until << oversampling_factor;
|
||||
decelerate_after = current_block->decelerate_after << oversampling_factor;
|
||||
|
||||
TERN_(MIXING_EXTRUDER, mixer.stepper_setup(current_block->b_color));
|
||||
|
||||
@@ -2393,7 +2552,7 @@ uint32_t Stepper::block_phase_isr() {
|
||||
#endif
|
||||
if (current_block->la_advance_rate) {
|
||||
// apply LA scaling and discount the effect of frequency scaling
|
||||
la_dividend = (advance_dividend.e << current_block->la_scaling) << oversampling;
|
||||
la_dividend = (advance_dividend.e << current_block->la_scaling) << oversampling_factor;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -2472,31 +2631,28 @@ uint32_t Stepper::block_phase_isr() {
|
||||
// the acceleration and speed values calculated in block_phase_isr().
|
||||
// This helps keep LA in sync with, for example, S_CURVE_ACCELERATION.
|
||||
la_delta_error += la_dividend;
|
||||
if (la_delta_error >= 0) {
|
||||
const bool step_needed = la_delta_error >= 0;
|
||||
if (step_needed) {
|
||||
count_position.e += count_direction.e;
|
||||
la_advance_steps += count_direction.e;
|
||||
la_delta_error -= advance_divisor;
|
||||
|
||||
// Set the STEP pulse ON
|
||||
#if ENABLED(MIXING_EXTRUDER)
|
||||
E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN);
|
||||
#else
|
||||
E_STEP_WRITE(stepper_extruder, !INVERT_E_STEP_PIN);
|
||||
#endif
|
||||
E_STEP_WRITE(TERN(MIXING_EXTRUDER, mixer.get_next_stepper(), stepper_extruder), !INVERT_E_STEP_PIN);
|
||||
}
|
||||
|
||||
TERN_(I2S_STEPPER_STREAM, i2s_push_sample());
|
||||
|
||||
if (step_needed) {
|
||||
// Enforce a minimum duration for STEP pulse ON
|
||||
#if ISR_PULSE_CONTROL
|
||||
USING_TIMED_PULSE();
|
||||
START_HIGH_PULSE();
|
||||
START_TIMED_PULSE();
|
||||
AWAIT_HIGH_PULSE();
|
||||
#endif
|
||||
|
||||
// Set the STEP pulse OFF
|
||||
#if ENABLED(MIXING_EXTRUDER)
|
||||
E_STEP_WRITE(mixer.get_stepper(), INVERT_E_STEP_PIN);
|
||||
#else
|
||||
E_STEP_WRITE(stepper_extruder, INVERT_E_STEP_PIN);
|
||||
#endif
|
||||
E_STEP_WRITE(TERN(MIXING_EXTRUDER, mixer.get_stepper(), stepper_extruder), INVERT_E_STEP_PIN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2822,6 +2978,79 @@ void Stepper::init() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAS_SHAPING
|
||||
|
||||
/**
|
||||
* Calculate a fixed point factor to apply to the signal and its echo
|
||||
* when shaping an axis.
|
||||
*/
|
||||
void Stepper::set_shaping_damping_ratio(const AxisEnum axis, const float zeta) {
|
||||
// from the damping ratio, get a factor that can be applied to advance_dividend for fixed point maths
|
||||
// for ZV, we use amplitudes 1/(1+K) and K/(1+K) where K = exp(-zeta * M_PI / sqrt(1.0f - zeta * zeta))
|
||||
// which can be converted to 1:7 fixed point with an excellent fit with a 3rd order polynomial
|
||||
float factor2;
|
||||
if (zeta <= 0.0f) factor2 = 64.0f;
|
||||
else if (zeta >= 1.0f) factor2 = 0.0f;
|
||||
else {
|
||||
factor2 = 64.44056192 + -99.02008832 * zeta;
|
||||
const float zeta2 = zeta * zeta;
|
||||
factor2 += -7.58095488 * zeta2;
|
||||
const float zeta3 = zeta2 * zeta;
|
||||
factor2 += 43.073216 * zeta3;
|
||||
factor2 = floor(factor2);
|
||||
}
|
||||
|
||||
const bool was_on = hal.isr_state();
|
||||
hal.isr_off();
|
||||
TERN_(INPUT_SHAPING_X, if (axis == X_AXIS) { shaping_x.factor2 = factor2; shaping_x.factor1 = 128 - factor2; shaping_x.zeta = zeta; })
|
||||
TERN_(INPUT_SHAPING_Y, if (axis == Y_AXIS) { shaping_y.factor2 = factor2; shaping_y.factor1 = 128 - factor2; shaping_y.zeta = zeta; })
|
||||
if (was_on) hal.isr_on();
|
||||
}
|
||||
|
||||
float Stepper::get_shaping_damping_ratio(const AxisEnum axis) {
|
||||
TERN_(INPUT_SHAPING_X, if (axis == X_AXIS) return shaping_x.zeta);
|
||||
TERN_(INPUT_SHAPING_Y, if (axis == Y_AXIS) return shaping_y.zeta);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Stepper::set_shaping_frequency(const AxisEnum axis, const float freq) {
|
||||
// enabling or disabling shaping whilst moving can result in lost steps
|
||||
Planner::synchronize();
|
||||
|
||||
const bool was_on = hal.isr_state();
|
||||
hal.isr_off();
|
||||
|
||||
const shaping_time_t delay = freq ? float(uint32_t(STEPPER_TIMER_RATE) / 2) / freq : shaping_time_t(-1);
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
if (axis == X_AXIS) {
|
||||
ShapingQueue::set_delay(X_AXIS, delay);
|
||||
shaping_x.frequency = freq;
|
||||
shaping_x.enabled = !!freq;
|
||||
shaping_x.delta_error = 0;
|
||||
shaping_x.last_block_end_pos = count_position.x;
|
||||
}
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
if (axis == Y_AXIS) {
|
||||
ShapingQueue::set_delay(Y_AXIS, delay);
|
||||
shaping_y.frequency = freq;
|
||||
shaping_y.enabled = !!freq;
|
||||
shaping_y.delta_error = 0;
|
||||
shaping_y.last_block_end_pos = count_position.y;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (was_on) hal.isr_on();
|
||||
}
|
||||
|
||||
float Stepper::get_shaping_frequency(const AxisEnum axis) {
|
||||
TERN_(INPUT_SHAPING_X, if (axis == X_AXIS) return shaping_x.frequency);
|
||||
TERN_(INPUT_SHAPING_Y, if (axis == Y_AXIS) return shaping_y.frequency);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif // HAS_SHAPING
|
||||
|
||||
/**
|
||||
* Set the stepper positions directly in steps
|
||||
*
|
||||
@@ -2832,6 +3061,13 @@ void Stepper::init() {
|
||||
* derive the current XYZE position later on.
|
||||
*/
|
||||
void Stepper::_set_position(const abce_long_t &spos) {
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
const int32_t x_shaping_delta = count_position.x - shaping_x.last_block_end_pos;
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
const int32_t y_shaping_delta = count_position.y - shaping_y.last_block_end_pos;
|
||||
#endif
|
||||
|
||||
#if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX)
|
||||
#if CORE_IS_XY
|
||||
// corexy positioning
|
||||
@@ -2861,6 +3097,19 @@ void Stepper::_set_position(const abce_long_t &spos) {
|
||||
// default non-h-bot planning
|
||||
count_position = spos;
|
||||
#endif
|
||||
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
if (shaping_x.enabled) {
|
||||
count_position.x += x_shaping_delta;
|
||||
shaping_x.last_block_end_pos = spos.x;
|
||||
}
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
if (shaping_y.enabled) {
|
||||
count_position.y += y_shaping_delta;
|
||||
shaping_y.last_block_end_pos = spos.y;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2900,6 +3149,8 @@ void Stepper::set_axis_position(const AxisEnum a, const int32_t &v) {
|
||||
#endif
|
||||
|
||||
count_position[a] = v;
|
||||
TERN_(INPUT_SHAPING_X, if (a == X_AXIS) shaping_x.last_block_end_pos = v);
|
||||
TERN_(INPUT_SHAPING_Y, if (a == Y_AXIS) shaping_y.last_block_end_pos = v);
|
||||
|
||||
#ifdef __AVR__
|
||||
// Reenable Stepper ISR
|
||||
@@ -3027,7 +3278,7 @@ void Stepper::report_positions() {
|
||||
|
||||
#if EXTRA_CYCLES_BABYSTEP > 20
|
||||
#define _SAVE_START() const hal_timer_t pulse_start = HAL_timer_get_count(MF_TIMER_PULSE)
|
||||
#define _PULSE_WAIT() while (EXTRA_CYCLES_BABYSTEP > (uint32_t)(HAL_timer_get_count(MF_TIMER_PULSE) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
|
||||
#define _PULSE_WAIT() while (EXTRA_CYCLES_BABYSTEP > uint32_t(HAL_timer_get_count(MF_TIMER_PULSE) - pulse_start) * (PULSE_TIMER_PRESCALE)) { /* nada */ }
|
||||
#else
|
||||
#define _SAVE_START() NOOP
|
||||
#if EXTRA_CYCLES_BABYSTEP > 0
|
||||
@@ -3865,30 +4116,53 @@ void Stepper::report_positions() {
|
||||
}
|
||||
}
|
||||
|
||||
// MS1 MS2 MS3 Stepper Driver Microstepping mode table
|
||||
#ifndef MICROSTEP1
|
||||
#define MICROSTEP1 LOW,LOW,LOW
|
||||
#endif
|
||||
#if ENABLED(HEROIC_STEPPER_DRIVERS)
|
||||
#ifndef MICROSTEP128
|
||||
#define MICROSTEP128 LOW,HIGH,LOW
|
||||
#endif
|
||||
#else
|
||||
#ifndef MICROSTEP2
|
||||
#define MICROSTEP2 HIGH,LOW,LOW
|
||||
#endif
|
||||
#ifndef MICROSTEP4
|
||||
#define MICROSTEP4 LOW,HIGH,LOW
|
||||
#endif
|
||||
#endif
|
||||
#ifndef MICROSTEP8
|
||||
#define MICROSTEP8 HIGH,HIGH,LOW
|
||||
#endif
|
||||
#ifndef MICROSTEP16
|
||||
#define MICROSTEP16 HIGH,HIGH,LOW
|
||||
#endif
|
||||
|
||||
void Stepper::microstep_mode(const uint8_t driver, const uint8_t stepping_mode) {
|
||||
switch (stepping_mode) {
|
||||
#if HAS_MICROSTEP1
|
||||
#ifdef MICROSTEP1
|
||||
case 1: microstep_ms(driver, MICROSTEP1); break;
|
||||
#endif
|
||||
#if HAS_MICROSTEP2
|
||||
#ifdef MICROSTEP2
|
||||
case 2: microstep_ms(driver, MICROSTEP2); break;
|
||||
#endif
|
||||
#if HAS_MICROSTEP4
|
||||
#ifdef MICROSTEP4
|
||||
case 4: microstep_ms(driver, MICROSTEP4); break;
|
||||
#endif
|
||||
#if HAS_MICROSTEP8
|
||||
#ifdef MICROSTEP8
|
||||
case 8: microstep_ms(driver, MICROSTEP8); break;
|
||||
#endif
|
||||
#if HAS_MICROSTEP16
|
||||
#ifdef MICROSTEP16
|
||||
case 16: microstep_ms(driver, MICROSTEP16); break;
|
||||
#endif
|
||||
#if HAS_MICROSTEP32
|
||||
#ifdef MICROSTEP32
|
||||
case 32: microstep_ms(driver, MICROSTEP32); break;
|
||||
#endif
|
||||
#if HAS_MICROSTEP64
|
||||
#ifdef MICROSTEP64
|
||||
case 64: microstep_ms(driver, MICROSTEP64); break;
|
||||
#endif
|
||||
#if HAS_MICROSTEP128
|
||||
#ifdef MICROSTEP128
|
||||
case 128: microstep_ms(driver, MICROSTEP128); break;
|
||||
#endif
|
||||
|
||||
|
||||
@@ -75,8 +75,8 @@
|
||||
*/
|
||||
#define TIMER_READ_ADD_AND_STORE_CYCLES 34UL
|
||||
|
||||
// The base ISR takes 792 cycles
|
||||
#define ISR_BASE_CYCLES 792UL
|
||||
// The base ISR
|
||||
#define ISR_BASE_CYCLES 770UL
|
||||
|
||||
// Linear advance base time is 64 cycles
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
@@ -87,26 +87,34 @@
|
||||
|
||||
// S curve interpolation adds 40 cycles
|
||||
#if ENABLED(S_CURVE_ACCELERATION)
|
||||
#define ISR_S_CURVE_CYCLES 40UL
|
||||
#ifdef STM32G0B1xx
|
||||
#define ISR_S_CURVE_CYCLES 500UL
|
||||
#else
|
||||
#define ISR_S_CURVE_CYCLES 40UL
|
||||
#endif
|
||||
#else
|
||||
#define ISR_S_CURVE_CYCLES 0UL
|
||||
#endif
|
||||
|
||||
// Input shaping base time
|
||||
#if HAS_SHAPING
|
||||
#define ISR_SHAPING_BASE_CYCLES 180UL
|
||||
#else
|
||||
#define ISR_SHAPING_BASE_CYCLES 0UL
|
||||
#endif
|
||||
|
||||
// Stepper Loop base cycles
|
||||
#define ISR_LOOP_BASE_CYCLES 4UL
|
||||
|
||||
// To start the step pulse, in the worst case takes
|
||||
#define ISR_START_STEPPER_CYCLES 13UL
|
||||
|
||||
// And each stepper (start + stop pulse) takes in worst case
|
||||
#define ISR_STEPPER_CYCLES 16UL
|
||||
#define ISR_STEPPER_CYCLES 100UL
|
||||
|
||||
#else
|
||||
// Cycles to perform actions in START_TIMED_PULSE
|
||||
#define TIMER_READ_ADD_AND_STORE_CYCLES 13UL
|
||||
|
||||
// The base ISR takes 752 cycles
|
||||
#define ISR_BASE_CYCLES 752UL
|
||||
// The base ISR
|
||||
#define ISR_BASE_CYCLES 1000UL
|
||||
|
||||
// Linear advance base time is 32 cycles
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
@@ -122,12 +130,16 @@
|
||||
#define ISR_S_CURVE_CYCLES 0UL
|
||||
#endif
|
||||
|
||||
// Input shaping base time
|
||||
#if HAS_SHAPING
|
||||
#define ISR_SHAPING_BASE_CYCLES 290UL
|
||||
#else
|
||||
#define ISR_SHAPING_BASE_CYCLES 0UL
|
||||
#endif
|
||||
|
||||
// Stepper Loop base cycles
|
||||
#define ISR_LOOP_BASE_CYCLES 32UL
|
||||
|
||||
// To start the step pulse, in the worst case takes
|
||||
#define ISR_START_STEPPER_CYCLES 57UL
|
||||
|
||||
// And each stepper (start + stop pulse) takes in worst case
|
||||
#define ISR_STEPPER_CYCLES 88UL
|
||||
|
||||
@@ -202,8 +214,12 @@
|
||||
#error "Expected at least one of MINIMUM_STEPPER_PULSE or MAXIMUM_STEPPER_RATE to be defined"
|
||||
#endif
|
||||
|
||||
// But the user could be enforcing a minimum time, so the loop time is
|
||||
#define ISR_LOOP_CYCLES (ISR_LOOP_BASE_CYCLES + _MAX(MIN_STEPPER_PULSE_CYCLES, MIN_ISR_LOOP_CYCLES))
|
||||
// The loop takes the base time plus the time for all the bresenham logic for R pulses plus the time
|
||||
// between pulses for (R-1) pulses. But the user could be enforcing a minimum time so the loop time is:
|
||||
#define ISR_LOOP_CYCLES(R) ((ISR_LOOP_BASE_CYCLES + MIN_ISR_LOOP_CYCLES + MIN_STEPPER_PULSE_CYCLES) * (R - 1) + _MAX(MIN_ISR_LOOP_CYCLES, MIN_STEPPER_PULSE_CYCLES))
|
||||
|
||||
// Model input shaping as an extra loop call
|
||||
#define ISR_SHAPING_LOOP_CYCLES(R) ((TERN0(HAS_SHAPING, ISR_LOOP_BASE_CYCLES) + TERN0(INPUT_SHAPING_X, ISR_X_STEPPER_CYCLES) + TERN0(INPUT_SHAPING_Y, ISR_Y_STEPPER_CYCLES)) * (R) + (MIN_ISR_LOOP_CYCLES) * (R - 1))
|
||||
|
||||
// If linear advance is enabled, then it is handled separately
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
@@ -228,7 +244,7 @@
|
||||
#endif
|
||||
|
||||
// Now estimate the total ISR execution time in cycles given a step per ISR multiplier
|
||||
#define ISR_EXECUTION_CYCLES(R) (((ISR_BASE_CYCLES + ISR_S_CURVE_CYCLES + (ISR_LOOP_CYCLES) * (R) + ISR_LA_BASE_CYCLES + ISR_LA_LOOP_CYCLES)) / (R))
|
||||
#define ISR_EXECUTION_CYCLES(R) (((ISR_BASE_CYCLES + ISR_S_CURVE_CYCLES + ISR_SHAPING_BASE_CYCLES + ISR_LOOP_CYCLES(R) + ISR_SHAPING_LOOP_CYCLES(R) + ISR_LA_BASE_CYCLES + ISR_LA_LOOP_CYCLES)) / (R))
|
||||
|
||||
// The maximum allowable stepping frequency when doing x128-x1 stepping (in Hz)
|
||||
#define MAX_STEP_ISR_FREQUENCY_128X ((F_CPU) / ISR_EXECUTION_CYCLES(128))
|
||||
@@ -312,12 +328,143 @@ constexpr ena_mask_t enable_overlap[] = {
|
||||
|
||||
//static_assert(!any_enable_overlap(), "There is some overlap.");
|
||||
|
||||
#if HAS_SHAPING
|
||||
|
||||
#ifdef SHAPING_MAX_STEPRATE
|
||||
constexpr float max_step_rate = SHAPING_MAX_STEPRATE;
|
||||
#else
|
||||
constexpr float _ISDASU[] = DEFAULT_AXIS_STEPS_PER_UNIT;
|
||||
constexpr feedRate_t _ISDMF[] = DEFAULT_MAX_FEEDRATE;
|
||||
constexpr float max_shaped_rate = TERN0(INPUT_SHAPING_X, _ISDMF[X_AXIS] * _ISDASU[X_AXIS]) +
|
||||
TERN0(INPUT_SHAPING_Y, _ISDMF[Y_AXIS] * _ISDASU[Y_AXIS]);
|
||||
#if defined(__AVR__) || !defined(ADAPTIVE_STEP_SMOOTHING)
|
||||
// MIN_STEP_ISR_FREQUENCY is known at compile time on AVRs and any reduction in SRAM is welcome
|
||||
template<int INDEX=DISTINCT_AXES> constexpr float max_isr_rate() {
|
||||
return _MAX(_ISDMF[INDEX - 1] * _ISDASU[INDEX - 1], max_isr_rate<INDEX - 1>());
|
||||
}
|
||||
template<> constexpr float max_isr_rate<0>() {
|
||||
return TERN0(ADAPTIVE_STEP_SMOOTHING, MIN_STEP_ISR_FREQUENCY);
|
||||
}
|
||||
constexpr float max_step_rate = _MIN(max_isr_rate(), max_shaped_rate);
|
||||
#else
|
||||
constexpr float max_step_rate = max_shaped_rate;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef SHAPING_MIN_FREQ
|
||||
#define SHAPING_MIN_FREQ _MIN(0x7FFFFFFFL OPTARG(INPUT_SHAPING_X, SHAPING_FREQ_X) OPTARG(INPUT_SHAPING_Y, SHAPING_FREQ_Y))
|
||||
#endif
|
||||
constexpr uint16_t shaping_min_freq = SHAPING_MIN_FREQ,
|
||||
shaping_echoes = max_step_rate / shaping_min_freq / 2 + 3;
|
||||
|
||||
typedef IF<ENABLED(__AVR__), uint16_t, uint32_t>::type shaping_time_t;
|
||||
enum shaping_echo_t { ECHO_NONE = 0, ECHO_FWD = 1, ECHO_BWD = 2 };
|
||||
struct shaping_echo_axis_t {
|
||||
TERN_(INPUT_SHAPING_X, shaping_echo_t x:2);
|
||||
TERN_(INPUT_SHAPING_Y, shaping_echo_t y:2);
|
||||
};
|
||||
|
||||
class ShapingQueue {
|
||||
private:
|
||||
static shaping_time_t now;
|
||||
static shaping_time_t times[shaping_echoes];
|
||||
static shaping_echo_axis_t echo_axes[shaping_echoes];
|
||||
static uint16_t tail;
|
||||
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
static shaping_time_t delay_x; // = shaping_time_t(-1) to disable queueing
|
||||
static shaping_time_t peek_x_val;
|
||||
static uint16_t head_x;
|
||||
static uint16_t _free_count_x;
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
static shaping_time_t delay_y; // = shaping_time_t(-1) to disable queueing
|
||||
static shaping_time_t peek_y_val;
|
||||
static uint16_t head_y;
|
||||
static uint16_t _free_count_y;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static void decrement_delays(const shaping_time_t interval) {
|
||||
now += interval;
|
||||
TERN_(INPUT_SHAPING_X, if (peek_x_val != shaping_time_t(-1)) peek_x_val -= interval);
|
||||
TERN_(INPUT_SHAPING_Y, if (peek_y_val != shaping_time_t(-1)) peek_y_val -= interval);
|
||||
}
|
||||
static void set_delay(const AxisEnum axis, const shaping_time_t delay) {
|
||||
TERN_(INPUT_SHAPING_X, if (axis == X_AXIS) delay_x = delay);
|
||||
TERN_(INPUT_SHAPING_Y, if (axis == Y_AXIS) delay_y = delay);
|
||||
}
|
||||
static void enqueue(const bool x_step, const bool x_forward, const bool y_step, const bool y_forward) {
|
||||
TERN_(INPUT_SHAPING_X, if (head_x == tail && x_step) peek_x_val = delay_x);
|
||||
TERN_(INPUT_SHAPING_Y, if (head_y == tail && y_step) peek_y_val = delay_y);
|
||||
times[tail] = now;
|
||||
TERN_(INPUT_SHAPING_X, echo_axes[tail].x = x_step ? (x_forward ? ECHO_FWD : ECHO_BWD) : ECHO_NONE);
|
||||
TERN_(INPUT_SHAPING_Y, echo_axes[tail].y = y_step ? (y_forward ? ECHO_FWD : ECHO_BWD) : ECHO_NONE);
|
||||
if (++tail == shaping_echoes) tail = 0;
|
||||
TERN_(INPUT_SHAPING_X, _free_count_x--);
|
||||
TERN_(INPUT_SHAPING_Y, _free_count_y--);
|
||||
TERN_(INPUT_SHAPING_X, if (echo_axes[head_x].x == ECHO_NONE) dequeue_x());
|
||||
TERN_(INPUT_SHAPING_Y, if (echo_axes[head_y].y == ECHO_NONE) dequeue_y());
|
||||
}
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
static shaping_time_t peek_x() { return peek_x_val; }
|
||||
static bool dequeue_x() {
|
||||
bool forward = echo_axes[head_x].x == ECHO_FWD;
|
||||
do {
|
||||
_free_count_x++;
|
||||
if (++head_x == shaping_echoes) head_x = 0;
|
||||
} while (head_x != tail && echo_axes[head_x].x == ECHO_NONE);
|
||||
peek_x_val = head_x == tail ? shaping_time_t(-1) : times[head_x] + delay_x - now;
|
||||
return forward;
|
||||
}
|
||||
static bool empty_x() { return head_x == tail; }
|
||||
static uint16_t free_count_x() { return _free_count_x; }
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
static shaping_time_t peek_y() { return peek_y_val; }
|
||||
static bool dequeue_y() {
|
||||
bool forward = echo_axes[head_y].y == ECHO_FWD;
|
||||
do {
|
||||
_free_count_y++;
|
||||
if (++head_y == shaping_echoes) head_y = 0;
|
||||
} while (head_y != tail && echo_axes[head_y].y == ECHO_NONE);
|
||||
peek_y_val = head_y == tail ? shaping_time_t(-1) : times[head_y] + delay_y - now;
|
||||
return forward;
|
||||
}
|
||||
static bool empty_y() { return head_y == tail; }
|
||||
static uint16_t free_count_y() { return _free_count_y; }
|
||||
#endif
|
||||
static void purge() {
|
||||
const auto st = shaping_time_t(-1);
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
head_x = tail; _free_count_x = shaping_echoes - 1; peek_x_val = st;
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
head_y = tail; _free_count_y = shaping_echoes - 1; peek_y_val = st;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
struct ShapeParams {
|
||||
float frequency;
|
||||
float zeta;
|
||||
bool enabled;
|
||||
int16_t delta_error = 0; // delta_error for seconday bresenham mod 128
|
||||
uint8_t factor1;
|
||||
uint8_t factor2;
|
||||
bool forward;
|
||||
int32_t last_block_end_pos = 0;
|
||||
};
|
||||
|
||||
#endif // HAS_SHAPING
|
||||
|
||||
//
|
||||
// Stepper class definition
|
||||
//
|
||||
class Stepper {
|
||||
friend class KinematicSystem;
|
||||
friend class DeltaKinematicSystem;
|
||||
friend void stepperTask(void *);
|
||||
|
||||
public:
|
||||
|
||||
@@ -390,7 +537,7 @@ class Stepper {
|
||||
|
||||
// Delta error variables for the Bresenham line tracer
|
||||
static xyze_long_t delta_error;
|
||||
static xyze_ulong_t advance_dividend;
|
||||
static xyze_long_t advance_dividend;
|
||||
static uint32_t advance_divisor,
|
||||
step_events_completed, // The number of step events executed in the current block
|
||||
accelerate_until, // The point from where we need to stop acceleration
|
||||
@@ -415,6 +562,15 @@ class Stepper {
|
||||
static bool bezier_2nd_half; // If Bézier curve has been initialized or not
|
||||
#endif
|
||||
|
||||
#if HAS_SHAPING
|
||||
#if ENABLED(INPUT_SHAPING_X)
|
||||
static ShapeParams shaping_x;
|
||||
#endif
|
||||
#if ENABLED(INPUT_SHAPING_Y)
|
||||
static ShapeParams shaping_y;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
static constexpr uint32_t LA_ADV_NEVER = 0xFFFFFFFF;
|
||||
static uint32_t nextAdvanceISR,
|
||||
@@ -474,6 +630,10 @@ class Stepper {
|
||||
// The stepper block processing ISR phase
|
||||
static uint32_t block_phase_isr();
|
||||
|
||||
#if HAS_SHAPING
|
||||
static void shaping_isr();
|
||||
#endif
|
||||
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
// The Linear advance ISR phase
|
||||
static void advance_isr();
|
||||
@@ -493,6 +653,20 @@ class Stepper {
|
||||
// Check if the given block is busy or not - Must not be called from ISR contexts
|
||||
static bool is_block_busy(const block_t * const block);
|
||||
|
||||
#if HAS_SHAPING
|
||||
// Check whether the stepper is processing any input shaping echoes
|
||||
static bool input_shaping_busy() {
|
||||
const bool was_on = hal.isr_state();
|
||||
hal.isr_off();
|
||||
|
||||
const bool result = TERN0(INPUT_SHAPING_X, !ShapingQueue::empty_x()) || TERN0(INPUT_SHAPING_Y, !ShapingQueue::empty_y());
|
||||
|
||||
if (was_on) hal.isr_on();
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get the position of a stepper, in steps
|
||||
static int32_t position(const AxisEnum axis);
|
||||
|
||||
@@ -627,6 +801,13 @@ class Stepper {
|
||||
set_directions();
|
||||
}
|
||||
|
||||
#if HAS_SHAPING
|
||||
static void set_shaping_damping_ratio(const AxisEnum axis, const float zeta);
|
||||
static float get_shaping_damping_ratio(const AxisEnum axis);
|
||||
static void set_shaping_frequency(const AxisEnum axis, const float freq);
|
||||
static float get_shaping_frequency(const AxisEnum axis);
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
// Set the current position in steps
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
#include "TMC26X.h"
|
||||
|
||||
#define _TMC26X_DEFINE(ST) TMC26XStepper stepper##ST(200, ST##_CS_PIN, ST##_STEP_PIN, ST##_DIR_PIN, ST##_MAX_CURRENT, ST##_SENSE_RESISTOR)
|
||||
#define _TMC26X_DEFINE(ST) TMC26XStepper stepper##ST(200, ST##_CS_PIN, ST##_STEP_PIN, ST##_DIR_PIN, ST##_CURRENT, int(ST##_RSENSE * 1000))
|
||||
|
||||
#if AXIS_DRIVER_TYPE_X(TMC26X)
|
||||
_TMC26X_DEFINE(X);
|
||||
|
||||
@@ -1023,8 +1023,6 @@ void reset_trinamic_drivers() {
|
||||
// 2. For each axis in use, static_assert using a constexpr function, which counts the
|
||||
// number of matching/conflicting axis. If the value is not exactly 1, fail.
|
||||
|
||||
#define ALL_AXIS_NAMES X, X2, Y, Y2, Z, Z2, Z3, Z4, I, J, K, U, V, W, E0, E1, E2, E3, E4, E5, E6, E7
|
||||
|
||||
#if ANY_AXIS_HAS(HW_SERIAL)
|
||||
// Hardware serial names are compared as strings, since actually resolving them cannot occur in a constexpr.
|
||||
// Using a fixed-length character array for the port name allows this to be constexpr compatible.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "../MarlinCore.h"
|
||||
#include "../HAL/shared/Delay.h"
|
||||
#include "../lcd/marlinui.h"
|
||||
#include "../gcode/gcode.h"
|
||||
|
||||
#include "temperature.h"
|
||||
#include "endstops.h"
|
||||
@@ -63,10 +64,6 @@
|
||||
#include "../feature/host_actions.h"
|
||||
#endif
|
||||
|
||||
#if EITHER(HAS_TEMP_SENSOR, LASER_FEATURE)
|
||||
#include "../gcode/gcode.h"
|
||||
#endif
|
||||
|
||||
#if ENABLED(NOZZLE_PARK_FEATURE)
|
||||
#include "../libs/nozzle.h"
|
||||
#endif
|
||||
@@ -116,13 +113,16 @@
|
||||
// 3. CS, MISO, and SCK pins w/ FORCE_HW_SPI: Hardware SPI on the default bus, ignoring MISO, SCK.
|
||||
//
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(0) && TEMP_SENSOR_0_HAS_SPI_PINS && DISABLED(TEMP_SENSOR_FORCE_HW_SPI)
|
||||
#define TEMP_SENSOR_0_USES_SW_SPI 1
|
||||
#define TEMP_SENSOR_0_USES_SW_SPI 1
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(1) && TEMP_SENSOR_1_HAS_SPI_PINS && DISABLED(TEMP_SENSOR_FORCE_HW_SPI)
|
||||
#define TEMP_SENSOR_1_USES_SW_SPI 1
|
||||
#define TEMP_SENSOR_1_USES_SW_SPI 1
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(2) && TEMP_SENSOR_2_HAS_SPI_PINS && DISABLED(TEMP_SENSOR_FORCE_HW_SPI)
|
||||
#define TEMP_SENSOR_2_USES_SW_SPI 1
|
||||
#endif
|
||||
|
||||
#if (TEMP_SENSOR_0_USES_SW_SPI || TEMP_SENSOR_1_USES_SW_SPI) && !HAS_MAXTC_LIBRARIES
|
||||
#if (TEMP_SENSOR_0_USES_SW_SPI || TEMP_SENSOR_1_USES_SW_SPI || TEMP_SENSOR_2_USES_SW_SPI) && !HAS_MAXTC_LIBRARIES
|
||||
#include "../libs/private_spi.h"
|
||||
#define HAS_MAXTC_SW_SPI 1
|
||||
|
||||
@@ -133,12 +133,18 @@
|
||||
#if PIN_EXISTS(TEMP_0_MOSI)
|
||||
#define SW_SPI_MOSI_PIN TEMP_0_MOSI_PIN
|
||||
#endif
|
||||
#else
|
||||
#elif TEMP_SENSOR_1_USES_SW_SPI
|
||||
#define SW_SPI_SCK_PIN TEMP_1_SCK_PIN
|
||||
#define SW_SPI_MISO_PIN TEMP_1_MISO_PIN
|
||||
#if PIN_EXISTS(TEMP_1_MOSI)
|
||||
#define SW_SPI_MOSI_PIN TEMP_1_MOSI_PIN
|
||||
#endif
|
||||
#elif TEMP_SENSOR_2_USES_SW_SPI
|
||||
#define SW_SPI_SCK_PIN TEMP_2_SCK_PIN
|
||||
#define SW_SPI_MISO_PIN TEMP_2_MISO_PIN
|
||||
#if PIN_EXISTS(TEMP_2_MOSI)
|
||||
#define SW_SPI_MOSI_PIN TEMP_2_MOSI_PIN
|
||||
#endif
|
||||
#endif
|
||||
#ifndef SW_SPI_MOSI_PIN
|
||||
#define SW_SPI_MOSI_PIN SD_MOSI_PIN
|
||||
@@ -259,6 +265,9 @@ PGMSTR(str_t_heating_failed, STR_T_HEATING_FAILED);
|
||||
#if TEMP_SENSOR_IS_MAX(1, 6675)
|
||||
MAXTC_INIT(1, 6675);
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_MAX(2, 6675)
|
||||
MAXTC_INIT(2, 6675);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if HAS_MAX31855_LIBRARY
|
||||
@@ -268,12 +277,16 @@ PGMSTR(str_t_heating_failed, STR_T_HEATING_FAILED);
|
||||
#if TEMP_SENSOR_IS_MAX(1, 31855)
|
||||
MAXTC_INIT(1, 31855);
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_MAX(2, 31855)
|
||||
MAXTC_INIT(2, 31855);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// MAX31865 always uses a library, unlike '55 & 6675
|
||||
#if HAS_MAX31865
|
||||
#define _MAX31865_0_SW TEMP_SENSOR_0_USES_SW_SPI
|
||||
#define _MAX31865_1_SW TEMP_SENSOR_1_USES_SW_SPI
|
||||
#define _MAX31865_2_SW TEMP_SENSOR_2_USES_SW_SPI
|
||||
|
||||
#if TEMP_SENSOR_IS_MAX(0, 31865)
|
||||
MAXTC_INIT(0, 31865);
|
||||
@@ -281,9 +294,13 @@ PGMSTR(str_t_heating_failed, STR_T_HEATING_FAILED);
|
||||
#if TEMP_SENSOR_IS_MAX(1, 31865)
|
||||
MAXTC_INIT(1, 31865);
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_MAX(2, 31865)
|
||||
MAXTC_INIT(2, 31865);
|
||||
#endif
|
||||
|
||||
#undef _MAX31865_0_SW
|
||||
#undef _MAX31865_1_SW
|
||||
#undef _MAX31865_2_SW
|
||||
#endif
|
||||
|
||||
#undef MAXTC_INIT
|
||||
@@ -309,19 +326,19 @@ PGMSTR(str_t_heating_failed, STR_T_HEATING_FAILED);
|
||||
#endif
|
||||
|
||||
#if EITHER(AUTO_POWER_E_FANS, HAS_FANCHECK)
|
||||
uint8_t Temperature::autofan_speed[HOTENDS]; // = { 0 }
|
||||
uint8_t Temperature::autofan_speed[HOTENDS] = ARRAY_N_1(HOTENDS, FAN_OFF_PWM);
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_POWER_CHAMBER_FAN)
|
||||
uint8_t Temperature::chamberfan_speed; // = 0
|
||||
uint8_t Temperature::chamberfan_speed = FAN_OFF_PWM;
|
||||
#endif
|
||||
|
||||
#if ENABLED(AUTO_POWER_COOLER_FAN)
|
||||
uint8_t Temperature::coolerfan_speed; // = 0
|
||||
uint8_t Temperature::coolerfan_speed = FAN_OFF_PWM;
|
||||
#endif
|
||||
|
||||
#if BOTH(FAN_SOFT_PWM, USE_CONTROLLER_FAN)
|
||||
uint8_t Temperature::soft_pwm_controller_speed;
|
||||
uint8_t Temperature::soft_pwm_controller_speed = FAN_OFF_PWM;
|
||||
#endif
|
||||
|
||||
// Init fans according to whether they're native PWM or Software PWM
|
||||
@@ -345,11 +362,11 @@ PGMSTR(str_t_heating_failed, STR_T_HEATING_FAILED);
|
||||
// HAS_FAN does not include CONTROLLER_FAN
|
||||
#if HAS_FAN
|
||||
|
||||
uint8_t Temperature::fan_speed[FAN_COUNT]; // = { 0 }
|
||||
uint8_t Temperature::fan_speed[FAN_COUNT] = ARRAY_N_1(FAN_COUNT, FAN_OFF_PWM);
|
||||
|
||||
#if ENABLED(EXTRA_FAN_SPEED)
|
||||
|
||||
Temperature::extra_fan_t Temperature::extra_fan_speed[FAN_COUNT];
|
||||
Temperature::extra_fan_t Temperature::extra_fan_speed[FAN_COUNT] = ARRAY_N_1(FAN_COUNT, FAN_OFF_PWM);
|
||||
|
||||
/**
|
||||
* Handle the M106 P<fan> T<speed> command:
|
||||
@@ -376,7 +393,7 @@ PGMSTR(str_t_heating_failed, STR_T_HEATING_FAILED);
|
||||
|
||||
#if EITHER(PROBING_FANS_OFF, ADVANCED_PAUSE_FANS_PAUSE)
|
||||
bool Temperature::fans_paused; // = false;
|
||||
uint8_t Temperature::saved_fan_speed[FAN_COUNT]; // = { 0 }
|
||||
uint8_t Temperature::saved_fan_speed[FAN_COUNT] = ARRAY_N_1(FAN_COUNT, FAN_OFF_PWM);
|
||||
#endif
|
||||
|
||||
#if ENABLED(ADAPTIVE_FAN_SLOWING)
|
||||
@@ -544,6 +561,7 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
#endif
|
||||
|
||||
#if MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWED > 1
|
||||
#define MULTI_MAX_CONSECUTIVE_LOW_TEMP_ERR 1
|
||||
uint8_t Temperature::consecutive_low_temperature_error[HOTENDS] = { 0 };
|
||||
#endif
|
||||
|
||||
@@ -597,7 +615,7 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
millis_t next_temp_ms = millis(), t1 = next_temp_ms, t2 = next_temp_ms;
|
||||
long t_high = 0, t_low = 0;
|
||||
|
||||
PID_t tune_pid = { 0, 0, 0 };
|
||||
raw_pid_t tune_pid = { 0, 0, 0 };
|
||||
celsius_float_t maxT = 0, minT = 10000;
|
||||
|
||||
const bool isbed = (heater_id == H_BED),
|
||||
@@ -716,16 +734,16 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
pf = (ischamber || isbed) ? 0.2f : 0.6f,
|
||||
df = (ischamber || isbed) ? 1.0f / 3.0f : 1.0f / 8.0f;
|
||||
|
||||
tune_pid.Kp = Ku * pf;
|
||||
tune_pid.Ki = tune_pid.Kp * 2.0f / Tu;
|
||||
tune_pid.Kd = tune_pid.Kp * Tu * df;
|
||||
tune_pid.p = Ku * pf;
|
||||
tune_pid.i = tune_pid.p * 2.0f / Tu;
|
||||
tune_pid.d = tune_pid.p * Tu * df;
|
||||
|
||||
SERIAL_ECHOLNPGM(STR_KU, Ku, STR_TU, Tu);
|
||||
if (ischamber || isbed)
|
||||
SERIAL_ECHOLNPGM(" No overshoot");
|
||||
else
|
||||
SERIAL_ECHOLNPGM(STR_CLASSIC_PID);
|
||||
SERIAL_ECHOLNPGM(STR_KP, tune_pid.Kp, STR_KI, tune_pid.Ki, STR_KD, tune_pid.Kd);
|
||||
SERIAL_ECHOLNPGM(STR_KP, tune_pid.p, STR_KI, tune_pid.i, STR_KD, tune_pid.d);
|
||||
}
|
||||
}
|
||||
SHV((bias + d) >> 1);
|
||||
@@ -795,39 +813,36 @@ volatile bool Temperature::raw_temps_ready = false;
|
||||
|
||||
#if EITHER(PIDTEMPBED, PIDTEMPCHAMBER)
|
||||
FSTR_P const estring = GHV(F("chamber"), F("bed"), FPSTR(NUL_STR));
|
||||
say_default_(); SERIAL_ECHOF(estring); SERIAL_ECHOLNPGM("Kp ", tune_pid.Kp);
|
||||
say_default_(); SERIAL_ECHOF(estring); SERIAL_ECHOLNPGM("Ki ", tune_pid.Ki);
|
||||
say_default_(); SERIAL_ECHOF(estring); SERIAL_ECHOLNPGM("Kd ", tune_pid.Kd);
|
||||
say_default_(); SERIAL_ECHOF(estring); SERIAL_ECHOLNPGM("Kp ", tune_pid.p);
|
||||
say_default_(); SERIAL_ECHOF(estring); SERIAL_ECHOLNPGM("Ki ", tune_pid.i);
|
||||
say_default_(); SERIAL_ECHOF(estring); SERIAL_ECHOLNPGM("Kd ", tune_pid.d);
|
||||
#else
|
||||
say_default_(); SERIAL_ECHOLNPGM("Kp ", tune_pid.Kp);
|
||||
say_default_(); SERIAL_ECHOLNPGM("Ki ", tune_pid.Ki);
|
||||
say_default_(); SERIAL_ECHOLNPGM("Kd ", tune_pid.Kd);
|
||||
say_default_(); SERIAL_ECHOLNPGM("Kp ", tune_pid.p);
|
||||
say_default_(); SERIAL_ECHOLNPGM("Ki ", tune_pid.i);
|
||||
say_default_(); SERIAL_ECHOLNPGM("Kd ", tune_pid.d);
|
||||
#endif
|
||||
|
||||
auto _set_hotend_pid = [](const uint8_t e, const PID_t &in_pid) {
|
||||
auto _set_hotend_pid = [](const uint8_t tool, const raw_pid_t &in_pid) {
|
||||
#if ENABLED(PIDTEMP)
|
||||
PID_PARAM(Kp, e) = in_pid.Kp;
|
||||
PID_PARAM(Ki, e) = scalePID_i(in_pid.Ki);
|
||||
PID_PARAM(Kd, e) = scalePID_d(in_pid.Kd);
|
||||
#if ENABLED(PID_PARAMS_PER_HOTEND)
|
||||
thermalManager.temp_hotend[tool].pid.set(in_pid);
|
||||
#else
|
||||
HOTEND_LOOP() thermalManager.temp_hotend[e].pid.set(in_pid);
|
||||
#endif
|
||||
updatePID();
|
||||
#else
|
||||
UNUSED(e); UNUSED(in_pid);
|
||||
#endif
|
||||
UNUSED(tool); UNUSED(in_pid);
|
||||
};
|
||||
|
||||
#if ENABLED(PIDTEMPBED)
|
||||
auto _set_bed_pid = [](const PID_t &in_pid) {
|
||||
temp_bed.pid.Kp = in_pid.Kp;
|
||||
temp_bed.pid.Ki = scalePID_i(in_pid.Ki);
|
||||
temp_bed.pid.Kd = scalePID_d(in_pid.Kd);
|
||||
auto _set_bed_pid = [](const raw_pid_t &in_pid) {
|
||||
temp_bed.pid.set(in_pid);
|
||||
};
|
||||
#endif
|
||||
|
||||
#if ENABLED(PIDTEMPCHAMBER)
|
||||
auto _set_chamber_pid = [](const PID_t &in_pid) {
|
||||
temp_chamber.pid.Kp = in_pid.Kp;
|
||||
temp_chamber.pid.Ki = scalePID_i(in_pid.Ki);
|
||||
temp_chamber.pid.Kd = scalePID_d(in_pid.Kd);
|
||||
auto _set_chamber_pid = [](const raw_pid_t &in_pid) {
|
||||
temp_chamber.pid.set(in_pid);
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -1374,13 +1389,13 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
||||
FORCE_INLINE void debug(const_celsius_float_t c, const_float_t pid_out, FSTR_P const name=nullptr, const int8_t index=-1) {
|
||||
if (TERN0(HAS_PID_DEBUG, thermalManager.pid_debug_flag)) {
|
||||
SERIAL_ECHO_START();
|
||||
if (name) SERIAL_ECHOLNF(name);
|
||||
if (name) SERIAL_ECHOF(name);
|
||||
if (index >= 0) SERIAL_ECHO(index);
|
||||
SERIAL_ECHOLNPGM(
|
||||
STR_PID_DEBUG_INPUT, c,
|
||||
STR_PID_DEBUG_OUTPUT, pid_out
|
||||
#if DISABLED(PID_OPENLOOP)
|
||||
, "pTerm", work_pid.Kp, "iTerm", work_pid.Ki, "dTerm", work_pid.Kd
|
||||
, " pTerm ", work_pid.Kp, " iTerm ", work_pid.Ki, " dTerm ", work_pid.Kd
|
||||
#endif
|
||||
);
|
||||
}
|
||||
@@ -1394,6 +1409,8 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
||||
float Temperature::get_pid_output_hotend(const uint8_t E_NAME) {
|
||||
const uint8_t ee = HOTEND_INDEX;
|
||||
|
||||
const bool is_idling = TERN0(HEATER_IDLE_HANDLER, heater_idle[ee].timed_out);
|
||||
|
||||
#if ENABLED(PIDTEMP)
|
||||
|
||||
typedef PIDRunner<hotend_info_t, 0, PID_MAX> PIDRunnerHotend;
|
||||
@@ -1403,7 +1420,7 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
||||
REPEAT(HOTENDS, _HOTENDPID)
|
||||
};
|
||||
|
||||
const float pid_output = hotend_pid[ee].get_pid_output();
|
||||
const float pid_output = is_idling ? 0 : hotend_pid[ee].get_pid_output();
|
||||
|
||||
#if ENABLED(PID_DEBUG)
|
||||
if (ee == active_extruder)
|
||||
@@ -1466,7 +1483,7 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
||||
hotend.modeled_ambient_temp += delta_to_apply > 0.f ? _MAX(delta_to_apply, MPC_MIN_AMBIENT_CHANGE * MPC_dT) : _MIN(delta_to_apply, -MPC_MIN_AMBIENT_CHANGE * MPC_dT);
|
||||
|
||||
float power = 0.0;
|
||||
if (hotend.target != 0 && TERN1(HEATER_IDLE_HANDLER, !heater_idle[ee].timed_out)) {
|
||||
if (hotend.target != 0 && !is_idling) {
|
||||
// Plan power level to get to target temperature in 2 seconds
|
||||
power = (hotend.target - hotend.modeled_block_temp) * constants.block_heat_capacity / 2.0f;
|
||||
power -= (hotend.modeled_ambient_temp - hotend.modeled_block_temp) * ambient_xfer_coeff;
|
||||
@@ -1492,7 +1509,6 @@ void Temperature::min_temp_error(const heater_id_t heater_id) {
|
||||
|
||||
#else // No PID or MPC enabled
|
||||
|
||||
const bool is_idling = TERN0(HEATER_IDLE_HANDLER, heater_idle[ee].timed_out);
|
||||
const float pid_output = (!is_idling && temp_hotend[ee].is_below_target()) ? BANG_MAX : 0;
|
||||
|
||||
#endif
|
||||
@@ -1850,6 +1866,14 @@ void Temperature::task() {
|
||||
emergency_parser.quickstop_by_M410 = false; // quickstop_stepper may call idle so clear this now!
|
||||
quickstop_stepper();
|
||||
}
|
||||
|
||||
#if ENABLED(SDSUPPORT)
|
||||
if (emergency_parser.sd_abort_by_M524) { // abort SD print immediately
|
||||
emergency_parser.sd_abort_by_M524 = false;
|
||||
card.flag.abort_sd_printing = true;
|
||||
gcode.process_subcommands_now(F("M524"));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
if (!updateTemperaturesIfReady()) return; // Will also reset the watchdog if temperatures are ready
|
||||
@@ -1863,6 +1887,10 @@ void Temperature::task() {
|
||||
if (degHotend(1) > _MIN(HEATER_1_MAXTEMP, TEMP_SENSOR_1_MAX_TC_TMAX - 1.0)) max_temp_error(H_E1);
|
||||
if (degHotend(1) < _MAX(HEATER_1_MINTEMP, TEMP_SENSOR_1_MAX_TC_TMIN + .01)) min_temp_error(H_E1);
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_MAX_TC(2)
|
||||
if (degHotend(2) > _MIN(HEATER_2_MAXTEMP, TEMP_SENSOR_2_MAX_TC_TMAX - 1.0)) max_temp_error(H_E2);
|
||||
if (degHotend(2) < _MAX(HEATER_2_MINTEMP, TEMP_SENSOR_2_MAX_TC_TMIN + .01)) min_temp_error(H_E2);
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_MAX_TC(REDUNDANT)
|
||||
if (degRedundant() > TEMP_SENSOR_REDUNDANT_MAX_TC_TMAX - 1.0) max_temp_error(H_REDUNDANT);
|
||||
if (degRedundant() < TEMP_SENSOR_REDUNDANT_MAX_TC_TMIN + .01) min_temp_error(H_REDUNDANT);
|
||||
@@ -2109,6 +2137,15 @@ void Temperature::task() {
|
||||
case 2:
|
||||
#if TEMP_SENSOR_2_IS_CUSTOM
|
||||
return user_thermistor_to_deg_c(CTI_HOTEND_2, raw);
|
||||
#elif TEMP_SENSOR_IS_MAX_TC(2)
|
||||
#if TEMP_SENSOR_0_IS_MAX31865
|
||||
return TERN(LIB_INTERNAL_MAX31865,
|
||||
max31865_2.temperature(raw),
|
||||
max31865_2.temperature(MAX31865_SENSOR_OHMS_2, MAX31865_CALIBRATION_OHMS_2)
|
||||
);
|
||||
#else
|
||||
return (int16_t)raw * 0.25;
|
||||
#endif
|
||||
#elif TEMP_SENSOR_2_IS_AD595
|
||||
return TEMP_AD595(raw);
|
||||
#elif TEMP_SENSOR_2_IS_AD8495
|
||||
@@ -2278,6 +2315,8 @@ void Temperature::task() {
|
||||
return TERN(TEMP_SENSOR_REDUNDANT_IS_MAX31865, max31865_0.temperature(raw), (int16_t)raw * 0.25);
|
||||
#elif TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E1)
|
||||
return TERN(TEMP_SENSOR_REDUNDANT_IS_MAX31865, max31865_1.temperature(raw), (int16_t)raw * 0.25);
|
||||
#elif TEMP_SENSOR_IS_MAX_TC(REDUNDANT) && REDUNDANT_TEMP_MATCH(SOURCE, E2)
|
||||
return TERN(TEMP_SENSOR_REDUNDANT_IS_MAX31865, max31865_2.temperature(raw), (int16_t)raw * 0.25);
|
||||
#elif TEMP_SENSOR_REDUNDANT_IS_THERMISTOR
|
||||
SCAN_THERMISTOR_TABLE(TEMPTABLE_REDUNDANT, TEMPTABLE_REDUNDANT_LEN);
|
||||
#elif TEMP_SENSOR_REDUNDANT_IS_AD595
|
||||
@@ -2313,6 +2352,9 @@ void Temperature::updateTemperaturesFromRawValues() {
|
||||
#if TEMP_SENSOR_IS_MAX_TC(1)
|
||||
temp_hotend[1].setraw(READ_MAX_TC(1));
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_MAX_TC(2)
|
||||
temp_hotend[2].setraw(READ_MAX_TC(2));
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_MAX_TC(REDUNDANT)
|
||||
temp_redundant.setraw(READ_MAX_TC(HEATER_ID(TEMP_SENSOR_REDUNDANT_SOURCE)));
|
||||
#endif
|
||||
@@ -2332,7 +2374,7 @@ void Temperature::updateTemperaturesFromRawValues() {
|
||||
TERN_(HAS_POWER_MONITOR, power_monitor.capture_values());
|
||||
|
||||
#if HAS_HOTEND
|
||||
static constexpr int8_t temp_dir[] = {
|
||||
static constexpr int8_t temp_dir[HOTENDS] = {
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(0)
|
||||
0
|
||||
#else
|
||||
@@ -2344,30 +2386,41 @@ void Temperature::updateTemperaturesFromRawValues() {
|
||||
#else
|
||||
, TEMPDIR(1)
|
||||
#endif
|
||||
#if HOTENDS > 2
|
||||
#define _TEMPDIR(N) , TEMPDIR(N)
|
||||
REPEAT_S(2, HOTENDS, _TEMPDIR)
|
||||
#endif
|
||||
#if HOTENDS > 2
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(2)
|
||||
, 0
|
||||
#else
|
||||
, TEMPDIR(2)
|
||||
#endif
|
||||
#endif
|
||||
#if HOTENDS > 3
|
||||
#define _TEMPDIR(N) , TEMPDIR(N)
|
||||
REPEAT_S(3, HOTENDS, _TEMPDIR)
|
||||
#endif
|
||||
};
|
||||
|
||||
LOOP_L_N(e, COUNT(temp_dir)) {
|
||||
HOTEND_LOOP() {
|
||||
const raw_adc_t r = temp_hotend[e].getraw();
|
||||
const bool neg = temp_dir[e] < 0, pos = temp_dir[e] > 0;
|
||||
if ((neg && r < temp_range[e].raw_max) || (pos && r > temp_range[e].raw_max))
|
||||
max_temp_error((heater_id_t)e);
|
||||
|
||||
/**
|
||||
// DEBUG PREHEATING TIME
|
||||
SERIAL_ECHOLNPGM("\nExtruder = ", e, " Preheat On/Off = ", is_preheating(e));
|
||||
const float test_is_preheating = (preheat_end_time[HOTEND_INDEX] - millis()) * 0.001f;
|
||||
if (test_is_preheating < 31) SERIAL_ECHOLNPGM("Extruder = ", e, " Preheat remaining time = ", test_is_preheating, "s", "\n");
|
||||
//*/
|
||||
|
||||
const bool heater_on = temp_hotend[e].target > 0;
|
||||
if (heater_on && ((neg && r > temp_range[e].raw_min) || (pos && r < temp_range[e].raw_min))) {
|
||||
#if MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWED > 1
|
||||
if (++consecutive_low_temperature_error[e] >= MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWED)
|
||||
#endif
|
||||
min_temp_error((heater_id_t)e);
|
||||
if (heater_on && !is_preheating(e) && ((neg && r > temp_range[e].raw_min) || (pos && r < temp_range[e].raw_min))) {
|
||||
if (TERN1(MULTI_MAX_CONSECUTIVE_LOW_TEMP_ERR, ++consecutive_low_temperature_error[e] >= MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWED))
|
||||
min_temp_error((heater_id_t)e);
|
||||
}
|
||||
else {
|
||||
TERN_(MULTI_MAX_CONSECUTIVE_LOW_TEMP_ERR, consecutive_low_temperature_error[e] = 0);
|
||||
}
|
||||
#if MAX_CONSECUTIVE_LOW_TEMPERATURE_ERROR_ALLOWED > 1
|
||||
else
|
||||
consecutive_low_temperature_error[e] = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // HAS_HOTEND
|
||||
@@ -2429,6 +2482,9 @@ void Temperature::init() {
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(1) && PIN_EXISTS(TEMP_1_CS)
|
||||
OUT_WRITE(TEMP_1_CS_PIN, HIGH);
|
||||
#endif
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(2) && PIN_EXISTS(TEMP_2_CS)
|
||||
OUT_WRITE(TEMP_2_CS_PIN, HIGH);
|
||||
#endif
|
||||
|
||||
// Setup objects for library-based polling of MAX TCs
|
||||
#if HAS_MAXTC_LIBRARIES
|
||||
@@ -2456,6 +2512,18 @@ void Temperature::init() {
|
||||
OPTARG(LIB_INTERNAL_MAX31865, MAX31865_SENSOR_OHMS_1, MAX31865_CALIBRATION_OHMS_1, MAX31865_WIRE_OHMS_1)
|
||||
);
|
||||
#endif
|
||||
|
||||
#if TEMP_SENSOR_IS_MAX(2, 6675) && HAS_MAX6675_LIBRARY
|
||||
max6675_2.begin();
|
||||
#elif TEMP_SENSOR_IS_MAX(2, 31855) && HAS_MAX31855_LIBRARY
|
||||
max31855_2.begin();
|
||||
#elif TEMP_SENSOR_IS_MAX(2, 31865)
|
||||
max31865_2.begin(
|
||||
MAX31865_WIRES(MAX31865_SENSOR_WIRES_2) // MAX31865_2WIRE, MAX31865_3WIRE, MAX31865_4WIRE
|
||||
OPTARG(LIB_INTERNAL_MAX31865, MAX31865_SENSOR_OHMS_2, MAX31865_CALIBRATION_OHMS_2, MAX31865_WIRE_OHMS_2)
|
||||
);
|
||||
#endif
|
||||
|
||||
#undef MAX31865_WIRES
|
||||
#undef _MAX31865_WIRES
|
||||
#endif
|
||||
@@ -2488,6 +2556,15 @@ void Temperature::init() {
|
||||
#endif
|
||||
));
|
||||
#endif
|
||||
#if PIN_EXISTS(TEMP_2_TR_ENABLE)
|
||||
OUT_WRITE(TEMP_2_TR_ENABLE_PIN, (
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(2)
|
||||
HIGH
|
||||
#else
|
||||
LOW
|
||||
#endif
|
||||
));
|
||||
#endif
|
||||
|
||||
#if ENABLED(MPCTEMP)
|
||||
HOTEND_LOOP() temp_hotend[e].modeled_block_temp = NAN;
|
||||
@@ -2643,7 +2720,7 @@ void Temperature::init() {
|
||||
temp_range[NR].raw_max -= TEMPDIR(NR) * (OVERSAMPLENR); \
|
||||
}while(0)
|
||||
|
||||
#define _MINMAX_TEST(N,M) (HOTENDS > N && TEMP_SENSOR_##N > 0 && TEMP_SENSOR_##N != 998 && TEMP_SENSOR_##N != 999 && defined(HEATER_##N##_##M##TEMP))
|
||||
#define _MINMAX_TEST(N,M) (HOTENDS > N && TEMP_SENSOR(N) > 0 && TEMP_SENSOR(N) != 998 && TEMP_SENSOR(N) != 999 && defined(HEATER_##N##_##M##TEMP))
|
||||
|
||||
#if _MINMAX_TEST(0, MIN)
|
||||
_TEMP_MIN_E(0);
|
||||
@@ -3006,25 +3083,34 @@ void Temperature::disable_all_heaters() {
|
||||
// Needed to return the correct temp when this is called between readings
|
||||
static raw_adc_t max_tc_temp_previous[MAX_TC_COUNT] = { 0 };
|
||||
#define THERMO_TEMP(I) max_tc_temp_previous[I]
|
||||
#define THERMO_SEL(A,B) (hindex ? (B) : (A))
|
||||
#define MAXTC_CS_WRITE(V) do{ switch (hindex) { case 1: WRITE(TEMP_1_CS_PIN, V); break; default: WRITE(TEMP_0_CS_PIN, V); } }while(0)
|
||||
#if MAX_TC_COUNT > 2
|
||||
#define THERMO_SEL(A,B,C) (hindex > 1 ? (C) : hindex == 1 ? (B) : (A))
|
||||
#define MAXTC_CS_WRITE(V) do{ switch (hindex) { case 1: WRITE(TEMP_1_CS_PIN, V); break; case 2: WRITE(TEMP_2_CS_PIN, V); break; default: WRITE(TEMP_0_CS_PIN, V); } }while(0)
|
||||
#elif MAX_TC_COUNT > 1
|
||||
#define THERMO_SEL(A,B,C) ( hindex == 1 ? (B) : (A))
|
||||
#define MAXTC_CS_WRITE(V) do{ switch (hindex) { case 1: WRITE(TEMP_1_CS_PIN, V); break; default: WRITE(TEMP_0_CS_PIN, V); } }while(0)
|
||||
#endif
|
||||
#else
|
||||
// When we have only 1 max tc, THERMO_SEL will pick the appropriate sensor
|
||||
// variable, and MAXTC_*() macros will be hardcoded to the correct CS pin.
|
||||
constexpr uint8_t hindex = 0;
|
||||
#define THERMO_TEMP(I) max_tc_temp
|
||||
#if TEMP_SENSOR_IS_ANY_MAX_TC(0)
|
||||
#define THERMO_SEL(A,B) A
|
||||
#define THERMO_SEL(A,B,C) A
|
||||
#define MAXTC_CS_WRITE(V) WRITE(TEMP_0_CS_PIN, V)
|
||||
#else
|
||||
#define THERMO_SEL(A,B) B
|
||||
#elif TEMP_SENSOR_IS_ANY_MAX_TC(1)
|
||||
#define THERMO_SEL(A,B,C) B
|
||||
#define MAXTC_CS_WRITE(V) WRITE(TEMP_1_CS_PIN, V)
|
||||
#elif TEMP_SENSOR_IS_ANY_MAX_TC(2)
|
||||
#define THERMO_SEL(A,B,C) C
|
||||
#define MAXTC_CS_WRITE(V) WRITE(TEMP_2_CS_PIN, V)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static TERN(HAS_MAX31855, uint32_t, uint16_t) max_tc_temp = THERMO_SEL(
|
||||
TEMP_SENSOR_0_MAX_TC_TMAX,
|
||||
TEMP_SENSOR_1_MAX_TC_TMAX
|
||||
TEMP_SENSOR_1_MAX_TC_TMAX,
|
||||
TEMP_SENSOR_2_MAX_TC_TMAX
|
||||
);
|
||||
|
||||
static uint8_t max_tc_errors[MAX_TC_COUNT] = { 0 };
|
||||
@@ -3059,17 +3145,17 @@ void Temperature::disable_all_heaters() {
|
||||
MAXTC_CS_WRITE(HIGH); // Disable MAXTC
|
||||
#else
|
||||
#if HAS_MAX6675_LIBRARY
|
||||
MAX6675 &max6675ref = THERMO_SEL(max6675_0, max6675_1);
|
||||
MAX6675 &max6675ref = THERMO_SEL(max6675_0, max6675_1, max6675_2);
|
||||
max_tc_temp = max6675ref.readRaw16();
|
||||
#endif
|
||||
|
||||
#if HAS_MAX31855_LIBRARY
|
||||
MAX31855 &max855ref = THERMO_SEL(max31855_0, max31855_1);
|
||||
MAX31855 &max855ref = THERMO_SEL(max31855_0, max31855_1, max31855_2);
|
||||
max_tc_temp = max855ref.readRaw32();
|
||||
#endif
|
||||
|
||||
#if HAS_MAX31865
|
||||
MAX31865 &max865ref = THERMO_SEL(max31865_0, max31865_1);
|
||||
MAX31865 &max865ref = THERMO_SEL(max31865_0, max31865_1, max31865_2);
|
||||
max_tc_temp = TERN(LIB_INTERNAL_MAX31865, max865ref.readRaw(), max865ref.readRTD_with_Fault());
|
||||
#endif
|
||||
#endif
|
||||
@@ -3114,7 +3200,7 @@ void Temperature::disable_all_heaters() {
|
||||
#endif
|
||||
|
||||
// Set thermocouple above max temperature (TMAX)
|
||||
max_tc_temp = THERMO_SEL(TEMP_SENSOR_0_MAX_TC_TMAX, TEMP_SENSOR_1_MAX_TC_TMAX) << (MAX_TC_DISCARD_BITS + 1);
|
||||
max_tc_temp = THERMO_SEL(TEMP_SENSOR_0_MAX_TC_TMAX, TEMP_SENSOR_1_MAX_TC_TMAX, TEMP_SENSOR_2_MAX_TC_TMAX) << (MAX_TC_DISCARD_BITS + 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -3152,6 +3238,10 @@ void Temperature::update_raw_temperatures() {
|
||||
temp_hotend[1].update();
|
||||
#endif
|
||||
|
||||
#if HAS_TEMP_ADC_2 && !TEMP_SENSOR_IS_MAX_TC(2)
|
||||
temp_hotend[2].update();
|
||||
#endif
|
||||
|
||||
#if HAS_TEMP_ADC_REDUNDANT && !TEMP_SENSOR_IS_MAX_TC(REDUNDANT)
|
||||
temp_redundant.update();
|
||||
#endif
|
||||
|
||||
@@ -60,53 +60,6 @@ typedef enum : int8_t {
|
||||
H_NONE = -128
|
||||
} heater_id_t;
|
||||
|
||||
// PID storage
|
||||
typedef struct { float Kp, Ki, Kd; } PID_t;
|
||||
typedef struct { float Kp, Ki, Kd, Kc; } PIDC_t;
|
||||
typedef struct { float Kp, Ki, Kd, Kf; } PIDF_t;
|
||||
typedef struct { float Kp, Ki, Kd, Kc, Kf; } PIDCF_t;
|
||||
|
||||
typedef
|
||||
#if BOTH(PID_EXTRUSION_SCALING, PID_FAN_SCALING)
|
||||
PIDCF_t
|
||||
#elif ENABLED(PID_EXTRUSION_SCALING)
|
||||
PIDC_t
|
||||
#elif ENABLED(PID_FAN_SCALING)
|
||||
PIDF_t
|
||||
#else
|
||||
PID_t
|
||||
#endif
|
||||
hotend_pid_t;
|
||||
|
||||
#if ENABLED(PID_EXTRUSION_SCALING)
|
||||
typedef IF<(LPQ_MAX_LEN > 255), uint16_t, uint8_t>::type lpq_ptr_t;
|
||||
#endif
|
||||
|
||||
#define PID_PARAM(F,H) _PID_##F(TERN(PID_PARAMS_PER_HOTEND, H, 0 & H)) // Always use 'H' to suppress warning
|
||||
#define _PID_Kp(H) TERN(PIDTEMP, Temperature::temp_hotend[H].pid.Kp, NAN)
|
||||
#define _PID_Ki(H) TERN(PIDTEMP, Temperature::temp_hotend[H].pid.Ki, NAN)
|
||||
#define _PID_Kd(H) TERN(PIDTEMP, Temperature::temp_hotend[H].pid.Kd, NAN)
|
||||
#if ENABLED(PIDTEMP)
|
||||
#define _PID_Kc(H) TERN(PID_EXTRUSION_SCALING, Temperature::temp_hotend[H].pid.Kc, 1)
|
||||
#define _PID_Kf(H) TERN(PID_FAN_SCALING, Temperature::temp_hotend[H].pid.Kf, 0)
|
||||
#else
|
||||
#define _PID_Kc(H) 1
|
||||
#define _PID_Kf(H) 0
|
||||
#endif
|
||||
|
||||
#if ENABLED(MPCTEMP)
|
||||
typedef struct {
|
||||
float heater_power; // M306 P
|
||||
float block_heat_capacity; // M306 C
|
||||
float sensor_responsiveness; // M306 R
|
||||
float ambient_xfer_coeff_fan0; // M306 A
|
||||
#if ENABLED(MPC_INCLUDE_FAN)
|
||||
float fan255_adjustment; // M306 F
|
||||
#endif
|
||||
float filament_heat_capacity_permm; // M306 H
|
||||
} MPC_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* States for ADC reading in the ISR
|
||||
*/
|
||||
@@ -188,7 +141,15 @@ enum ADCSensorState : char {
|
||||
|
||||
#define ACTUAL_ADC_SAMPLES _MAX(int(MIN_ADC_ISR_LOOPS), int(SensorsReady))
|
||||
|
||||
//
|
||||
// PID
|
||||
//
|
||||
|
||||
typedef struct { float p, i, d; } raw_pid_t;
|
||||
typedef struct { float p, i, d, c, f; } raw_pidcf_t;
|
||||
|
||||
#if HAS_PID_HEATING
|
||||
|
||||
#define PID_K2 (1-float(PID_K1))
|
||||
#define PID_dT ((OVERSAMPLENR * float(ACTUAL_ADC_SAMPLES)) / (TEMP_TIMER_FREQUENCY))
|
||||
|
||||
@@ -197,10 +158,116 @@ enum ADCSensorState : char {
|
||||
#define unscalePID_i(i) ( float(i) / PID_dT )
|
||||
#define scalePID_d(d) ( float(d) / PID_dT )
|
||||
#define unscalePID_d(d) ( float(d) * PID_dT )
|
||||
|
||||
typedef struct {
|
||||
float Kp, Ki, Kd;
|
||||
float p() const { return Kp; }
|
||||
float i() const { return unscalePID_i(Ki); }
|
||||
float d() const { return unscalePID_d(Kd); }
|
||||
float c() const { return 1; }
|
||||
float f() const { return 0; }
|
||||
void set_Kp(float p) { Kp = p; }
|
||||
void set_Ki(float i) { Ki = scalePID_i(i); }
|
||||
void set_Kd(float d) { Kd = scalePID_d(d); }
|
||||
void set_Kc(float) {}
|
||||
void set_Kf(float) {}
|
||||
void set(float p, float i, float d, float c=1, float f=0) { set_Kp(p); set_Ki(i); set_Kd(d); UNUSED(c); UNUSED(f); }
|
||||
void set(const raw_pid_t &raw) { set(raw.p, raw.i, raw.d); }
|
||||
void set(const raw_pidcf_t &raw) { set(raw.p, raw.i, raw.d); }
|
||||
} PID_t;
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(MPCTEMP)
|
||||
#if ENABLED(PIDTEMP)
|
||||
|
||||
typedef struct {
|
||||
float Kp, Ki, Kd, Kc;
|
||||
float p() const { return Kp; }
|
||||
float i() const { return unscalePID_i(Ki); }
|
||||
float d() const { return unscalePID_d(Kd); }
|
||||
float c() const { return Kc; }
|
||||
float f() const { return 0; }
|
||||
void set_Kp(float p) { Kp = p; }
|
||||
void set_Ki(float i) { Ki = scalePID_i(i); }
|
||||
void set_Kd(float d) { Kd = scalePID_d(d); }
|
||||
void set_Kc(float c) { Kc = c; }
|
||||
void set_Kf(float) {}
|
||||
void set(float p, float i, float d, float c=1, float f=0) { set_Kp(p); set_Ki(i); set_Kd(d); set_Kc(c); set_Kf(f); }
|
||||
void set(const raw_pid_t &raw) { set(raw.p, raw.i, raw.d); }
|
||||
void set(const raw_pidcf_t &raw) { set(raw.p, raw.i, raw.d, raw.c); }
|
||||
} PIDC_t;
|
||||
|
||||
typedef struct {
|
||||
float Kp, Ki, Kd, Kf;
|
||||
float p() const { return Kp; }
|
||||
float i() const { return unscalePID_i(Ki); }
|
||||
float d() const { return unscalePID_d(Kd); }
|
||||
float c() const { return 1; }
|
||||
float f() const { return Kf; }
|
||||
void set_Kp(float p) { Kp = p; }
|
||||
void set_Ki(float i) { Ki = scalePID_i(i); }
|
||||
void set_Kd(float d) { Kd = scalePID_d(d); }
|
||||
void set_Kc(float) {}
|
||||
void set_Kf(float f) { Kf = f; }
|
||||
void set(float p, float i, float d, float c=1, float f=0) { set_Kp(p); set_Ki(i); set_Kd(d); set_Kf(f); }
|
||||
void set(const raw_pid_t &raw) { set(raw.p, raw.i, raw.d); }
|
||||
void set(const raw_pidcf_t &raw) { set(raw.p, raw.i, raw.d, raw.f); }
|
||||
} PIDF_t;
|
||||
|
||||
typedef struct {
|
||||
float Kp, Ki, Kd, Kc, Kf;
|
||||
float p() const { return Kp; }
|
||||
float i() const { return unscalePID_i(Ki); }
|
||||
float d() const { return unscalePID_d(Kd); }
|
||||
float c() const { return Kc; }
|
||||
float f() const { return Kf; }
|
||||
void set_Kp(float p) { Kp = p; }
|
||||
void set_Ki(float i) { Ki = scalePID_i(i); }
|
||||
void set_Kd(float d) { Kd = scalePID_d(d); }
|
||||
void set_Kc(float c) { Kc = c; }
|
||||
void set_Kf(float f) { Kf = f; }
|
||||
void set(float p, float i, float d, float c=1, float f=0) { set_Kp(p); set_Ki(i); set_Kd(d); set_Kc(c); set_Kf(f); }
|
||||
void set(const raw_pid_t &raw) { set(raw.p, raw.i, raw.d); }
|
||||
void set(const raw_pidcf_t &raw) { set(raw.p, raw.i, raw.d, raw.c, raw.f); }
|
||||
} PIDCF_t;
|
||||
|
||||
typedef
|
||||
#if BOTH(PID_EXTRUSION_SCALING, PID_FAN_SCALING)
|
||||
PIDCF_t
|
||||
#elif ENABLED(PID_EXTRUSION_SCALING)
|
||||
PIDC_t
|
||||
#elif ENABLED(PID_FAN_SCALING)
|
||||
PIDF_t
|
||||
#else
|
||||
PID_t
|
||||
#endif
|
||||
hotend_pid_t;
|
||||
|
||||
#if ENABLED(PID_EXTRUSION_SCALING)
|
||||
typedef IF<(LPQ_MAX_LEN > 255), uint16_t, uint8_t>::type lpq_ptr_t;
|
||||
#endif
|
||||
|
||||
#if ENABLED(PID_PARAMS_PER_HOTEND)
|
||||
#define SET_HOTEND_PID(F,H,V) thermalManager.temp_hotend[H].pid.set_##F(V)
|
||||
#else
|
||||
#define SET_HOTEND_PID(F,_,V) do{ HOTEND_LOOP() thermalManager.temp_hotend[e].pid.set_##F(V); }while(0)
|
||||
#endif
|
||||
|
||||
#elif ENABLED(MPCTEMP)
|
||||
|
||||
typedef struct {
|
||||
float heater_power; // M306 P
|
||||
float block_heat_capacity; // M306 C
|
||||
float sensor_responsiveness; // M306 R
|
||||
float ambient_xfer_coeff_fan0; // M306 A
|
||||
#if ENABLED(MPC_INCLUDE_FAN)
|
||||
float fan255_adjustment; // M306 F
|
||||
#endif
|
||||
float filament_heat_capacity_permm; // M306 H
|
||||
} MPC_t;
|
||||
|
||||
#define MPC_dT ((OVERSAMPLENR * float(ACTUAL_ADC_SAMPLES)) / (TEMP_TIMER_FREQUENCY))
|
||||
|
||||
#endif
|
||||
|
||||
#if ENABLED(G26_MESH_VALIDATION) && EITHER(HAS_MARLINUI_MENU, EXTENSIBLE_UI)
|
||||
@@ -218,7 +285,7 @@ public:
|
||||
inline void sample(const raw_adc_t s) { acc += s; }
|
||||
inline void update() { raw = acc; }
|
||||
void setraw(const raw_adc_t r) { raw = r; }
|
||||
raw_adc_t getraw() { return raw; }
|
||||
raw_adc_t getraw() const { return raw; }
|
||||
} temp_info_t;
|
||||
|
||||
#if HAS_TEMP_REDUNDANT
|
||||
@@ -393,6 +460,7 @@ class Temperature {
|
||||
static const celsius_t hotend_maxtemp[HOTENDS];
|
||||
static celsius_t hotend_max_target(const uint8_t e) { return hotend_maxtemp[e] - (HOTEND_OVERSHOOT); }
|
||||
#endif
|
||||
|
||||
#if HAS_HEATED_BED
|
||||
static bed_info_t temp_bed;
|
||||
#endif
|
||||
@@ -965,12 +1033,16 @@ class Temperature {
|
||||
static constexpr bool adaptive_fan_slowing = true;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Update the temp manager when PID values change
|
||||
*/
|
||||
// Update the temp manager when PID values change
|
||||
#if ENABLED(PIDTEMP)
|
||||
static void updatePID() {
|
||||
TERN_(PID_EXTRUSION_SCALING, pes_e_position = 0);
|
||||
static void updatePID() { TERN_(PID_EXTRUSION_SCALING, pes_e_position = 0); }
|
||||
static void setPID(const uint8_t hotend, const_float_t p, const_float_t i, const_float_t d) {
|
||||
#if ENABLED(PID_PARAMS_PER_HOTEND)
|
||||
temp_hotend[hotend].pid.set(p, i, d);
|
||||
#else
|
||||
HOTEND_LOOP() temp_hotend[e].pid.set(p, i, d);
|
||||
#endif
|
||||
updatePID();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
* Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
* Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// R25 = 2.5 MOhm, beta25 = 4500 K, 4.7 kOhm pull-up, DyzeDesign 500 °C Thermistor
|
||||
// R25 = 2.5 MOhm, beta25 = 4500 K, 4.7 kOhm pull-up, DyzeDesign / Trianglelab T-D500 500 °C Thermistor
|
||||
constexpr temp_entry_t temptable_66[] PROGMEM = {
|
||||
{ OV( 17.5), 850 },
|
||||
{ OV( 17.9), 500 },
|
||||
|
||||
@@ -193,6 +193,9 @@ typedef struct { raw_adc_t value; celsius_t celsius; } temp_entry_t;
|
||||
#if ANY_THERMISTOR_IS(1010) // Pt1000 with 1k0 pullup
|
||||
#include "thermistor_1010.h"
|
||||
#endif
|
||||
#if ANY_THERMISTOR_IS(1022) // Pt1000 with 2k2 pullup
|
||||
#include "thermistor_1022.h"
|
||||
#endif
|
||||
#if ANY_THERMISTOR_IS(1047) // Pt1000 with 4k7 pullup
|
||||
#include "thermistor_1047.h"
|
||||
#endif
|
||||
@@ -335,7 +338,7 @@ static_assert(255 > TEMPTABLE_0_LEN || 255 > TEMPTABLE_1_LEN || 255 > TEMPTABLE_
|
||||
// For thermocouples the highest temperature results in the highest ADC value
|
||||
|
||||
#define _TT_REV(N) REVERSE_TEMP_SENSOR_RANGE_##N
|
||||
#define TT_REV(N) TERN0(TEMP_SENSOR_##N##_IS_THERMISTOR, DEFER4(_TT_REV)(TEMP_SENSOR_##N))
|
||||
#define TT_REV(N) TERN0(TEMP_SENSOR_##N##_IS_THERMISTOR, DEFER4(_TT_REV)(TEMP_SENSOR(N)))
|
||||
#define _TT_REVRAW(N) !TEMP_SENSOR_##N##_IS_THERMISTOR
|
||||
#define TT_REVRAW(N) (TT_REV(N) || _TT_REVRAW(N))
|
||||
|
||||
|
||||
@@ -115,7 +115,8 @@
|
||||
|
||||
void move_extruder_servo(const uint8_t e) {
|
||||
planner.synchronize();
|
||||
if ((EXTRUDERS & 1) && e < EXTRUDERS - 1) {
|
||||
constexpr bool evenExtruders = !(EXTRUDERS & 1);
|
||||
if (evenExtruders || e < EXTRUDERS - 1) {
|
||||
servo[_SERVO_NR(e)].move(servo_angles[_SERVO_NR(e)][e & 1]);
|
||||
safe_delay(500);
|
||||
}
|
||||
@@ -440,6 +441,11 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif // TOOL_SENSOR
|
||||
|
||||
#if ENABLED(SWITCHING_TOOLHEAD)
|
||||
|
||||
inline void switching_toolhead_lock(const bool locked) {
|
||||
#ifdef SWITCHING_TOOLHEAD_SERVO_ANGLES
|
||||
const uint16_t swt_angles[2] = SWITCHING_TOOLHEAD_SERVO_ANGLES;
|
||||
@@ -452,8 +458,6 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
||||
#endif
|
||||
}
|
||||
|
||||
#include <bitset>
|
||||
|
||||
void swt_init() {
|
||||
switching_toolhead_lock(true);
|
||||
|
||||
@@ -494,10 +498,6 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
||||
#endif // TOOL_SENSOR
|
||||
}
|
||||
|
||||
#endif // TOOL_SENSOR
|
||||
|
||||
#if ENABLED(SWITCHING_TOOLHEAD)
|
||||
|
||||
inline void switching_toolhead_tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
|
||||
if (no_move) return;
|
||||
|
||||
@@ -918,7 +918,7 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
||||
#if HAS_FAN && TOOLCHANGE_FS_FAN >= 0
|
||||
thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = toolchange_settings.fan_speed;
|
||||
gcode.dwell(SEC_TO_MS(toolchange_settings.fan_time));
|
||||
thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = 0;
|
||||
thermalManager.fan_speed[TOOLCHANGE_FS_FAN] = FAN_OFF_PWM;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -940,13 +940,13 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
||||
* Cutting recovery -- Recover from cutting retraction that occurs at the end of nozzle priming
|
||||
*
|
||||
* If the active_extruder is up to temp (!too_cold):
|
||||
* Extrude filament distance = toolchange_settings.extra_resume + TOOLCHANGE_FS_WIPE_RETRACT
|
||||
* Extrude filament distance = toolchange_settings.extra_resume + toolchange_settings.wipe_retract
|
||||
* current_position.e = e;
|
||||
* sync_plan_position_e();
|
||||
*/
|
||||
void extruder_cutting_recover(const_float_t e) {
|
||||
if (!too_cold(active_extruder)) {
|
||||
const float dist = toolchange_settings.extra_resume + (TOOLCHANGE_FS_WIPE_RETRACT);
|
||||
const float dist = toolchange_settings.extra_resume + toolchange_settings.wipe_retract;
|
||||
FS_DEBUG("Performing Cutting Recover | Distance: ", dist, " | Speed: ", MMM_TO_MMS(toolchange_settings.unretract_speed), "mm/s");
|
||||
unscaled_e_move(dist, MMM_TO_MMS(toolchange_settings.unretract_speed));
|
||||
planner.synchronize();
|
||||
@@ -973,17 +973,17 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
||||
float fr = toolchange_settings.unretract_speed; // Set default speed for unretract
|
||||
|
||||
#if ENABLED(TOOLCHANGE_FS_SLOW_FIRST_PRIME)
|
||||
/*
|
||||
* Perform first unretract movement at the slower Prime_Speed to avoid breakage on first prime
|
||||
*/
|
||||
static Flags<EXTRUDERS> extruder_did_first_prime; // Extruders first priming status
|
||||
if (!extruder_did_first_prime[active_extruder]) {
|
||||
extruder_did_first_prime.set(active_extruder); // Log first prime complete
|
||||
// new nozzle - prime at user-specified speed.
|
||||
FS_DEBUG("First time priming T", active_extruder, ", reducing speed from ", MMM_TO_MMS(fr), " to ", MMM_TO_MMS(toolchange_settings.prime_speed), "mm/s");
|
||||
fr = toolchange_settings.prime_speed;
|
||||
unscaled_e_move(0, MMM_TO_MMS(fr)); // Init planner with 0 length move
|
||||
}
|
||||
/**
|
||||
* Perform first unretract movement at the slower Prime_Speed to avoid breakage on first prime
|
||||
*/
|
||||
static Flags<EXTRUDERS> extruder_did_first_prime; // Extruders first priming status
|
||||
if (!extruder_did_first_prime[active_extruder]) {
|
||||
extruder_did_first_prime.set(active_extruder); // Log first prime complete
|
||||
// new nozzle - prime at user-specified speed.
|
||||
FS_DEBUG("First time priming T", active_extruder, ", reducing speed from ", MMM_TO_MMS(fr), " to ", MMM_TO_MMS(toolchange_settings.prime_speed), "mm/s");
|
||||
fr = toolchange_settings.prime_speed;
|
||||
unscaled_e_move(0, MMM_TO_MMS(fr)); // Init planner with 0 length move
|
||||
}
|
||||
#endif
|
||||
|
||||
//Calculate and perform the priming distance
|
||||
@@ -1011,8 +1011,8 @@ void fast_line_to_current(const AxisEnum fr_axis) { _line_to_current(fr_axis, 0.
|
||||
|
||||
// Cutting retraction
|
||||
#if TOOLCHANGE_FS_WIPE_RETRACT
|
||||
FS_DEBUG("Performing Cutting Retraction | Distance: ", -(TOOLCHANGE_FS_WIPE_RETRACT), " | Speed: ", MMM_TO_MMS(toolchange_settings.retract_speed), "mm/s");
|
||||
unscaled_e_move(-(TOOLCHANGE_FS_WIPE_RETRACT), MMM_TO_MMS(toolchange_settings.retract_speed));
|
||||
FS_DEBUG("Performing Cutting Retraction | Distance: ", -toolchange_settings.wipe_retract, " | Speed: ", MMM_TO_MMS(toolchange_settings.retract_speed), "mm/s");
|
||||
unscaled_e_move(-toolchange_settings.wipe_retract, MMM_TO_MMS(toolchange_settings.retract_speed));
|
||||
#endif
|
||||
|
||||
// Cool down with fan
|
||||
@@ -1157,8 +1157,8 @@ void tool_change(const uint8_t new_tool, bool no_move/*=false*/) {
|
||||
const uint8_t old_tool = active_extruder;
|
||||
const bool can_move_away = !no_move && !idex_full_control;
|
||||
|
||||
#if HAS_LEVELING
|
||||
// Set current position to the physical position
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
// Workaround for UBL mesh boundary, possibly?
|
||||
TEMPORARY_BED_LEVELING_STATE(false);
|
||||
#endif
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
float extra_prime; // M217 E
|
||||
float extra_resume; // M217 B
|
||||
int16_t prime_speed; // M217 P
|
||||
int16_t wipe_retract; // M217 G
|
||||
int16_t retract_speed; // M217 R
|
||||
int16_t unretract_speed; // M217 U
|
||||
uint8_t fan_speed; // M217 F
|
||||
|
||||
Reference in New Issue
Block a user