Prusa MINI Firmware overview
Planner Class Reference

#include <planner.h>

Collaboration diagram for Planner:

Public Member Functions

 Planner ()
 
void init ()
 

Static Public Member Functions

static void reset_acceleration_rates ()
 
static void refresh_positioning ()
 
static void set_max_acceleration (const uint8_t axis, float targetValue)
 
static void set_max_feedrate (const uint8_t axis, float targetValue)
 
static void set_max_jerk (const AxisEnum axis, float targetValue)
 
static void check_axes_activity ()
 
static void calculate_volumetric_multipliers ()
 
static FORCE_INLINE float fade_scaling_factor_for_z (const float &)
 
static FORCE_INLINE bool leveling_active_at_z (const float &)
 
static void apply_leveling (xyz_pos_t &raw)
 
static void unapply_leveling (xyz_pos_t &raw)
 
static FORCE_INLINE void force_unapply_leveling (xyz_pos_t &raw)
 
static FORCE_INLINE void apply_modifiers (xyze_pos_t &pos, bool leveling=false)
 
static FORCE_INLINE void unapply_modifiers (xyze_pos_t &pos, bool leveling=false)
 
static FORCE_INLINE uint8_t movesplanned ()
 
static FORCE_INLINE uint8_t nonbusy_movesplanned ()
 
static FORCE_INLINE void clear_block_buffer ()
 
static FORCE_INLINE bool is_full ()
 
static FORCE_INLINE uint8_t moves_free ()
 
static FORCE_INLINE block_tget_next_free_block (uint8_t &next_buffer_head, const uint8_t count=1)
 
static bool _buffer_steps (const xyze_long_t &target, const xyze_pos_t &target_float, feedRate_t fr_mm_s, const uint8_t extruder, const float &millimeters=0.0)
 
static bool _populate_block (block_t *const block, bool split_move, const xyze_long_t &target, const xyze_pos_t &target_float, feedRate_t fr_mm_s, const uint8_t extruder, const float &millimeters=0.0)
 
static void buffer_sync_block ()
 
static bool buffer_segment (const float &a, const float &b, const float &c, const float &e, const feedRate_t &fr_mm_s, const uint8_t extruder, const float &millimeters=0.0)
 
static FORCE_INLINE bool buffer_segment (abce_pos_t &abce, const feedRate_t &fr_mm_s, const uint8_t extruder, const float &millimeters=0.0)
 
static bool buffer_line (const float &rx, const float &ry, const float &rz, const float &e, const feedRate_t &fr_mm_s, const uint8_t extruder, const float millimeters=0.0)
 
static FORCE_INLINE bool buffer_line (const xyze_pos_t &cart, const feedRate_t &fr_mm_s, const uint8_t extruder, const float millimeters=0.0)
 
static void set_position_mm (const float &rx, const float &ry, const float &rz, const float &e)
 
static FORCE_INLINE void set_position_mm (const xyze_pos_t &cart)
 
static void set_e_position_mm (const float &e)
 
static void set_machine_position_mm (const float &a, const float &b, const float &c, const float &e)
 
static FORCE_INLINE void set_machine_position_mm (const abce_pos_t &abce)
 
static float get_axis_position_mm (const AxisEnum axis)
 
static void quick_stop ()
 
static void endstop_triggered (const AxisEnum axis)
 
static float triggered_position_mm (const AxisEnum axis)
 
static void synchronize ()
 
static void finish_and_disable ()
 
static void tick ()
 
static FORCE_INLINE bool has_blocks_queued ()
 
static block_tget_current_block ()
 
static FORCE_INLINE void discard_current_block ()
 

Static Public Attributes

static block_t block_buffer [BLOCK_BUFFER_SIZE]
 
static volatile uint8_t block_buffer_head
 
static volatile uint8_t block_buffer_nonbusy
 
static volatile uint8_t block_buffer_planned
 
static volatile uint8_t block_buffer_tail
 
static uint16_t cleaning_buffer_counter
 
static uint8_t delay_before_delivering
 
static planner_settings_t settings
 
static uint32_t max_acceleration_steps_per_s2 [XYZE_N]
 
static float steps_to_mm [XYZE_N]
 
static bool leveling_active = false
 
static matrix_3x3 bed_level_matrix
 
static xyze_pos_t position_float
 
static skew_factor_t skew_factor
 

Constructor & Destructor Documentation

◆ Planner()

Planner::Planner ( )

Instance Methods

Class and Instance Methods

232 { init(); }
Here is the call graph for this function:

Member Function Documentation

◆ init()

