diff options
-rw-r--r-- | klippy/stepper.py | 35 | ||||
-rw-r--r-- | src/Kconfig | 2 | ||||
-rw-r--r-- | src/ar100/Kconfig | 2 | ||||
-rw-r--r-- | src/atsam/Kconfig | 2 | ||||
-rw-r--r-- | src/atsamd/Kconfig | 2 | ||||
-rw-r--r-- | src/hc32f460/Kconfig | 2 | ||||
-rw-r--r-- | src/lpc176x/Kconfig | 2 | ||||
-rw-r--r-- | src/rp2040/Kconfig | 2 | ||||
-rw-r--r-- | src/stepper.c | 60 | ||||
-rw-r--r-- | src/stm32/Kconfig | 2 |
10 files changed, 74 insertions, 37 deletions
diff --git a/klippy/stepper.py b/klippy/stepper.py index fd44effb..a2f8c0a6 100644 --- a/klippy/stepper.py +++ b/klippy/stepper.py @@ -1,6 +1,6 @@ # Printer stepper support # -# Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net> +# Copyright (C) 2016-2025 Kevin O'Connor <kevin@koconnor.net> # # This file may be distributed under the terms of the GNU GPLv3 license. import math, logging, collections @@ -14,7 +14,8 @@ class error(Exception): # Steppers ###################################################################### -MIN_BOTH_EDGE_DURATION = 0.000000200 +MIN_BOTH_EDGE_DURATION = 0.000000500 +MIN_OPTIMIZED_BOTH_EDGE_DURATION = 0.000000150 # Interface to low-level mcu and chelper code class MCU_stepper: @@ -75,13 +76,33 @@ class MCU_stepper: if self._step_pulse_duration is None: self._step_pulse_duration = .000002 invert_step = self._invert_step - sbe = int(self._mcu.get_constants().get('STEPPER_BOTH_EDGE', '0')) - if (self._req_step_both_edge and sbe - and self._step_pulse_duration <= MIN_BOTH_EDGE_DURATION): - # Enable stepper optimized step on both edges + # Check if can enable "step on both edges" + constants = self._mcu.get_constants() + ssbe = int(constants.get('STEPPER_STEP_BOTH_EDGE', '0')) + sbe = int(constants.get('STEPPER_BOTH_EDGE', '0')) + sou = int(constants.get('STEPPER_OPTIMIZED_UNSTEP', '0')) + want_both_edges = self._req_step_both_edge + if self._step_pulse_duration > MIN_BOTH_EDGE_DURATION: + # If user has requested a very large step pulse duration + # then disable step on both edges (rise and fall times may + # not be symetric) + want_both_edges = False + elif sbe and self._step_pulse_duration>MIN_OPTIMIZED_BOTH_EDGE_DURATION: + # Older MCU and user has requested large pulse duration + want_both_edges = False + elif not sbe and not ssbe: + # Older MCU that doesn't support step on both edges + want_both_edges = False + elif sou: + # MCU has optimized step/unstep - better to use that + want_both_edges = False + if want_both_edges: self._step_both_edge = True - self._step_pulse_duration = 0. invert_step = -1 + if sbe: + # Older MCU requires setting step_pulse_ticks=0 to enable + self._step_pulse_duration = 0. + # Configure stepper object step_pulse_ticks = self._mcu.seconds_to_clock(self._step_pulse_duration) self._mcu.add_config_cmd( "config_stepper oid=%d step_pin=%s dir_pin=%s invert_step=%d" diff --git a/src/Kconfig b/src/Kconfig index 9923ed7f..0e4ea1c5 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -229,7 +229,7 @@ config HAVE_STRICT_TIMING bool config HAVE_CHIPID bool -config HAVE_STEPPER_BOTH_EDGE +config HAVE_STEPPER_OPTIMIZED_BOTH_EDGE bool config HAVE_BOOTLOADER_REQUEST bool diff --git a/src/ar100/Kconfig b/src/ar100/Kconfig index bd305083..4967fb6f 100644 --- a/src/ar100/Kconfig +++ b/src/ar100/Kconfig @@ -7,7 +7,7 @@ config AR100_SELECT default y select HAVE_GPIO select HAVE_GPIO_SPI - select HAVE_STEPPER_BOTH_EDGE + select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE select HAVE_LIMITED_CODE_SIZE config BOARD_DIRECTORY diff --git a/src/atsam/Kconfig b/src/atsam/Kconfig index 4c20c244..6447874e 100644 --- a/src/atsam/Kconfig +++ b/src/atsam/Kconfig @@ -12,7 +12,7 @@ config ATSAM_SELECT select HAVE_GPIO_HARD_PWM if !MACH_SAME70 select HAVE_STRICT_TIMING select HAVE_CHIPID - select HAVE_STEPPER_BOTH_EDGE + select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE select HAVE_BOOTLOADER_REQUEST config BOARD_DIRECTORY diff --git a/src/atsamd/Kconfig b/src/atsamd/Kconfig index 5711566a..991e0376 100644 --- a/src/atsamd/Kconfig +++ b/src/atsamd/Kconfig @@ -12,7 +12,7 @@ config ATSAMD_SELECT select HAVE_GPIO_HARD_PWM if MACH_SAMX2 select HAVE_STRICT_TIMING select HAVE_CHIPID - select HAVE_STEPPER_BOTH_EDGE + select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE select HAVE_BOOTLOADER_REQUEST config HAVE_SERCOM diff --git a/src/hc32f460/Kconfig b/src/hc32f460/Kconfig index 5dee95f9..26a4fe64 100644 --- a/src/hc32f460/Kconfig +++ b/src/hc32f460/Kconfig @@ -9,7 +9,7 @@ config HC32F460_SELECT select HAVE_GPIO_ADC select HAVE_STRICT_TIMING select HAVE_GPIO_HARD_PWM - select HAVE_STEPPER_BOTH_EDGE + select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE config BOARD_DIRECTORY string diff --git a/src/lpc176x/Kconfig b/src/lpc176x/Kconfig index ee758030..753b8b22 100644 --- a/src/lpc176x/Kconfig +++ b/src/lpc176x/Kconfig @@ -12,7 +12,7 @@ config LPC_SELECT select HAVE_GPIO_HARD_PWM select HAVE_STRICT_TIMING select HAVE_CHIPID - select HAVE_STEPPER_BOTH_EDGE + select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE select HAVE_BOOTLOADER_REQUEST config BOARD_DIRECTORY diff --git a/src/rp2040/Kconfig b/src/rp2040/Kconfig index 5ec7c918..97c31b85 100644 --- a/src/rp2040/Kconfig +++ b/src/rp2040/Kconfig @@ -12,7 +12,7 @@ config RPXXXX_SELECT select HAVE_STRICT_TIMING select HAVE_CHIPID select HAVE_GPIO_HARD_PWM - select HAVE_STEPPER_BOTH_EDGE + select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE select HAVE_BOOTLOADER_REQUEST config BOARD_DIRECTORY diff --git a/src/stepper.c b/src/stepper.c index 00a8ff01..f6f798c6 100644 --- a/src/stepper.c +++ b/src/stepper.c @@ -1,6 +1,6 @@ // Handling of stepper drivers. // -// Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net> +// Copyright (C) 2016-2025 Kevin O'Connor <kevin@koconnor.net> // // This file may be distributed under the terms of the GNU GPLv3 license. @@ -14,17 +14,18 @@ #include "stepper.h" // stepper_event #include "trsync.h" // trsync_add_signal -#if CONFIG_INLINE_STEPPER_HACK && CONFIG_HAVE_STEPPER_BOTH_EDGE - #define HAVE_SINGLE_SCHEDULE 1 +DECL_CONSTANT("STEPPER_STEP_BOTH_EDGE", 1); + +#if CONFIG_INLINE_STEPPER_HACK && CONFIG_HAVE_STEPPER_OPTIMIZED_BOTH_EDGE + #define HAVE_OPTIMIZED_PATH 1 #define HAVE_EDGE_OPTIMIZATION 1 #define HAVE_AVR_OPTIMIZATION 0 - DECL_CONSTANT("STEPPER_BOTH_EDGE", 1); #elif CONFIG_INLINE_STEPPER_HACK && CONFIG_MACH_AVR - #define HAVE_SINGLE_SCHEDULE 1 + #define HAVE_OPTIMIZED_PATH 1 #define HAVE_EDGE_OPTIMIZATION 0 #define HAVE_AVR_OPTIMIZATION 1 #else - #define HAVE_SINGLE_SCHEDULE 0 + #define HAVE_OPTIMIZED_PATH 0 #define HAVE_EDGE_OPTIMIZATION 0 #define HAVE_AVR_OPTIMIZATION 0 #endif @@ -57,7 +58,7 @@ enum { POSITION_BIAS=0x40000000 }; enum { SF_LAST_DIR=1<<0, SF_NEXT_DIR=1<<1, SF_INVERT_STEP=1<<2, SF_NEED_RESET=1<<3, - SF_SINGLE_SCHED=1<<4, SF_HAVE_ADD=1<<5 + SF_SINGLE_SCHED=1<<4, SF_OPTIMIZED_PATH=1<<5, SF_HAVE_ADD=1<<6 }; // Setup a stepper for the next move in its queue @@ -75,7 +76,7 @@ stepper_load_next(struct stepper *s) struct stepper_move *m = container_of(mn, struct stepper_move, node); s->add = m->add; s->interval = m->interval + m->add; - if (HAVE_SINGLE_SCHEDULE && s->flags & SF_SINGLE_SCHED) { + if (HAVE_OPTIMIZED_PATH && s->flags & SF_OPTIMIZED_PATH) { s->time.waketime += m->interval; if (HAVE_AVR_OPTIMIZATION) s->flags = m->add ? s->flags|SF_HAVE_ADD : s->flags & ~SF_HAVE_ADD; @@ -85,7 +86,7 @@ stepper_load_next(struct stepper *s) // twice as many events. s->next_step_time += m->interval; s->time.waketime = s->next_step_time; - s->count = (uint32_t)m->count * 2; + s->count = s->flags & SF_SINGLE_SCHED ? m->count : (uint32_t)m->count*2; } // Add all steps to s->position (stepper_get_position() can calc mid-move) if (m->flags & MF_DIR) { @@ -99,8 +100,14 @@ stepper_load_next(struct stepper *s) return SF_RESCHEDULE; } +// Edge optimization only enabled when fastest rate notably slower than 100ns +#define EDGE_STEP_TICKS DIV_ROUND_UP(CONFIG_CLOCK_FREQ, 8000000) +#if HAVE_EDGE_OPTIMIZATION + DECL_CONSTANT("STEPPER_OPTIMIZED_EDGE", EDGE_STEP_TICKS); +#endif + // Optimized step function to step on each step pin edge -uint_fast8_t +static uint_fast8_t stepper_event_edge(struct timer *t) { struct stepper *s = container_of(t, struct stepper, time); @@ -115,7 +122,10 @@ stepper_event_edge(struct timer *t) return stepper_load_next(s); } -#define AVR_STEP_INSNS 40 // minimum instructions between step gpio pulses +#define AVR_STEP_TICKS 40 // minimum instructions between step gpio pulses +#if HAVE_AVR_OPTIMIZATION + DECL_CONSTANT("STEPPER_OPTIMIZED_UNSTEP", AVR_STEP_TICKS); +#endif // AVR optimized step function static uint_fast8_t @@ -137,8 +147,8 @@ stepper_event_avr(struct timer *t) return ret; } -// Regular "double scheduled" step function -uint_fast8_t +// Regular "fully scheduled" step function +static uint_fast8_t stepper_event_full(struct timer *t) { struct stepper *s = container_of(t, struct stepper, time); @@ -146,7 +156,7 @@ stepper_event_full(struct timer *t) uint32_t curtime = timer_read_time(); uint32_t min_next_time = curtime + s->step_pulse_ticks; s->count--; - if (likely(s->count & 1)) + if (likely(s->count & 1 && !(s->flags & SF_SINGLE_SCHED))) // Schedule unstep event goto reschedule_min; if (likely(s->count)) { @@ -186,20 +196,23 @@ command_config_stepper(uint32_t *args) { struct stepper *s = oid_alloc(args[0], command_config_stepper, sizeof(*s)); int_fast8_t invert_step = args[3]; - s->flags = invert_step > 0 ? SF_INVERT_STEP : 0; + if (invert_step > 0) + s->flags = SF_INVERT_STEP; + else if (invert_step < 0) + s->flags = SF_SINGLE_SCHED; s->step_pin = gpio_out_setup(args[1], s->flags & SF_INVERT_STEP); s->dir_pin = gpio_out_setup(args[2], 0); s->position = -POSITION_BIAS; s->step_pulse_ticks = args[4]; move_queue_setup(&s->mq, sizeof(struct stepper_move)); if (HAVE_EDGE_OPTIMIZATION) { - if (!s->step_pulse_ticks && invert_step < 0) - s->flags |= SF_SINGLE_SCHED; + if (invert_step < 0 && s->step_pulse_ticks <= EDGE_STEP_TICKS) + s->flags |= SF_OPTIMIZED_PATH; else s->time.func = stepper_event_full; } else if (HAVE_AVR_OPTIMIZATION) { - if (s->step_pulse_ticks <= AVR_STEP_INSNS) - s->flags |= SF_SINGLE_SCHED; + if (invert_step >= 0 && s->step_pulse_ticks <= AVR_STEP_TICKS) + s->flags |= SF_SINGLE_SCHED | SF_OPTIMIZED_PATH; else s->time.func = stepper_event_full; } else if (!CONFIG_INLINE_STEPPER_HACK) { @@ -284,7 +297,7 @@ stepper_get_position(struct stepper *s) { uint32_t position = s->position; // If stepper is mid-move, subtract out steps not yet taken - if (HAVE_SINGLE_SCHEDULE && s->flags & SF_SINGLE_SCHED) + if (s->flags & SF_SINGLE_SCHED) position -= s->count; else position -= s->count / 2; @@ -316,9 +329,12 @@ stepper_stop(struct trsync_signal *tss, uint8_t reason) s->next_step_time = s->time.waketime = 0; s->position = -stepper_get_position(s); s->count = 0; - s->flags = (s->flags & (SF_INVERT_STEP|SF_SINGLE_SCHED)) | SF_NEED_RESET; + s->flags = ((s->flags & (SF_INVERT_STEP|SF_SINGLE_SCHED|SF_OPTIMIZED_PATH)) + | SF_NEED_RESET); gpio_out_write(s->dir_pin, 0); - if (!(HAVE_EDGE_OPTIMIZATION && s->flags & SF_SINGLE_SCHED)) + if (!(s->flags & SF_SINGLE_SCHED) + || (HAVE_AVR_OPTIMIZATION && s->flags & SF_OPTIMIZED_PATH)) + // Must return step pin to "unstep" state gpio_out_write(s->step_pin, s->flags & SF_INVERT_STEP); while (!move_queue_empty(&s->mq)) { struct move_node *mn = move_queue_pop(&s->mq); diff --git a/src/stm32/Kconfig b/src/stm32/Kconfig index fad15bb0..1693f877 100644 --- a/src/stm32/Kconfig +++ b/src/stm32/Kconfig @@ -13,7 +13,7 @@ config STM32_SELECT select HAVE_GPIO_HARD_PWM if MACH_STM32F070 || MACH_STM32F072 || MACH_STM32F1 || MACH_STM32F4 || MACH_STM32F7 || MACH_STM32G0 || MACH_STM32H7 select HAVE_STRICT_TIMING select HAVE_CHIPID - select HAVE_STEPPER_BOTH_EDGE + select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE select HAVE_BOOTLOADER_REQUEST select HAVE_LIMITED_CODE_SIZE if FLASH_SIZE < 0x10000 |