1
0
mirror of https://github.com/MarlinFirmware/Marlin.git synced 2024-11-28 06:21:31 +00:00

️ Smart Adaptive Multi-Stepping (#25474)

This commit is contained in:
tombrazier 2023-03-18 10:34:53 +00:00 committed by GitHub
parent 6f2d8a3872
commit 3c88270361
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 124 additions and 71 deletions

View File

@ -194,6 +194,10 @@ uint32_t Stepper::acceleration_time, Stepper::deceleration_time;
uint8_t Stepper::steps_per_isr = 1; // Count of steps to perform per Stepper ISR call
#endif
#if DISABLED(OLD_ADAPTIVE_MULTISTEPPING)
hal_timer_t Stepper::time_spent_in_isr = 0, Stepper::time_spent_out_isr = 0;
#endif
#if ENABLED(FREEZE_FEATURE)
bool Stepper::frozen; // = false
#endif
@ -614,27 +618,26 @@ void Stepper::set_directions() {
TERN_(HAS_V_DIR, SET_STEP_DIR(V));
TERN_(HAS_W_DIR, SET_STEP_DIR(W));
#if ENABLED(MIXING_EXTRUDER)
#if HAS_EXTRUDERS
// 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)) {
#if ENABLED(MIXING_EXTRUDER)
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)) {
#else
REV_E_DIR(stepper_extruder);
#endif
count_direction.e = -1;
}
else {
#if ENABLED(MIXING_EXTRUDER)
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
#else
NORM_E_DIR(stepper_extruder);
#endif
count_direction.e = 1;
}
#endif
#endif // HAS_EXTRUDERS
DIR_WAIT_AFTER();
}
@ -1587,6 +1590,7 @@ void Stepper::isr() {
*/
min_ticks = HAL_timer_get_count(MF_TIMER_STEP) + hal_timer_t(TERN(__AVR__, 8, 1) * (STEPPER_TIMER_TICKS_PER_US));
#if ENABLED(OLD_ADAPTIVE_MULTISTEPPING)
/**
* NB: If for some reason the stepper monopolizes the MPU, eventually the
* timer will wrap around (and so will 'next_isr_ticks'). So, limit the
@ -1594,9 +1598,36 @@ void Stepper::isr() {
* timing, since the MCU isn't fast enough.
*/
if (!--max_loops) next_isr_ticks = min_ticks;
#endif
// Advance pulses if not enough time to wait for the next ISR
} while (next_isr_ticks < min_ticks);
} while (TERN(OLD_ADAPTIVE_MULTISTEPPING, true, --max_loops) && next_isr_ticks < min_ticks);
#if DISABLED(OLD_ADAPTIVE_MULTISTEPPING)
// Track the time spent in the ISR
const hal_timer_t time_spent = HAL_timer_get_count(MF_TIMER_STEP);
time_spent_in_isr += time_spent;
if (next_isr_ticks < min_ticks) {
next_isr_ticks = min_ticks;
// When forced out of the ISR, increase multi-stepping
#if MULTISTEPPING_LIMIT > 1
if (steps_per_isr < MULTISTEPPING_LIMIT) {
steps_per_isr <<= 1;
// ticks_nominal will need to be recalculated if we are in cruise phase
ticks_nominal = 0;
}
#endif
}
else {
// Track the time spent voluntarily outside the ISR
time_spent_out_isr += next_isr_ticks;
time_spent_out_isr -= time_spent;
}
#endif // !OLD_ADAPTIVE_MULTISTEPPING
// Now 'next_isr_ticks' contains the period to the next Stepper ISR - And we are
// sure that the time has not arrived yet - Warrantied by the scheduler
@ -2091,6 +2122,9 @@ hal_timer_t Stepper::calc_timer_interval(uint32_t step_rate) {
// Get the timer interval and the number of loops to perform per tick
hal_timer_t Stepper::calc_multistep_timer_interval(uint32_t step_rate) {
#if ENABLED(OLD_ADAPTIVE_MULTISTEPPING)
#if MULTISTEPPING_LIMIT == 1
// Just make sure the step rate is doable
@ -2101,24 +2135,24 @@ hal_timer_t Stepper::calc_multistep_timer_interval(uint32_t step_rate) {
// 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)
, (((F_CPU) / ISR_EXECUTION_CYCLES(1)) >> 1)
#if MULTISTEPPING_LIMIT >= 4
, ( MAX_STEP_ISR_FREQUENCY_4X >> 2)
, (((F_CPU) / ISR_EXECUTION_CYCLES(2)) >> 2)
#endif
#if MULTISTEPPING_LIMIT >= 8
, ( MAX_STEP_ISR_FREQUENCY_8X >> 3)
, (((F_CPU) / ISR_EXECUTION_CYCLES(3)) >> 3)
#endif
#if MULTISTEPPING_LIMIT >= 16
, ( MAX_STEP_ISR_FREQUENCY_16X >> 4)
, (((F_CPU) / ISR_EXECUTION_CYCLES(4)) >> 4)
#endif
#if MULTISTEPPING_LIMIT >= 32
, ( MAX_STEP_ISR_FREQUENCY_32X >> 5)
, (((F_CPU) / ISR_EXECUTION_CYCLES(5)) >> 5)
#endif
#if MULTISTEPPING_LIMIT >= 64
, ( MAX_STEP_ISR_FREQUENCY_64X >> 6)
, (((F_CPU) / ISR_EXECUTION_CYCLES(6)) >> 6)
#endif
#if MULTISTEPPING_LIMIT >= 128
, (MAX_STEP_ISR_FREQUENCY_128X >> 7)
, (((F_CPU) / ISR_EXECUTION_CYCLES(7)) >> 7)
#endif
};
@ -2132,6 +2166,15 @@ hal_timer_t Stepper::calc_multistep_timer_interval(uint32_t step_rate) {
#endif
#elif MULTISTEPPING_LIMIT > 1
uint8_t loops = steps_per_isr;
if (MULTISTEPPING_LIMIT >= 16 && loops >= 16) { step_rate >>= 4; loops >>= 4; }
if (MULTISTEPPING_LIMIT >= 4 && loops >= 4) { step_rate >>= 2; loops >>= 2; }
if (MULTISTEPPING_LIMIT >= 2 && loops >= 2) { step_rate >>= 1; }
#endif
return calc_timer_interval(step_rate);
}
@ -2141,6 +2184,19 @@ hal_timer_t Stepper::calc_multistep_timer_interval(uint32_t step_rate) {
* have been done, so it is less time critical.
*/
hal_timer_t Stepper::block_phase_isr() {
#if DISABLED(OLD_ADAPTIVE_MULTISTEPPING)
// If the ISR uses < 50% of MPU time, halve multi-stepping
const hal_timer_t time_spent = HAL_timer_get_count(MF_TIMER_STEP);
#if MULTISTEPPING_LIMIT > 1
if (steps_per_isr > 1 && time_spent_out_isr >= time_spent_in_isr + time_spent) {
steps_per_isr >>= 1;
// ticks_nominal will need to be recalculated if we are in cruise phase
ticks_nominal = 0;
}
#endif
time_spent_in_isr = -time_spent; // unsigned but guaranteed to be +ve when needed
time_spent_out_isr = 0;
#endif
// If no queued movements, just wait 1ms for the next block
hal_timer_t interval = (STEPPER_TIMER_RATE) / 1000UL;

View File

@ -212,12 +212,12 @@
#error "Expected at least one of MINIMUM_STEPPER_PULSE or MAXIMUM_STEPPER_RATE to be defined"
#endif
// The loop takes the base time plus the time for all the bresenham logic for R pulses plus the time
// between pulses for (R-1) pulses. But the user could be enforcing a minimum time so the loop time is:
// The loop takes the base time plus the time for all the bresenham logic for 1 << R pulses plus the time
// between pulses for ((1 << R) - 1) pulses. But the user could be enforcing a minimum time so the loop time is:
#define ISR_LOOP_CYCLES(R) ((ISR_LOOP_BASE_CYCLES + MIN_ISR_LOOP_CYCLES + MIN_STEPPER_PULSE_CYCLES) * ((1UL << R) - 1) + _MAX(MIN_ISR_LOOP_CYCLES, MIN_STEPPER_PULSE_CYCLES))
// Model input shaping as an extra loop call
#define ISR_SHAPING_LOOP_CYCLES(R) (TERN0(HAS_SHAPING, ((ISR_LOOP_BASE_CYCLES) + TERN0(INPUT_SHAPING_X, ISR_X_STEPPER_CYCLES) + TERN0(INPUT_SHAPING_Y, ISR_Y_STEPPER_CYCLES)) << R))
#define ISR_SHAPING_LOOP_CYCLES(R) (TERN0(HAS_SHAPING, (ISR_LOOP_BASE_CYCLES + TERN0(INPUT_SHAPING_X, ISR_X_STEPPER_CYCLES) + TERN0(INPUT_SHAPING_Y, ISR_Y_STEPPER_CYCLES)) << R))
// If linear advance is enabled, then it is handled separately
#if ENABLED(LIN_ADVANCE)
@ -241,24 +241,17 @@
#define ISR_LA_LOOP_CYCLES 0UL
#endif
// Now estimate the total ISR execution time in cycles given a step per ISR multiplier
#define ISR_EXECUTION_CYCLES(R) (((ISR_BASE_CYCLES + ISR_S_CURVE_CYCLES + ISR_SHAPING_BASE_CYCLES + ISR_LOOP_CYCLES(R) + ISR_SHAPING_LOOP_CYCLES(R) + ISR_LA_BASE_CYCLES + ISR_LA_LOOP_CYCLES)) >> R)
// Estimate the total ISR execution time in cycles given a step-per-ISR shift multiplier
#define ISR_EXECUTION_CYCLES(R) ((ISR_BASE_CYCLES + ISR_S_CURVE_CYCLES + ISR_SHAPING_BASE_CYCLES + ISR_LOOP_CYCLES(R) + ISR_SHAPING_LOOP_CYCLES(R) + ISR_LA_BASE_CYCLES + ISR_LA_LOOP_CYCLES) >> R)
// The maximum allowable stepping frequency when doing x128-x1 stepping (in Hz)
#define MAX_STEP_ISR_FREQUENCY_128X ((F_CPU) / ISR_EXECUTION_CYCLES(7))
#define MAX_STEP_ISR_FREQUENCY_64X ((F_CPU) / ISR_EXECUTION_CYCLES(6))
#define MAX_STEP_ISR_FREQUENCY_32X ((F_CPU) / ISR_EXECUTION_CYCLES(5))
#define MAX_STEP_ISR_FREQUENCY_16X ((F_CPU) / ISR_EXECUTION_CYCLES(4))
#define MAX_STEP_ISR_FREQUENCY_8X ((F_CPU) / ISR_EXECUTION_CYCLES(3))
#define MAX_STEP_ISR_FREQUENCY_4X ((F_CPU) / ISR_EXECUTION_CYCLES(2))
#define MAX_STEP_ISR_FREQUENCY_2X ((F_CPU) / ISR_EXECUTION_CYCLES(1))
// The maximum allowable stepping frequency when doing 1x stepping (in Hz)
#define MAX_STEP_ISR_FREQUENCY_1X ((F_CPU) / ISR_EXECUTION_CYCLES(0))
// The minimum step ISR rate used by ADAPTIVE_STEP_SMOOTHING to target 50% CPU usage
// This does not account for the possibility of multi-stepping.
// Should a MULTISTEPPING_LIMIT of 1 should be required with ADAPTIVE_STEP_SMOOTHING?
#define MIN_STEP_ISR_FREQUENCY (MAX_STEP_ISR_FREQUENCY_1X / 2)
#define MIN_STEP_ISR_FREQUENCY (MAX_STEP_ISR_FREQUENCY_1X >> 1)
// Number of axes that could be enabled/disabled. Dual/multiple steppers are combined.
#define ENABLE_COUNT (NUM_AXES + E_STEPPERS)
typedef bits_t(ENABLE_COUNT) ena_mask_t;
@ -547,6 +540,10 @@ class Stepper {
static uint8_t steps_per_isr;
#endif
#if DISABLED(OLD_ADAPTIVE_MULTISTEPPING)
static hal_timer_t time_spent_in_isr, time_spent_out_isr;
#endif
#if ENABLED(ADAPTIVE_STEP_SMOOTHING)
static uint8_t oversampling_factor; // Oversampling factor (log2(multiplier)) to increase temporal resolution of axis
#else