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 uint8_t Stepper::steps_per_isr = 1; // Count of steps to perform per Stepper ISR call
#endif #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) #if ENABLED(FREEZE_FEATURE)
bool Stepper::frozen; // = false bool Stepper::frozen; // = false
#endif #endif
@ -614,27 +618,26 @@ void Stepper::set_directions() {
TERN_(HAS_V_DIR, SET_STEP_DIR(V)); TERN_(HAS_V_DIR, SET_STEP_DIR(V));
TERN_(HAS_W_DIR, SET_STEP_DIR(W)); 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 // Because this is valid for the whole block we don't know
// what E steppers will step. Likely all. Set all. // what E steppers will step. Likely all. Set all.
if (motor_direction(E_AXIS)) { if (motor_direction(E_AXIS)) {
MIXER_STEPPER_LOOP(j) REV_E_DIR(j); #if ENABLED(MIXING_EXTRUDER)
MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
#else
REV_E_DIR(stepper_extruder);
#endif
count_direction.e = -1; count_direction.e = -1;
} }
else { else {
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j); #if ENABLED(MIXING_EXTRUDER)
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
#else
NORM_E_DIR(stepper_extruder);
#endif
count_direction.e = 1; count_direction.e = 1;
} }
#elif HAS_EXTRUDERS #endif // 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(); DIR_WAIT_AFTER();
} }
@ -1587,16 +1590,44 @@ void Stepper::isr() {
*/ */
min_ticks = HAL_timer_get_count(MF_TIMER_STEP) + hal_timer_t(TERN(__AVR__, 8, 1) * (STEPPER_TIMER_TICKS_PER_US)); min_ticks = HAL_timer_get_count(MF_TIMER_STEP) + hal_timer_t(TERN(__AVR__, 8, 1) * (STEPPER_TIMER_TICKS_PER_US));
/** #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 * NB: If for some reason the stepper monopolizes the MPU, eventually the
* loop to 10 iterations. Beyond that, there's no way to ensure correct pulse * timer will wrap around (and so will 'next_isr_ticks'). So, limit the
* timing, since the MCU isn't fast enough. * loop to 10 iterations. Beyond that, there's no way to ensure correct pulse
*/ * timing, since the MCU isn't fast enough.
if (!--max_loops) next_isr_ticks = min_ticks; */
if (!--max_loops) next_isr_ticks = min_ticks;
#endif
// Advance pulses if not enough time to wait for the next ISR // 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 // 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 // sure that the time has not arrived yet - Warrantied by the scheduler
@ -2091,44 +2122,56 @@ hal_timer_t Stepper::calc_timer_interval(uint32_t step_rate) {
// Get the timer interval and the number of loops to perform per tick // 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) { hal_timer_t Stepper::calc_multistep_timer_interval(uint32_t step_rate) {
#if MULTISTEPPING_LIMIT == 1
// Just make sure the step rate is doable #if ENABLED(OLD_ADAPTIVE_MULTISTEPPING)
NOMORE(step_rate, uint32_t(MAX_STEP_ISR_FREQUENCY_1X));
#else #if MULTISTEPPING_LIMIT == 1
// The stepping frequency limits for each multistepping rate // Just make sure the step rate is doable
static const uint32_t limit[] PROGMEM = { NOMORE(step_rate, uint32_t(MAX_STEP_ISR_FREQUENCY_1X));
( MAX_STEP_ISR_FREQUENCY_1X )
, ( MAX_STEP_ISR_FREQUENCY_2X >> 1)
#if MULTISTEPPING_LIMIT >= 4
, ( MAX_STEP_ISR_FREQUENCY_4X >> 2)
#endif
#if MULTISTEPPING_LIMIT >= 8
, ( MAX_STEP_ISR_FREQUENCY_8X >> 3)
#endif
#if MULTISTEPPING_LIMIT >= 16
, ( MAX_STEP_ISR_FREQUENCY_16X >> 4)
#endif
#if MULTISTEPPING_LIMIT >= 32
, ( MAX_STEP_ISR_FREQUENCY_32X >> 5)
#endif
#if MULTISTEPPING_LIMIT >= 64
, ( MAX_STEP_ISR_FREQUENCY_64X >> 6)
#endif
#if MULTISTEPPING_LIMIT >= 128
, (MAX_STEP_ISR_FREQUENCY_128X >> 7)
#endif
};
// Find a doable step rate using multistepping #else
uint8_t multistep = 1;
for (uint8_t i = 0; i < COUNT(limit) && step_rate > uint32_t(pgm_read_dword(&limit[i])); ++i) { // The stepping frequency limits for each multistepping rate
step_rate >>= 1; static const uint32_t limit[] PROGMEM = {
multistep <<= 1; ( MAX_STEP_ISR_FREQUENCY_1X )
} , (((F_CPU) / ISR_EXECUTION_CYCLES(1)) >> 1)
steps_per_isr = multistep; #if MULTISTEPPING_LIMIT >= 4
, (((F_CPU) / ISR_EXECUTION_CYCLES(2)) >> 2)
#endif
#if MULTISTEPPING_LIMIT >= 8
, (((F_CPU) / ISR_EXECUTION_CYCLES(3)) >> 3)
#endif
#if MULTISTEPPING_LIMIT >= 16
, (((F_CPU) / ISR_EXECUTION_CYCLES(4)) >> 4)
#endif
#if MULTISTEPPING_LIMIT >= 32
, (((F_CPU) / ISR_EXECUTION_CYCLES(5)) >> 5)
#endif
#if MULTISTEPPING_LIMIT >= 64
, (((F_CPU) / ISR_EXECUTION_CYCLES(6)) >> 6)
#endif
#if MULTISTEPPING_LIMIT >= 128
, (((F_CPU) / ISR_EXECUTION_CYCLES(7)) >> 7)
#endif
};
// Find a doable step rate using multistepping
uint8_t multistep = 1;
for (uint8_t i = 0; i < COUNT(limit) && step_rate > uint32_t(pgm_read_dword(&limit[i])); ++i) {
step_rate >>= 1;
multistep <<= 1;
}
steps_per_isr = multistep;
#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 #endif
@ -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. * have been done, so it is less time critical.
*/ */
hal_timer_t Stepper::block_phase_isr() { 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 // If no queued movements, just wait 1ms for the next block
hal_timer_t interval = (STEPPER_TIMER_RATE) / 1000UL; 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" #error "Expected at least one of MINIMUM_STEPPER_PULSE or MAXIMUM_STEPPER_RATE to be defined"
#endif #endif
// The loop takes the base time plus the time for all the bresenham logic for R pulses plus the time // The loop takes the base time plus the time for all the bresenham logic for 1 << 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: // 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)) #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 // 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 linear advance is enabled, then it is handled separately
#if ENABLED(LIN_ADVANCE) #if ENABLED(LIN_ADVANCE)
@ -241,24 +241,17 @@
#define ISR_LA_LOOP_CYCLES 0UL #define ISR_LA_LOOP_CYCLES 0UL
#endif #endif
// Now estimate the total ISR execution time in cycles given a step per ISR multiplier // 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) #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) // The maximum allowable stepping frequency when doing 1x stepping (in Hz)
#define MAX_STEP_ISR_FREQUENCY_128X ((F_CPU) / ISR_EXECUTION_CYCLES(7)) #define MAX_STEP_ISR_FREQUENCY_1X ((F_CPU) / ISR_EXECUTION_CYCLES(0))
#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))
#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 // 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. // 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 >> 1)
#define MIN_STEP_ISR_FREQUENCY (MAX_STEP_ISR_FREQUENCY_1X / 2)
// Number of axes that could be enabled/disabled. Dual/multiple steppers are combined.
#define ENABLE_COUNT (NUM_AXES + E_STEPPERS) #define ENABLE_COUNT (NUM_AXES + E_STEPPERS)
typedef bits_t(ENABLE_COUNT) ena_mask_t; typedef bits_t(ENABLE_COUNT) ena_mask_t;
@ -547,6 +540,10 @@ class Stepper {
static uint8_t steps_per_isr; static uint8_t steps_per_isr;
#endif #endif
#if DISABLED(OLD_ADAPTIVE_MULTISTEPPING)
static hal_timer_t time_spent_in_isr, time_spent_out_isr;
#endif
#if ENABLED(ADAPTIVE_STEP_SMOOTHING) #if ENABLED(ADAPTIVE_STEP_SMOOTHING)
static uint8_t oversampling_factor; // Oversampling factor (log2(multiplier)) to increase temporal resolution of axis static uint8_t oversampling_factor; // Oversampling factor (log2(multiplier)) to increase temporal resolution of axis
#else #else