aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--klippy/stepper.py35
-rw-r--r--src/Kconfig2
-rw-r--r--src/ar100/Kconfig2
-rw-r--r--src/atsam/Kconfig2
-rw-r--r--src/atsamd/Kconfig2
-rw-r--r--src/hc32f460/Kconfig2
-rw-r--r--src/lpc176x/Kconfig2
-rw-r--r--src/rp2040/Kconfig2
-rw-r--r--src/stepper.c60
-rw-r--r--src/stm32/Kconfig2
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