Refactor and optimize Stepper/Planner
Better encapsulation and considerably reduce stepper jitter
This commit is contained in:
@@ -100,13 +100,18 @@ Planner planner;
|
||||
* A ring buffer of moves described in steps
|
||||
*/
|
||||
block_t Planner::block_buffer[BLOCK_BUFFER_SIZE];
|
||||
volatile uint8_t Planner::block_buffer_head, // Index of the next block to be pushed
|
||||
Planner::block_buffer_tail;
|
||||
volatile uint8_t Planner::block_buffer_head, // Index of the next block to be pushed
|
||||
Planner::block_buffer_tail; // Index of the busy block, if any
|
||||
uint16_t Planner::cleaning_buffer_counter; // A counter to disable queuing of blocks
|
||||
|
||||
float Planner::max_feedrate_mm_s[XYZE_N], // Max speeds in mm per second
|
||||
float Planner::max_feedrate_mm_s[XYZE_N], // Max speeds in mm per second
|
||||
Planner::axis_steps_per_mm[XYZE_N],
|
||||
Planner::steps_to_mm[XYZE_N];
|
||||
|
||||
#if ENABLED(ABORT_ON_ENDSTOP_HIT_FEATURE_ENABLED)
|
||||
bool Planner::abort_on_endstop_hit = false;
|
||||
#endif
|
||||
|
||||
#if ENABLED(DISTINCT_E_FACTORS)
|
||||
uint8_t Planner::last_extruder = 0; // Respond to extruder change
|
||||
#endif
|
||||
@@ -175,7 +180,7 @@ int32_t Planner::position[NUM_AXIS] = { 0 };
|
||||
uint32_t Planner::cutoff_long;
|
||||
|
||||
float Planner::previous_speed[NUM_AXIS],
|
||||
Planner::previous_nominal_speed;
|
||||
Planner::previous_nominal_speed_sqr;
|
||||
|
||||
#if ENABLED(DISABLE_INACTIVE_EXTRUDER)
|
||||
uint8_t Planner::g_uc_extruder_last_move[EXTRUDERS] = { 0 };
|
||||
@@ -212,7 +217,7 @@ void Planner::init() {
|
||||
ZERO(position_float);
|
||||
#endif
|
||||
ZERO(previous_speed);
|
||||
previous_nominal_speed = 0.0;
|
||||
previous_nominal_speed_sqr = 0.0;
|
||||
#if ABL_PLANAR
|
||||
bed_level_matrix.set_to_identity();
|
||||
#endif
|
||||
@@ -363,7 +368,7 @@ void Planner::init() {
|
||||
//
|
||||
static uint32_t get_period_inverse(uint32_t d) {
|
||||
|
||||
static const uint8_t inv_tab[256] PROGMEM = {
|
||||
static const uint8_t inv_tab[256] PROGMEM = {
|
||||
255,253,252,250,248,246,244,242,240,238,236,234,233,231,229,227,
|
||||
225,224,222,220,218,217,215,213,212,210,208,207,205,203,202,200,
|
||||
199,197,195,194,192,191,189,188,186,185,183,182,180,179,178,176,
|
||||
@@ -727,12 +732,9 @@ void Planner::init() {
|
||||
}
|
||||
#else
|
||||
// All the other 32 CPUs can easily perform the inverse using hardware division,
|
||||
// so we don´t need to reduce precision or to use assembly language at all.
|
||||
|
||||
// so we don't need to reduce precision or to use assembly language at all.
|
||||
// This routine, for all the other archs, returns 0x100000000 / d ~= 0xFFFFFFFF / d
|
||||
static FORCE_INLINE uint32_t get_period_inverse(uint32_t d) {
|
||||
return 0xFFFFFFFF / d;
|
||||
}
|
||||
static FORCE_INLINE uint32_t get_period_inverse(const uint32_t d) { return 0xFFFFFFFF / d; }
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -743,6 +745,7 @@ void Planner::init() {
|
||||
* by the provided factors.
|
||||
*/
|
||||
void Planner::calculate_trapezoid_for_block(block_t* const block, const float &entry_factor, const float &exit_factor) {
|
||||
|
||||
uint32_t initial_rate = CEIL(block->nominal_rate * entry_factor),
|
||||
final_rate = CEIL(block->nominal_rate * exit_factor); // (steps per second)
|
||||
|
||||
@@ -757,19 +760,18 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
|
||||
const int32_t accel = block->acceleration_steps_per_s2;
|
||||
|
||||
// Steps required for acceleration, deceleration to/from nominal rate
|
||||
int32_t accelerate_steps = CEIL(estimate_acceleration_distance(initial_rate, block->nominal_rate, accel)),
|
||||
decelerate_steps = FLOOR(estimate_acceleration_distance(block->nominal_rate, final_rate, -accel)),
|
||||
uint32_t accelerate_steps = CEIL(estimate_acceleration_distance(initial_rate, block->nominal_rate, accel)),
|
||||
decelerate_steps = FLOOR(estimate_acceleration_distance(block->nominal_rate, final_rate, -accel));
|
||||
// Steps between acceleration and deceleration, if any
|
||||
plateau_steps = block->step_event_count - accelerate_steps - decelerate_steps;
|
||||
int32_t plateau_steps = block->step_event_count - accelerate_steps - decelerate_steps;
|
||||
|
||||
// Does accelerate_steps + decelerate_steps exceed step_event_count?
|
||||
// Then we can't possibly reach the nominal rate, there will be no cruising.
|
||||
// Use intersection_distance() to calculate accel / braking time in order to
|
||||
// reach the final_rate exactly at the end of this block.
|
||||
if (plateau_steps < 0) {
|
||||
accelerate_steps = CEIL(intersection_distance(initial_rate, final_rate, accel, block->step_event_count));
|
||||
NOLESS(accelerate_steps, 0); // Check limits due to numerical round-off
|
||||
accelerate_steps = min((uint32_t)accelerate_steps, block->step_event_count);//(We can cast here to unsigned, because the above line ensures that we are above zero)
|
||||
const float accelerate_steps_float = CEIL(intersection_distance(initial_rate, final_rate, accel, block->step_event_count));
|
||||
accelerate_steps = MIN(uint32_t(MAX(accelerate_steps_float, 0)), block->step_event_count);
|
||||
plateau_steps = 0;
|
||||
|
||||
#if ENABLED(BEZIER_JERK_CONTROL)
|
||||
@@ -796,7 +798,10 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
|
||||
|
||||
#endif
|
||||
|
||||
CRITICAL_SECTION_START; // Fill variables used by the stepper in a critical section
|
||||
// Fill variables used by the stepper in a critical section
|
||||
const bool was_enabled = STEPPER_ISR_ENABLED();
|
||||
if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
|
||||
if (!TEST(block->flag, BLOCK_BIT_BUSY)) { // Don't update variables if block is busy.
|
||||
block->accelerate_until = accelerate_steps;
|
||||
block->decelerate_after = accelerate_steps + plateau_steps;
|
||||
@@ -810,32 +815,35 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
|
||||
#endif
|
||||
block->final_rate = final_rate;
|
||||
}
|
||||
CRITICAL_SECTION_END;
|
||||
if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
}
|
||||
|
||||
// "Junction jerk" in this context is the immediate change in speed at the junction of two blocks.
|
||||
// This method will calculate the junction jerk as the euclidean distance between the nominal
|
||||
// velocities of the respective blocks.
|
||||
//inline float junction_jerk(block_t *before, block_t *after) {
|
||||
// return SQRT(
|
||||
// POW((before->speed_x-after->speed_x), 2)+POW((before->speed_y-after->speed_y), 2));
|
||||
//}
|
||||
|
||||
// The kernel called by recalculate() when scanning the plan from last to first entry.
|
||||
void Planner::reverse_pass_kernel(block_t* const current, const block_t* const next) {
|
||||
if (current && next) {
|
||||
// If entry speed is already at the maximum entry speed, no need to recheck. Block is cruising.
|
||||
// If not, block in state of acceleration or deceleration. Reset entry speed to maximum and
|
||||
// check for maximum allowable speed reductions to ensure maximum possible planned speed.
|
||||
const float max_entry_speed = current->max_entry_speed;
|
||||
if (current->entry_speed != max_entry_speed || TEST(next->flag, BLOCK_BIT_RECALCULATE)) {
|
||||
// If nominal length true, max junction speed is guaranteed to be reached. Only compute
|
||||
// for max allowable speed if block is decelerating and nominal length is false.
|
||||
const float new_entry_speed = (TEST(current->flag, BLOCK_BIT_NOMINAL_LENGTH) || max_entry_speed <= next->entry_speed)
|
||||
? max_entry_speed
|
||||
: MIN(max_entry_speed, max_allowable_speed(-current->acceleration, next->entry_speed, current->millimeters));
|
||||
if (new_entry_speed != current->entry_speed) {
|
||||
current->entry_speed = new_entry_speed;
|
||||
void Planner::reverse_pass_kernel(block_t* const current, const block_t * const next) {
|
||||
if (current) {
|
||||
// If entry speed is already at the maximum entry speed, and there was no change of speed
|
||||
// in the next block, there is no need to recheck. Block is cruising and there is no need to
|
||||
// compute anything for this block,
|
||||
// If not, block entry speed needs to be recalculated to ensure maximum possible planned speed.
|
||||
const float max_entry_speed_sqr = current->max_entry_speed_sqr;
|
||||
|
||||
// Compute maximum entry speed decelerating over the current block from its exit speed.
|
||||
// If not at the maximum entry speed, or the previous block entry speed changed
|
||||
if (current->entry_speed_sqr != max_entry_speed_sqr || (next && TEST(next->flag, BLOCK_BIT_RECALCULATE))) {
|
||||
|
||||
// If nominal length true, max junction speed is guaranteed to be reached.
|
||||
// If a block can de/ac-celerate from nominal speed to zero within the length of the block, then
|
||||
// the current block and next block junction speeds are guaranteed to always be at their maximum
|
||||
// junction speeds in deceleration and acceleration, respectively. This is due to how the current
|
||||
// block nominal speed limits both the current and next maximum junction speeds. Hence, in both
|
||||
// the reverse and forward planners, the corresponding block junction speed will always be at the
|
||||
// the maximum junction speed and may always be ignored for any speed reduction checks.
|
||||
|
||||
const float new_entry_speed_sqr = TEST(current->flag, BLOCK_BIT_NOMINAL_LENGTH)
|
||||
? max_entry_speed_sqr
|
||||
: MIN(max_entry_speed_sqr, max_allowable_speed_sqr(-current->acceleration, next ? next->entry_speed_sqr : sq(MINIMUM_PLANNER_SPEED), current->millimeters));
|
||||
if (current->entry_speed_sqr != new_entry_speed_sqr) {
|
||||
current->entry_speed_sqr = new_entry_speed_sqr;
|
||||
SBI(current->flag, BLOCK_BIT_RECALCULATE);
|
||||
}
|
||||
}
|
||||
@@ -850,44 +858,37 @@ void Planner::reverse_pass() {
|
||||
if (movesplanned() > 2) {
|
||||
const uint8_t endnr = next_block_index(block_buffer_tail); // tail is running. tail+1 shouldn't be altered because it's connected to the running block.
|
||||
uint8_t blocknr = prev_block_index(block_buffer_head);
|
||||
block_t* current = &block_buffer[blocknr];
|
||||
|
||||
// Last/newest block in buffer:
|
||||
const float max_entry_speed = current->max_entry_speed;
|
||||
if (current->entry_speed != max_entry_speed) {
|
||||
// If nominal length true, max junction speed is guaranteed to be reached. Only compute
|
||||
// for max allowable speed if block is decelerating and nominal length is false.
|
||||
const float new_entry_speed = TEST(current->flag, BLOCK_BIT_NOMINAL_LENGTH)
|
||||
? max_entry_speed
|
||||
: MIN(max_entry_speed, max_allowable_speed(-current->acceleration, MINIMUM_PLANNER_SPEED, current->millimeters));
|
||||
if (current->entry_speed != new_entry_speed) {
|
||||
current->entry_speed = new_entry_speed;
|
||||
SBI(current->flag, BLOCK_BIT_RECALCULATE);
|
||||
}
|
||||
}
|
||||
|
||||
do {
|
||||
const block_t * const next = current;
|
||||
blocknr = prev_block_index(blocknr);
|
||||
// Perform the reverse pass
|
||||
block_t *current, *next = NULL;
|
||||
while (blocknr != endnr) {
|
||||
// Perform the reverse pass - Only consider non sync blocks
|
||||
current = &block_buffer[blocknr];
|
||||
reverse_pass_kernel(current, next);
|
||||
} while (blocknr != endnr);
|
||||
if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) {
|
||||
reverse_pass_kernel(current, next);
|
||||
next = current;
|
||||
}
|
||||
// Advance to the next
|
||||
blocknr = prev_block_index(blocknr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The kernel called by recalculate() when scanning the plan from first to last entry.
|
||||
void Planner::forward_pass_kernel(const block_t* const previous, block_t* const current) {
|
||||
void Planner::forward_pass_kernel(const block_t * const previous, block_t* const current) {
|
||||
if (previous) {
|
||||
// If the previous block is an acceleration block, too short to complete the full speed
|
||||
// change, adjust the entry speed accordingly. Entry speeds have already been reset,
|
||||
// maximized, and reverse-planned. If nominal length is set, max junction speed is
|
||||
// guaranteed to be reached. No need to recheck.
|
||||
if (!TEST(previous->flag, BLOCK_BIT_NOMINAL_LENGTH)) {
|
||||
if (previous->entry_speed < current->entry_speed) {
|
||||
const float new_entry_speed = MIN(current->entry_speed, max_allowable_speed(-previous->acceleration, previous->entry_speed, previous->millimeters));
|
||||
// Check for junction speed change
|
||||
if (current->entry_speed != new_entry_speed) {
|
||||
current->entry_speed = new_entry_speed;
|
||||
if (previous->entry_speed_sqr < current->entry_speed_sqr) {
|
||||
// Compute the maximum allowable speed
|
||||
const float new_entry_speed_sqr = max_allowable_speed_sqr(-previous->acceleration, previous->entry_speed_sqr, previous->millimeters);
|
||||
// If true, current block is full-acceleration
|
||||
if (current->entry_speed_sqr > new_entry_speed_sqr) {
|
||||
// Always <= max_entry_speed_sqr. Backward pass sets this.
|
||||
current->entry_speed_sqr = new_entry_speed_sqr;
|
||||
SBI(current->flag, BLOCK_BIT_RECALCULATE);
|
||||
}
|
||||
}
|
||||
@@ -900,15 +901,21 @@ void Planner::forward_pass_kernel(const block_t* const previous, block_t* const
|
||||
* Once in reverse and once forward. This implements the forward pass.
|
||||
*/
|
||||
void Planner::forward_pass() {
|
||||
block_t* block[3] = { NULL, NULL, NULL };
|
||||
const uint8_t endnr = block_buffer_head;
|
||||
uint8_t blocknr = block_buffer_tail;
|
||||
|
||||
for (uint8_t b = block_buffer_tail; b != block_buffer_head; b = next_block_index(b)) {
|
||||
block[0] = block[1];
|
||||
block[1] = block[2];
|
||||
block[2] = &block_buffer[b];
|
||||
forward_pass_kernel(block[0], block[1]);
|
||||
// Perform the forward pass
|
||||
block_t *current, *previous = NULL;
|
||||
while (blocknr != endnr) {
|
||||
// Perform the forward pass - Only consider non-sync blocks
|
||||
current = &block_buffer[blocknr];
|
||||
if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) {
|
||||
forward_pass_kernel(previous, current);
|
||||
previous = current;
|
||||
}
|
||||
// Advance to the previous
|
||||
blocknr = next_block_index(blocknr);
|
||||
}
|
||||
forward_pass_kernel(block[1], block[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -917,38 +924,72 @@ void Planner::forward_pass() {
|
||||
* recalculate() after updating the blocks.
|
||||
*/
|
||||
void Planner::recalculate_trapezoids() {
|
||||
int8_t block_index = block_buffer_tail;
|
||||
block_t *current, *next = NULL;
|
||||
uint8_t block_index = block_buffer_tail;
|
||||
|
||||
// As there could be a sync block in the head of the queue, and the next loop must not
|
||||
// recalculate the head block (as it needs to be specially handled), scan backwards until
|
||||
// we find the first non SYNC block
|
||||
uint8_t head_block_index = block_buffer_head;
|
||||
while (head_block_index != block_index) {
|
||||
|
||||
// Go back (head always point to the first free block)
|
||||
uint8_t prev_index = prev_block_index(head_block_index);
|
||||
|
||||
// Get the pointer to the block
|
||||
block_t *prev = &block_buffer[prev_index];
|
||||
|
||||
// If not dealing with a sync block, we are done. The last block is not a SYNC block
|
||||
if (!TEST(prev->flag, BLOCK_BIT_SYNC_POSITION)) break;
|
||||
|
||||
// Examine the previous block. This and all following are SYNC blocks
|
||||
head_block_index = prev_index;
|
||||
};
|
||||
|
||||
// Go from the tail (currently executed block) to the first block, without including it)
|
||||
block_t *current = NULL, *next = NULL;
|
||||
float current_entry_speed = 0.0, next_entry_speed = 0.0;
|
||||
while (block_index != head_block_index) {
|
||||
|
||||
while (block_index != block_buffer_head) {
|
||||
current = next;
|
||||
next = &block_buffer[block_index];
|
||||
if (current) {
|
||||
// Recalculate if current block entry or exit junction speed has changed.
|
||||
if (TEST(current->flag, BLOCK_BIT_RECALCULATE) || TEST(next->flag, BLOCK_BIT_RECALCULATE)) {
|
||||
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
|
||||
const float nomr = 1.0 / current->nominal_speed;
|
||||
calculate_trapezoid_for_block(current, current->entry_speed * nomr, next->entry_speed * nomr);
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
if (current->use_advance_lead) {
|
||||
const float comp = current->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS];
|
||||
current->max_adv_steps = current->nominal_speed * comp;
|
||||
current->final_adv_steps = next->entry_speed * comp;
|
||||
}
|
||||
#endif
|
||||
CBI(current->flag, BLOCK_BIT_RECALCULATE); // Reset current only to ensure next trapezoid is computed
|
||||
|
||||
// Skip sync blocks
|
||||
if (!TEST(next->flag, BLOCK_BIT_SYNC_POSITION)) {
|
||||
next_entry_speed = SQRT(next->entry_speed_sqr);
|
||||
|
||||
if (current) {
|
||||
// Recalculate if current block entry or exit junction speed has changed.
|
||||
if (TEST(current->flag, BLOCK_BIT_RECALCULATE) || TEST(next->flag, BLOCK_BIT_RECALCULATE)) {
|
||||
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
|
||||
const float current_nominal_speed = SQRT(current->nominal_speed_sqr),
|
||||
nomr = 1.0 / current_nominal_speed;
|
||||
calculate_trapezoid_for_block(current, current_entry_speed * nomr, next_entry_speed * nomr);
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
if (current->use_advance_lead) {
|
||||
const float comp = current->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS];
|
||||
current->max_adv_steps = current_nominal_speed * comp;
|
||||
current->final_adv_steps = next_entry_speed * comp;
|
||||
}
|
||||
#endif
|
||||
CBI(current->flag, BLOCK_BIT_RECALCULATE); // Reset current only to ensure next trapezoid is computed
|
||||
}
|
||||
}
|
||||
|
||||
current = next;
|
||||
current_entry_speed = next_entry_speed;
|
||||
}
|
||||
|
||||
block_index = next_block_index(block_index);
|
||||
}
|
||||
|
||||
// Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated.
|
||||
if (next) {
|
||||
const float nomr = 1.0 / next->nominal_speed;
|
||||
calculate_trapezoid_for_block(next, next->entry_speed * nomr, (MINIMUM_PLANNER_SPEED) * nomr);
|
||||
const float next_nominal_speed = SQRT(next->nominal_speed_sqr),
|
||||
nomr = 1.0 / next_nominal_speed;
|
||||
calculate_trapezoid_for_block(next, next_entry_speed * nomr, (MINIMUM_PLANNER_SPEED) * nomr);
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
if (next->use_advance_lead) {
|
||||
const float comp = next->e_D_ratio * extruder_advance_K * axis_steps_per_mm[E_AXIS];
|
||||
next->max_adv_steps = next->nominal_speed * comp;
|
||||
next->max_adv_steps = next_nominal_speed * comp;
|
||||
next->final_adv_steps = (MINIMUM_PLANNER_SPEED) * comp;
|
||||
}
|
||||
#endif
|
||||
@@ -998,7 +1039,7 @@ void Planner::recalculate() {
|
||||
for (uint8_t b = block_buffer_tail; b != block_buffer_head; b = next_block_index(b)) {
|
||||
block_t* block = &block_buffer[b];
|
||||
if (block->steps[X_AXIS] || block->steps[Y_AXIS] || block->steps[Z_AXIS]) {
|
||||
float se = (float)block->steps[E_AXIS] / block->step_event_count * block->nominal_speed; // mm/sec;
|
||||
const float se = (float)block->steps[E_AXIS] / block->step_event_count * SQRT(block->nominal_speed_sqr); // mm/sec;
|
||||
NOLESS(high, se);
|
||||
}
|
||||
}
|
||||
@@ -1299,6 +1340,59 @@ void Planner::check_axes_activity() {
|
||||
|
||||
#endif // PLANNER_LEVELING
|
||||
|
||||
void Planner::quick_stop() {
|
||||
// Remove all the queued blocks. Note that this function is NOT
|
||||
// called from the Stepper ISR, so we must consider tail as readonly!
|
||||
// that is why we set head to tail!
|
||||
block_buffer_head = block_buffer_tail;
|
||||
|
||||
#if ENABLED(ULTRA_LCD)
|
||||
// Clear the accumulated runtime
|
||||
clear_block_buffer_runtime();
|
||||
#endif
|
||||
|
||||
// Make sure to drop any attempt of queuing moves for at least 1 second
|
||||
cleaning_buffer_counter = 1000;
|
||||
|
||||
// And stop the stepper ISR
|
||||
stepper.quick_stop();
|
||||
}
|
||||
|
||||
void Planner::endstop_triggered(const AxisEnum axis) {
|
||||
|
||||
/*NB: This will be called via endstops.update()
|
||||
and endstops.update() can be called from the temperature
|
||||
ISR. So Stepper interrupts are enabled */
|
||||
|
||||
// Disable stepper ISR
|
||||
bool stepper_isr_enabled = STEPPER_ISR_ENABLED();
|
||||
DISABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
|
||||
// Record stepper position
|
||||
stepper.endstop_triggered(axis);
|
||||
|
||||
// Discard the active block that led to the trigger
|
||||
discard_current_block();
|
||||
|
||||
// Discard the CONTINUED block, if any. Note the planner can only queue 1 continued
|
||||
// block after a previous non continued block, as the condition to queue them
|
||||
// is that there are no queued blocks at the time a new block is queued.
|
||||
const bool discard = has_blocks_queued() && TEST(block_buffer[block_buffer_tail].flag, BLOCK_BIT_CONTINUED);
|
||||
if (discard) discard_current_block();
|
||||
|
||||
// Reenable stepper ISR if it was enabled
|
||||
if (stepper_isr_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
}
|
||||
|
||||
float Planner::triggered_position_mm(const AxisEnum axis) {
|
||||
return stepper.triggered_position(axis) * steps_to_mm[axis];
|
||||
}
|
||||
|
||||
void Planner::finish_and_disable() {
|
||||
while (has_blocks_queued() || cleaning_buffer_counter) idle();
|
||||
disable_all_steppers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an axis position according to stepper position(s)
|
||||
* For CORE machines apply translation from ABC to XYZ.
|
||||
@@ -1311,7 +1405,7 @@ float Planner::get_axis_position_mm(const AxisEnum axis) {
|
||||
|
||||
// Protect the access to the position.
|
||||
const bool was_enabled = STEPPER_ISR_ENABLED();
|
||||
DISABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
|
||||
// ((a1+a2)+(a1-a2))/2 -> (a1+a2+a1-a2)/2 -> (a1+a1)/2 -> a1
|
||||
// ((a1+a2)-(a1-a2))/2 -> (a1+a2-a1+a2)/2 -> (a2+a2)/2 -> a2
|
||||
@@ -1333,18 +1427,69 @@ float Planner::get_axis_position_mm(const AxisEnum axis) {
|
||||
/**
|
||||
* Block until all buffered steps are executed / cleaned
|
||||
*/
|
||||
void Planner::synchronize() { while (has_blocks_queued() || stepper.cleaning_buffer_counter) idle(); }
|
||||
void Planner::synchronize() { while (has_blocks_queued() || cleaning_buffer_counter) idle(); }
|
||||
|
||||
/**
|
||||
* Planner::_buffer_steps
|
||||
*
|
||||
* Add a new linear movement to the buffer (in terms of steps).
|
||||
* Add a new linear movement to the planner queue (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 properly queued, false otherwise
|
||||
*/
|
||||
void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
bool Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
#if HAS_POSITION_FLOAT
|
||||
, const float (&target_float)[XYZE]
|
||||
#endif
|
||||
, float fr_mm_s, const uint8_t extruder, const float &millimeters
|
||||
) {
|
||||
|
||||
// If we are cleaning, do not accept queuing of movements
|
||||
if (cleaning_buffer_counter) return false;
|
||||
|
||||
// Wait for the next available block
|
||||
uint8_t next_buffer_head;
|
||||
block_t * const block = get_next_free_block(next_buffer_head);
|
||||
|
||||
// Fill the block with the specified movement
|
||||
if (!_populate_block(block, false, target
|
||||
#if HAS_POSITION_FLOAT
|
||||
, target_float
|
||||
#endif
|
||||
, fr_mm_s, extruder, millimeters
|
||||
)) {
|
||||
// Movement was not queued, probably because it was too short.
|
||||
// Simply accept that as movement queued and done
|
||||
return true;
|
||||
}
|
||||
|
||||
// Move buffer head
|
||||
block_buffer_head = next_buffer_head;
|
||||
|
||||
// Recalculate and optimize trapezoidal speed profiles
|
||||
recalculate();
|
||||
|
||||
// Movement successfully queued!
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
bool Planner::_populate_block(block_t * const block, bool split_move,
|
||||
const int32_t (&target)[XYZE]
|
||||
#if HAS_POSITION_FLOAT
|
||||
, const float (&target_float)[XYZE]
|
||||
#endif
|
||||
@@ -1358,7 +1503,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
int32_t de = target[E_AXIS] - position[E_AXIS];
|
||||
|
||||
/* <-- add a slash to enable
|
||||
SERIAL_ECHOPAIR(" _buffer_steps FR:", fr_mm_s);
|
||||
SERIAL_ECHOPAIR(" _populate_block FR:", fr_mm_s);
|
||||
SERIAL_ECHOPAIR(" A:", target[A_AXIS]);
|
||||
SERIAL_ECHOPAIR(" (", da);
|
||||
SERIAL_ECHOPAIR(" steps) B:", target[B_AXIS]);
|
||||
@@ -1427,10 +1572,6 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
const float esteps_float = de * e_factor[extruder];
|
||||
const int32_t esteps = ABS(esteps_float) + 0.5;
|
||||
|
||||
// Wait for the next available block
|
||||
uint8_t next_buffer_head;
|
||||
block_t * const block = get_next_free_block(next_buffer_head);
|
||||
|
||||
// Clear all flags, including the "busy" bit
|
||||
block->flag = 0x00;
|
||||
|
||||
@@ -1466,7 +1607,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
block->step_event_count = MAX4(block->steps[A_AXIS], block->steps[B_AXIS], block->steps[C_AXIS], esteps);
|
||||
|
||||
// Bail if this is a zero-length block
|
||||
if (block->step_event_count < MIN_STEPS_PER_SEGMENT) return;
|
||||
if (block->step_event_count < MIN_STEPS_PER_SEGMENT) return false;
|
||||
|
||||
// For a mixing extruder, get a magnified step_event_count for each
|
||||
#if ENABLED(MIXING_EXTRUDER)
|
||||
@@ -1706,12 +1847,16 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
#endif
|
||||
|
||||
#if ENABLED(ULTRA_LCD)
|
||||
CRITICAL_SECTION_START
|
||||
block_buffer_runtime_us += segment_time_us;
|
||||
CRITICAL_SECTION_END
|
||||
// Protect the access to the position.
|
||||
const bool was_enabled = STEPPER_ISR_ENABLED();
|
||||
if (was_enabled) DISABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
|
||||
block_buffer_runtime_us += segment_time_us;
|
||||
|
||||
if (was_enabled) ENABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
#endif
|
||||
|
||||
block->nominal_speed = block->millimeters * inverse_secs; // (mm/sec) Always > 0
|
||||
block->nominal_speed_sqr = sq(block->millimeters * inverse_secs); // (mm/sec)^2 Always > 0
|
||||
block->nominal_rate = CEIL(block->step_event_count * inverse_secs); // (step/sec) Always > 0
|
||||
|
||||
#if ENABLED(FILAMENT_WIDTH_SENSOR)
|
||||
@@ -1799,8 +1944,8 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
// Correct the speed
|
||||
if (speed_factor < 1.0) {
|
||||
LOOP_XYZE(i) current_speed[i] *= speed_factor;
|
||||
block->nominal_speed *= speed_factor;
|
||||
block->nominal_rate *= speed_factor;
|
||||
block->nominal_speed_sqr = block->nominal_speed_sqr * sq(speed_factor);
|
||||
}
|
||||
|
||||
// Compute and limit the acceleration rate for the trapezoid generator.
|
||||
@@ -1895,13 +2040,13 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
block->acceleration_steps_per_s2 = accel;
|
||||
block->acceleration = accel / steps_per_mm;
|
||||
#if DISABLED(BEZIER_JERK_CONTROL)
|
||||
block->acceleration_rate = (long)(accel * (4096.0 * 4096.0 / (HAL_STEPPER_TIMER_RATE)));
|
||||
block->acceleration_rate = (uint32_t)(accel * (4096.0 * 4096.0 / (HAL_STEPPER_TIMER_RATE)));
|
||||
#endif
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
if (block->use_advance_lead) {
|
||||
block->advance_speed = (HAL_STEPPER_TIMER_RATE) / (extruder_advance_K * block->e_D_ratio * block->acceleration * axis_steps_per_mm[E_AXIS_N]);
|
||||
#if ENABLED(LA_DEBUG)
|
||||
if (extruder_advance_K * block->e_D_ratio * block->acceleration * 2 < block->nominal_speed * block->e_D_ratio)
|
||||
if (extruder_advance_K * block->e_D_ratio * block->acceleration * 2 < SQRT(block->nominal_speed_sqr) * block->e_D_ratio)
|
||||
SERIAL_ECHOLNPGM("More than 2 steps per eISR loop executed.");
|
||||
if (block->advance_speed < 200)
|
||||
SERIAL_ECHOLNPGM("eISR running at > 10kHz.");
|
||||
@@ -1909,7 +2054,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
}
|
||||
#endif
|
||||
|
||||
float vmax_junction; // Initial limit on the segment entry velocity
|
||||
float vmax_junction_sqr; // Initial limit on the segment entry velocity (mm/s)^2
|
||||
|
||||
#if ENABLED(JUNCTION_DEVIATION)
|
||||
|
||||
@@ -1935,7 +2080,17 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
* changed dynamically during operation nor can the line move geometry. This must be kept in
|
||||
* memory in the event of a feedrate override changing the nominal speeds of blocks, which can
|
||||
* change the overall maximum entry speed conditions of all blocks.
|
||||
*/
|
||||
*
|
||||
* #######
|
||||
* https://github.com/MarlinFirmware/Marlin/issues/10341#issuecomment-388191754
|
||||
*
|
||||
* hoffbaked: on May 10 2018 tuned and improved the GRBL algorithm for Marlin:
|
||||
Okay! It seems to be working good. I somewhat arbitrarily cut it off at 1mm
|
||||
on then on anything with less sides than an octagon. With this, and the
|
||||
reverse pass actually recalculating things, a corner acceleration value
|
||||
of 1000 junction deviation of .05 are pretty reasonable. If the cycles
|
||||
can be spared, a better acos could be used. For all I know, it may be
|
||||
already calculated in a different place. */
|
||||
|
||||
// Unit vector of previous path line segment
|
||||
static float previous_unit_vec[
|
||||
@@ -1956,7 +2111,7 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
};
|
||||
|
||||
// Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles.
|
||||
if (moves_queued && !UNEAR_ZERO(previous_nominal_speed)) {
|
||||
if (moves_queued && !UNEAR_ZERO(previous_nominal_speed_sqr)) {
|
||||
// Compute cosine of angle between previous and current path. (prev_unit_vec is negative)
|
||||
// NOTE: Max junction velocity is computed without sin() or acos() by trig half angle identity.
|
||||
float junction_cos_theta = -previous_unit_vec[X_AXIS] * unit_vec[X_AXIS]
|
||||
@@ -1970,21 +2125,33 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
// NOTE: Computed without any expensive trig, sin() or acos(), by trig half angle identity of cos(theta).
|
||||
if (junction_cos_theta > 0.999999) {
|
||||
// For a 0 degree acute junction, just set minimum junction speed.
|
||||
vmax_junction = MINIMUM_PLANNER_SPEED;
|
||||
vmax_junction_sqr = sq(MINIMUM_PLANNER_SPEED);
|
||||
}
|
||||
else {
|
||||
junction_cos_theta = MAX(junction_cos_theta, -0.999999); // Check for numerical round-off to avoid divide by zero.
|
||||
NOLESS(junction_cos_theta, -0.999999); // Check for numerical round-off to avoid divide by zero.
|
||||
const float sin_theta_d2 = SQRT(0.5 * (1.0 - junction_cos_theta)); // Trig half angle identity. Always positive.
|
||||
|
||||
// TODO: Technically, the acceleration used in calculation needs to be limited by the minimum of the
|
||||
// two junctions. However, this shouldn't be a significant problem except in extreme circumstances.
|
||||
vmax_junction = SQRT((block->acceleration * JUNCTION_DEVIATION_FACTOR * sin_theta_d2) / (1.0 - sin_theta_d2));
|
||||
vmax_junction_sqr = (JUNCTION_ACCELERATION_FACTOR * JUNCTION_DEVIATION_FACTOR * sin_theta_d2) / (1.0 - sin_theta_d2);
|
||||
if (block->millimeters < 1.0) {
|
||||
|
||||
// Fast acos approximation, minus the error bar to be safe
|
||||
float junction_theta = (RADIANS(-40) * sq(junction_cos_theta) - RADIANS(50)) * junction_cos_theta + RADIANS(90) - 0.18;
|
||||
|
||||
// If angle is greater than 135 degrees (octagon), find speed for approximate arc
|
||||
if (junction_theta > RADIANS(135)) {
|
||||
const float limit_sqr = block->millimeters / (RADIANS(180) - junction_theta) * JUNCTION_ACCELERATION_FACTOR;
|
||||
NOMORE(vmax_junction_sqr, limit_sqr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vmax_junction = MIN3(vmax_junction, block->nominal_speed, previous_nominal_speed);
|
||||
// Get the lowest speed
|
||||
vmax_junction_sqr = MIN3(vmax_junction_sqr, block->nominal_speed_sqr, previous_nominal_speed_sqr);
|
||||
}
|
||||
else // Init entry speed to zero. Assume it starts from rest. Planner will correct this later.
|
||||
vmax_junction = 0.0;
|
||||
vmax_junction_sqr = 0.0;
|
||||
|
||||
COPY(previous_unit_vec, unit_vec);
|
||||
|
||||
@@ -2000,13 +2167,15 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
// Exit speed limited by a jerk to full halt of a previous last segment
|
||||
static float previous_safe_speed;
|
||||
|
||||
float safe_speed = block->nominal_speed;
|
||||
const float nominal_speed = SQRT(block->nominal_speed_sqr);
|
||||
float safe_speed = nominal_speed;
|
||||
|
||||
uint8_t limited = 0;
|
||||
LOOP_XYZE(i) {
|
||||
const float jerk = ABS(current_speed[i]), maxj = max_jerk[i];
|
||||
if (jerk > maxj) {
|
||||
if (limited) {
|
||||
const float mjerk = maxj * block->nominal_speed;
|
||||
const float mjerk = maxj * nominal_speed;
|
||||
if (jerk * safe_speed > mjerk) safe_speed = mjerk / jerk;
|
||||
}
|
||||
else {
|
||||
@@ -2016,19 +2185,21 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
}
|
||||
}
|
||||
|
||||
if (moves_queued && !UNEAR_ZERO(previous_nominal_speed)) {
|
||||
float vmax_junction;
|
||||
if (moves_queued && !UNEAR_ZERO(previous_nominal_speed_sqr)) {
|
||||
// Estimate a maximum velocity allowed at a joint of two successive segments.
|
||||
// If this maximum velocity allowed is lower than the minimum of the entry / exit safe velocities,
|
||||
// then the machine is not coasting anymore and the safe entry / exit velocities shall be used.
|
||||
|
||||
// The junction velocity will be shared between successive segments. Limit the junction velocity to their minimum.
|
||||
// Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
|
||||
vmax_junction = MIN(block->nominal_speed, previous_nominal_speed);
|
||||
|
||||
// Factor to multiply the previous / current nominal velocities to get componentwise limited velocities.
|
||||
float v_factor = 1;
|
||||
limited = 0;
|
||||
|
||||
// The junction velocity will be shared between successive segments. Limit the junction velocity to their minimum.
|
||||
// Pick the smaller of the nominal speeds. Higher speed shall not be achieved at the junction during coasting.
|
||||
const float previous_nominal_speed = SQRT(previous_nominal_speed_sqr);
|
||||
vmax_junction = MIN(nominal_speed, previous_nominal_speed);
|
||||
|
||||
// Now limit the jerk in all axes.
|
||||
const float smaller_speed_factor = vmax_junction / previous_nominal_speed;
|
||||
LOOP_XYZE(axis) {
|
||||
@@ -2063,16 +2234,19 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
vmax_junction = safe_speed;
|
||||
|
||||
previous_safe_speed = safe_speed;
|
||||
vmax_junction_sqr = sq(vmax_junction);
|
||||
|
||||
#endif // Classic Jerk Limiting
|
||||
|
||||
// Max entry speed of this block equals the max exit speed of the previous block.
|
||||
block->max_entry_speed = vmax_junction;
|
||||
block->max_entry_speed_sqr = vmax_junction_sqr;
|
||||
|
||||
// Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED.
|
||||
const float v_allowable = max_allowable_speed(-block->acceleration, MINIMUM_PLANNER_SPEED, block->millimeters);
|
||||
// If stepper ISR is disabled, this indicates buffer_segment wants to add a split block.
|
||||
// In this case start with the max. allowed speed to avoid an interrupted first move.
|
||||
block->entry_speed = STEPPER_ISR_ENABLED() ? MINIMUM_PLANNER_SPEED : MIN(vmax_junction, v_allowable);
|
||||
const float v_allowable_sqr = max_allowable_speed_sqr(-block->acceleration, sq(MINIMUM_PLANNER_SPEED), block->millimeters);
|
||||
|
||||
// If we are trying to add a split block, start with the
|
||||
// max. allowed speed to avoid an interrupted first move.
|
||||
block->entry_speed_sqr = !split_move ? sq(MINIMUM_PLANNER_SPEED) : MIN(vmax_junction_sqr, v_allowable_sqr);
|
||||
|
||||
// Initialize planner efficiency flags
|
||||
// Set flag if block will always reach maximum junction speed regardless of entry/exit speeds.
|
||||
@@ -2082,25 +2256,22 @@ void Planner::_buffer_steps(const int32_t (&target)[XYZE]
|
||||
// block nominal speed limits both the current and next maximum junction speeds. Hence, in both
|
||||
// the reverse and forward planners, the corresponding block junction speed will always be at the
|
||||
// the maximum junction speed and may always be ignored for any speed reduction checks.
|
||||
block->flag |= block->nominal_speed <= v_allowable ? BLOCK_FLAG_RECALCULATE | BLOCK_FLAG_NOMINAL_LENGTH : BLOCK_FLAG_RECALCULATE;
|
||||
block->flag |= block->nominal_speed_sqr <= v_allowable_sqr ? BLOCK_FLAG_RECALCULATE | BLOCK_FLAG_NOMINAL_LENGTH : BLOCK_FLAG_RECALCULATE;
|
||||
|
||||
// Update previous path unit_vector and nominal speed
|
||||
COPY(previous_speed, current_speed);
|
||||
previous_nominal_speed = block->nominal_speed;
|
||||
previous_nominal_speed_sqr = block->nominal_speed_sqr;
|
||||
|
||||
// Move buffer head
|
||||
block_buffer_head = next_buffer_head;
|
||||
|
||||
// Update the position (only when a move was queued)
|
||||
// Update the position
|
||||
static_assert(COUNT(target) > 1, "Parameter to _buffer_steps must be (&target)[XYZE]!");
|
||||
COPY(position, target);
|
||||
#if HAS_POSITION_FLOAT
|
||||
COPY(position_float, target_float);
|
||||
#endif
|
||||
|
||||
recalculate();
|
||||
|
||||
} // _buffer_steps()
|
||||
// Movement was accepted
|
||||
return true;
|
||||
} // _populate_block()
|
||||
|
||||
/**
|
||||
* Planner::buffer_sync_block
|
||||
@@ -2111,29 +2282,15 @@ void Planner::buffer_sync_block() {
|
||||
uint8_t next_buffer_head;
|
||||
block_t * const block = get_next_free_block(next_buffer_head);
|
||||
|
||||
// Clear block
|
||||
memset(block, 0, sizeof(block_t));
|
||||
|
||||
block->flag = BLOCK_FLAG_SYNC_POSITION;
|
||||
|
||||
block->steps[A_AXIS] = position[A_AXIS];
|
||||
block->steps[B_AXIS] = position[B_AXIS];
|
||||
block->steps[C_AXIS] = position[C_AXIS];
|
||||
block->steps[E_AXIS] = position[E_AXIS];
|
||||
|
||||
#if ENABLED(LIN_ADVANCE)
|
||||
block->use_advance_lead = false;
|
||||
#endif
|
||||
|
||||
block->nominal_speed =
|
||||
block->entry_speed =
|
||||
block->max_entry_speed =
|
||||
block->millimeters =
|
||||
block->acceleration = 0;
|
||||
|
||||
block->step_event_count =
|
||||
block->nominal_rate =
|
||||
block->initial_rate =
|
||||
block->final_rate =
|
||||
block->acceleration_steps_per_s2 =
|
||||
block->segment_time_us = 0;
|
||||
block->position[A_AXIS] = position[A_AXIS];
|
||||
block->position[B_AXIS] = position[B_AXIS];
|
||||
block->position[C_AXIS] = position[C_AXIS];
|
||||
block->position[E_AXIS] = position[E_AXIS];
|
||||
|
||||
block_buffer_head = next_buffer_head;
|
||||
stepper.wake_up();
|
||||
@@ -2151,7 +2308,11 @@ void Planner::buffer_sync_block() {
|
||||
* extruder - target extruder
|
||||
* millimeters - the length of the movement, if known
|
||||
*/
|
||||
void Planner::buffer_segment(const float &a, const float &b, const float &c, const float &e, const float &fr_mm_s, const uint8_t extruder, const float &millimeters/*=0.0*/) {
|
||||
bool Planner::buffer_segment(const float &a, const float &b, const float &c, const float &e, const float &fr_mm_s, const uint8_t extruder, const float &millimeters/*=0.0*/) {
|
||||
|
||||
// If we are cleaning, do not accept queuing of movements
|
||||
if (cleaning_buffer_counter) return false;
|
||||
|
||||
// When changing extruders recalculate steps corresponding to the E position
|
||||
#if ENABLED(DISTINCT_E_FACTORS)
|
||||
if (last_extruder != extruder && axis_steps_per_mm[E_AXIS_N] != axis_steps_per_mm[E_AXIS + last_extruder]) {
|
||||
@@ -2220,37 +2381,80 @@ void Planner::buffer_segment(const float &a, const float &b, const float &c, con
|
||||
const float between_float[ABCE] = { _BETWEEN_F(A), _BETWEEN_F(B), _BETWEEN_F(C), _BETWEEN_F(E) };
|
||||
#endif
|
||||
|
||||
DISABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
// The new head value is not assigned yet
|
||||
uint8_t buffer_head = 0;
|
||||
bool added = false;
|
||||
|
||||
_buffer_steps(between
|
||||
uint8_t next_buffer_head;
|
||||
block_t *block = get_next_free_block(next_buffer_head, 2);
|
||||
|
||||
// Fill the block with the specified movement
|
||||
if (
|
||||
_populate_block(block, true, between
|
||||
#if HAS_POSITION_FLOAT
|
||||
, between_float
|
||||
#endif
|
||||
, fr_mm_s, extruder, millimeters * 0.5
|
||||
)
|
||||
) {
|
||||
// Movement accepted - Point to the next reserved block
|
||||
block = &block_buffer[next_buffer_head];
|
||||
|
||||
// Store into the new to be stored head
|
||||
buffer_head = next_buffer_head;
|
||||
added = true;
|
||||
|
||||
// And advance the pointer to the next unused slot
|
||||
next_buffer_head = next_block_index(next_buffer_head);
|
||||
}
|
||||
|
||||
// Fill the second part of the block with the 2nd part of the movement
|
||||
if (
|
||||
_populate_block(block, true, target
|
||||
#if HAS_POSITION_FLOAT
|
||||
, target_float
|
||||
#endif
|
||||
, fr_mm_s, extruder, millimeters * 0.5
|
||||
)
|
||||
) {
|
||||
// Movement accepted - If this block is a continuation
|
||||
// of the previous one, mark it as such
|
||||
if (added) SBI(block->flag, BLOCK_BIT_CONTINUED);
|
||||
|
||||
// Store into the new to be stored head
|
||||
buffer_head = next_buffer_head;
|
||||
added = true;
|
||||
}
|
||||
|
||||
// If any of the movements was added
|
||||
if (added) {
|
||||
|
||||
// Move buffer head and add all the blocks that were filled
|
||||
// successfully to the movement queue.
|
||||
block_buffer_head = buffer_head;
|
||||
|
||||
// Update the position (only when a move was queued)
|
||||
static_assert(COUNT(target) > 1, "Parameter to _buffer_steps must be (&target)[XYZE]!");
|
||||
COPY(position, target);
|
||||
#if HAS_POSITION_FLOAT
|
||||
, between_float
|
||||
COPY(position_float, target_float);
|
||||
#endif
|
||||
, fr_mm_s, extruder, millimeters * 0.5
|
||||
);
|
||||
|
||||
const uint8_t next = block_buffer_head;
|
||||
|
||||
_buffer_steps(target
|
||||
#if HAS_POSITION_FLOAT
|
||||
, target_float
|
||||
#endif
|
||||
, fr_mm_s, extruder, millimeters * 0.5
|
||||
);
|
||||
|
||||
SBI(block_buffer[next].flag, BLOCK_BIT_CONTINUED);
|
||||
ENABLE_STEPPER_DRIVER_INTERRUPT();
|
||||
// Recalculate and optimize trapezoidal speed profiles
|
||||
recalculate();
|
||||
}
|
||||
}
|
||||
else
|
||||
_buffer_steps(target
|
||||
else if (
|
||||
!_buffer_steps(target
|
||||
#if HAS_POSITION_FLOAT
|
||||
, target_float
|
||||
#endif
|
||||
, fr_mm_s, extruder, millimeters
|
||||
);
|
||||
)
|
||||
) return false;
|
||||
|
||||
stepper.wake_up();
|
||||
|
||||
return true;
|
||||
} // buffer_segment()
|
||||
|
||||
/**
|
||||
@@ -2277,7 +2481,7 @@ void Planner::_set_position_mm(const float &a, const float &b, const float &c, c
|
||||
position_float[C_AXIS] = c;
|
||||
position_float[E_AXIS] = e;
|
||||
#endif
|
||||
previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest.
|
||||
previous_nominal_speed_sqr = 0.0; // Resets planner junction speeds. Assumes start from rest.
|
||||
ZERO(previous_speed);
|
||||
buffer_sync_block();
|
||||
}
|
||||
@@ -2297,22 +2501,6 @@ void Planner::set_position_mm_kinematic(const float (&cart)[XYZE]) {
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync from the stepper positions. (e.g., after an interrupted move)
|
||||
*/
|
||||
void Planner::sync_from_steppers() {
|
||||
LOOP_XYZE(i) {
|
||||
position[i] = stepper.position((AxisEnum)i);
|
||||
#if HAS_POSITION_FLOAT
|
||||
position_float[i] = position[i] * steps_to_mm[i
|
||||
#if ENABLED(DISTINCT_E_FACTORS)
|
||||
+ (i == E_AXIS ? active_extruder : 0)
|
||||
#endif
|
||||
];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setters for planner position (also setting stepper position).
|
||||
*/
|
||||
|
Reference in New Issue
Block a user