Skip to content

Commit b9113be

Browse files
tombrazierthinkyhead
authored andcommitted
⚡️ Smart Adaptive Multi-Stepping (MarlinFirmware#25474)
1 parent bfbf780 commit b9113be

File tree

2 files changed

+124
-71
lines changed

2 files changed

+124
-71
lines changed

Marlin/src/module/stepper.cpp

+111-55
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,10 @@ uint32_t Stepper::acceleration_time, Stepper::deceleration_time;
194194
uint8_t Stepper::steps_per_isr = 1; // Count of steps to perform per Stepper ISR call
195195
#endif
196196

197+
#if DISABLED(OLD_ADAPTIVE_MULTISTEPPING)
198+
hal_timer_t Stepper::time_spent_in_isr = 0, Stepper::time_spent_out_isr = 0;
199+
#endif
200+
197201
#if ENABLED(FREEZE_FEATURE)
198202
bool Stepper::frozen; // = false
199203
#endif
@@ -614,27 +618,26 @@ void Stepper::set_directions() {
614618
TERN_(HAS_V_DIR, SET_STEP_DIR(V));
615619
TERN_(HAS_W_DIR, SET_STEP_DIR(W));
616620

617-
#if ENABLED(MIXING_EXTRUDER)
621+
#if HAS_EXTRUDERS
618622
// Because this is valid for the whole block we don't know
619623
// what E steppers will step. Likely all. Set all.
620624
if (motor_direction(E_AXIS)) {
621-
MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
622-
count_direction.e = -1;
623-
}
624-
else {
625-
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
626-
count_direction.e = 1;
627-
}
628-
#elif HAS_EXTRUDERS
629-
if (motor_direction(E_AXIS)) {
630-
REV_E_DIR(stepper_extruder);
625+
#if ENABLED(MIXING_EXTRUDER)
626+
MIXER_STEPPER_LOOP(j) REV_E_DIR(j);
627+
#else
628+
REV_E_DIR(stepper_extruder);
629+
#endif
631630
count_direction.e = -1;
632631
}
633632
else {
634-
NORM_E_DIR(stepper_extruder);
633+
#if ENABLED(MIXING_EXTRUDER)
634+
MIXER_STEPPER_LOOP(j) NORM_E_DIR(j);
635+
#else
636+
NORM_E_DIR(stepper_extruder);
637+
#endif
635638
count_direction.e = 1;
636639
}
637-
#endif
640+
#endif // HAS_EXTRUDERS
638641

639642
DIR_WAIT_AFTER();
640643
}
@@ -1587,16 +1590,44 @@ void Stepper::isr() {
15871590
*/
15881591
min_ticks = HAL_timer_get_count(MF_TIMER_STEP) + hal_timer_t(TERN(__AVR__, 8, 1) * (STEPPER_TIMER_TICKS_PER_US));
15891592

1590-
/**
1591-
* NB: If for some reason the stepper monopolizes the MPU, eventually the
1592-
* timer will wrap around (and so will 'next_isr_ticks'). So, limit the
1593-
* loop to 10 iterations. Beyond that, there's no way to ensure correct pulse
1594-
* timing, since the MCU isn't fast enough.
1595-
*/
1596-
if (!--max_loops) next_isr_ticks = min_ticks;
1593+
#if ENABLED(OLD_ADAPTIVE_MULTISTEPPING)
1594+
/**
1595+
* NB: If for some reason the stepper monopolizes the MPU, eventually the
1596+
* timer will wrap around (and so will 'next_isr_ticks'). So, limit the
1597+
* loop to 10 iterations. Beyond that, there's no way to ensure correct pulse
1598+
* timing, since the MCU isn't fast enough.
1599+
*/
1600+
if (!--max_loops) next_isr_ticks = min_ticks;
1601+
#endif
15971602

15981603
// Advance pulses if not enough time to wait for the next ISR
1599-
} while (next_isr_ticks < min_ticks);
1604+
} while (TERN(OLD_ADAPTIVE_MULTISTEPPING, true, --max_loops) && next_isr_ticks < min_ticks);
1605+
1606+
#if DISABLED(OLD_ADAPTIVE_MULTISTEPPING)
1607+
1608+
// Track the time spent in the ISR
1609+
const hal_timer_t time_spent = HAL_timer_get_count(MF_TIMER_STEP);
1610+
time_spent_in_isr += time_spent;
1611+
1612+
if (next_isr_ticks < min_ticks) {
1613+
next_isr_ticks = min_ticks;
1614+
1615+
// When forced out of the ISR, increase multi-stepping
1616+
#if MULTISTEPPING_LIMIT > 1
1617+
if (steps_per_isr < MULTISTEPPING_LIMIT) {
1618+
steps_per_isr <<= 1;
1619+
// ticks_nominal will need to be recalculated if we are in cruise phase
1620+
ticks_nominal = 0;
1621+
}
1622+
#endif
1623+
}
1624+
else {
1625+
// Track the time spent voluntarily outside the ISR
1626+
time_spent_out_isr += next_isr_ticks;
1627+
time_spent_out_isr -= time_spent;
1628+
}
1629+
1630+
#endif // !OLD_ADAPTIVE_MULTISTEPPING
16001631

