From 689231df3a6c1ed606d227cce7115703b153193b Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Thu, 28 Oct 2021 17:10:10 -0400 Subject: [PATCH] stepper: Add support for stepping on both edges of a step pulse Add an optimized step function for drivers that support stepping on both rising and falling edges of the step pin. Enable this optimization on 32bit ARM micro-controllers. Automatically detect this capability in the host code and enable on TMC drivers running in SPI/UART mode. Signed-off-by: Kevin O'Connor --- klippy/extras/tmc.py | 15 +++++++++--- klippy/extras/tmc2660.py | 1 - klippy/stepper.py | 16 ++++++++++++- src/Kconfig | 3 +++ src/atsam/Kconfig | 1 + src/atsamd/Kconfig | 1 + src/lpc176x/Kconfig | 1 + src/rp2040/Kconfig | 1 + src/stepper.c | 49 ++++++++++++++++++++++++++++++++++------ src/stm32/Kconfig | 1 + 10 files changed, 77 insertions(+), 12 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 7a6b4fa5..6a59bbfb 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -224,6 +224,8 @@ class TMCCommandHelper: self.stepper_enable = self.printer.load_object(config, "stepper_enable") self.printer.register_event_handler("stepper:sync_mcu_position", self._handle_sync_mcu_pos) + self.printer.register_event_handler("klippy:mcu_identify", + self._handle_mcu_identify) self.printer.register_event_handler("klippy:connect", self._handle_connect) # Set microstep config options @@ -345,6 +347,12 @@ class TMCCommandHelper: self.echeck_helper.stop_checks() except self.printer.command_error as e: self.printer.invoke_shutdown(str(e)) + def _handle_mcu_identify(self): + # Lookup stepper object + force_move = self.printer.lookup_object("force_move") + self.stepper = force_move.lookup_stepper(self.stepper_name) + # Note pulse duration and step_both_edge optimizations available + self.stepper.setup_default_pulse_duration(.000000100, True) def _handle_stepper_enable(self, print_time, is_enable): if is_enable: cb = (lambda ev: self._do_enable(print_time)) @@ -352,9 +360,10 @@ class TMCCommandHelper: cb = (lambda ev: self._do_disable(print_time)) self.printer.get_reactor().register_callback(cb) def _handle_connect(self): - # Lookup stepper object - force_move = self.printer.lookup_object("force_move") - self.stepper = force_move.lookup_stepper(self.stepper_name) + # Check if using step on both edges optimization + pulse_duration, step_both_edge = self.stepper.get_pulse_duration() + if step_both_edge: + self.fields.set_field("dedge", 1) # Check for soft stepper enable/disable enable_line = self.stepper_enable.lookup_enable(self.stepper_name) enable_line.register_state_callback(self._handle_stepper_enable) diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index f55f6f01..c0d8fa9b 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -98,7 +98,6 @@ SignedFields = ["sgt"] FieldFormatters = dict(tmc2130.FieldFormatters) FieldFormatters.update({ - "dedge": (lambda v: "1(Both Edges Active!)" if v else ""), "chm": (lambda v: "1(constant toff)" if v else "0(spreadCycle)"), "vsense": (lambda v: "1(165mV)" if v else "0(305mV)"), "sdoff": (lambda v: "1(Step/Dir disabled!)" if v else ""), diff --git a/klippy/stepper.py b/klippy/stepper.py index c5f81382..2e6655ef 100644 --- a/klippy/stepper.py +++ b/klippy/stepper.py @@ -32,6 +32,7 @@ class MCU_stepper: "Stepper dir pin must be on same mcu as step pin") self._dir_pin = dir_pin_params['pin'] self._invert_dir = dir_pin_params['invert'] + self._step_both_edge = self._req_step_both_edge = False self._mcu_position_offset = 0. self._reset_cmd_tag = self._get_position_cmd = None self._active_callbacks = [] @@ -54,6 +55,12 @@ class MCU_stepper: def units_in_radians(self): # Returns true if distances are in radians instead of millimeters return self._units_in_radians + def get_pulse_duration(self): + return self._step_pulse_duration, self._step_both_edge + def setup_default_pulse_duration(self, pulse_duration, step_both_edge): + if self._step_pulse_duration is None: + self._step_pulse_duration = pulse_duration + self._req_step_both_edge = step_both_edge def setup_itersolve(self, alloc_func, *params): ffi_main, ffi_lib = chelper.get_ffi() sk = ffi_main.gc(getattr(ffi_lib, alloc_func)(*params), ffi_lib.free) @@ -61,11 +68,18 @@ class MCU_stepper: def _build_config(self): 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: + # Enable stepper optimized step on both edges + self._step_both_edge = True + self._step_pulse_duration = 0. + invert_step = -1 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" " step_pulse_ticks=%u" % (self._oid, self._step_pin, self._dir_pin, - self._invert_step, step_pulse_ticks)) + invert_step, step_pulse_ticks)) self._mcu.add_config_cmd("reset_step_clock oid=%d clock=0" % (self._oid,), on_restart=True) step_cmd_tag = self._mcu.lookup_command_tag( diff --git a/src/Kconfig b/src/Kconfig index 43ab2bc6..e36094e6 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -108,6 +108,9 @@ config HAVE_STRICT_TIMING config HAVE_CHIPID bool default n +config HAVE_STEPPER_BOTH_EDGE + bool + default n config INLINE_STEPPER_HACK # Enables gcc to inline stepper_event() into the main timer irq handler diff --git a/src/atsam/Kconfig b/src/atsam/Kconfig index a3d8566d..f9d13c34 100644 --- a/src/atsam/Kconfig +++ b/src/atsam/Kconfig @@ -13,6 +13,7 @@ config ATSAM_SELECT select HAVE_GPIO_BITBANGING select HAVE_STRICT_TIMING select HAVE_CHIPID + select HAVE_STEPPER_BOTH_EDGE config BOARD_DIRECTORY string diff --git a/src/atsamd/Kconfig b/src/atsamd/Kconfig index 5684ff6f..57c99c9f 100644 --- a/src/atsamd/Kconfig +++ b/src/atsamd/Kconfig @@ -13,6 +13,7 @@ config ATSAMD_SELECT select HAVE_GPIO_BITBANGING select HAVE_STRICT_TIMING select HAVE_CHIPID + select HAVE_STEPPER_BOTH_EDGE config HAVE_SERCOM depends on HAVE_GPIO_I2C || HAVE_GPIO_SPI diff --git a/src/lpc176x/Kconfig b/src/lpc176x/Kconfig index 81a98b05..f40c3943 100644 --- a/src/lpc176x/Kconfig +++ b/src/lpc176x/Kconfig @@ -13,6 +13,7 @@ config LPC_SELECT select HAVE_STRICT_TIMING select HAVE_CHIPID select HAVE_GPIO_HARD_PWM + select HAVE_STEPPER_BOTH_EDGE config BOARD_DIRECTORY string diff --git a/src/rp2040/Kconfig b/src/rp2040/Kconfig index 1682fa9a..b509bc04 100644 --- a/src/rp2040/Kconfig +++ b/src/rp2040/Kconfig @@ -12,6 +12,7 @@ config RP2040_SELECT select HAVE_STRICT_TIMING select HAVE_CHIPID select HAVE_GPIO_HARD_PWM + select HAVE_STEPPER_BOTH_EDGE config BOARD_DIRECTORY string diff --git a/src/stepper.c b/src/stepper.c index 5d69d9df..f81d7041 100644 --- a/src/stepper.c +++ b/src/stepper.c @@ -14,9 +14,18 @@ #include "stepper.h" // stepper_event #include "trsync.h" // trsync_add_signal -#if CONFIG_INLINE_STEPPER_HACK && CONFIG_MACH_AVR +#if CONFIG_INLINE_STEPPER_HACK && CONFIG_HAVE_STEPPER_BOTH_EDGE + #define HAVE_SINGLE_SCHEDULE 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_EDGE_OPTIMIZATION 0 #define HAVE_AVR_OPTIMIZATION 1 #else + #define HAVE_SINGLE_SCHEDULE 0 + #define HAVE_EDGE_OPTIMIZATION 0 #define HAVE_AVR_OPTIMIZATION 0 #endif @@ -66,9 +75,10 @@ stepper_load_next(struct stepper *s, uint32_t min_next_time) struct stepper_move *m = container_of(mn, struct stepper_move, node); s->add = m->add; s->interval = m->interval + m->add; - if (HAVE_AVR_OPTIMIZATION && s->flags & SF_SINGLE_SCHED) { + if (HAVE_SINGLE_SCHEDULE && s->flags & SF_SINGLE_SCHED) { s->time.waketime += m->interval; - s->flags = m->add ? s->flags | SF_HAVE_ADD : s->flags & ~SF_HAVE_ADD; + if (HAVE_AVR_OPTIMIZATION) + s->flags = m->add ? s->flags|SF_HAVE_ADD : s->flags & ~SF_HAVE_ADD; s->count = m->count; } else { // On faster mcus, it is necessary to schedule unstep events @@ -97,6 +107,22 @@ stepper_load_next(struct stepper *s, uint32_t min_next_time) return SF_RESCHEDULE; } +// Optimized step function to step on each step pin edge +uint_fast8_t +stepper_event_edge(struct timer *t) +{ + struct stepper *s = container_of(t, struct stepper, time); + gpio_out_toggle_noirq(s->step_pin); + uint32_t count = s->count - 1; + if (likely(count)) { + s->count = count; + s->time.waketime += s->interval; + s->interval += s->add; + return SF_RESCHEDULE; + } + return stepper_load_next(s, 0); +} + #define AVR_STEP_INSNS 40 // minimum instructions between step gpio pulses // AVR optimized step function @@ -150,6 +176,8 @@ reschedule_min: uint_fast8_t stepper_event(struct timer *t) { + if (HAVE_EDGE_OPTIMIZATION) + return stepper_event_edge(t); if (HAVE_AVR_OPTIMIZATION) return stepper_event_avr(t); return stepper_event_full(t); @@ -159,13 +187,19 @@ void command_config_stepper(uint32_t *args) { struct stepper *s = oid_alloc(args[0], command_config_stepper, sizeof(*s)); - s->flags = args[3] ? SF_INVERT_STEP : 0; + int_fast8_t invert_step = args[3]; + s->flags = invert_step > 0 ? SF_INVERT_STEP : 0; 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_AVR_OPTIMIZATION) { + if (HAVE_EDGE_OPTIMIZATION) { + if (!s->step_pulse_ticks && invert_step < 0) + s->flags |= SF_SINGLE_SCHED; + 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; else @@ -252,7 +286,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_AVR_OPTIMIZATION && s->flags & SF_SINGLE_SCHED) + if (HAVE_SINGLE_SCHEDULE && s->flags & SF_SINGLE_SCHED) position -= s->count; else position -= s->count / 2; @@ -286,7 +320,8 @@ stepper_stop(struct trsync_signal *tss, uint8_t reason) s->count = 0; s->flags = (s->flags & (SF_INVERT_STEP|SF_SINGLE_SCHED)) | SF_NEED_RESET; gpio_out_write(s->dir_pin, 0); - gpio_out_write(s->step_pin, s->flags & SF_INVERT_STEP); + if (!(HAVE_EDGE_OPTIMIZATION && s->flags & SF_SINGLE_SCHED)) + 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); struct stepper_move *m = container_of(mn, struct stepper_move, node); diff --git a/src/stm32/Kconfig b/src/stm32/Kconfig index 3fef9a16..c14abc6d 100644 --- a/src/stm32/Kconfig +++ b/src/stm32/Kconfig @@ -13,6 +13,7 @@ config STM32_SELECT select HAVE_GPIO_BITBANGING if !MACH_STM32F031 select HAVE_STRICT_TIMING select HAVE_CHIPID + select HAVE_STEPPER_BOTH_EDGE config BOARD_DIRECTORY string