Browse Source

Fix, improve Linear Advance (#24533)

FB4S_WIFI
tombrazier 2 years ago
committed by Scott Lahteine
parent
commit
fd319928d2
  1. 219
      Marlin/src/module/planner.cpp
  2. 11
      Marlin/src/module/planner.h
  3. 351
      Marlin/src/module/stepper.cpp
  4. 75
      Marlin/src/module/stepper.h

219
Marlin/src/module/planner.cpp

@ -788,7 +788,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
NOLESS(initial_rate, uint32_t(MINIMAL_STEP_RATE));
NOLESS(final_rate, uint32_t(MINIMAL_STEP_RATE));
#if ENABLED(S_CURVE_ACCELERATION)
#if EITHER(S_CURVE_ACCELERATION, LIN_ADVANCE)
// If we have some plateau time, the cruise rate will be the nominal rate
uint32_t cruise_rate = block->nominal_rate;
#endif
@ -820,7 +820,7 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
accelerate_steps = _MIN(uint32_t(_MAX(accelerate_steps_float, 0)), block->step_event_count);
decelerate_steps = block->step_event_count - accelerate_steps;
#if ENABLED(S_CURVE_ACCELERATION)
#if EITHER(S_CURVE_ACCELERATION, LIN_ADVANCE)
// We won't reach the cruising rate. Let's calculate the speed we will reach
cruise_rate = final_speed(initial_rate, accel, accelerate_steps);
#endif
@ -849,6 +849,14 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
#endif
block->final_rate = final_rate;
#if ENABLED(LIN_ADVANCE)
if (block->la_advance_rate) {
const float comp = extruder_advance_K[block->extruder] * block->steps.e / block->step_event_count;
block->max_adv_steps = cruise_rate * comp;
block->final_adv_steps = final_rate * comp;
}
#endif
#if ENABLED(LASER_POWER_TRAP)
/**
* Laser Trapezoid Calculations
@ -899,75 +907,76 @@ void Planner::calculate_trapezoid_for_block(block_t * const block, const_float_t
#endif // LASER_POWER_TRAP
}
/* PLANNER SPEED DEFINITION
+--------+ <- current->nominal_speed
/ \
current->entry_speed -> + \
| + <- next->entry_speed (aka exit speed)
+-------------+
time -->
Recalculates the motion plan according to the following basic guidelines:
1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
(i.e. current->entry_speed) such that:
a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
neighboring blocks.
b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
with a maximum allowable deceleration over the block travel distance.
c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero).
2. Go over every block in chronological (forward) order and dial down junction speed values if
a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
acceleration over the block travel distance.
When these stages are complete, the planner will have maximized the velocity profiles throughout the all
of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements
are possible. If a new block is added to the buffer, the plan is recomputed according to the said
guidelines for a new optimal plan.
To increase computational efficiency of these guidelines, a set of planner block pointers have been
created to indicate stop-compute points for when the planner guidelines cannot logically make any further
changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
point) are all accelerating, they are all optimal and can not be altered by a new block added to the
planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
recomputed as stated in the general guidelines.
Planner buffer index mapping:
- block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
- block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
the buffer is full or empty. As described for standard ring buffers, this block is always empty.
- block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
planner buffer that don't change with the addition of a new block, as describe above. In addition,
this block can never be less than block_buffer_tail and will always be pushed forward and maintain
this requirement when encountered by the Planner::release_current_block() routine during a cycle.
NOTE: Since the planner only computes on what's in the planner buffer, some motions with many short
segments (e.g., complex curves) may seem to move slowly. This is because there simply isn't
enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and
then decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this
happens and becomes an annoyance, there are a few simple solutions:
- Maximize the machine acceleration. The planner will be able to compute higher velocity profiles
within the same combined distance.
- Maximize line motion(s) distance per block to a desired tolerance. The more combined distance the
planner has to use, the faster it can go.
- Maximize the planner buffer size. This also will increase the combined distance for the planner to
compute over. It also increases the number of computations the planner has to perform to compute an
optimal plan, so select carefully.
- Use G2/G3 arcs instead of many short segments. Arcs inform the planner of a safe exit speed at the
end of the last segment, which alleviates this problem.
*/
/**
* PLANNER SPEED DEFINITION
* +--------+ <- current->nominal_speed
* / \
* current->entry_speed -> + \
* | + <- next->entry_speed (aka exit speed)
* +-------------+
* time -->
*
* Recalculates the motion plan according to the following basic guidelines:
*
* 1. Go over every feasible block sequentially in reverse order and calculate the junction speeds
* (i.e. current->entry_speed) such that:
* a. No junction speed exceeds the pre-computed maximum junction speed limit or nominal speeds of
* neighboring blocks.
* b. A block entry speed cannot exceed one reverse-computed from its exit speed (next->entry_speed)
* with a maximum allowable deceleration over the block travel distance.
* c. The last (or newest appended) block is planned from a complete stop (an exit speed of zero).
* 2. Go over every block in chronological (forward) order and dial down junction speed values if
* a. The exit speed exceeds the one forward-computed from its entry speed with the maximum allowable
* acceleration over the block travel distance.
*
* When these stages are complete, the planner will have maximized the velocity profiles throughout the all
* of the planner blocks, where every block is operating at its maximum allowable acceleration limits. In
* other words, for all of the blocks in the planner, the plan is optimal and no further speed improvements
* are possible. If a new block is added to the buffer, the plan is recomputed according to the said
* guidelines for a new optimal plan.
*
* To increase computational efficiency of these guidelines, a set of planner block pointers have been
* created to indicate stop-compute points for when the planner guidelines cannot logically make any further
* changes or improvements to the plan when in normal operation and new blocks are streamed and added to the
* planner buffer. For example, if a subset of sequential blocks in the planner have been planned and are
* bracketed by junction velocities at their maximums (or by the first planner block as well), no new block
* added to the planner buffer will alter the velocity profiles within them. So we no longer have to compute
* them. Or, if a set of sequential blocks from the first block in the planner (or a optimal stop-compute
* point) are all accelerating, they are all optimal and can not be altered by a new block added to the
* planner buffer, as this will only further increase the plan speed to chronological blocks until a maximum
* junction velocity is reached. However, if the operational conditions of the plan changes from infrequently
* used feed holds or feedrate overrides, the stop-compute pointers will be reset and the entire plan is
* recomputed as stated in the general guidelines.
*
* Planner buffer index mapping:
* - block_buffer_tail: Points to the beginning of the planner buffer. First to be executed or being executed.
* - block_buffer_head: Points to the buffer block after the last block in the buffer. Used to indicate whether
* the buffer is full or empty. As described for standard ring buffers, this block is always empty.
* - block_buffer_planned: Points to the first buffer block after the last optimally planned block for normal
* streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
* planner buffer that don't change with the addition of a new block, as describe above. In addition,
* this block can never be less than block_buffer_tail and will always be pushed forward and maintain
* this requirement when encountered by the Planner::release_current_block() routine during a cycle.
*
* NOTE: Since the planner only computes on what's in the planner buffer, some motions with many short
* segments (e.g., complex curves) may seem to move slowly. This is because there simply isn't
* enough combined distance traveled in the entire buffer to accelerate up to the nominal speed and
* then decelerate to a complete stop at the end of the buffer, as stated by the guidelines. If this
* happens and becomes an annoyance, there are a few simple solutions:
*
* - Maximize the machine acceleration. The planner will be able to compute higher velocity profiles
* within the same combined distance.
*
* - Maximize line motion(s) distance per block to a desired tolerance. The more combined distance the
* planner has to use, the faster it can go.
*
* - Maximize the planner buffer size. This also will increase the combined distance for the planner to
* compute over. It also increases the number of computations the planner has to perform to compute an
* optimal plan, so select carefully.
*
* - Use G2/G3 arcs instead of many short segments. Arcs inform the planner of a safe exit speed at the
* end of the last segment, which alleviates this problem.
*/
// 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
@ -1211,13 +1220,6 @@ void Planner::recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t
// NOTE: Entry and exit factors always > 0 by all previous logic operations.
const float nomr = 1.0f / block->nominal_speed;
calculate_trapezoid_for_block(block, current_entry_speed * nomr, next_entry_speed * nomr);
#if ENABLED(LIN_ADVANCE)
if (block->use_advance_lead) {
const float comp = block->e_D_ratio * extruder_advance_K[active_extruder] * settings.axis_steps_per_mm[E_AXIS];
block->max_adv_steps = block->nominal_speed * comp;
block->final_adv_steps = next_entry_speed * comp;
}
#endif
}
// Reset current only to ensure next trapezoid is computed - The
@ -1251,13 +1253,6 @@ void Planner::recalculate_trapezoids(TERN_(HINTS_SAFE_EXIT_SPEED, const_float_t
const float nomr = 1.0f / block->nominal_speed;
calculate_trapezoid_for_block(block, current_entry_speed * nomr, next_entry_speed * nomr);
#if ENABLED(LIN_ADVANCE)
if (block->use_advance_lead) {
const float comp = block->e_D_ratio * extruder_advance_K[active_extruder] * settings.axis_steps_per_mm[E_AXIS];
block->max_adv_steps = block->nominal_speed * comp;
block->final_adv_steps = next_entry_speed * comp;
}
#endif
}
// Reset block to ensure its trapezoid is computed - The stepper is free to use
@ -2502,13 +2497,15 @@ bool Planner::_populate_block(
// Compute and limit the acceleration rate for the trapezoid generator.
const float steps_per_mm = block->step_event_count * inverse_millimeters;
uint32_t accel;
#if ENABLED(LIN_ADVANCE)
bool use_advance_lead = false;
#endif
if (NUM_AXIS_GANG(
!block->steps.a, && !block->steps.b, && !block->steps.c,
&& !block->steps.i, && !block->steps.j, && !block->steps.k,
&& !block->steps.u, && !block->steps.v, && !block->steps.w)
) { // Is this a retract / recover move?
accel = CEIL(settings.retract_acceleration * steps_per_mm); // Convert to: acceleration steps/sec^2
TERN_(LIN_ADVANCE, block->use_advance_lead = false); // No linear advance for simple retract/recover
}
else {
#define LIMIT_ACCEL_LONG(AXIS,INDX) do{ \
@ -2535,33 +2532,29 @@ bool Planner::_populate_block(
/**
* Use LIN_ADVANCE for blocks if all these are true:
*
* esteps : This is a print move, because we checked for A, B, C steps before.
* esteps : This is a print move, because we checked for A, B, C steps before.
*
* extruder_advance_K[active_extruder] : There is an advance factor set for this extruder.
* extruder_advance_K[extruder] : There is an advance factor set for this extruder.
*
* de > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
* de > 0 : Extruder is running forward (e.g., for "Wipe while retracting" (Slic3r) or "Combing" (Cura) moves)
*/
block->use_advance_lead = esteps
&& extruder_advance_K[active_extruder]
&& de > 0;
if (block->use_advance_lead) {
block->e_D_ratio = (target_float.e - position_float.e) /
#if IS_KINEMATIC
block->millimeters
#else
use_advance_lead = esteps && extruder_advance_K[extruder] && de > 0;
if (use_advance_lead) {
float e_D_ratio = (target_float.e - position_float.e) /
TERN(IS_KINEMATIC, block->millimeters,
SQRT(sq(target_float.x - position_float.x)
+ sq(target_float.y - position_float.y)
+ sq(target_float.z - position_float.z))
#endif
;
);
// 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!
// 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.
if (block->e_D_ratio > 3.0f)
block->use_advance_lead = false;
if (e_D_ratio > 3.0f)
use_advance_lead = false;
else {
const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[active_extruder] * block->e_D_ratio) * steps_per_mm;
// Scale E acceleration so that it will be possible to jump to the advance speed.
const uint32_t max_accel_steps_per_s2 = MAX_E_JERK(extruder) / (extruder_advance_K[extruder] * e_D_ratio) * steps_per_mm;
if (TERN0(LA_DEBUG, accel > max_accel_steps_per_s2))
SERIAL_ECHOLNPGM("Acceleration limited.");
NOMORE(accel, max_accel_steps_per_s2);
@ -2593,13 +2586,21 @@ bool Planner::_populate_block(
block->acceleration_rate = (uint32_t)(accel * (float(1UL << 24) / (STEPPER_TIMER_RATE)));
#endif
#if ENABLED(LIN_ADVANCE)
if (block->use_advance_lead) {
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)]);
block->la_advance_rate = 0;
block->la_scaling = 0;
if (use_advance_lead) {
// the Bresenham algorithm will convert this step rate into extruder steps
block->la_advance_rate = extruder_advance_K[extruder] * block->acceleration_steps_per_s2;
// reduce LA ISR frequency by calling it only often enough to ensure that there will
// never be more than four extruder steps per call
for (uint32_t dividend = block->steps.e << 1; dividend <= (block->step_event_count >> 2); dividend <<= 1)
block->la_scaling++;
#if ENABLED(LA_DEBUG)
if (extruder_advance_K[active_extruder] * block->e_D_ratio * block->acceleration * 2 < block->nominal_speed * 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.");
if (block->la_advance_rate >> block->la_scaling > 10000)
SERIAL_ECHOLNPGM("eISR running at > 10kHz: ", block->la_advance_rate);
#endif
}
#endif

11
Marlin/src/module/planner.h

@ -239,11 +239,10 @@ typedef struct PlannerBlock {
// Advance extrusion
#if ENABLED(LIN_ADVANCE)
bool use_advance_lead;
uint16_t advance_speed, // STEP timer value for extruder speed offset ISR
max_adv_steps, // max. advance steps to get cruising speed pressure (not always nominal_speed!)
final_adv_steps; // advance steps due to exit speed
float e_D_ratio;
uint32_t la_advance_rate; // The rate at which steps are added whilst accelerating
uint8_t la_scaling; // Scale ISR frequency down and step frequency up by 2 ^ la_scaling
uint16_t max_adv_steps, // Max advance steps to get cruising speed pressure
final_adv_steps; // Advance steps for exit speed pressure
#endif
uint32_t nominal_rate, // The nominal step rate for this block in step_events/sec
@ -1018,7 +1017,7 @@ class Planner {
return target_velocity_sqr - 2 * accel * distance;
}
#if ENABLED(S_CURVE_ACCELERATION)
#if EITHER(S_CURVE_ACCELERATION, LIN_ADVANCE)
/**
* Calculate the speed reached given initial speed, acceleration and distance
*/

351
Marlin/src/module/stepper.cpp

@ -217,18 +217,12 @@ uint32_t Stepper::advance_divisor = 0,
#endif
#if ENABLED(LIN_ADVANCE)
uint32_t Stepper::nextAdvanceISR = LA_ADV_NEVER,
Stepper::LA_isr_rate = LA_ADV_NEVER;
uint16_t Stepper::LA_current_adv_steps = 0,
Stepper::LA_final_adv_steps,
Stepper::LA_max_adv_steps;
int8_t Stepper::LA_steps = 0;
bool Stepper::LA_use_advance_lead;
#endif // LIN_ADVANCE
Stepper::la_interval = LA_ADV_NEVER;
int32_t Stepper::la_delta_error = 0,
Stepper::la_dividend = 0,
Stepper::la_advance_steps = 0;
#endif
#if ENABLED(INTEGRATED_BABYSTEPPING)
uint32_t Stepper::nextBabystepISR = BABYSTEP_NEVER;
@ -588,29 +582,27 @@ void Stepper::set_directions() {
TERN_(HAS_V_DIR, SET_STEP_DIR(V));
TERN_(HAS_W_DIR, SET_STEP_DIR(W));
#if DISABLED(LIN_ADVANCE)
#if ENABLED(MIXING_EXTRUDER)
// Because this is valid for the whole block we don't know
// what E steppers will step. Likely all. Set all.
if (motor_direction(E_AXIS)) {
MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
count_direction.e = -1;
}
else {
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
count_direction.e = 1;
}
#elif HAS_EXTRUDERS
if (motor_direction(E_AXIS)) {
REV_E_DIR(stepper_extruder);
count_direction.e = -1;
}
else {
NORM_E_DIR(stepper_extruder);
count_direction.e = 1;
}
#endif
#endif // !LIN_ADVANCE
#if ENABLED(MIXING_EXTRUDER)
// Because this is valid for the whole block we don't know
// what E steppers will step. Likely all. Set all.
if (motor_direction(E_AXIS)) {
MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
count_direction.e = -1;
}
else {
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
count_direction.e = 1;
}
#elif HAS_EXTRUDERS
if (motor_direction(E_AXIS)) {
REV_E_DIR(stepper_extruder);
count_direction.e = -1;
}
else {
NORM_E_DIR(stepper_extruder);
count_direction.e = 1;
}
#endif
DIR_WAIT_AFTER();
}
@ -1467,14 +1459,19 @@ void Stepper::isr() {
// Enable ISRs to reduce USART processing latency
hal.isr_on();
if (!nextMainISR) pulse_phase_isr(); // 0 = Do coordinated axes Stepper pulses
if (!nextMainISR) pulse_phase_isr(); // 0 = Do coordinated axes Stepper pulses
#if ENABLED(LIN_ADVANCE)
if (!nextAdvanceISR) nextAdvanceISR = advance_isr(); // 0 = Do Linear Advance E Stepper pulses
if (!nextAdvanceISR) { // 0 = Do Linear Advance E Stepper pulses
advance_isr();
nextAdvanceISR = la_interval;
}
else if (nextAdvanceISR == LA_ADV_NEVER) // Start LA steps if necessary
nextAdvanceISR = la_interval;
#endif
#if ENABLED(INTEGRATED_BABYSTEPPING)
const bool is_babystep = (nextBabystepISR == 0); // 0 = Do Babystepping (XY)Z pulses
const bool is_babystep = (nextBabystepISR == 0); // 0 = Do Babystepping (XY)Z pulses
if (is_babystep) nextBabystepISR = babystepping_isr();
#endif
@ -1796,20 +1793,18 @@ void Stepper::pulse_phase_isr() {
PULSE_PREP(W);
#endif
#if EITHER(LIN_ADVANCE, MIXING_EXTRUDER)
delta_error.e += advance_dividend.e;
if (delta_error.e >= 0) {
#if ENABLED(LIN_ADVANCE)
delta_error.e -= advance_divisor;
// Don't step E here - But remember the number of steps to perform
motor_direction(E_AXIS) ? --LA_steps : ++LA_steps;
#else
count_position.e += count_direction.e;
step_needed.e = true;
#endif
}
#elif HAS_E0_STEP
#if EITHER(HAS_E0_STEP, MIXING_EXTRUDER)
PULSE_PREP(E);
#if ENABLED(LIN_ADVANCE)
if (step_needed.e && current_block->la_advance_rate) {
// don't actually step here, but do subtract movements steps
// from the linear advance step count
step_needed.e = false;
count_position.e -= count_direction.e;
la_advance_steps--;
}
#endif
#endif
}
@ -1849,12 +1844,10 @@ void Stepper::pulse_phase_isr() {
PULSE_START(W);
#endif
#if DISABLED(LIN_ADVANCE)
#if ENABLED(MIXING_EXTRUDER)
if (step_needed.e) E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN);
#elif HAS_E0_STEP
PULSE_START(E);
#endif
#if ENABLED(MIXING_EXTRUDER)
if (step_needed.e) E_STEP_WRITE(mixer.get_next_stepper(), !INVERT_E_STEP_PIN);
#elif HAS_E0_STEP
PULSE_START(E);
#endif
TERN_(I2S_STEPPER_STREAM, i2s_push_sample());
@ -1894,15 +1887,10 @@ void Stepper::pulse_phase_isr() {
PULSE_STOP(W);
#endif
#if DISABLED(LIN_ADVANCE)
#if ENABLED(MIXING_EXTRUDER)
if (delta_error.e >= 0) {
delta_error.e -= advance_divisor;
E_STEP_WRITE(mixer.get_stepper(), INVERT_E_STEP_PIN);
}
#elif HAS_E0_STEP
PULSE_STOP(E);
#endif
#if ENABLED(MIXING_EXTRUDER)
if (step_needed.e) E_STEP_WRITE(mixer.get_stepper(), INVERT_E_STEP_PIN);
#elif HAS_E0_STEP
PULSE_STOP(E);
#endif
#if ISR_MULTI_STEPS
@ -1912,6 +1900,69 @@ void Stepper::pulse_phase_isr() {
} while (--events_to_do);
}
// Calculate timer interval, with all limits applied.
uint32_t Stepper::calc_timer_interval(uint32_t step_rate) {
#ifdef CPU_32_BIT
// In case of high-performance processor, it is able to calculate in real-time
return uint32_t(STEPPER_TIMER_RATE) / step_rate;
#else
// AVR is able to keep up at 30khz Stepping ISR rate.
constexpr uint32_t min_step_rate = (F_CPU) / 500000U;
if (step_rate <= min_step_rate) {
step_rate = 0;
uintptr_t table_address = (uintptr_t)&speed_lookuptable_slow[0][0];
return uint16_t(pgm_read_word(table_address));
}
else {
step_rate -= min_step_rate; // Correct for minimal speed
if (step_rate >= 0x0800) { // higher step rate
const uint8_t rate_mod_256 = (step_rate & 0x00FF);
const uintptr_t table_address = uintptr_t(&speed_lookuptable_fast[uint8_t(step_rate >> 8)][0]),
gain = uint16_t(pgm_read_word(table_address + 2));
return uint16_t(pgm_read_word(table_address)) - MultiU16X8toH16(rate_mod_256, gain);
}
else { // lower step rates
uintptr_t table_address = uintptr_t(&speed_lookuptable_slow[0][0]);
table_address += (step_rate >> 1) & 0xFFFC;
return uint16_t(pgm_read_word(table_address))
- ((uint16_t(pgm_read_word(table_address + 2)) * uint8_t(step_rate & 0x0007)) >> 3);
}
}
#endif
}
// Get the timer interval and the number of loops to perform per tick
uint32_t Stepper::calc_timer_interval(uint32_t step_rate, uint8_t &loops) {
uint8_t multistep = 1;
#if DISABLED(DISABLE_MULTI_STEPPING)
// The stepping frequency limits for each multistepping rate
static const uint32_t limit[] PROGMEM = {
( MAX_STEP_ISR_FREQUENCY_1X ),
( MAX_STEP_ISR_FREQUENCY_2X >> 1),
( MAX_STEP_ISR_FREQUENCY_4X >> 2),
( MAX_STEP_ISR_FREQUENCY_8X >> 3),
( MAX_STEP_ISR_FREQUENCY_16X >> 4),
( MAX_STEP_ISR_FREQUENCY_32X >> 5),
( MAX_STEP_ISR_FREQUENCY_64X >> 6),
(MAX_STEP_ISR_FREQUENCY_128X >> 7)
};
// Select the proper multistepping
uint8_t idx = 0;
while (idx < 7 && step_rate > (uint32_t)pgm_read_dword(&limit[idx])) {
step_rate >>= 1;
multistep <<= 1;
++idx;
};
#else
NOMORE(step_rate, uint32_t(MAX_STEP_ISR_FREQUENCY_1X));
#endif
loops = multistep;
return calc_timer_interval(step_rate);
}
// This is the last half of the stepper interrupt: This one processes and
// properly schedules blocks from the planner. This is executed after creating
// the step pulses, so it is not time critical, as pulses are already done.
@ -1964,15 +2015,14 @@ uint32_t Stepper::block_phase_isr() {
// acc_step_rate is in steps/second
// step_rate to timer interval and steps per stepper isr
interval = calc_timer_interval(acc_step_rate, &steps_per_isr);
interval = calc_timer_interval(acc_step_rate << oversampling_factor, steps_per_isr);
acceleration_time += interval;
#if ENABLED(LIN_ADVANCE)
if (LA_use_advance_lead) {
// Fire ISR if final adv_rate is reached
if (LA_steps && LA_isr_rate != current_block->advance_speed) nextAdvanceISR = 0;
if (current_block->la_advance_rate) {
const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0;
la_interval = calc_timer_interval(acc_step_rate + la_step_rate) << current_block->la_scaling;
}
else if (LA_steps) nextAdvanceISR = 0;
#endif
/**
@ -2035,18 +2085,41 @@ uint32_t Stepper::block_phase_isr() {
#endif
// step_rate to timer interval and steps per stepper isr
interval = calc_timer_interval(step_rate, &steps_per_isr);
interval = calc_timer_interval(step_rate << oversampling_factor, steps_per_isr);
deceleration_time += interval;
#if ENABLED(LIN_ADVANCE)
if (LA_use_advance_lead) {
// Wake up eISR on first deceleration loop and fire ISR if final adv_rate is reached
if (step_events_completed <= decelerate_after + steps_per_isr || (LA_steps && LA_isr_rate != current_block->advance_speed)) {
initiateLA();
LA_isr_rate = current_block->advance_speed;
if (current_block->la_advance_rate) {
const uint32_t la_step_rate = la_advance_steps > current_block->final_adv_steps ? current_block->la_advance_rate : 0;
if (la_step_rate != step_rate) {
bool reverse_e = la_step_rate > step_rate;
la_interval = calc_timer_interval(reverse_e ? la_step_rate - step_rate : step_rate - la_step_rate) << current_block->la_scaling;
if (reverse_e != motor_direction(E_AXIS)) {
TBI(last_direction_bits, E_AXIS);
count_direction.e = -count_direction.e;
DIR_WAIT_BEFORE();
if (reverse_e) {
#if ENABLED(MIXING_EXTRUDER)
MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
#else
REV_E_DIR(stepper_extruder);
#endif
}
else {
#if ENABLED(MIXING_EXTRUDER)
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
#else
NORM_E_DIR(stepper_extruder);
#endif
}
DIR_WAIT_AFTER();
}
}
}
else if (LA_steps) nextAdvanceISR = 0;
#endif // LIN_ADVANCE
/*
@ -2069,15 +2142,15 @@ uint32_t Stepper::block_phase_isr() {
}
else { // Must be in cruise phase otherwise
#if ENABLED(LIN_ADVANCE)
// If there are any esteps, fire the next advance_isr "now"
if (LA_steps && LA_isr_rate != current_block->advance_speed) initiateLA();
#endif
// Calculate the ticks_nominal for this nominal speed, if not done yet
if (ticks_nominal < 0) {
// step_rate to timer interval and loops for the nominal speed
ticks_nominal = calc_timer_interval(current_block->nominal_rate, &steps_per_isr);
ticks_nominal = calc_timer_interval(current_block->nominal_rate << oversampling_factor, steps_per_isr);
#if ENABLED(LIN_ADVANCE)
if (current_block->la_advance_rate)
la_interval = calc_timer_interval(current_block->nominal_rate) << current_block->la_scaling;
#endif
}
// The timer interval is just the nominal value for the nominal speed
@ -2291,7 +2364,7 @@ uint32_t Stepper::block_phase_isr() {
step_event_count = current_block->step_event_count << oversampling;
// Initialize Bresenham delta errors to 1/2
delta_error = -int32_t(step_event_count);
delta_error = TERN_(LIN_ADVANCE, la_delta_error =) -int32_t(step_event_count);
// Calculate Bresenham dividends and divisors
advance_dividend = current_block->steps << 1;
@ -2312,16 +2385,12 @@ uint32_t Stepper::block_phase_isr() {
#if ENABLED(LIN_ADVANCE)
#if DISABLED(MIXING_EXTRUDER) && E_STEPPERS > 1
// If the now active extruder wasn't in use during the last move, its pressure is most likely gone.
if (stepper_extruder != last_moved_extruder) LA_current_adv_steps = 0;
if (stepper_extruder != last_moved_extruder) la_advance_steps = 0;
#endif
if ((LA_use_advance_lead = current_block->use_advance_lead)) {
LA_final_adv_steps = current_block->final_adv_steps;
LA_max_adv_steps = current_block->max_adv_steps;
initiateLA(); // Start the ISR
LA_isr_rate = current_block->advance_speed;
if (current_block->la_advance_rate) {
// apply LA scaling and discount the effect of frequency scaling
la_dividend = (advance_dividend.e << current_block->la_scaling) << oversampling;
}
else LA_isr_rate = LA_ADV_NEVER;
#endif
if ( ENABLED(DUAL_X_CARRIAGE) // TODO: Find out why this fixes "jittery" small circles
@ -2375,7 +2444,15 @@ uint32_t Stepper::block_phase_isr() {
#endif
// Calculate the initial timer interval
interval = calc_timer_interval(current_block->initial_rate, &steps_per_isr);
interval = calc_timer_interval(current_block->initial_rate << oversampling_factor, steps_per_isr);
acceleration_time += interval;
#if ENABLED(LIN_ADVANCE)
if (current_block->la_advance_rate) {
const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0;
la_interval = calc_timer_interval(current_block->initial_rate + la_step_rate) << current_block->la_scaling;
}
#endif
}
}
@ -2386,71 +2463,15 @@ uint32_t Stepper::block_phase_isr() {
#if ENABLED(LIN_ADVANCE)
// Timer interrupt for E. LA_steps is set in the main routine
uint32_t Stepper::advance_isr() {
uint32_t interval;
if (LA_use_advance_lead) {
if (step_events_completed > decelerate_after && LA_current_adv_steps > LA_final_adv_steps) {
LA_steps--;
LA_current_adv_steps--;
interval = LA_isr_rate;
}
else if (step_events_completed < decelerate_after && LA_current_adv_steps < LA_max_adv_steps) {
LA_steps++;
LA_current_adv_steps++;
interval = LA_isr_rate;
}
else
interval = LA_isr_rate = LA_ADV_NEVER;
}
else
interval = LA_ADV_NEVER;
if (!LA_steps) return interval; // Leave pins alone if there are no steps!
DIR_WAIT_BEFORE();
#if ENABLED(MIXING_EXTRUDER)
// We don't know which steppers will be stepped because LA loop follows,
// with potentially multiple steps. Set all.
if (LA_steps > 0) {
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
count_direction.e = 1;
}
else if (LA_steps < 0) {
MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
count_direction.e = -1;
}
#else
if (LA_steps > 0) {
NORM_E_DIR(stepper_extruder);
count_direction.e = 1;
}
else if (LA_steps < 0) {
REV_E_DIR(stepper_extruder);
count_direction.e = -1;
}
#endif
DIR_WAIT_AFTER();
//const hal_timer_t added_step_ticks = hal_timer_t(ADDED_STEP_TICKS);
// Step E stepper if we have steps
#if ISR_MULTI_STEPS
bool firstStep = true;
USING_TIMED_PULSE();
#endif
while (LA_steps) {
#if ISR_MULTI_STEPS
if (firstStep)
firstStep = false;
else
AWAIT_LOW_PULSE();
#endif
void Stepper::advance_isr() {
// Apply Bresenham algorithm so that linear advance can piggy back on
// the acceleration and speed values calculated in block_phase_isr().
// This helps keep LA in sync with, for example, S_CURVE_ACCELERATION.
la_delta_error += la_dividend;
if (la_delta_error >= 0) {
count_position.e += count_direction.e;
la_advance_steps += count_direction.e;
la_delta_error -= advance_divisor;
// Set the STEP pulse ON
#if ENABLED(MIXING_EXTRUDER)
@ -2461,12 +2482,8 @@ uint32_t Stepper::block_phase_isr() {
// Enforce a minimum duration for STEP pulse ON
#if ISR_PULSE_CONTROL
USING_TIMED_PULSE();
START_HIGH_PULSE();
#endif
LA_steps < 0 ? ++LA_steps : --LA_steps;
#if ISR_PULSE_CONTROL
AWAIT_HIGH_PULSE();
#endif
@ -2476,15 +2493,7 @@ uint32_t Stepper::block_phase_isr() {
#else
E_STEP_WRITE(stepper_extruder, INVERT_E_STEP_PIN);
#endif
// For minimum pulse time wait before looping
// Just wait for the requested pulse duration
#if ISR_PULSE_CONTROL
if (LA_steps) START_LOW_PULSE();
#endif
} // LA_steps
return interval;
}
}
#endif // LIN_ADVANCE

75
Marlin/src/module/stepper.h

@ -417,10 +417,11 @@ class Stepper {
#if ENABLED(LIN_ADVANCE)
static constexpr uint32_t LA_ADV_NEVER = 0xFFFFFFFF;
static uint32_t nextAdvanceISR, LA_isr_rate;
static uint16_t LA_current_adv_steps, LA_final_adv_steps, LA_max_adv_steps; // Copy from current executed block. Needed because current_block is set to NULL "too early".
static int8_t LA_steps;
static bool LA_use_advance_lead;
static uint32_t nextAdvanceISR,
la_interval; // Interval between ISR calls for LA
static int32_t la_delta_error, // Analogue of delta_error.e for E steps in LA ISR
la_dividend, // Analogue of advance_dividend.e for E steps in LA ISR
la_advance_steps; // Count of steps added to increase nozzle pressure
#endif
#if ENABLED(INTEGRATED_BABYSTEPPING)
@ -475,8 +476,7 @@ class Stepper {
#if ENABLED(LIN_ADVANCE)
// The Linear advance ISR phase
static uint32_t advance_isr();
FORCE_INLINE static void initiateLA() { nextAdvanceISR = 0; }
static void advance_isr();
#endif
#if ENABLED(INTEGRATED_BABYSTEPPING)
@ -512,6 +512,7 @@ class Stepper {
current_block = nullptr;
axis_did_move = 0;
planner.release_current_block();
TERN_(LIN_ADVANCE, la_interval = nextAdvanceISR = LA_ADV_NEVER);
}
// Quickly stop all steppers
@ -631,65 +632,9 @@ class Stepper {
// Set the current position in steps
static void _set_position(const abce_long_t &spos);
FORCE_INLINE static uint32_t calc_timer_interval(uint32_t step_rate, uint8_t *loops) {
uint32_t timer;
// Scale the frequency, as requested by the caller
step_rate <<= oversampling_factor;
uint8_t multistep = 1;
#if DISABLED(DISABLE_MULTI_STEPPING)
// The stepping frequency limits for each multistepping rate
static const uint32_t limit[] PROGMEM = {
( MAX_STEP_ISR_FREQUENCY_1X ),
( MAX_STEP_ISR_FREQUENCY_2X >> 1),
( MAX_STEP_ISR_FREQUENCY_4X >> 2),
( MAX_STEP_ISR_FREQUENCY_8X >> 3),
( MAX_STEP_ISR_FREQUENCY_16X >> 4),
( MAX_STEP_ISR_FREQUENCY_32X >> 5),
( MAX_STEP_ISR_FREQUENCY_64X >> 6),
(MAX_STEP_ISR_FREQUENCY_128X >> 7)
};
// Select the proper multistepping
uint8_t idx = 0;
while (idx < 7 && step_rate > (uint32_t)pgm_read_dword(&limit[idx])) {
step_rate >>= 1;
multistep <<= 1;
++idx;
};
#else
NOMORE(step_rate, uint32_t(MAX_STEP_ISR_FREQUENCY_1X));
#endif
*loops = multistep;
#ifdef CPU_32_BIT
// In case of high-performance processor, it is able to calculate in real-time
timer = uint32_t(STEPPER_TIMER_RATE) / step_rate;
#else
constexpr uint32_t min_step_rate = (F_CPU) / 500000U;
NOLESS(step_rate, min_step_rate);
step_rate -= min_step_rate; // Correct for minimal speed
if (step_rate >= (8 * 256)) { // higher step rate
const uint8_t tmp_step_rate = (step_rate & 0x00FF);
const uint16_t table_address = (uint16_t)&speed_lookuptable_fast[(uint8_t)(step_rate >> 8)][0],
gain = (uint16_t)pgm_read_word(table_address + 2);
timer = MultiU16X8toH16(tmp_step_rate, gain);
timer = (uint16_t)pgm_read_word(table_address) - timer;
}
else { // lower step rates
uint16_t table_address = (uint16_t)&speed_lookuptable_slow[0][0];
table_address += ((step_rate) >> 1) & 0xFFFC;
timer = (uint16_t)pgm_read_word(table_address)
- (((uint16_t)pgm_read_word(table_address + 2) * (uint8_t)(step_rate & 0x0007)) >> 3);
}
// (there is no need to limit the timer value here. All limits have been
// applied above, and AVR is able to keep up at 30khz Stepping ISR rate)
#endif
return timer;
}
// Calculate timing interval for the given step rate
static uint32_t calc_timer_interval(uint32_t step_rate);
static uint32_t calc_timer_interval(uint32_t step_rate, uint8_t &loops);
#if ENABLED(S_CURVE_ACCELERATION)
static void _calc_bezier_curve_coeffs(const int32_t v0, const int32_t v1, const uint32_t av);

Loading…
Cancel
Save