16011632
// Now 'next_isr_ticks' contains the period to the next Stepper ISR - And we are
16021633
// 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) {
20912122

20922123
// Get the timer interval and the number of loops to perform per tick
20932124
hal_timer_t Stepper::calc_multistep_timer_interval(uint32_t step_rate) {
2094-
#if MULTISTEPPING_LIMIT == 1
20952125

2096-
// Just make sure the step rate is doable
2097-
NOMORE(step_rate, uint32_t(MAX_STEP_ISR_FREQUENCY_1X));
2126+
#if ENABLED(OLD_ADAPTIVE_MULTISTEPPING)
20982127

2099-
#else
2128+
#if MULTISTEPPING_LIMIT == 1
21002129

2101-
// The stepping frequency limits for each multistepping rate
2102-
static const uint32_t limit[] PROGMEM = {
2103-
( MAX_STEP_ISR_FREQUENCY_1X )
2104-
, ( MAX_STEP_ISR_FREQUENCY_2X >> 1)
2105-
#if MULTISTEPPING_LIMIT >= 4
2106-
, ( MAX_STEP_ISR_FREQUENCY_4X >> 2)
2107-
#endif
2108-
#if MULTISTEPPING_LIMIT >= 8
2109-
, ( MAX_STEP_ISR_FREQUENCY_8X >> 3)
2110-
#endif
2111-
#if MULTISTEPPING_LIMIT >= 16
2112-
, ( MAX_STEP_ISR_FREQUENCY_16X >> 4)
2113-
#endif
2114-
#if MULTISTEPPING_LIMIT >= 32
2115-
, ( MAX_STEP_ISR_FREQUENCY_32X >> 5)
2116-
#endif
2117-
#if MULTISTEPPING_LIMIT >= 64
2118-
, ( MAX_STEP_ISR_FREQUENCY_64X >> 6)
2119-
#endif
2120-
#if MULTISTEPPING_LIMIT >= 128
2121-
, (MAX_STEP_ISR_FREQUENCY_128X >> 7)
2122-
#endif
2123-
};
2130+
// Just make sure the step rate is doable
2131+
NOMORE(step_rate, uint32_t(MAX_STEP_ISR_FREQUENCY_1X));
21242132

2125-
// Find a doable step rate using multistepping
2126-
uint8_t multistep = 1;
2127-
for (uint8_t i = 0; i < COUNT(limit) && step_rate > uint32_t(pgm_read_dword(&limit[i])); ++i) {
2128-
step_rate >>= 1;
2129-
multistep <<= 1;
2130-
}
2131-
steps_per_isr = multistep;
2133+
#else
2134+
2135+
// The stepping frequency limits for each multistepping rate
2136+
static const uint32_t limit[] PROGMEM = {
2137+
( MAX_STEP_ISR_FREQUENCY_1X )
2138+
, (((F_CPU) / ISR_EXECUTION_CYCLES(1)) >> 1)
2139+
#if MULTISTEPPING_LIMIT >= 4
2140+
, (((F_CPU) / ISR_EXECUTION_CYCLES(2)) >> 2)
2141+
#endif
2142+
#if MULTISTEPPING_LIMIT >= 8
2143+
, (((F_CPU) / ISR_EXECUTION_CYCLES(3)) >> 3)
2144+
#endif
2145+
#if MULTISTEPPING_LIMIT >= 16
2146+
, (((F_CPU) / ISR_EXECUTION_CYCLES(4)) >> 4)
2147+
#endif
2148+
#if MULTISTEPPING_LIMIT >= 32
2149+
, (((F_CPU) / ISR_EXECUTION_CYCLES(5)) >> 5)
2150+
#endif
2151+
#if MULTISTEPPING_LIMIT >= 64
2152+
, (((F_CPU) / ISR_EXECUTION_CYCLES(6)) >> 6)
2153+
#endif
2154+
#if MULTISTEPPING_LIMIT >= 128
2155+
, (((F_CPU) / ISR_EXECUTION_CYCLES(7)) >> 7)
2156+
#endif
2157+
};
2158+
2159+
// Find a doable step rate using multistepping
2160+
uint8_t multistep = 1;
2161+
for (uint8_t i = 0; i < COUNT(limit) && step_rate > uint32_t(pgm_read_dword(&limit[i])); ++i) {
2162+
step_rate >>= 1;
2163+
multistep <<= 1;
2164+
}
2165+
steps_per_isr = multistep;
2166+
2167+
#endif
2168+
2169+
#elif MULTISTEPPING_LIMIT > 1
2170+
2171+
uint8_t loops = steps_per_isr;
2172+
if (MULTISTEPPING_LIMIT >= 16 && loops >= 16) { step_rate >>= 4; loops >>= 4; }
2173+
if (MULTISTEPPING_LIMIT >= 4 && loops >= 4) { step_rate >>= 2; loops >>= 2; }
2174+
if (MULTISTEPPING_LIMIT >= 2 && loops >= 2) { step_rate >>= 1; }
21322175

21332176
#endif
21342177

@@ -2141,6 +2184,19 @@ hal_timer_t Stepper::calc_multistep_timer_interval(uint32_t step_rate) {
21412184
* have been done, so it is less time critical.
21422185
*/
21432186
hal_timer_t Stepper::block_phase_isr() {
2187+
#if DISABLED(OLD_ADAPTIVE_MULTISTEPPING)
2188+
// If the ISR uses < 50% of MPU time, halve multi-stepping
2189+
const hal_timer_t time_spent = HAL_timer_get_count(MF_TIMER_STEP);
2190+
#if MULTISTEPPING_LIMIT > 1
2191+
if (steps_per_isr > 1 && time_spent_out_isr >= time_spent_in_isr + time_spent) {
2192+
steps_per_isr >>= 1;
2193+
// ticks_nominal will need to be recalculated if we are in cruise phase
2194+
ticks_nominal = 0;
2195+
}
2196+
#endif
2197+
time_spent_in_isr = -time_spent; // unsigned but guaranteed to be +ve when needed
2198+
time_spent_out_isr = 0;
2199+
#endif
21442200

21452201
// If no queued movements, just wait 1ms for the next block
21462202
hal_timer_t interval = (STEPPER_TIMER_RATE) / 1000UL;

Marlin/src/module/stepper.h

+13-16
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,12 @@
212212
#error "Expected at least one of MINIMUM_STEPPER_PULSE or MAXIMUM_STEPPER_RATE to be defined"
213213
#endif
214214

215-
// The loop takes the base time plus the time for all the bresenham logic for R pulses plus the time
216-
// between pulses for (R-1) pulses. But the user could be enforcing a minimum time so the loop time is:
215+
// The loop takes the base time plus the time for all the bresenham logic for 1 << R pulses plus the time
216+
// between pulses for ((1 << R) - 1) pulses. But the user could be enforcing a minimum time so the loop time is:
217217
#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))
218218

219219
// Model input shaping as an extra loop call
220-
#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))
220+
#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))
221221

222222
// If linear advance is enabled, then it is handled separately
223223
#if ENABLED(LIN_ADVANCE)
@@ -241,24 +241,17 @@
241241
#define ISR_LA_LOOP_CYCLES 0UL
242242
#endif
243243

244-
// Now estimate the total ISR execution time in cycles given a step per ISR multiplier
245-
#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)
244+
// Estimate the total ISR execution time in cycles given a step-per-ISR shift multiplier
245+
#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)
246246

247-
// The maximum allowable stepping frequency when doing x128-x1 stepping (in Hz)
248-
#define MAX_STEP_ISR_FREQUENCY_128X ((F_CPU) / ISR_EXECUTION_CYCLES(7))
249-
#define MAX_STEP_ISR_FREQUENCY_64X ((F_CPU) / ISR_EXECUTION_CYCLES(6))
250-
#define MAX_STEP_ISR_FREQUENCY_32X ((F_CPU) / ISR_EXECUTION_CYCLES(5))
251-
#define MAX_STEP_ISR_FREQUENCY_16X ((F_CPU) / ISR_EXECUTION_CYCLES(4))
252-
#define MAX_STEP_ISR_FREQUENCY_8X ((F_CPU) / ISR_EXECUTION_CYCLES(3))
253-
#define MAX_STEP_ISR_FREQUENCY_4X ((F_CPU) / ISR_EXECUTION_CYCLES(2))
254-
#define MAX_STEP_ISR_FREQUENCY_2X ((F_CPU) / ISR_EXECUTION_CYCLES(1))
255-
#define MAX_STEP_ISR_FREQUENCY_1X ((F_CPU) / ISR_EXECUTION_CYCLES(0))
247+
// The maximum allowable stepping frequency when doing 1x stepping (in Hz)
248+
#define MAX_STEP_ISR_FREQUENCY_1X ((F_CPU) / ISR_EXECUTION_CYCLES(0))
256249

257250
// The minimum step ISR rate used by ADAPTIVE_STEP_SMOOTHING to target 50% CPU usage
258251
// This does not account for the possibility of multi-stepping.
259-
// Should a MULTISTEPPING_LIMIT of 1 should be required with ADAPTIVE_STEP_SMOOTHING?
260-
#define MIN_STEP_ISR_FREQUENCY (MAX_STEP_ISR_FREQUENCY_1X / 2)
252+
#define MIN_STEP_ISR_FREQUENCY (MAX_STEP_ISR_FREQUENCY_1X >> 1)
261253

254+
// Number of axes that could be enabled/disabled. Dual/multiple steppers are combined.
262255
#define ENABLE_COUNT (NUM_AXES + E_STEPPERS)
263256
typedef bits_t(ENABLE_COUNT) ena_mask_t;
264257

@@ -547,6 +540,10 @@ class Stepper {
547540
static uint8_t steps_per_isr;
548541
#endif
549542

543+
#if DISABLED(OLD_ADAPTIVE_MULTISTEPPING)
544+
static hal_timer_t time_spent_in_isr, time_spent_out_isr;
545+
#endif
546+
550547
#if ENABLED(ADAPTIVE_STEP_SMOOTHING)
551548
static uint8_t oversampling_factor; // Oversampling factor (log2(multiplier)) to increase temporal resolution of axis
552549
#else

0 commit comments

Comments
 (0)