void Planner::init ( )
234  {
235  position.reset();
236  #if HAS_POSITION_FLOAT
238  #endif
239  #if IS_KINEMATIC
240  position_cart.reset();
241  #endif
242  previous_speed.reset();
243  previous_nominal_speed_sqr = 0;
244  #if ABL_PLANAR
246  #endif
249 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ reset_acceleration_rates()

void Planner::reset_acceleration_rates ( )
static

Static (class) Methods

2788  {
2789  #if ENABLED(DISTINCT_E_FACTORS)
2790  #define AXIS_CONDITION (i < E_AXIS || i == E_AXIS_N(active_extruder))
2791  #else
2792  #define AXIS_CONDITION true
2793  #endif
2794  uint32_t highest_rate = 1;
2795  LOOP_XYZE_N(i) {
2798  }
2799  cutoff_long = 4294967295UL / highest_rate; // 0xFFFFFFFFUL
2800  #if HAS_LINEAR_E_JERK
2801  recalculate_max_e_jerk();
2802  #endif
2803 }
Here is the caller graph for this function:

◆ refresh_positioning()

void Planner::refresh_positioning ( )
static
Here is the call graph for this function:

◆ set_max_acceleration()

void Planner::set_max_acceleration ( const uint8_t  axis,
float  targetValue 
)
static
2824  {
2825  #if ENABLED(LIMITED_MAX_ACCEL_EDITING)
2826  #ifdef MAX_ACCEL_EDIT_VALUES
2827  constexpr xyze_float_t max_accel_edit = MAX_ACCEL_EDIT_VALUES;
2828  const xyze_float_t &max_acc_edit_scaled = max_accel_edit;
2829  #else
2830  constexpr xyze_float_t max_accel_edit = DEFAULT_MAX_ACCELERATION,
2831  max_acc_edit_scaled = max_accel_edit * 2;
2832  #endif
2833  limit_and_warn(targetValue, axis, PSTR("Acceleration"), max_acc_edit_scaled);
2834  #endif
2835  settings.max_acceleration_mm_per_s2[axis] = targetValue;
2836 
2837  // Update steps per s2 to agree with the units per s2 (since they are used in the planner)
2839 }
Here is the call graph for this function:

◆ set_max_feedrate()

void Planner::set_max_feedrate ( const uint8_t  axis,
float  targetValue 
)
static
2841  {
2842  #if ENABLED(LIMITED_MAX_FR_EDITING)
2843  #ifdef MAX_FEEDRATE_EDIT_VALUES
2844  constexpr xyze_float_t max_fr_edit = MAX_FEEDRATE_EDIT_VALUES;
2845  const xyze_float_t &max_fr_edit_scaled = max_fr_edit;
2846  #else
2847  constexpr xyze_float_t max_fr_edit = DEFAULT_MAX_FEEDRATE,
2848  max_fr_edit_scaled = max_fr_edit * 2;
2849  #endif
2850  limit_and_warn(targetValue, axis, PSTR("Feedrate"), max_fr_edit_scaled);
2851  #endif
2852  settings.max_feedrate_mm_s[axis] = targetValue;
2853 }
Here is the call graph for this function:

◆ set_max_jerk()

void Planner::set_max_jerk ( const AxisEnum  axis,
float  targetValue 
)
static
2855  {
2856  #if HAS_CLASSIC_JERK
2857  #if ENABLED(LIMITED_JERK_EDITING)
2858  constexpr xyze_float_t max_jerk_edit =
2859  #ifdef MAX_JERK_EDIT_VALUES
2860  MAX_JERK_EDIT_VALUES
2861  #else
2862  { (DEFAULT_XJERK) * 2, (DEFAULT_YJERK) * 2,
2863  (DEFAULT_ZJERK) * 2, (DEFAULT_EJERK) * 2 }
2864  #endif
2865  ;
2866  limit_and_warn(targetValue, axis, PSTR("Jerk"), max_jerk_edit);
2867  #endif
2868  max_jerk[axis] = targetValue;
2869  #else
2870  UNUSED(axis); UNUSED(targetValue);
2871  #endif
2872 }
Here is the call graph for this function:

◆ check_axes_activity()

void Planner::check_axes_activity ( )
static

Maintain fans, paste extruder pressure,

1180  {
1181 
1182  #if ANY(DISABLE_X, DISABLE_Y, DISABLE_Z, DISABLE_E)
1183  xyze_bool_t axis_active = { false };
1184  #endif
1185 
1186  #if FAN_COUNT > 0
1187  uint8_t tail_fan_speed[FAN_COUNT];
1188  #endif
1189 
1190  #if ENABLED(BARICUDA)
1191  #if HAS_HEATER_1
1192  uint8_t tail_valve_pressure;
1193  #endif
1194  #if HAS_HEATER_2
1195  uint8_t tail_e_to_p_pressure;
1196  #endif
1197  #endif
1198 
1199  if (has_blocks_queued()) {
1200 
1201  #if FAN_COUNT > 0 || ENABLED(BARICUDA)
1203  #endif
1204 
1205  #if FAN_COUNT > 0
1206  FANS_LOOP(i)
1207  tail_fan_speed[i] = thermalManager.scaledFanSpeed(i, block->fan_speed[i]);
1208  #endif
1209 
1210  #if ENABLED(BARICUDA)
1211  #if HAS_HEATER_1
1212  tail_valve_pressure = block->valve_pressure;
1213  #endif
1214  #if HAS_HEATER_2
1215  tail_e_to_p_pressure = block->e_to_p_pressure;
1216  #endif
1217  #endif
1218 
1219  #if ANY(DISABLE_X, DISABLE_Y, DISABLE_Z, DISABLE_E)
1220  for (uint8_t b = block_buffer_tail; b != block_buffer_head; b = next_block_index(b)) {
1221  block_t *block = &block_buffer[b];
1222  LOOP_XYZE(i) if (block->steps[i]) axis_active[i] = true;
1223  }
1224  #endif
1225  }
1226  else {
1227 
1228  #if HAS_CUTTER
1229  cutter.refresh();
1230  #endif
1231 
1232  #if FAN_COUNT > 0
1233  FANS_LOOP(i)
1234  tail_fan_speed[i] = thermalManager.scaledFanSpeed(i);
1235  #endif
1236 
1237  #if ENABLED(BARICUDA)
1238  #if HAS_HEATER_1
1239  tail_valve_pressure = baricuda_valve_pressure;
1240  #endif
1241  #if HAS_HEATER_2
1242  tail_e_to_p_pressure = baricuda_e_to_p_pressure;
1243  #endif
1244  #endif
1245  }
1246 
1247  //
1248  // Disable inactive axes
1249  //
1250  #if ENABLED(DISABLE_X)
1251  if (!axis_active.x) disable_X();
1252  #endif
1253  #if ENABLED(DISABLE_Y)
1254  if (!axis_active.y) disable_Y();
1255  #endif
1256  #if ENABLED(DISABLE_Z)
1257  if (!axis_active.z) disable_Z();
1258  #endif
1259  #if ENABLED(DISABLE_E)
1260  if (!axis_active.e) disable_e_steppers();
1261  #endif
1262 
1263  //
1264  // Update Fan speeds
1265  //
1266  #if FAN_COUNT > 0
1267 
1268  #if FAN_KICKSTART_TIME > 0
1269  static millis_t fan_kick_end[FAN_COUNT] = { 0 };
1270  #define KICKSTART_FAN(f) \
1271  if (tail_fan_speed[f]) { \
1272  millis_t ms = millis(); \
1273  if (fan_kick_end[f] == 0) { \
1274  fan_kick_end[f] = ms + FAN_KICKSTART_TIME; \
1275  tail_fan_speed[f] = 255; \
1276  } else if (PENDING(ms, fan_kick_end[f])) \
1277  tail_fan_speed[f] = 255; \
1278  } else fan_kick_end[f] = 0
1279  #else
1280  #define KICKSTART_FAN(f) NOOP
1281  #endif
1282 
1283  #if FAN_MIN_PWM != 0 || FAN_MAX_PWM != 255
1284  #define CALC_FAN_SPEED(f) (tail_fan_speed[f] ? map(tail_fan_speed[f], 1, 255, FAN_MIN_PWM, FAN_MAX_PWM) : 0)
1285  #else
1286  #define CALC_FAN_SPEED(f) tail_fan_speed[f]
1287  #endif
1288 
1289  #if ENABLED(FAN_SOFT_PWM)
1290  #define _FAN_SET(F) thermalManager.soft_pwm_amount_fan[F] = CALC_FAN_SPEED(F);
1291  #elif ENABLED(FAST_PWM_FAN)
1292  #define _FAN_SET(F) set_pwm_duty(FAN##F##_PIN, CALC_FAN_SPEED(F));
1293  #else
1294  #define _FAN_SET(F) analogWrite(pin_t(FAN##F##_PIN), CALC_FAN_SPEED(F));
1295  #endif
1296  #define FAN_SET(F) do{ KICKSTART_FAN(F); _FAN_SET(F); }while(0)
1297 
1298  #if HAS_FAN0
1299  FAN_SET(0);
1300  #endif
1301  #if HAS_FAN1
1302  FAN_SET(1);
1303  #endif
1304  #if HAS_FAN2
1305  FAN_SET(2);
1306  #endif
1307 
1308  #endif // FAN_COUNT > 0
1309 
1310  #if ENABLED(AUTOTEMP)
1311  getHighESpeed();
1312  #endif
1313 
1314  #if ENABLED(BARICUDA)
1315  #if HAS_HEATER_1
1316  analogWrite(pin_t(HEATER_1_PIN), tail_valve_pressure);
1317  #endif
1318  #if HAS_HEATER_2
1319  analogWrite(pin_t(HEATER_2_PIN), tail_e_to_p_pressure);
1320  #endif
1321  #endif
1322 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ calculate_volumetric_multipliers()

static void Planner::calculate_volumetric_multipliers ( )
static

◆ fade_scaling_factor_for_z()

static FORCE_INLINE float Planner::fade_scaling_factor_for_z ( const float &  )
static
445 { return 1; }
Here is the caller graph for this function:

◆ leveling_active_at_z()

static FORCE_INLINE bool Planner::leveling_active_at_z ( const float &  )
static
447 { return true; }
Here is the caller graph for this function:

◆ apply_leveling()

void Planner::apply_leveling ( xyz_pos_t raw)
static

Apply leveling to transform a cartesian position as it will be given to the planner and steppers.

rx, ry, rz - Cartesian positions in mm Leveled XYZ on completion

1381  {
1382  if (!leveling_active) return;
1383 
1384  #if ABL_PLANAR
1385 
1386  xy_pos_t d = raw - level_fulcrum;
1387  apply_rotation_xyz(bed_level_matrix, d.x, d.y, raw.z);
1388  raw = d + level_fulcrum;
1389 
1390  #elif HAS_MESH
1391 
1392  #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
1393  const float fade_scaling_factor = fade_scaling_factor_for_z(raw.z);
1394  #else
1395  constexpr float fade_scaling_factor = 1.0;
1396  #endif
1397 
1398  raw.z += (
1399  #if ENABLED(MESH_BED_LEVELING)
1400  mbl.get_z(raw
1401  #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
1402  , fade_scaling_factor
1403  #endif
1404  )
1405  #elif ENABLED(AUTO_BED_LEVELING_UBL)
1406  fade_scaling_factor ? fade_scaling_factor * ubl.get_z_correction(raw) : 0.0
1408  fade_scaling_factor ? fade_scaling_factor * bilinear_z_offset(raw) : 0.0
1409  #endif
1410  );
1411 
1412  #endif
1413  }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ unapply_leveling()

void Planner::unapply_leveling ( xyz_pos_t raw)
static
1415  {
1416 
1417  if (leveling_active) {
1418 
1419  #if ABL_PLANAR
1420 
1422 
1423  xy_pos_t d = raw - level_fulcrum;
1424  apply_rotation_xyz(inverse, d.x, d.y, raw.z);
1425  raw = d + level_fulcrum;
1426 
1427  #elif HAS_MESH
1428 
1429  #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
1430  const float fade_scaling_factor = fade_scaling_factor_for_z(raw.z);
1431  #else
1432  constexpr float fade_scaling_factor = 1.0;
1433  #endif
1434 
1435  raw.z -= (
1436  #if ENABLED(MESH_BED_LEVELING)
1437  mbl.get_z(raw
1438  #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT)
1439  , fade_scaling_factor
1440  #endif
1441  )
1442  #elif ENABLED(AUTO_BED_LEVELING_UBL)
1443  fade_scaling_factor ? fade_scaling_factor * ubl.get_z_correction(raw) : 0.0
1445  fade_scaling_factor ? fade_scaling_factor * bilinear_z_offset(raw) : 0.0
1446  #endif
1447  );
1448 
1449  #endif
1450  }
1451 
1452  #if ENABLED(SKEW_CORRECTION)
1453  unskew(raw);
1454  #endif
1455  }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ force_unapply_leveling()

static FORCE_INLINE void Planner::force_unapply_leveling ( xyz_pos_t raw)
static
484  {
485  leveling_active = true;
486  unapply_leveling(raw);
487  leveling_active = false;
488  }
Here is the call graph for this function:

◆ apply_modifiers()

static FORCE_INLINE void Planner::apply_modifiers ( xyze_pos_t pos,
bool  leveling = false 
)
static
508  {
509  #if ENABLED(SKEW_CORRECTION)
510  skew(pos);
511  #endif
512  #if HAS_LEVELING
513  if (leveling) apply_leveling(pos);
514  #endif
515  #if ENABLED(FWRETRACT)
516  apply_retract(pos);
517  #endif
518  }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ unapply_modifiers()

static FORCE_INLINE void Planner::unapply_modifiers ( xyze_pos_t pos,
bool  leveling = false 
)
static
529  {
530  #if ENABLED(FWRETRACT)
531  unapply_retract(pos);
532  #endif
533  #if HAS_LEVELING
534  if (leveling) unapply_leveling(pos);
535  #endif
536  #if ENABLED(SKEW_CORRECTION)
537  unskew(pos);
538  #endif
539  }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ movesplanned()

static FORCE_INLINE uint8_t Planner::movesplanned ( )
static
Here is the caller graph for this function:

◆ nonbusy_movesplanned()

static FORCE_INLINE uint8_t Planner::nonbusy_movesplanned ( )
static
Here is the caller graph for this function:

◆ clear_block_buffer()

static FORCE_INLINE void Planner::clear_block_buffer ( )
static
Here is the caller graph for this function:

◆ is_full()

static FORCE_INLINE bool Planner::is_full ( )
static
552 { return block_buffer_tail == next_block_index(block_buffer_head); }

◆ moves_free()

static FORCE_INLINE uint8_t Planner::moves_free ( )
static
555 { return BLOCK_BUFFER_SIZE - 1 - movesplanned(); }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_next_free_block()

static FORCE_INLINE block_t* Planner::get_next_free_block ( uint8_t next_buffer_head,
const uint8_t  count = 1 
)
static

Planner::get_next_free_block

  • Get the next head indices (passed by reference)
  • Wait for the number of spaces to open up in the planner
  • Return the first head block
564  {
565 
566  // Wait until there are enough slots free
567  while (moves_free() < count) { idle(); }
568 
569  // Return the first available block
570  next_buffer_head = next_block_index(block_buffer_head);
572  }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ _buffer_steps()

bool Planner::_buffer_steps ( const xyze_long_t target,
const xyze_pos_t target_float,
feedRate_t  fr_mm_s,
const uint8_t  extruder,
const float &  millimeters = 0.0 
)
static

Planner::_buffer_steps

Add a new linear movement to the buffer (in terms of steps).

target - target position in steps units fr_mm_s - (target) speed of the move extruder - target extruder millimeters - the length of the movement, if known

Returns true if movement was buffered, false otherwise

Planner::_buffer_steps

Add a new linear movement to the planner queue (in terms of steps).

target - target position in steps units target_float - target position in direct (mm, degrees) units. optional fr_mm_s - (target) speed of the move extruder - target extruder millimeters - the length of the movement, if known

Returns true if movement was properly queued, false otherwise

1586  {
1587 
1588  // If we are cleaning, do not accept queuing of movements
1589  if (cleaning_buffer_counter) return false;
1590 
1591  // Wait for the next available block
1592  uint8_t next_buffer_head;
1593  block_t * const block = get_next_free_block(next_buffer_head);
1594 
1595  // Fill the block with the specified movement
1596  if (!_populate_block(block, false, target
1597  #if HAS_POSITION_FLOAT
1598  , target_float
1599  #endif
1600  #if IS_KINEMATIC && DISABLED(CLASSIC_JERK)
1601  , delta_mm_cart
1602  #endif
1603  , fr_mm_s, extruder, millimeters
1604  )) {
1605  // Movement was not queued, probably because it was too short.
1606  // Simply accept that as movement queued and done
1607  return true;
1608  }
1609 
1610  // If this is the first added movement, reload the delay, otherwise, cancel it.
1612  // If it was the first queued block, restart the 1st block delivery delay, to
1613  // give the planner an opportunity to queue more movements and plan them
1614  // As there are no queued movements, the Stepper ISR will not touch this
1615  // variable, so there is no risk setting this here (but it MUST be done
1616  // before the following line!!)
1618  }
1619 
1620  // Move buffer head
1621  block_buffer_head = next_buffer_head;
1622 
1623  // Recalculate and optimize trapezoidal speed profiles
1624  recalculate();
1625 
1626  // Movement successfully queued!
1627  return true;
1628 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ _populate_block()

bool Planner::_populate_block ( block_t *const  block,
bool  split_move,
const xyze_long_t target,
const xyze_pos_t target_float,
feedRate_t  fr_mm_s,
const uint8_t  extruder,
const float &  millimeters = 0.0 
)
static

Planner::_populate_block

Fills a new linear movement in the block (in terms of steps).

target - target position in steps units fr_mm_s - (target) speed of the move extruder - target extruder millimeters - the length of the movement, if known

Returns true is movement is acceptable, false otherwise

Planner::_populate_block

Fills a new linear movement in the block (in terms of steps).

target - target position in steps units fr_mm_s - (target) speed of the move extruder - target extruder

Returns true is movement is acceptable, false otherwise

This part of the code calculates the total length of the movement. For cartesian bots, the X_AXIS is the real X movement and same for Y_AXIS. But for corexy bots, that is not true. The "X_AXIS" and "Y_AXIS" motors (that should be named to A_AXIS and B_AXIS) cannot be used for X and Y length, because A=X+Y and B=X-Y. So we need to create other 2 "AXIS", named X_HEAD and Y_HEAD, meaning the real displacement of the Head. Having the real displacement of the head, we can calculate the total movement length and apply the desired speed.

At this point at least one of the axes has more steps than MIN_STEPS_PER_SEGMENT, ensuring the segment won't get dropped as zero-length. It's important to not apply corrections to blocks that would get dropped!

A correction function is permitted to add steps to an axis, it should never remove steps!

1650  {
1651 
1652  const int32_t da = target.a - position.a,
1653  db = target.b - position.b,
1654  dc = target.c - position.c;
1655 
1656  #if EXTRUDERS
1657  int32_t de = target.e - position.e;
1658  #else
1659  constexpr int32_t de = 0;
1660  #endif
1661 
1662  /* <-- add a slash to enable
1663  SERIAL_ECHOLNPAIR(" _populate_block FR:", fr_mm_s,
1664  " A:", target.a, " (", da, " steps)"
1665  " B:", target.b, " (", db, " steps)"
1666  " C:", target.c, " (", dc, " steps)"
1667  #if EXTRUDERS
1668  " E:", target.e, " (", de, " steps)"
1669  #endif
1670  );
1671  //*/
1672 
1673  #if EITHER(PREVENT_COLD_EXTRUSION, PREVENT_LENGTHY_EXTRUDE)
1674  if (de) {
1675  #if ENABLED(PREVENT_COLD_EXTRUSION)
1676  if (thermalManager.tooColdToExtrude(extruder)) {
1677  position.e = target.e; // Behave as if the move really took place, but ignore E part
1678  #if HAS_POSITION_FLOAT
1679  position_float.e = target_float.e;
1680  #endif
1681  de = 0; // no difference
1683  }
1684  #endif // PREVENT_COLD_EXTRUSION
1685  #if ENABLED(PREVENT_LENGTHY_EXTRUDE)
1686  const float e_steps = ABS(de * e_factor[extruder]);
1687  const float max_e_steps = settings.axis_steps_per_mm[E_AXIS_N(extruder)] * (EXTRUDE_MAXLENGTH);
1688  if (e_steps > max_e_steps) {
1689  #if ENABLED(MIXING_EXTRUDER)
1690  bool ignore_e = false;
1691  float collector[MIXING_STEPPERS];
1692  mixer.refresh_collector(1.0, mixer.get_current_vtool(), collector);
1694  if (e_steps * collector[e] > max_e_steps) { ignore_e = true; break; }
1695  #else
1696  constexpr bool ignore_e = true;
1697  #endif
1698  if (ignore_e) {
1699  position.e = target.e; // Behave as if the move really took place, but ignore E part
1700  #if HAS_POSITION_FLOAT
1701  position_float.e = target_float.e;
1702  #endif
1703  de = 0; // no difference
1705  }
1706  }
1707  #endif // PREVENT_LENGTHY_EXTRUDE
1708  }
1709  #endif // PREVENT_COLD_EXTRUSION || PREVENT_LENGTHY_EXTRUDE
1710 
1711  // Compute direction bit-mask for this block
1712  uint8_t dm = 0;
1713  #if CORE_IS_XY
1714  if (da < 0) SBI(dm, X_HEAD); // Save the real Extruder (head) direction in X Axis
1715  if (db < 0) SBI(dm, Y_HEAD); // ...and Y
1716  if (dc < 0) SBI(dm, Z_AXIS);
1717  if (da + db < 0) SBI(dm, A_AXIS); // Motor A direction
1718  if (CORESIGN(da - db) < 0) SBI(dm, B_AXIS); // Motor B direction
1719  #elif CORE_IS_XZ
1720  if (da < 0) SBI(dm, X_HEAD); // Save the real Extruder (head) direction in X Axis
1721  if (db < 0) SBI(dm, Y_AXIS);
1722  if (dc < 0) SBI(dm, Z_HEAD); // ...and Z
1723  if (da + dc < 0) SBI(dm, A_AXIS); // Motor A direction
1724  if (CORESIGN(da - dc) < 0) SBI(dm, C_AXIS); // Motor C direction
1725  #elif CORE_IS_YZ
1726  if (da < 0) SBI(dm, X_AXIS);
1727  if (db < 0) SBI(dm, Y_HEAD); // Save the real Extruder (head) direction in Y Axis
1728  if (dc < 0) SBI(dm, Z_HEAD); // ...and Z
1729  if (db + dc < 0) SBI(dm, B_AXIS); // Motor B direction
1730  if (CORESIGN(db - dc) < 0) SBI(dm, C_AXIS); // Motor C direction
1731  #else
1732  if (da < 0) SBI(dm, X_AXIS);
1733  if (db < 0) SBI(dm, Y_AXIS);
1734  if (dc < 0) SBI(dm, Z_AXIS);
1735  #endif
1736  if (de < 0) SBI(dm, E_AXIS);
1737 
1738  #if EXTRUDERS
1739  const float esteps_float = de * e_factor[extruder];
1740  const uint32_t esteps = ABS(esteps_float) + 0.5f;
1741  #else
1742  constexpr uint32_t esteps = 0;
1743  #endif
1744 
1745  // Clear all flags, including the "busy" bit
1746  block->flag = 0x00;
1747 
1748  // Set direction bits
1749  block->direction_bits = dm;
1750 
1751  // Number of steps for each axis
1752  // See http://www.corexy.com/theory.html
1753  #if CORE_IS_XY
1754  block->steps.set(ABS(da + db), ABS(da - db), ABS(dc));
1755  #elif CORE_IS_XZ
1756  block->steps.set(ABS(da + dc), ABS(db), ABS(da - dc));
1757  #elif CORE_IS_YZ
1758  block->steps.set(ABS(da), ABS(db + dc), ABS(db - dc));
1759  #elif IS_SCARA
1760  block->steps.set(ABS(da), ABS(db), ABS(dc));
1761  #else
1762  // default non-h-bot planning
1763  block->steps.set(ABS(da), ABS(db), ABS(dc));
1764  #endif
1765 
1766  /**
1767  * This part of the code calculates the total length of the movement.
1768  * For cartesian bots, the X_AXIS is the real X movement and same for Y_AXIS.
1769  * But for corexy bots, that is not true. The "X_AXIS" and "Y_AXIS" motors (that should be named to A_AXIS
1770  * and B_AXIS) cannot be used for X and Y length, because A=X+Y and B=X-Y.
1771  * So we need to create other 2 "AXIS", named X_HEAD and Y_HEAD, meaning the real displacement of the Head.
1772  * Having the real displacement of the head, we can calculate the total movement length and apply the desired speed.
1773  */
1774  struct DeltaMM : abce_float_t {
1775  #if IS_CORE
1776  xyz_pos_t head;
1777  #endif
1778  } delta_mm;
1779  #if IS_CORE
1780  #if CORE_IS_XY
1781  delta_mm.head.x = da * steps_to_mm[A_AXIS];
1782  delta_mm.head.y = db * steps_to_mm[B_AXIS];
1783  delta_mm.z = dc * steps_to_mm[Z_AXIS];
1784  delta_mm.a = (da + db) * steps_to_mm[A_AXIS];
1785  delta_mm.b = CORESIGN(da - db) * steps_to_mm[B_AXIS];
1786  #elif CORE_IS_XZ
1787  delta_mm.head.x = da * steps_to_mm[A_AXIS];
1788  delta_mm.y = db * steps_to_mm[Y_AXIS];
1789  delta_mm.head.z = dc * steps_to_mm[C_AXIS];
1790  delta_mm.a = (da + dc) * steps_to_mm[A_AXIS];
1791  delta_mm.c = CORESIGN(da - dc) * steps_to_mm[C_AXIS];
1792  #elif CORE_IS_YZ
1793  delta_mm.x = da * steps_to_mm[X_AXIS];
1794  delta_mm.head.y = db * steps_to_mm[B_AXIS];
1795  delta_mm.head.z = dc * steps_to_mm[C_AXIS];
1796  delta_mm.b = (db + dc) * steps_to_mm[B_AXIS];
1797  delta_mm.c = CORESIGN(db - dc) * steps_to_mm[C_AXIS];
1798  #endif
1799  #else
1800  delta_mm.a = da * steps_to_mm[A_AXIS];
1801  delta_mm.b = db * steps_to_mm[B_AXIS];
1802  delta_mm.c = dc * steps_to_mm[C_AXIS];
1803  #endif
1804 
1805  #if EXTRUDERS
1806  delta_mm.e = esteps_float * steps_to_mm[E_AXIS_N(extruder)];
1807  #endif
1808 
1809  if (block->steps.a < MIN_STEPS_PER_SEGMENT && block->steps.b < MIN_STEPS_PER_SEGMENT && block->steps.c < MIN_STEPS_PER_SEGMENT) {
1810  block->millimeters = (0
1811  #if EXTRUDERS
1812  + ABS(delta_mm.e)
1813  #endif
1814  );
1815  }
1816  else {
1817  if (millimeters)
1818  block->millimeters = millimeters;
1819  else
1820  block->millimeters = SQRT(
1821  #if CORE_IS_XY
1822  sq(delta_mm.head.x) + sq(delta_mm.head.y) + sq(delta_mm.z)
1823  #elif CORE_IS_XZ
1824  sq(delta_mm.head.x) + sq(delta_mm.y) + sq(delta_mm.head.z)
1825  #elif CORE_IS_YZ
1826  sq(delta_mm.x) + sq(delta_mm.head.y) + sq(delta_mm.head.z)
1827  #else
1828  sq(delta_mm.x) + sq(delta_mm.y) + sq(delta_mm.z)
1829  #endif
1830  );
1831 
1832  /**
1833  * At this point at least one of the axes has more steps than
1834  * MIN_STEPS_PER_SEGMENT, ensuring the segment won't get dropped as
1835  * zero-length. It's important to not apply corrections
1836  * to blocks that would get dropped!
1837  *
1838  * A correction function is permitted to add steps to an axis, it
1839  * should *never* remove steps!
1840  */
1841  #if ENABLED(BACKLASH_COMPENSATION)
1842  backlash.add_correction_steps(da, db, dc, dm, block);
1843  #endif
1844  }
1845 
1846  #if EXTRUDERS
1847  block->steps.e = esteps;
1848  #endif
1849 
1850  block->step_event_count = _MAX(block->steps.a, block->steps.b, block->steps.c, esteps);
1851 
1852  // Bail if this is a zero-length block
1853  if (block->step_event_count < MIN_STEPS_PER_SEGMENT) return false;
1854 
1855  #if ENABLED(MIXING_EXTRUDER)
1857  #endif
1858 
1859  #if HAS_CUTTER
1860  block->cutter_power = cutter.power;
1861  #endif
1862 
1863  #if FAN_COUNT > 0
1864  FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
1865  #endif
1866 
1867  #if ENABLED(BARICUDA)
1868  block->valve_pressure = baricuda_valve_pressure;
1869  block->e_to_p_pressure = baricuda_e_to_p_pressure;
1870  #endif
1871 
1872  #if EXTRUDERS > 1
1873  block->extruder = extruder;
1874  #endif
1875 
1876  #if ENABLED(AUTO_POWER_CONTROL)
1877  if (block->steps.x || block->steps.y || block->steps.z)
1879  #endif
1880 
1881  // Enable active axes
1882  #if CORE_IS_XY
1883  if (block->steps.a || block->steps.b) {
1884  enable_X();
1885  enable_Y();
1886  }
1887  #if DISABLED(Z_LATE_ENABLE)
1888  if (block->steps.z) enable_Z();
1889  #endif
1890  #elif CORE_IS_XZ
1891  if (block->steps.a || block->steps.c) {
1892  enable_X();
1893  enable_Z();
1894  }
1895  if (block->steps.y) enable_Y();
1896  #elif CORE_IS_YZ
1897  if (block->steps.b || block->steps.c) {
1898  enable_Y();
1899  enable_Z();
1900  }
1901  if (block->steps.x) enable_X();
1902  #else
1903  if (block->steps.x) enable_X();
1904  if (block->steps.y) enable_Y();
1905  #if DISABLED(Z_LATE_ENABLE)
1906  if (block->steps.z) enable_Z();
1907  #endif
1908  #endif
1909 
1910  // Enable extruder(s)
1911  #if EXTRUDERS
1912  if (esteps) {
1913  #if ENABLED(AUTO_POWER_CONTROL)
1915  #endif
1916 
1917  #if ENABLED(DISABLE_INACTIVE_EXTRUDER) // Enable only the selected extruder
1918 
1919  #define DISABLE_IDLE_E(N) if (!g_uc_extruder_last_move[N]) disable_E##N();
1920 
1921  for (uint8_t i = 0; i < EXTRUDERS; i++)
1922  if (g_uc_extruder_last_move[i] > 0) g_uc_extruder_last_move[i]--;
1923 
1924  switch (extruder) {
1925  case 0:
1926  #if EXTRUDERS > 1
1927  DISABLE_IDLE_E(1);
1928  #if EXTRUDERS > 2
1929  DISABLE_IDLE_E(2);
1930  #if EXTRUDERS > 3
1931  DISABLE_IDLE_E(3);
1932  #if EXTRUDERS > 4
1933  DISABLE_IDLE_E(4);
1934  #if EXTRUDERS > 5
1935  DISABLE_IDLE_E(5);
1936  #endif // EXTRUDERS > 5
1937  #endif // EXTRUDERS > 4
1938  #endif // EXTRUDERS > 3
1939  #endif // EXTRUDERS > 2
1940  #endif // EXTRUDERS > 1
1941  enable_E0();
1942  g_uc_extruder_last_move[0] = (BLOCK_BUFFER_SIZE) * 2;
1943  #if HAS_DUPLICATION_MODE
1945  enable_E1();
1946  g_uc_extruder_last_move[1] = (BLOCK_BUFFER_SIZE) * 2;
1947  }
1948  #endif
1949  break;
1950  #if EXTRUDERS > 1
1951  case 1:
1952  DISABLE_IDLE_E(0);
1953  #if EXTRUDERS > 2
1954  DISABLE_IDLE_E(2);
1955  #if EXTRUDERS > 3
1956  DISABLE_IDLE_E(3);
1957  #if EXTRUDERS > 4
1958  DISABLE_IDLE_E(4);
1959  #if EXTRUDERS > 5
1960  DISABLE_IDLE_E(5);
1961  #endif // EXTRUDERS > 5
1962  #endif // EXTRUDERS > 4
1963  #endif // EXTRUDERS > 3
1964  #endif // EXTRUDERS > 2
1965  enable_E1();
1966  g_uc_extruder_last_move[1] = (BLOCK_BUFFER_SIZE) * 2;
1967  break;
1968  #if EXTRUDERS > 2
1969  case 2:
1970  DISABLE_IDLE_E(0);
1971  DISABLE_IDLE_E(1);
1972  #if EXTRUDERS > 3
1973  DISABLE_IDLE_E(3);
1974  #if EXTRUDERS > 4
1975  DISABLE_IDLE_E(4);
1976  #if EXTRUDERS > 5
1977  DISABLE_IDLE_E(5);
1978  #endif
1979  #endif
1980  #endif
1981  enable_E2();
1982  g_uc_extruder_last_move[2] = (BLOCK_BUFFER_SIZE) * 2;
1983  break;
1984  #if EXTRUDERS > 3
1985  case 3:
1986  DISABLE_IDLE_E(0);
1987  DISABLE_IDLE_E(1);
1988  DISABLE_IDLE_E(2);
1989  #if EXTRUDERS > 4
1990  DISABLE_IDLE_E(4);
1991  #if EXTRUDERS > 5
1992  DISABLE_IDLE_E(5);
1993  #endif
1994  #endif
1995  enable_E3();
1996  g_uc_extruder_last_move[3] = (BLOCK_BUFFER_SIZE) * 2;
1997  break;
1998  #if EXTRUDERS > 4
1999  case 4:
2000  DISABLE_IDLE_E(0);
2001  DISABLE_IDLE_E(1);
2002  DISABLE_IDLE_E(2);
2003  DISABLE_IDLE_E(3);
2004  #if EXTRUDERS > 5
2005  DISABLE_IDLE_E(5);
2006  #endif
2007  enable_E4();
2008  g_uc_extruder_last_move[4] = (BLOCK_BUFFER_SIZE) * 2;
2009  break;
2010  #if EXTRUDERS > 5
2011  case 5:
2012  DISABLE_IDLE_E(0);
2013  DISABLE_IDLE_E(1);
2014  DISABLE_IDLE_E(2);
2015  DISABLE_IDLE_E(3);
2016  DISABLE_IDLE_E(4);
2017  enable_E5();
2018  g_uc_extruder_last_move[5] = (BLOCK_BUFFER_SIZE) * 2;
2019  break;
2020  #endif // EXTRUDERS > 5
2021  #endif // EXTRUDERS > 4
2022  #endif // EXTRUDERS > 3
2023  #endif // EXTRUDERS > 2
2024  #endif // EXTRUDERS > 1
2025  }
2026  #else
2027  enable_E0();
2028  enable_E1();
2029  enable_E2();
2030  enable_E3();
2031  enable_E4();
2032  enable_E5();
2033  #endif
2034  }
2035  #endif // EXTRUDERS
2036 
2037  if (esteps)
2038  NOLESS(fr_mm_s, settings.min_feedrate_mm_s);
2039  else
2041 
2042  const float inverse_millimeters = 1.0f / block->millimeters; // Inverse millimeters to remove multiple divides
2043 
2044  // Calculate inverse time for this move. No divide by zero due to previous checks.
2045  // Example: At 120mm/s a 60mm move takes 0.5s. So this will give 2.0.
2046  float inverse_secs = fr_mm_s * inverse_millimeters;
2047 
2048  // Get the number of non busy movements in queue (non busy means that they can be altered)
2049  const uint8_t moves_queued = nonbusy_movesplanned();
2050 
2051  // Slow down when the buffer starts to empty, rather than wait at the corner for a buffer refill
2052  #if EITHER(SLOWDOWN, ULTRA_LCD) || defined(XY_FREQUENCY_LIMIT)
2053  // Segment time im micro seconds
2054  uint32_t segment_time_us = LROUND(1000000.0f / inverse_secs);
2055  #endif
2056 
2057  #if ENABLED(SLOWDOWN)
2058  if (WITHIN(moves_queued, 2, (BLOCK_BUFFER_SIZE) / 2 - 1)) {
2059  if (segment_time_us < settings.min_segment_time_us) {
2060  // buffer is draining, add extra time. The amount of time added increases if the buffer is still emptied more.
2061  const uint32_t nst = segment_time_us + LROUND(2 * (settings.min_segment_time_us - segment_time_us) / moves_queued);
2062  inverse_secs = 1000000.0f / nst;
2063  #if defined(XY_FREQUENCY_LIMIT) || HAS_SPI_LCD
2064  segment_time_us = nst;
2065  #endif
2066  }
2067  }
2068  #endif
2069 
2070  #if HAS_SPI_LCD
2071  // Protect the access to the position.
2072  const bool was_enabled = STEPPER_ISR_ENABLED();
2073  if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
2074 
2075  block_buffer_runtime_us += segment_time_us;
2076  block->segment_time_us = segment_time_us;
2077 
2078  if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
2079  #endif
2080 
2081  block->nominal_speed_sqr = sq(block->millimeters * inverse_secs); // (mm/sec)^2 Always > 0
2082  block->nominal_rate = CEIL(block->step_event_count * inverse_secs); // (step/sec) Always > 0
2083 
2084  #if ENABLED(FILAMENT_WIDTH_SENSOR)
2085  if (extruder == FILAMENT_SENSOR_EXTRUDER_NUM) // Only for extruder with filament sensor
2086  filwidth.advance_e(delta_mm.e);
2087  #endif
2088 
2089  // Calculate and limit speed in mm/sec for each axis
2090  xyze_float_t current_speed;
2091  float speed_factor = 1.0f; // factor <1 decreases speed
2092  LOOP_XYZE(i) {
2093  #if BOTH(MIXING_EXTRUDER, RETRACT_SYNC_MIXING)
2094  // In worst case, only one extruder running, no change is needed.
2095  // In best case, all extruders run the same amount, we can divide by MIXING_STEPPERS
2096  float delta_mm_i = 0;
2097  if (i == E_AXIS && mixer.get_current_vtool() == MIXER_AUTORETRACT_TOOL)
2098  delta_mm_i = delta_mm.e / MIXING_STEPPERS;
2099  else
2100  delta_mm_i = delta_mm.e;
2101  #else
2102  const float delta_mm_i = delta_mm[i];
2103  #endif
2104  const feedRate_t cs = ABS(current_speed[i] = delta_mm_i * inverse_secs);
2105  #if ENABLED(DISTINCT_E_FACTORS)
2106  if (i == E_AXIS) i += extruder;
2107  #endif
2108  if (cs > settings.max_feedrate_mm_s[i]) NOMORE(speed_factor, settings.max_feedrate_mm_s[i] / cs);
2109  }
2110 
2111  // Max segment time in µs.
2112  #ifdef XY_FREQUENCY_LIMIT
2113 
2114  // Check and limit the xy direction change frequency
2115  const unsigned char direction_change = block->direction_bits ^ old_direction_bits;
2116  old_direction_bits = block->direction_bits;
2117  segment_time_us = LROUND((float)segment_time_us / speed_factor);
2118 
2119  uint32_t xs0 = axis_segment_time_us[0].x,
2120  xs1 = axis_segment_time_us[1].x,
2121  xs2 = axis_segment_time_us[2].x,
2122  ys0 = axis_segment_time_us[0].y,
2123  ys1 = axis_segment_time_us[1].y,
2124  ys2 = axis_segment_time_us[2].y;
2125 
2126  if (TEST(direction_change, X_AXIS)) {
2127  xs2 = axis_segment_time_us[2].x = xs1;
2128  xs1 = axis_segment_time_us[1].x = xs0;
2129  xs0 = 0;
2130  }
2131  xs0 = axis_segment_time_us[0].x = xs0 + segment_time_us;
2132 
2133  if (TEST(direction_change, Y_AXIS)) {
2134  ys2 = axis_segment_time_us[2].y = axis_segment_time_us[1].y;
2135  ys1 = axis_segment_time_us[1].y = axis_segment_time_us[0].y;
2136  ys0 = 0;
2137  }
2138  ys0 = axis_segment_time_us[0].y = ys0 + segment_time_us;
2139 
2140  const uint32_t max_x_segment_time = _MAX(xs0, xs1, xs2),
2141  max_y_segment_time = _MAX(ys0, ys1, ys2),
2142  min_xy_segment_time = _MIN(max_x_segment_time, max_y_segment_time);
2143  if (min_xy_segment_time < MAX_FREQ_TIME_US) {
2144  const float low_sf = speed_factor * min_xy_segment_time / (MAX_FREQ_TIME_US);
2145  NOMORE(speed_factor, low_sf);
2146  }
2147  #endif // XY_FREQUENCY_LIMIT
2148 
2149  // Correct the speed
2150  if (speed_factor < 1.0f) {
2151  current_speed *= speed_factor;
2152  block->nominal_rate *= speed_factor;
2153  block->nominal_speed_sqr = block->nominal_speed_sqr * sq(speed_factor);
2154  }
2155 
2156  // Compute and limit the acceleration rate for the trapezoid generator.
2157  const float steps_per_mm = block->step_event_count * inverse_millimeters;
2158  uint32_t accel;
2159  if (!block->steps.a && !block->steps.b && !block->steps.c) {
2160  // convert to: acceleration steps/sec^2
2161  accel = CEIL(settings.retract_acceleration * steps_per_mm);
2162  #if ENABLED(LIN_ADVANCE)
2163  block->use_advance_lead = false;
2164  #endif
2165  }
2166  else {
2167  #define LIMIT_ACCEL_LONG(AXIS,INDX) do{ \
2168  if (block->steps[AXIS] && max_acceleration_steps_per_s2[AXIS+INDX] < accel) { \
2169  const uint32_t comp = max_acceleration_steps_per_s2[AXIS+INDX] * block->step_event_count; \
2170  if (accel * block->steps[AXIS] > comp) accel = comp / block->steps[AXIS]; \
2171  } \
2172  }while(0)
2173 
2174  #define LIMIT_ACCEL_FLOAT(AXIS,INDX) do{ \
2175  if (block->steps[AXIS] && max_acceleration_steps_per_s2[AXIS+INDX] < accel) { \
2176  const float comp = (float)max_acceleration_steps_per_s2[AXIS+INDX] * (float)block->step_event_count; \
2177  if ((float)accel * (float)block->steps[AXIS] > comp) accel = comp / (float)block->steps[AXIS]; \
2178  } \
2179  }while(0)
2180 
2181  // Start with print or travel acceleration
2182  accel = CEIL((esteps ? settings.acceleration : settings.travel_acceleration) * steps_per_mm);
2183 
2184  #if ENABLED(LIN_ADVANCE)
2185 
2186  #if DISABLED(CLASSIC_JERK)
2187  #if ENABLED(DISTINCT_E_FACTORS)
2188  #define MAX_E_JERK max_e_jerk[extruder]
2189  #else
2190  #define MAX_E_JERK max_e_jerk
2191  #endif
2192  #else
2193  #define MAX_E_JERK max_jerk.e
2194  #endif
2195 
2196  /**
2197  *
2198  * Use LIN_ADVANCE for blocks if all these are true:
2199  *
2200  * esteps : This is a print move, because we checked for A, B, C steps before.
2201  *
2202  * extruder_advance_K[active_extruder] : There is an advance factor set for this extruder.
2203  *
2204  * de > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
2205  */
2206  block->use_advance_lead = esteps
2207  && extruder_advance_K[active_extruder]
2208  && de > 0;
2209 
2210  if (block->use_advance_lead) {
2211  block->e_D_ratio = (target_float.e - position_float.e) /
2212  #if IS_KINEMATIC
2213  block->millimeters
2214  #else
2215  SQRT(sq(target_float.x - position_float.x)
2216  + sq(target_float.y - position_float.y)
2217  + sq(target_float.z - position_float.z))
2218  #endif
2219  ;
2220 
2221  // Check for unusual high e_D ratio to detect if a retract move was combined with the last print move due to min. steps per segment. Never execute this with advance!
2222  // This assumes no one will use a retract length of 0mm < retr_length < ~0.2mm and no one will print 100mm wide lines using 3mm filament or 35mm wide lines using 1.75mm filament.
2223  if (block->e_D_ratio > 3.0f)
2224  block->use_advance_lead = false;
2225  else {
2226  const uint32_t max_accel_steps_per_s2 = MAX_E_JERK / (extruder_advance_K[active_extruder] * block->e_D_ratio) * steps_per_mm;
2227  #if ENABLED(LA_DEBUG)
2228  if (accel > max_accel_steps_per_s2) SERIAL_ECHOLNPGM("Acceleration limited.");
2229  #endif
2230  NOMORE(accel, max_accel_steps_per_s2);
2231  }
2232  }
2233  #endif
2234 
2235  #if ENABLED(DISTINCT_E_FACTORS)
2236  #define ACCEL_IDX extruder
2237  #else
2238  #define ACCEL_IDX 0
2239  #endif
2240 
2241  // Limit acceleration per axis
2242  if (block->step_event_count <= cutoff_long) {
2247  }
2248  else {
2253  }
2254  }
2255  block->acceleration_steps_per_s2 = accel;
2256  block->acceleration = accel / steps_per_mm;
2257  #if DISABLED(S_CURVE_ACCELERATION)
2258  block->acceleration_rate = (uint32_t)(accel * (4096.0f * 4096.0f / (STEPPER_TIMER_RATE)));
2259  #endif
2260  #if ENABLED(LIN_ADVANCE)
2261  if (block->use_advance_lead) {
2262  block->advance_speed = (STEPPER_TIMER_RATE) / (extruder_advance_K[active_extruder] * block->e_D_ratio * block->acceleration * settings.axis_steps_per_mm[E_AXIS_N(extruder)]);
2263  #if ENABLED(LA_DEBUG)
2264  if (extruder_advance_K[active_extruder] * block->e_D_ratio * block->acceleration * 2 < SQRT(block->nominal_speed_sqr) * block->e_D_ratio)
2265  SERIAL_ECHOLNPGM("More than 2 steps per eISR loop executed.");
2266  if (block->advance_speed < 200)
2267  SERIAL_ECHOLNPGM("eISR running at > 10kHz.");
2268  #endif
2269  }
2270  #endif
2271 
2272  float vmax_junction_sqr; // Initial limit on the segment entry velocity (mm/s)^2
2273 
2274  #if DISABLED(CLASSIC_JERK)
2275  /**
2276  * Compute maximum allowable entry speed at junction by centripetal acceleration approximation.
2277  * Let a circle be tangent to both previous and current path line segments, where the junction
2278  * deviation is defined as the distance from the junction to the closest edge of the circle,
2279  * colinear with the circle center. The circular segment joining the two paths represents the
2280  * path of centripetal acceleration. Solve for max velocity based on max acceleration about the
2281  * radius of the circle, defined indirectly by junction deviation. This may be also viewed as
2282  * path width or max_jerk in the previous Grbl version. This approach does not actually deviate
2283  * from path, but used as a robust way to compute cornering speeds, as it takes into account the
2284  * nonlinearities of both the junction angle and junction velocity.
2285  *
2286  * NOTE: If the junction deviation value is finite, Grbl executes the motions in an exact path
2287  * mode (G61). If the junction deviation value is zero, Grbl will execute the motion in an exact
2288  * stop mode (G61.1) manner. In the future, if continuous mode (G64) is desired, the math here
2289  * is exactly the same. Instead of motioning all the way to junction point, the machine will
2290  * just follow the arc circle defined here. The Arduino doesn't have the CPU cycles to perform
2291  * a continuous mode path, but ARM-based microcontrollers most certainly do.
2292  *
2293  * NOTE: The max junction speed is a fixed value, since machine acceleration limits cannot be
2294  * changed dynamically during operation nor can the line move geometry. This must be kept in
2295  * memory in the event of a feedrate override changing the nominal speeds of blocks, which can
2296  * change the overall maximum entry speed conditions of all blocks.
2297  *
2298  * #######
2299  * https://github.com/MarlinFirmware/Marlin/issues/10341#issuecomment-388191754
2300  *
2301  * hoffbaked: on May 10 2018 tuned and improved the GRBL algorithm for Marlin:
2302  Okay! It seems to be working good. I somewhat arbitrarily cut it off at 1mm
2303  on then on anything with less sides than an octagon. With this, and the
2304  reverse pass actually recalculating things, a corner acceleration value
2305  of 1000 junction deviation of .05 are pretty reasonable. If the cycles
2306  can be spared, a better acos could be used. For all I know, it may be
2307  already calculated in a different place. */
2308 
2309  // Unit vector of previous path line segment
2310  static xyze_float_t prev_unit_vec;
2311 
2312  xyze_float_t unit_vec =
2313  #if IS_KINEMATIC && DISABLED(CLASSIC_JERK)
2314  delta_mm_cart
2315  #else
2316  { delta_mm.x, delta_mm.y, delta_mm.z, delta_mm.e }
2317  #endif
2318  ;
2319  unit_vec *= inverse_millimeters;
2320 
2321  #if IS_CORE && DISABLED(CLASSIC_JERK)
2322  /**
2323  * On CoreXY the length of the vector [A,B] is SQRT(2) times the length of the head movement vector [X,Y].
2324  * So taking Z and E into account, we cannot scale to a unit vector with "inverse_millimeters".
2325  * => normalize the complete junction vector
2326  */
2327  normalize_junction_vector(unit_vec);
2328  #endif
2329 
2330  // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles.
2331  if (moves_queued && !UNEAR_ZERO(previous_nominal_speed_sqr)) {
2332  // Compute cosine of angle between previous and current path. (prev_unit_vec is negative)
2333  // NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
2334  float junction_cos_theta = (-prev_unit_vec.x * unit_vec.x) + (-prev_unit_vec.y * unit_vec.y)
2335  + (-prev_unit_vec.z * unit_vec.z) + (-prev_unit_vec.e * unit_vec.e);
2336 
2337  // NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
2338  if (junction_cos_theta > 0.999999f) {
2339  // For a 0 degree acute junction, just set minimum junction speed.
2340  vmax_junction_sqr = sq(float(MINIMUM_PLANNER_SPEED));
2341  }
2342  else {
2343  NOLESS(junction_cos_theta, -0.999999f); // Check for numerical round-off to avoid divide by zero.
2344 
2345  // Convert delta vector to unit vector
2346  xyze_float_t junction_unit_vec = unit_vec - prev_unit_vec;
2347  normalize_junction_vector(junction_unit_vec);
2348 
2349  const float junction_acceleration = limit_value_by_axis_maximum(block->acceleration, junction_unit_vec),
2350  sin_theta_d2 = SQRT(0.5f * (1.0f - junction_cos_theta)); // Trig half angle identity. Always positive.
2351 
2352  vmax_junction_sqr = (junction_acceleration * junction_deviation_mm * sin_theta_d2) / (1.0f - sin_theta_d2);
2353  if (block->millimeters < 1) {
2354 
2355  // Fast acos approximation, minus the error bar to be safe
2356  const float junction_theta = (RADIANS(-40) * sq(junction_cos_theta) - RADIANS(50)) * junction_cos_theta + RADIANS(90) - 0.18f;
2357 
2358  // If angle is greater than 135 degrees (octagon), find speed for approximate arc
2359  if (junction_theta > RADIANS(135)) {
2360  const float limit_sqr = block->millimeters / (RADIANS(180) - junction_theta) * junction_acceleration;
2361  NOMORE(vmax_junction_sqr, limit_sqr);
2362  }
2363  }
2364  }
2365 
2366  // Get the lowest speed
2367  vmax_junction_sqr = _MIN(vmax_junction_sqr, block->nominal_speed_sqr, previous_nominal_speed_sqr);
2368  }
2369  else // Init entry speed to zero. Assume it starts from rest. Planner will correct this later.
2370  vmax_junction_sqr = 0;
2371 
2372  prev_unit_vec = unit_vec;
2373 
2374  #endif
2375 
2376  #ifdef USE_CACHED_SQRT
2377  #define CACHED_SQRT(N, V) \
2378  static float saved_V, N; \
2379  if (V != saved_V) { N = SQRT(V); saved_V = V; }
2380  #else
2381  #define CACHED_SQRT(N, V) const float N = SQRT(V)
2382  #endif
2383 
2384  #if HAS_CLASSIC_JERK
2385 
2386  /**
2387  * Adapted from Průša MKS firmware
2388  * https://github.com/prusa3d/Prusa-Firmware
2389  */
2390  CACHED_SQRT(nominal_speed, block->nominal_speed_sqr);
2391 
2392  // Exit speed limited by a jerk to full halt of a previous last segment
2393  static float previous_safe_speed;
2394 
2395  // Start with a safe speed (from which the machine may halt to stop immediately).
2396  float safe_speed = nominal_speed;
2397 
2398  uint8_t limited = 0;
2399  #if HAS_LINEAR_E_JERK
2400  LOOP_XYZ(i)
2401  #else
2402  LOOP_XYZE(i)
2403  #endif
2404  {
2405  const float jerk = ABS(current_speed[i]), // cs : Starting from zero, change in speed for this axis
2406  maxj = max_jerk[i]; // mj : The max jerk setting for this axis
2407  if (jerk > maxj) { // cs > mj : New current speed too fast?
2408  if (limited) { // limited already?
2409  const float mjerk = nominal_speed * maxj; // ns*mj
2410  if (jerk * safe_speed > mjerk) safe_speed = mjerk / jerk; // ns*mj/cs
2411  }
2412  else {
2413  safe_speed *= maxj / jerk; // Initial limit: ns*mj/cs
2414  ++limited; // Initially limited
2415  }
2416  }
2417  }
2418 
2419  float vmax_junction;
2420  if (moves_queued && !UNEAR_ZERO(previous_nominal_speed_sqr)) {
2421  // Estimate a maximum velocity allowed at a joint of two successive segments.
2422  // If this maximum velocity allowed is lower than the minimum of the entry / exit safe velocities,
2423  // then the machine is not coasting anymore and the safe entry / exit velocities shall be used.
2424 
2425  // Factor to multiply the previous / current nominal velocities to get componentwise limited velocities.
2426  float v_factor = 1;
2427  limited = 0;
2428 
2429  // The junction velocity will be shared between successive segments. Limit the junction velocity to their minimum.
2430  // Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
2431  CACHED_SQRT(previous_nominal_speed, previous_nominal_speed_sqr);
2432 
2433  vmax_junction = _MIN(nominal_speed, previous_nominal_speed);
2434 
2435  // Now limit the jerk in all axes.
2436  const float smaller_speed_factor = vmax_junction / previous_nominal_speed;
2437  #if HAS_LINEAR_E_JERK
2438  LOOP_XYZ(axis)
2439  #else
2440  LOOP_XYZE(axis)
2441  #endif
2442  {
2443  // Limit an axis. We have to differentiate: coasting, reversal of an axis, full stop.
2444  float v_exit = previous_speed[axis] * smaller_speed_factor,
2445  v_entry = current_speed[axis];
2446  if (limited) {
2447  v_exit *= v_factor;
2448  v_entry *= v_factor;
2449  }
2450 
2451  // Calculate jerk depending on whether the axis is coasting in the same direction or reversing.
2452  const float jerk = (v_exit > v_entry)
2453  ? // coasting axis reversal
2454  ( (v_entry > 0 || v_exit < 0) ? (v_exit - v_entry) : _MAX(v_exit, -v_entry) )
2455  : // v_exit <= v_entry coasting axis reversal
2456  ( (v_entry < 0 || v_exit > 0) ? (v_entry - v_exit) : _MAX(-v_exit, v_entry) );
2457 
2458  if (jerk > max_jerk[axis]) {
2459  v_factor *= max_jerk[axis] / jerk;
2460  ++limited;
2461  }
2462  }
2463  if (limited) vmax_junction *= v_factor;
2464  // Now the transition velocity is known, which maximizes the shared exit / entry velocity while
2465  // respecting the jerk factors, it may be possible, that applying separate safe exit / entry velocities will achieve faster prints.
2466  const float vmax_junction_threshold = vmax_junction * 0.99f;
2467  if (previous_safe_speed > vmax_junction_threshold && safe_speed > vmax_junction_threshold)
2468  vmax_junction = safe_speed;
2469  }
2470  else
2471  vmax_junction = safe_speed;
2472 
2473  previous_safe_speed = safe_speed;
2474 
2475  #if DISABLED(CLASSIC_JERK)
2476  vmax_junction_sqr = _MIN(vmax_junction_sqr, sq(vmax_junction));
2477  #else
2478  vmax_junction_sqr = sq(vmax_junction);
2479  #endif
2480 
2481  #endif // Classic Jerk Limiting
2482 
2483  // Max entry speed of this block equals the max exit speed of the previous block.
2484  block->max_entry_speed_sqr = vmax_junction_sqr;
2485 
2486  // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED.
2487  const float v_allowable_sqr = max_allowable_speed_sqr(-block->acceleration, sq(float(MINIMUM_PLANNER_SPEED)), block->millimeters);
2488 
2489  // If we are trying to add a split block, start with the
2490  // max. allowed speed to avoid an interrupted first move.
2491  block->entry_speed_sqr = !split_move ? sq(float(MINIMUM_PLANNER_SPEED)) : _MIN(vmax_junction_sqr, v_allowable_sqr);
2492 
2493  // Initialize planner efficiency flags
2494  // Set flag if block will always reach maximum junction speed regardless of entry/exit speeds.
2495  // If a block can de/ac-celerate from nominal speed to zero within the length of the block, then
2496  // the current block and next block junction speeds are guaranteed to always be at their maximum
2497  // junction speeds in deceleration and acceleration, respectively. This is due to how the current
2498  // block nominal speed limits both the current and next maximum junction speeds. Hence, in both
2499  // the reverse and forward planners, the corresponding block junction speed will always be at the
2500  // the maximum junction speed and may always be ignored for any speed reduction checks.
2502 
2503  // Update previous path unit_vector and nominal speed
2504  previous_speed = current_speed;
2505  previous_nominal_speed_sqr = block->nominal_speed_sqr;
2506 
2507  // Update the position
2508  position = target;
2509  #if HAS_POSITION_FLOAT
2510  position_float = target_float;
2511  #endif
2512 
2513  #if ENABLED(GRADIENT_MIX)
2514  mixer.gradient_control(target_float.z);
2515  #endif
2516 
2517  #if ENABLED(POWER_LOSS_RECOVERY)
2518  block->sdpos = recovery.command_sdpos();
2519  #endif
2520 
2521  // Movement was accepted
2522  return true;
2523 } // _populate_block()
Here is the call graph for this function:
Here is the caller graph for this function:

◆ buffer_sync_block()

void Planner::buffer_sync_block ( )
static

Planner::buffer_sync_block Add a block to the buffer that just updates the position

2529  {
2530  // Wait for the next available block
2531  uint8_t next_buffer_head;
2532  block_t * const block = get_next_free_block(next_buffer_head);
2533 
2534  // Clear block
2535  memset(block, 0, sizeof(block_t));
2536 
2537  block->flag = BLOCK_FLAG_SYNC_POSITION;
2538 
2539  block->position = position;
2540 
2541  // If this is the first added movement, reload the delay, otherwise, cancel it.
2543  // If it was the first queued block, restart the 1st block delivery delay, to
2544  // give the planner an opportunity to queue more movements and plan them
2545  // As there are no queued movements, the Stepper ISR will not touch this
2546  // variable, so there is no risk setting this here (but it MUST be done
2547  // before the following line!!)
2549  }
2550 
2551  block_buffer_head = next_buffer_head;
2552 
2553  stepper.wake_up();
2554 } // buffer_sync_block()
Here is the call graph for this function:
Here is the caller graph for this function:

◆ buffer_segment() [1/2]

bool Planner::buffer_segment ( const float &  a,
const float &  b,
const float &  c,
const float &  e,
const feedRate_t fr_mm_s,
const uint8_t  extruder,
const float &  millimeters = 0.0 
)
static

Planner::buffer_segment

Add a new linear movement to the buffer in axis units.

Leveling and kinematics should be applied ahead of calling this.

a,b,c,e - target positions in mm and/or degrees fr_mm_s - (target) speed of the move extruder - target extruder millimeters - the length of the movement, if known

2573  {
2574 
2575  // If we are cleaning, do not accept queuing of movements
2576  if (cleaning_buffer_counter) return false;
2577 
2578  // When changing extruders recalculate steps corresponding to the E position
2579  #if ENABLED(DISTINCT_E_FACTORS)
2580  if (last_extruder != extruder && settings.axis_steps_per_mm[E_AXIS_N(extruder)] != settings.axis_steps_per_mm[E_AXIS_N(last_extruder)]) {
2581  position.e = LROUND(position.e * settings.axis_steps_per_mm[E_AXIS_N(extruder)] * steps_to_mm[E_AXIS_N(last_extruder)]);
2582  last_extruder = extruder;
2583  }
2584  #endif
2585 
2586  // The target position of the tool in absolute steps
2587  // Calculate target position in absolute steps
2588  const abce_long_t target = {
2589  int32_t(LROUND(a * settings.axis_steps_per_mm[A_AXIS])),
2590  int32_t(LROUND(b * settings.axis_steps_per_mm[B_AXIS])),
2591  int32_t(LROUND(c * settings.axis_steps_per_mm[C_AXIS])),
2592  int32_t(LROUND(e * settings.axis_steps_per_mm[E_AXIS_N(extruder)]))
2593  };
2594 
2595  #if HAS_POSITION_FLOAT
2596  const xyze_pos_t target_float = { a, b, c, e };
2597  #endif
2598 
2599  // DRYRUN prevents E moves from taking place
2600  if (DEBUGGING(DRYRUN)) {
2601  position.e = target.e;
2602  #if HAS_POSITION_FLOAT
2603  position_float.e = e;
2604  #endif
2605  }
2606 
2607  /* <-- add a slash to enable
2608  SERIAL_ECHOPAIR(" buffer_segment FR:", fr_mm_s);
2609  #if IS_KINEMATIC
2610  SERIAL_ECHOPAIR(" A:", a);
2611  SERIAL_ECHOPAIR(" (", position.a);
2612  SERIAL_ECHOPAIR("->", target.a);
2613  SERIAL_ECHOPAIR(") B:", b);
2614  #else
2615  SERIAL_ECHOPAIR(" X:", a);
2616  SERIAL_ECHOPAIR(" (", position.x);
2617  SERIAL_ECHOPAIR("->", target.x);
2618  SERIAL_ECHOPAIR(") Y:", b);
2619  #endif
2620  SERIAL_ECHOPAIR(" (", position.y);
2621  SERIAL_ECHOPAIR("->", target.y);
2622  #if ENABLED(DELTA)
2623  SERIAL_ECHOPAIR(") C:", c);
2624  #else
2625  SERIAL_ECHOPAIR(") Z:", c);
2626  #endif
2627  SERIAL_ECHOPAIR(" (", position.z);
2628  SERIAL_ECHOPAIR("->", target.z);
2629  SERIAL_ECHOPAIR(") E:", e);
2630  SERIAL_ECHOPAIR(" (", position.e);
2631  SERIAL_ECHOPAIR("->", target.e);
2632  SERIAL_ECHOLNPGM(")");
2633  //*/
2634 
2635  // Queue the movement
2636  if (
2637  !_buffer_steps(target
2638  #if HAS_POSITION_FLOAT
2639  , target_float
2640  #endif
2641  #if IS_KINEMATIC && DISABLED(CLASSIC_JERK)
2642  , delta_mm_cart
2643  #endif
2644  , fr_mm_s, extruder, millimeters
2645  )
2646  ) return false;
2647 
2648  stepper.wake_up();
2649  return true;
2650 } // buffer_segment()
Here is the call graph for this function:
Here is the caller graph for this function:

◆ buffer_segment() [2/2]

static FORCE_INLINE bool Planner::buffer_segment ( abce_pos_t abce,
const feedRate_t fr_mm_s,
const uint8_t  extruder,
const float &  millimeters = 0.0 
)
static
656  {
657  return buffer_segment(abce.a, abce.b, abce.c, abce.e
658  #if IS_KINEMATIC && DISABLED(CLASSIC_JERK)
659  , delta_mm_cart
660  #endif
661  , fr_mm_s, extruder, millimeters);
662  }
Here is the call graph for this function:

◆ buffer_line() [1/2]

bool Planner::buffer_line ( const float &  rx,
const float &  ry,
const float &  rz,
const float &  e,
const feedRate_t fr_mm_s,
const uint8_t  extruder,
const float  millimeters = 0.0 
)
static

Add a new linear movement to the buffer. The target is cartesian. It's translated to delta/scara if needed.

rx,ry,rz,e - target position in mm or degrees fr_mm_s - (target) speed of the move (mm/s) extruder - target extruder millimeters - the length of the movement, if known inv_duration - the reciprocal if the duration of the movement, if known (kinematic only if feeedrate scaling is enabled)

2667  {
2668  xyze_pos_t machine = { rx, ry, rz, e };
2669  #if HAS_POSITION_MODIFIERS
2670  apply_modifiers(machine);
2671  #endif
2672 
2673  #if IS_KINEMATIC
2674 
2675  #if DISABLED(CLASSIC_JERK)
2676  const xyze_pos_t delta_mm_cart = {
2677  rx - position_cart.x, ry - position_cart.y,
2678  rz - position_cart.z, e - position_cart.e
2679  };
2680  #else
2681  const xyz_pos_t delta_mm_cart = { rx - position_cart.x, ry - position_cart.y, rz - position_cart.z };
2682  #endif
2683 
2684  float mm = millimeters;
2685  if (mm == 0.0)
2686  mm = (delta_mm_cart.x != 0.0 || delta_mm_cart.y != 0.0) ? delta_mm_cart.magnitude() : ABS(delta_mm_cart.z);
2687 
2688  inverse_kinematics(machine);
2689 
2690  #if ENABLED(SCARA_FEEDRATE_SCALING)
2691  // For SCARA scale the feed rate from mm/s to degrees/s
2692  // i.e., Complete the angular vector in the given time.
2693  const float duration_recip = inv_duration ?: fr_mm_s / mm;
2694  const feedRate_t feedrate = HYPOT(delta.a - position_float.a, delta.b - position_float.b) * duration_recip;
2695  #else
2696  const feedRate_t feedrate = fr_mm_s;
2697  #endif
2698  if (buffer_segment(delta.a, delta.b, delta.c, machine.e
2699  #if DISABLED(CLASSIC_JERK)
2700  , delta_mm_cart
2701  #endif
2702  , feedrate, extruder, mm
2703  )) {
2704  position_cart.set(rx, ry, rz, e);
2705  return true;
2706  }
2707  else
2708  return false;
2709  #else
2710  return buffer_segment(machine, fr_mm_s, extruder, millimeters);
2711  #endif
2712 } // buffer_line()
Here is the call graph for this function:
Here is the caller graph for this function:

◆ buffer_line() [2/2]

static FORCE_INLINE bool Planner::buffer_line ( const xyze_pos_t cart,
const feedRate_t fr_mm_s,
const uint8_t  extruder,
const float  millimeters = 0.0 
)
static
687  {
688  return buffer_line(cart.x, cart.y, cart.z, cart.e, fr_mm_s, extruder, millimeters
689  #if ENABLED(SCARA_FEEDRATE_SCALING)
690  , inv_duration
691  #endif
692  );
693  }
Here is the call graph for this function:

◆ set_position_mm() [1/2]

void Planner::set_position_mm ( const float &  rx,
const float &  ry,
const float &  rz,
const float &  e 
)
static

Set the planner.position and individual stepper positions. Used by G92, G28, G29, and other procedures.

The supplied position is in the cartesian coordinate space and is translated in to machine space as needed. Modifiers such as leveling and skew are also applied.

Multiplies by axis_steps_per_mm[] and does necessary conversion for COREXY / COREXZ / COREYZ to set the corresponding stepper positions.

Clears previous speed values.

2741  {
2742  xyze_pos_t machine = { rx, ry, rz, e };
2743  #if HAS_POSITION_MODIFIERS
2744  {
2745  apply_modifiers(machine
2746  #if HAS_LEVELING
2747  , true
2748  #endif
2749  );
2750  }
2751  #endif
2752  #if IS_KINEMATIC
2753  position_cart.set(rx, ry, rz, e);
2754  inverse_kinematics(machine);
2755  set_machine_position_mm(delta.a, delta.b, delta.c, machine.e);
2756  #else
2757  set_machine_position_mm(machine);
2758  #endif
2759 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_position_mm() [2/2]

static FORCE_INLINE void Planner::set_position_mm ( const xyze_pos_t cart)
static
709 { set_position_mm(cart.x, cart.y, cart.z, cart.e); }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_e_position_mm()

void Planner::set_e_position_mm ( const float &  e)
static

Setters for planner position (also setting stepper position).

2764  {
2765  const uint8_t axis_index = E_AXIS_N(active_extruder);
2766  #if ENABLED(DISTINCT_E_FACTORS)
2767  last_extruder = active_extruder;
2768  #endif
2769  #if ENABLED(FWRETRACT)
2770  float e_new = e - fwretract.current_retract[active_extruder];
2771  #else
2772  const float e_new = e;
2773  #endif
2774  position.e = LROUND(settings.axis_steps_per_mm[axis_index] * e_new);
2775  #if HAS_POSITION_FLOAT
2776  position_float.e = e_new;
2777  #endif
2778  #if IS_KINEMATIC
2779  position_cart.e = e;
2780  #endif
2781  if (has_blocks_queued())
2783  else
2784  stepper.set_axis_position(E_AXIS, position.e);
2785 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_machine_position_mm() [1/2]

void Planner::set_machine_position_mm ( const float &  a,
const float &  b,
const float &  c,
const float &  e 
)
static

Set the planner.position and individual stepper positions.

The supplied position is in machine space, and no additional conversions are applied.

Directly set the planner ABC position (and stepper positions) converting mm (or angles for SCARA) into steps.

The provided ABC position is in machine units.

2721  {
2722  #if ENABLED(DISTINCT_E_FACTORS)
2723  last_extruder = active_extruder;
2724  #endif
2725  #if HAS_POSITION_FLOAT
2726  position_float.set(a, b, c, e);
2727  #endif
2732  if (has_blocks_queued()) {
2733  //previous_nominal_speed_sqr = 0.0; // Reset planner junction speeds. Assume start from rest.
2734  //previous_speed.reset();
2736  }
2737  else
2738  stepper.set_position(position);
2739 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_machine_position_mm() [2/2]

static FORCE_INLINE void Planner::set_machine_position_mm ( const abce_pos_t abce)
static
719 { set_machine_position_mm(abce.a, abce.b, abce.c, abce.e); }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_axis_position_mm()

float Planner::get_axis_position_mm ( const AxisEnum  axis)
static

Get an axis position according to stepper position(s) For CORE machines apply translation from ABC to XYZ.

1526  {
1527  float axis_steps;
1528  #if IS_CORE
1529  // Requesting one of the "core" axes?
1530  if (axis == CORE_AXIS_1 || axis == CORE_AXIS_2) {
1531 
1532  // Protect the access to the position.
1533  const bool was_enabled = STEPPER_ISR_ENABLED();
1534  if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
1535 
1536  const int32_t p1 = stepper.position(CORE_AXIS_1),
1537  p2 = stepper.position(CORE_AXIS_2);
1538 
1539  if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
1540 
1541  // ((a1+a2)+(a1-a2))/2 -> (a1+a2+a1-a2)/2 -> (a1+a1)/2 -> a1
1542  // ((a1+a2)-(a1-a2))/2 -> (a1+a2-a1+a2)/2 -> (a2+a2)/2 -> a2
1543  axis_steps = (axis == CORE_AXIS_2 ? CORESIGN(p1 - p2) : p1 + p2) * 0.5f;
1544  }
1545  else
1546  axis_steps = stepper.position(axis);
1547  #else
1548  axis_steps = stepper.position(axis);
1549  #endif
1550  return axis_steps * steps_to_mm[axis];
1551 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ quick_stop()

void Planner::quick_stop ( )
static
1475  {
1476 
1477  // Remove all the queued blocks. Note that this function is NOT
1478  // called from the Stepper ISR, so we must consider tail as readonly!
1479  // that is why we set head to tail - But there is a race condition that
1480  // must be handled: The tail could change between the read and the assignment
1481  // so this must be enclosed in a critical section
1482 
1483  const bool was_enabled = STEPPER_ISR_ENABLED();
1484  if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
1485 
1486  // Drop all queue entries
1488 
1489  // Restart the block delay for the first movement - As the queue was
1490  // forced to empty, there's no risk the ISR will touch this.
1492 
1493  #if HAS_SPI_LCD
1494  // Clear the accumulated runtime
1495  clear_block_buffer_runtime();
1496  #endif
1497 
1498  // Make sure to drop any attempt of queuing moves for at least 1 second
1499  cleaning_buffer_counter = 1000;
1500 
1501  // Reenable Stepper ISR
1502  if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
1503 
1504  // And stop the stepper ISR
1505  stepper.quick_stop();
1506 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ endstop_triggered()

void Planner::endstop_triggered ( const AxisEnum  axis)
static
1508  {
1509  // Record stepper position and discard the current block
1510  stepper.endstop_triggered(axis);
1511 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ triggered_position_mm()

float Planner::triggered_position_mm ( const AxisEnum  axis)
static
1513  {
1514  return stepper.triggered_position(axis) * steps_to_mm[axis];
1515 }
Here is the call graph for this function:

◆ synchronize()

void Planner::synchronize ( )
static

Block until all buffered steps are executed / cleaned

1556  {
1557  while (
1559  #if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER)
1560  || (READ(CLOSED_LOOP_ENABLE_PIN) && !READ(CLOSED_LOOP_MOVE_COMPLETE_PIN))
1561  #endif
1562  ) idle();
1563 }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ finish_and_disable()

void Planner::finish_and_disable ( )
static
1517  {
1520 }
Here is the call graph for this function:

◆ tick()

static void Planner::tick ( )
static
750  {
753  #if ENABLED(SD_FINISHED_STEPPERRELEASE) && defined(SD_FINISHED_RELEASECOMMAND)
754  if (!cleaning_buffer_counter) queue.inject_P(PSTR(SD_FINISHED_RELEASECOMMAND));
755  #endif
756  }
757  }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_blocks_queued()

static FORCE_INLINE bool Planner::has_blocks_queued ( )
static

Does the buffer have any blocks queued?

762 { return (block_buffer_head != block_buffer_tail); }
Here is the caller graph for this function:

◆ get_current_block()

static block_t* Planner::get_current_block ( )
static

The current block. nullptr if the buffer is empty. This also marks the block as busy. WARNING: Called from Stepper ISR context!

769  {
770 
771  // Get the number of moves in the planner queue so far
772  const uint8_t nr_moves = movesplanned();
773 
774  // If there are any moves queued ...
775  if (nr_moves) {
776 
777  // If there is still delay of delivery of blocks running, decrement it
780  // If the number of movements queued is less than 3, and there is still time
781  // to wait, do not deliver anything
782  if (nr_moves < 3 && delay_before_delivering) return nullptr;
784  }
785 
786  // If we are here, there is no excuse to deliver the block
787  block_t * const block = &block_buffer[block_buffer_tail];
788 
789  // No trapezoid calculated? Don't execute yet.
790  if (TEST(block->flag, BLOCK_BIT_RECALCULATE)) return nullptr;
791 
792  #if HAS_SPI_LCD
793  block_buffer_runtime_us -= block->segment_time_us; // We can't be sure how long an active block will take, so don't count it.
794  #endif
795 
796  // As this block is busy, advance the nonbusy block pointer
797  block_buffer_nonbusy = next_block_index(block_buffer_tail);
798 
799  // Push block_buffer_planned pointer, if encountered.
802 
803  // Return the block
804  return block;
805  }
806 
807  // The queue became empty
808  #if HAS_SPI_LCD
809  clear_block_buffer_runtime(); // paranoia. Buffer is empty now - so reset accumulated time to zero.
810  #endif
811 
812  return nullptr;
813  }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ discard_current_block()

static FORCE_INLINE void Planner::discard_current_block ( )
static

"Discard" the block and "release" the memory. Called when the current block is no longer needed. NB: There MUST be a current block to call this function!!

820  {
821  if (has_blocks_queued())
822  block_buffer_tail = next_block_index(block_buffer_tail);
823  }
Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ block_buffer

block_t Planner::block_buffer
static

The move buffer, calculated in stepper steps

block_buffer is a ring buffer...

        head,tail : indexes for write,read
       head==tail : the buffer is empty
       head!=tail : blocks are in the buffer

head==(tail-1)size : the buffer is full

Writer of head is Planner::buffer_segment(). Reader of tail is Stepper::isr(). Always consider tail busy / read-only

A ring buffer of moves described in steps

◆ block_buffer_head

volatile uint8_t Planner::block_buffer_head
static

◆ block_buffer_nonbusy

volatile uint8_t Planner::block_buffer_nonbusy
static

◆ block_buffer_planned

volatile uint8_t Planner::block_buffer_planned
static

◆ block_buffer_tail

volatile uint8_t Planner::block_buffer_tail
static

◆ cleaning_buffer_counter

uint16_t Planner::cleaning_buffer_counter
static

◆ delay_before_delivering

uint8_t Planner::delay_before_delivering
static

◆ settings

planner_settings_t Planner::settings
static

◆ max_acceleration_steps_per_s2

uint32_t Planner::max_acceleration_steps_per_s2
static

◆ steps_to_mm

float Planner::steps_to_mm
static

◆ leveling_active

bool Planner::leveling_active = false
static

◆ bed_level_matrix

matrix_3x3 Planner::bed_level_matrix
static

◆ position_float

xyze_pos_t Planner::position_float
static

◆ skew_factor

skew_factor_t Planner::skew_factor
static
enable_E2
#define enable_E2()
Definition: Marlin.h:276
WITHIN
#define WITHIN(N, L, H)
Definition: macros.h:195
Planner::buffer_sync_block
static void buffer_sync_block()
Definition: planner.cpp:2529
XYZval::z
T z
Definition: types.h:286
enable_E0
#define enable_E0()
Definition: Marlin.h:260
Planner::set_machine_position_mm
static void set_machine_position_mm(const float &a, const float &b, const float &c, const float &e)
Definition: planner.cpp:2721
Temperature::tooColdToExtrude
static FORCE_INLINE bool tooColdToExtrude(const uint8_t)
Definition: temperature.h:314
NOLESS
#define NOLESS(v, n)
Definition: macros.h:127
DEFAULT_EJERK
#define DEFAULT_EJERK
Definition: Configuration_A3ides_2209_MINI.h:733
backlash
Backlash backlash
XYZEval< float >
MIXER_POPULATE_BLOCK
#define MIXER_POPULATE_BLOCK()
Definition: mixing.h:72
sq
#define sq(x)
Definition: wiring_constants.h:83
XYZEval::z
T z
Definition: types.h:383
mbl
mesh_bed_leveling mbl
LIMIT_ACCEL_LONG
#define LIMIT_ACCEL_LONG(AXIS, INDX)
DEFAULT_MAX_ACCELERATION
#define DEFAULT_MAX_ACCELERATION
Definition: Configuration_A3ides_2209_MINI.h:696
planner_settings_t::min_feedrate_mm_s
feedRate_t min_feedrate_mm_s
Definition: planner.h:186
mixer
Mixer mixer
block_t::flag
volatile uint8_t flag
Definition: planner.h:97
XYZval::x
T x
Definition: types.h:286
mesh_bed_leveling::get_z
static float get_z(const xy_pos_t &pos)
Definition: mesh_bed_leveling.h:107
disable_e_steppers
void disable_e_steppers()
Definition: Marlin.cpp:293
planner_settings_t::max_feedrate_mm_s
feedRate_t max_feedrate_mm_s[XYZE_N]
Definition: planner.h:182
MSG_ERR_COLD_EXTRUDE_STOP
#define MSG_ERR_COLD_EXTRUDE_STOP
Definition: language.h:242
Planner::bed_level_matrix
static matrix_3x3 bed_level_matrix
Definition: planner.h:278
enable_E5
#define enable_E5()
Definition: Marlin.h:300
CORE_IS_XY
#define CORE_IS_XY
Definition: Conditionals_post.h:101
SpindleLaser::power
static cutter_power_t power
Definition: spindle_laser.h:48
queue
GCodeQueue queue
Definition: queue.cpp:28
BLOCK_MOD
#define BLOCK_MOD(n)
Definition: planner.h:176
X_AXIS
Definition: types.h:37
level_fulcrum
constexpr xy_pos_t level_fulcrum
Definition: planner.cpp:1369
enable_E3
#define enable_E3()
Definition: Marlin.h:284
UNEAR_ZERO
#define UNEAR_ZERO(x)
Definition: macros.h:269
PrintJobRecovery::command_sdpos
static uint32_t command_sdpos()
Definition: power_loss_recovery.h:135
stepper
Stepper stepper
Definition: stepper.cpp:82
Planner::buffer_segment
static bool buffer_segment(const float &a, const float &b, const float &c, const float &e, const feedRate_t &fr_mm_s, const uint8_t extruder, const float &millimeters=0.0)
Definition: planner.cpp:2568
planner_settings_t::retract_acceleration
float retract_acceleration
Definition: planner.h:183
_MAX
#define _MAX(V...)
Definition: macros.h:346
block_t::acceleration_steps_per_s2
uint32_t acceleration_steps_per_s2
Definition: planner.h:147
Planner::max_acceleration_steps_per_s2
static uint32_t max_acceleration_steps_per_s2[XYZE_N]
Definition: planner.h:253
Planner::_buffer_steps
static bool _buffer_steps(const xyze_long_t &target, const xyze_pos_t &target_float, feedRate_t fr_mm_s, const uint8_t extruder, const float &millimeters=0.0)
Definition: planner.cpp:1578
Planner::leveling_active
static bool leveling_active
Definition: planner.h:276
MIN_STEPS_PER_SEGMENT
#define MIN_STEPS_PER_SEGMENT
Definition: Configuration_A3ides_2209_MINI_adv.h:1108
BLOCK_BIT_RECALCULATE
Definition: planner.h:65
FilamentWidthSensor::advance_e
static void advance_e(const float &e_move)
Definition: filwidth.h:80
disable_Z
#define disable_Z()
Definition: Marlin.h:143
C_AXIS
Definition: types.h:39
XYZEval::e
T e
Definition: types.h:383
Planner::movesplanned
static FORCE_INLINE uint8_t movesplanned()
Definition: planner.h:543
i
uint8_t i
Definition: screen_test_graph.c:72
_MIN
#define _MIN(V...)
Definition: macros.h:333
LOOP_XYZE_N
#define LOOP_XYZE_N(VAR)
Definition: types.h:62
Planner::block_buffer_tail
static volatile uint8_t block_buffer_tail
Definition: planner.h:227
Stepper::wake_up
static void wake_up()
Definition: stepper.cpp:342
Stepper::endstop_triggered
static void endstop_triggered(const AxisEnum axis)
Definition: stepper.cpp:2237
disable_Y
#define disable_Y()
Definition: Marlin.h:104
enable_Z
#define enable_Z()
Definition: Marlin.h:142
CACHED_SQRT
#define CACHED_SQRT(N, V)
IS_KINEMATIC
#define IS_KINEMATIC
Definition: Conditionals_LCD.h:545
recovery
PrintJobRecovery recovery
block_t::steps
abce_ulong_t steps
Definition: planner.h:107
Planner::block_buffer_head
static volatile uint8_t block_buffer_head
Definition: planner.h:227
STEPPER_ISR_ENABLED
#define STEPPER_ISR_ENABLED()
Definition: HAL.h:143
feedRate_t
float feedRate_t
Definition: types.h:80
matrix_3x3::transpose
static matrix_3x3 transpose(const matrix_3x3 &original)
Definition: vector_3.cpp:131
Planner::position_float
static xyze_pos_t position_float
Definition: planner.h:292
extruder_duplication_enabled
bool extruder_duplication_enabled
Definition: motion.cpp:876
Stepper::set_axis_position
static void set_axis_position(const AxisEnum a, const int32_t &v)
Definition: stepper.h:446
cutter
SpindleLaser cutter
Definition: spindle_laser.cpp:33
matrix_3x3::set_to_identity
void set_to_identity()
Definition: vector_3.cpp:94
matrix_3x3
Definition: vector_3.h:73
Planner::get_next_free_block
static FORCE_INLINE block_t * get_next_free_block(uint8_t &next_buffer_head, const uint8_t count=1)
Definition: planner.h:564
HEATER_2_PIN
#define HEATER_2_PIN
Definition: pins_COHESION3D_REMIX.h:116
NOMORE
#define NOMORE(v, n)
Definition: macros.h:133
block_t::cutter_power
cutter_power_t cutter_power
Definition: planner.h:153
XYZval::magnitude
FI T magnitude() const
Definition: types.h:295
pin_t
int8_t pin_t
Definition: HAL.h:65
AXIS_CONDITION
#define AXIS_CONDITION
ABS
#define ABS(a)
Definition: macros.h:266
Planner::block_buffer_nonbusy
static volatile uint8_t block_buffer_nonbusy
Definition: planner.h:227
Planner::block_buffer
static block_t block_buffer[BLOCK_BUFFER_SIZE]
Definition: planner.h:226
E_AXIS_N
#define E_AXIS_N(E)
Definition: Conditionals_LCD.h:454
AUTO_BED_LEVELING_BILINEAR
#define AUTO_BED_LEVELING_BILINEAR
Definition: Configuration_A3ides_2209_MINI.h:1092
Planner::clear_block_buffer
static FORCE_INLINE void clear_block_buffer()
Definition: planner.h:549
XYZEval::a
T a
Definition: types.h:384
SERIAL_ECHO_MSG
#define SERIAL_ECHO_MSG(S)
Definition: serial.h:183
block_t::position
abce_long_t position
Definition: planner.h:108
RADIANS
#define RADIANS(d)
Definition: macros.h:98
XYZEval::c
T c
Definition: types.h:384
PSTR
#define PSTR(str)
Definition: pgmspace.h:31
current_position
xyze_pos_t current_position
Definition: motion.cpp:102
Planner::apply_modifiers
static FORCE_INLINE void apply_modifiers(xyze_pos_t &pos, bool leveling=false)
Definition: planner.h:499
LROUND
#define LROUND(x)
Definition: macros.h:285
HAS_LEVELING
#define HAS_LEVELING
Definition: Conditionals_post.h:1408
Backlash::add_correction_steps
void add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const uint8_t dm, block_t *const block)
BLOCK_BUFFER_SIZE
#define BLOCK_BUFFER_SIZE
Definition: Configuration_A3ides_2209_MINI_adv.h:1167
apply_rotation_xyz
void apply_rotation_xyz(const matrix_3x3 &matrix, float &_x, float &_y, float &_z)
Definition: vector_3.cpp:88
Planner::init
void init()
Definition: planner.cpp:234
XYZEval::b
T b
Definition: types.h:384
block_t::entry_speed_sqr
float entry_speed_sqr
Definition: planner.h:100
block_t::acceleration_rate
uint32_t acceleration_rate
Definition: planner.h:133
filwidth
FilamentWidthSensor filwidth
ENABLE_STEPPER_DRIVER_INTERRUPT
#define ENABLE_STEPPER_DRIVER_INTERRUPT()
Definition: HAL.h:141
LOOP_XYZ
#define LOOP_XYZ(VAR)
Definition: types.h:60
planner_settings_t::travel_acceleration
float travel_acceleration
Definition: planner.h:183
planner_settings_t::axis_steps_per_mm
float axis_steps_per_mm[XYZE_N]
Definition: planner.h:181
DISABLED
#define DISABLED(V...)
Definition: macros.h:178
block_t::millimeters
float millimeters
Definition: planner.h:100
CEIL
#define CEIL(x)
Definition: macros.h:283
HAS_POSITION_FLOAT
#define HAS_POSITION_FLOAT
Definition: planner.h:174
block_t::nominal_speed_sqr
float nominal_speed_sqr
Definition: planner.h:100
X_HEAD
Definition: types.h:41
Mixer::refresh_collector
static void refresh_collector(const float proportion=1.0, const uint8_t t=selected_vtool, float(&c)[MIXING_STEPPERS]=collector)
planner_settings_t::acceleration
float acceleration
Definition: planner.h:183
Stepper::triggered_position
static int32_t triggered_position(const AxisEnum axis)
Definition: stepper.cpp:2258
XYZEval::x
T x
Definition: types.h:383
baricuda_valve_pressure
uint8_t baricuda_valve_pressure
planner_settings_t::max_acceleration_mm_per_s2
uint32_t max_acceleration_mm_per_s2[XYZE_N]
Definition: planner.h:179
Planner::reset_acceleration_rates
static void reset_acceleration_rates()
Definition: planner.cpp:2788
powerManager
Power powerManager
HEATER_1_PIN
#define HEATER_1_PIN
Definition: pins_RAMPS_LINUX.h:196
Planner::buffer_line
static bool buffer_line(const float &rx, const float &ry, const float &rz, const float &e, const feedRate_t &fr_mm_s, const uint8_t extruder, const float millimeters=0.0)
Definition: planner.cpp:2663
ACCEL_IDX
#define ACCEL_IDX
enable_X
#define enable_X()
Definition: Marlin.h:76
XYval
Definition: types.h:99
createSpeedLookupTable.a
list a
Definition: createSpeedLookupTable.py:29
Stepper::quick_stop
static FORCE_INLINE void quick_stop()
Definition: stepper.h:375
enable_Y
#define enable_Y()
Definition: Marlin.h:103
disable_all_steppers
void disable_all_steppers()
Definition: Marlin.cpp:308
unified_bed_leveling::get_z_correction
static float get_z_correction(const float &rx0, const float &ry0)
Definition: ubl.h:238
XYZEval::set
FI void set(const T px)
Definition: types.h:391
SQRT
#define SQRT(x)
Definition: macros.h:281
XYval::x
T x
Definition: types.h:185
block_t::max_entry_speed_sqr
float max_entry_speed_sqr
Definition: planner.h:100
MIXER_STEPPER_LOOP
#define MIXER_STEPPER_LOOP(VAR)
Definition: mixing.h:68
BLOCK_DELAY_FOR_1ST_MOVE
#define BLOCK_DELAY_FOR_1ST_MOVE
Definition: planner.cpp:109
Y_HEAD
Definition: types.h:41
enable_E1
#define enable_E1()
Definition: Marlin.h:268
uint8_t
const uint8_t[]
Definition: 404_html.c:3
DEFAULT_MAX_FEEDRATE
#define DEFAULT_MAX_FEEDRATE
Definition: Configuration_A3ides_2209_MINI.h:687
bilinear_z_offset
float bilinear_z_offset(const xy_pos_t &raw)
block_t::direction_bits
uint8_t direction_bits
Definition: planner.h:136
DISABLE_STEPPER_DRIVER_INTERRUPT
#define DISABLE_STEPPER_DRIVER_INTERRUPT()
Definition: HAL.h:142
HYPOT
#define HYPOT(x, y)
Definition: macros.h:287
XYZval::y
T y
Definition: types.h:286
Planner::steps_to_mm
static float steps_to_mm[XYZE_N]
Definition: planner.h:254
Planner::cleaning_buffer_counter
static uint16_t cleaning_buffer_counter
Definition: planner.h:231
inverse_kinematics
void inverse_kinematics(const xyz_pos_t &raw)
Y_AXIS
Definition: types.h:38
disable_X
#define disable_X()
Definition: Marlin.h:77
block_t::step_event_count
uint32_t step_event_count
Definition: planner.h:110
enable_E4
#define enable_E4()
Definition: Marlin.h:292
ubl
unified_bed_leveling ubl
MSG_ERR_LONG_EXTRUDE_STOP
#define MSG_ERR_LONG_EXTRUDE_STOP
Definition: language.h:243
limit_and_warn
void limit_and_warn(float &val, const uint8_t axis, PGM_P const setting_name, const xyze_float_t &max_limit)
Definition: planner.cpp:2812
BLOCK_FLAG_SYNC_POSITION
Definition: planner.h:83
Planner::_populate_block
static bool _populate_block(block_t *const block, bool split_move, const xyze_long_t &target, const xyze_pos_t &target_float, feedRate_t fr_mm_s, const uint8_t extruder, const float &millimeters=0.0)
Definition: planner.cpp:1641
Planner::moves_free
static FORCE_INLINE uint8_t moves_free()
Definition: planner.h:555
Z_AXIS
Definition: types.h:39
EXTRUDERS
#define EXTRUDERS
Definition: Configuration_A3ides_2209_MINI.h:148
DEBUGGING
#define DEBUGGING(F)
Definition: serial.h:47
FAN_COUNT
#define FAN_COUNT
Definition: Conditionals_post.h:1292
A_AXIS
Definition: types.h:37
block_t::acceleration
float acceleration
Definition: planner.h:100
MINIMUM_PLANNER_SPEED
#define MINIMUM_PLANNER_SPEED
Definition: Configuration_A3ides_2209_MINI_adv.h:545
Planner::fade_scaling_factor_for_z
static FORCE_INLINE float fade_scaling_factor_for_z(const float &)
Definition: planner.h:445
BLOCK_FLAG_NOMINAL_LENGTH
Definition: planner.h:81
baricuda_e_to_p_pressure
uint8_t baricuda_e_to_p_pressure
Planner::nonbusy_movesplanned
static FORCE_INLINE uint8_t nonbusy_movesplanned()
Definition: planner.h:546
block_t::nominal_rate
uint32_t nominal_rate
Definition: planner.h:147
TEST
#define TEST(n, b)
Definition: macros.h:81
XYval::y
T y
Definition: types.h:185
Stepper::position
static int32_t position(const AxisEnum axis)
Definition: stepper.cpp:2214
XYZval< float >
GCodeQueue::inject_P
static void inject_P(PGM_P const pgcode)
Definition: queue.cpp:206
E_AXIS
Definition: types.h:40
B_AXIS
Definition: types.h:38
SBI
#define SBI(A, B)
Definition: macros.h:85
Planner::settings
static planner_settings_t settings
Definition: planner.h:251
SERIAL_ECHOLNPGM
#define SERIAL_ECHOLNPGM(S)
Definition: serial.h:174
Planner::set_position_mm
static void set_position_mm(const float &rx, const float &ry, const float &rz, const float &e)
Definition: planner.cpp:2741
BLOCK_FLAG_RECALCULATE
Definition: planner.h:80
Planner::apply_leveling
static void apply_leveling(xyz_pos_t &raw)
Definition: planner.cpp:1381
createSpeedLookupTable.b
list b
Definition: createSpeedLookupTable.py:30
analogWrite
void analogWrite(uint32_t ulPin, uint32_t ulValue)
Definition: wiring_analog.c:12
LOOP_XYZE
#define LOOP_XYZE(VAR)
Definition: types.h:61
Z_HEAD
Definition: types.h:41
idle
void idle()
Definition: Marlin.cpp:629
READ
#define READ(IO)
Definition: fastio.h:95
CORE_IS_XZ
#define CORE_IS_XZ
Definition: Conditionals_post.h:102
planner_settings_t::min_travel_feedrate_mm_s
feedRate_t min_travel_feedrate_mm_s
Definition: planner.h:186
Planner::unapply_leveling
static void unapply_leveling(xyz_pos_t &raw)
Definition: planner.cpp:1415
block_t
Definition: planner.h:95
thermalManager
Temperature thermalManager
Definition: temperature.cpp:89
UNUSED
#define UNUSED(X)
Definition: stm32f4xx_hal_def.h:74
XYZEval::reset
FI void reset()
Definition: types.h:387
Planner::block_buffer_planned
static volatile uint8_t block_buffer_planned
Definition: planner.h:227
block_t::extruder
static constexpr uint8_t extruder
Definition: planner.h:115
millis_t
uint32_t millis_t
Definition: millis_t.h:26
LIMIT_ACCEL_FLOAT
#define LIMIT_ACCEL_FLOAT(AXIS, INDX)
CORE_IS_YZ
#define CORE_IS_YZ
Definition: Conditionals_post.h:103
Mixer::get_current_vtool
static FORCE_INLINE uint8_t get_current_vtool()
Definition: mixing.h:111
Planner::delay_before_delivering
static uint8_t delay_before_delivering
Definition: planner.h:232
active_extruder
constexpr uint8_t active_extruder
Definition: motion.h:107
Power::power_on
static void power_on()
Stepper::set_position
static void set_position(const int32_t &a, const int32_t &b, const int32_t &c, const int32_t &e)
Definition: stepper.h:437
planner_settings_t::min_segment_time_us
uint32_t min_segment_time_us
Definition: planner.h:179
ENABLED
#define ENABLED(V...)
Definition: macros.h:177
Planner::has_blocks_queued
static FORCE_INLINE bool has_blocks_queued()
Definition: planner.h:762
XYZEval::y
T y
Definition: types.h:383
SpindleLaser::refresh
static void refresh()
Definition: spindle_laser.h:56
STEPPER_TIMER_RATE
#define STEPPER_TIMER_RATE
Definition: HAL.h:133
EXTRUDE_MAXLENGTH
#define EXTRUDE_MAXLENGTH
Definition: Configuration_A3ides_2209_MINI.h:524