stepper: Support step on both edges with custom minimum pulse duration
Add support for "step on both edges" to the main stepper_event_full() code. This makes that mode of operation available even when the micro-controller is not compiled for "optimized step on both edges". It also enables the custom pulse duration support (step_pulse_ticks) when in "step on both edges" mode. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
272e815522
commit
8faed8d9fe
@ -1,6 +1,6 @@
|
|||||||
# Printer stepper support
|
# 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.
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
import math, logging, collections
|
import math, logging, collections
|
||||||
@ -14,7 +14,8 @@ class error(Exception):
|
|||||||
# Steppers
|
# 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
|
# Interface to low-level mcu and chelper code
|
||||||
class MCU_stepper:
|
class MCU_stepper:
|
||||||
@ -75,13 +76,33 @@ class MCU_stepper:
|
|||||||
if self._step_pulse_duration is None:
|
if self._step_pulse_duration is None:
|
||||||
self._step_pulse_duration = .000002
|
self._step_pulse_duration = .000002
|
||||||
invert_step = self._invert_step
|
invert_step = self._invert_step
|
||||||
sbe = int(self._mcu.get_constants().get('STEPPER_BOTH_EDGE', '0'))
|
# Check if can enable "step on both edges"
|
||||||
if (self._req_step_both_edge and sbe
|
constants = self._mcu.get_constants()
|
||||||
and self._step_pulse_duration <= MIN_BOTH_EDGE_DURATION):
|
ssbe = int(constants.get('STEPPER_STEP_BOTH_EDGE', '0'))
|
||||||
# Enable stepper optimized step on both edges
|
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_both_edge = True
|
||||||
self._step_pulse_duration = 0.
|
|
||||||
invert_step = -1
|
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)
|
step_pulse_ticks = self._mcu.seconds_to_clock(self._step_pulse_duration)
|
||||||
self._mcu.add_config_cmd(
|
self._mcu.add_config_cmd(
|
||||||
"config_stepper oid=%d step_pin=%s dir_pin=%s invert_step=%d"
|
"config_stepper oid=%d step_pin=%s dir_pin=%s invert_step=%d"
|
||||||
|
@ -229,7 +229,7 @@ config HAVE_STRICT_TIMING
|
|||||||
bool
|
bool
|
||||||
config HAVE_CHIPID
|
config HAVE_CHIPID
|
||||||
bool
|
bool
|
||||||
config HAVE_STEPPER_BOTH_EDGE
|
config HAVE_STEPPER_OPTIMIZED_BOTH_EDGE
|
||||||
bool
|
bool
|
||||||
config HAVE_BOOTLOADER_REQUEST
|
config HAVE_BOOTLOADER_REQUEST
|
||||||
bool
|
bool
|
||||||
|
@ -7,7 +7,7 @@ config AR100_SELECT
|
|||||||
default y
|
default y
|
||||||
select HAVE_GPIO
|
select HAVE_GPIO
|
||||||
select HAVE_GPIO_SPI
|
select HAVE_GPIO_SPI
|
||||||
select HAVE_STEPPER_BOTH_EDGE
|
select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE
|
||||||
select HAVE_LIMITED_CODE_SIZE
|
select HAVE_LIMITED_CODE_SIZE
|
||||||
|
|
||||||
config BOARD_DIRECTORY
|
config BOARD_DIRECTORY
|
||||||
|
@ -12,7 +12,7 @@ config ATSAM_SELECT
|
|||||||
select HAVE_GPIO_HARD_PWM if !MACH_SAME70
|
select HAVE_GPIO_HARD_PWM if !MACH_SAME70
|
||||||
select HAVE_STRICT_TIMING
|
select HAVE_STRICT_TIMING
|
||||||
select HAVE_CHIPID
|
select HAVE_CHIPID
|
||||||
select HAVE_STEPPER_BOTH_EDGE
|
select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE
|
||||||
select HAVE_BOOTLOADER_REQUEST
|
select HAVE_BOOTLOADER_REQUEST
|
||||||
|
|
||||||
config BOARD_DIRECTORY
|
config BOARD_DIRECTORY
|
||||||
|
@ -12,7 +12,7 @@ config ATSAMD_SELECT
|
|||||||
select HAVE_GPIO_HARD_PWM if MACH_SAMX2
|
select HAVE_GPIO_HARD_PWM if MACH_SAMX2
|
||||||
select HAVE_STRICT_TIMING
|
select HAVE_STRICT_TIMING
|
||||||
select HAVE_CHIPID
|
select HAVE_CHIPID
|
||||||
select HAVE_STEPPER_BOTH_EDGE
|
select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE
|
||||||
select HAVE_BOOTLOADER_REQUEST
|
select HAVE_BOOTLOADER_REQUEST
|
||||||
|
|
||||||
config HAVE_SERCOM
|
config HAVE_SERCOM
|
||||||
|
@ -9,7 +9,7 @@ config HC32F460_SELECT
|
|||||||
select HAVE_GPIO_ADC
|
select HAVE_GPIO_ADC
|
||||||
select HAVE_STRICT_TIMING
|
select HAVE_STRICT_TIMING
|
||||||
select HAVE_GPIO_HARD_PWM
|
select HAVE_GPIO_HARD_PWM
|
||||||
select HAVE_STEPPER_BOTH_EDGE
|
select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE
|
||||||
|
|
||||||
config BOARD_DIRECTORY
|
config BOARD_DIRECTORY
|
||||||
string
|
string
|
||||||
|
@ -12,7 +12,7 @@ config LPC_SELECT
|
|||||||
select HAVE_GPIO_HARD_PWM
|
select HAVE_GPIO_HARD_PWM
|
||||||
select HAVE_STRICT_TIMING
|
select HAVE_STRICT_TIMING
|
||||||
select HAVE_CHIPID
|
select HAVE_CHIPID
|
||||||
select HAVE_STEPPER_BOTH_EDGE
|
select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE
|
||||||
select HAVE_BOOTLOADER_REQUEST
|
select HAVE_BOOTLOADER_REQUEST
|
||||||
|
|
||||||
config BOARD_DIRECTORY
|
config BOARD_DIRECTORY
|
||||||
|
@ -12,7 +12,7 @@ config RPXXXX_SELECT
|
|||||||
select HAVE_STRICT_TIMING
|
select HAVE_STRICT_TIMING
|
||||||
select HAVE_CHIPID
|
select HAVE_CHIPID
|
||||||
select HAVE_GPIO_HARD_PWM
|
select HAVE_GPIO_HARD_PWM
|
||||||
select HAVE_STEPPER_BOTH_EDGE
|
select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE
|
||||||
select HAVE_BOOTLOADER_REQUEST
|
select HAVE_BOOTLOADER_REQUEST
|
||||||
|
|
||||||
config BOARD_DIRECTORY
|
config BOARD_DIRECTORY
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Handling of stepper drivers.
|
// 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.
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
@ -14,17 +14,18 @@
|
|||||||
#include "stepper.h" // stepper_event
|
#include "stepper.h" // stepper_event
|
||||||
#include "trsync.h" // trsync_add_signal
|
#include "trsync.h" // trsync_add_signal
|
||||||
|
|
||||||
#if CONFIG_INLINE_STEPPER_HACK && CONFIG_HAVE_STEPPER_BOTH_EDGE
|
DECL_CONSTANT("STEPPER_STEP_BOTH_EDGE", 1);
|
||||||
#define HAVE_SINGLE_SCHEDULE 1
|
|
||||||
|
#if CONFIG_INLINE_STEPPER_HACK && CONFIG_HAVE_STEPPER_OPTIMIZED_BOTH_EDGE
|
||||||
|
#define HAVE_OPTIMIZED_PATH 1
|
||||||
#define HAVE_EDGE_OPTIMIZATION 1
|
#define HAVE_EDGE_OPTIMIZATION 1
|
||||||
#define HAVE_AVR_OPTIMIZATION 0
|
#define HAVE_AVR_OPTIMIZATION 0
|
||||||
DECL_CONSTANT("STEPPER_BOTH_EDGE", 1);
|
|
||||||
#elif CONFIG_INLINE_STEPPER_HACK && CONFIG_MACH_AVR
|
#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_EDGE_OPTIMIZATION 0
|
||||||
#define HAVE_AVR_OPTIMIZATION 1
|
#define HAVE_AVR_OPTIMIZATION 1
|
||||||
#else
|
#else
|
||||||
#define HAVE_SINGLE_SCHEDULE 0
|
#define HAVE_OPTIMIZED_PATH 0
|
||||||
#define HAVE_EDGE_OPTIMIZATION 0
|
#define HAVE_EDGE_OPTIMIZATION 0
|
||||||
#define HAVE_AVR_OPTIMIZATION 0
|
#define HAVE_AVR_OPTIMIZATION 0
|
||||||
#endif
|
#endif
|
||||||
@ -57,7 +58,7 @@ enum { POSITION_BIAS=0x40000000 };
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
SF_LAST_DIR=1<<0, SF_NEXT_DIR=1<<1, SF_INVERT_STEP=1<<2, SF_NEED_RESET=1<<3,
|
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
|
// 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);
|
struct stepper_move *m = container_of(mn, struct stepper_move, node);
|
||||||
s->add = m->add;
|
s->add = m->add;
|
||||||
s->interval = m->interval + 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;
|
s->time.waketime += m->interval;
|
||||||
if (HAVE_AVR_OPTIMIZATION)
|
if (HAVE_AVR_OPTIMIZATION)
|
||||||
s->flags = m->add ? s->flags|SF_HAVE_ADD : s->flags & ~SF_HAVE_ADD;
|
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.
|
// twice as many events.
|
||||||
s->next_step_time += m->interval;
|
s->next_step_time += m->interval;
|
||||||
s->time.waketime = s->next_step_time;
|
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)
|
// Add all steps to s->position (stepper_get_position() can calc mid-move)
|
||||||
if (m->flags & MF_DIR) {
|
if (m->flags & MF_DIR) {
|
||||||
@ -99,8 +100,14 @@ stepper_load_next(struct stepper *s)
|
|||||||
return SF_RESCHEDULE;
|
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
|
// Optimized step function to step on each step pin edge
|
||||||
uint_fast8_t
|
static uint_fast8_t
|
||||||
stepper_event_edge(struct timer *t)
|
stepper_event_edge(struct timer *t)
|
||||||
{
|
{
|
||||||
struct stepper *s = container_of(t, struct stepper, time);
|
struct stepper *s = container_of(t, struct stepper, time);
|
||||||
@ -115,7 +122,10 @@ stepper_event_edge(struct timer *t)
|
|||||||
return stepper_load_next(s);
|
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
|
// AVR optimized step function
|
||||||
static uint_fast8_t
|
static uint_fast8_t
|
||||||
@ -137,8 +147,8 @@ stepper_event_avr(struct timer *t)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular "double scheduled" step function
|
// Regular "fully scheduled" step function
|
||||||
uint_fast8_t
|
static uint_fast8_t
|
||||||
stepper_event_full(struct timer *t)
|
stepper_event_full(struct timer *t)
|
||||||
{
|
{
|
||||||
struct stepper *s = container_of(t, struct stepper, time);
|
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 curtime = timer_read_time();
|
||||||
uint32_t min_next_time = curtime + s->step_pulse_ticks;
|
uint32_t min_next_time = curtime + s->step_pulse_ticks;
|
||||||
s->count--;
|
s->count--;
|
||||||
if (likely(s->count & 1))
|
if (likely(s->count & 1 && !(s->flags & SF_SINGLE_SCHED)))
|
||||||
// Schedule unstep event
|
// Schedule unstep event
|
||||||
goto reschedule_min;
|
goto reschedule_min;
|
||||||
if (likely(s->count)) {
|
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));
|
struct stepper *s = oid_alloc(args[0], command_config_stepper, sizeof(*s));
|
||||||
int_fast8_t invert_step = args[3];
|
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->step_pin = gpio_out_setup(args[1], s->flags & SF_INVERT_STEP);
|
||||||
s->dir_pin = gpio_out_setup(args[2], 0);
|
s->dir_pin = gpio_out_setup(args[2], 0);
|
||||||
s->position = -POSITION_BIAS;
|
s->position = -POSITION_BIAS;
|
||||||
s->step_pulse_ticks = args[4];
|
s->step_pulse_ticks = args[4];
|
||||||
move_queue_setup(&s->mq, sizeof(struct stepper_move));
|
move_queue_setup(&s->mq, sizeof(struct stepper_move));
|
||||||
if (HAVE_EDGE_OPTIMIZATION) {
|
if (HAVE_EDGE_OPTIMIZATION) {
|
||||||
if (!s->step_pulse_ticks && invert_step < 0)
|
if (invert_step < 0 && s->step_pulse_ticks <= EDGE_STEP_TICKS)
|
||||||
s->flags |= SF_SINGLE_SCHED;
|
s->flags |= SF_OPTIMIZED_PATH;
|
||||||
else
|
else
|
||||||
s->time.func = stepper_event_full;
|
s->time.func = stepper_event_full;
|
||||||
} else if (HAVE_AVR_OPTIMIZATION) {
|
} else if (HAVE_AVR_OPTIMIZATION) {
|
||||||
if (s->step_pulse_ticks <= AVR_STEP_INSNS)
|
if (invert_step >= 0 && s->step_pulse_ticks <= AVR_STEP_TICKS)
|
||||||
s->flags |= SF_SINGLE_SCHED;
|
s->flags |= SF_SINGLE_SCHED | SF_OPTIMIZED_PATH;
|
||||||
else
|
else
|
||||||
s->time.func = stepper_event_full;
|
s->time.func = stepper_event_full;
|
||||||
} else if (!CONFIG_INLINE_STEPPER_HACK) {
|
} else if (!CONFIG_INLINE_STEPPER_HACK) {
|
||||||
@ -284,7 +297,7 @@ stepper_get_position(struct stepper *s)
|
|||||||
{
|
{
|
||||||
uint32_t position = s->position;
|
uint32_t position = s->position;
|
||||||
// If stepper is mid-move, subtract out steps not yet taken
|
// 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;
|
position -= s->count;
|
||||||
else
|
else
|
||||||
position -= s->count / 2;
|
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->next_step_time = s->time.waketime = 0;
|
||||||
s->position = -stepper_get_position(s);
|
s->position = -stepper_get_position(s);
|
||||||
s->count = 0;
|
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);
|
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);
|
gpio_out_write(s->step_pin, s->flags & SF_INVERT_STEP);
|
||||||
while (!move_queue_empty(&s->mq)) {
|
while (!move_queue_empty(&s->mq)) {
|
||||||
struct move_node *mn = move_queue_pop(&s->mq);
|
struct move_node *mn = move_queue_pop(&s->mq);
|
||||||
|
@ -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_GPIO_HARD_PWM if MACH_STM32F070 || MACH_STM32F072 || MACH_STM32F1 || MACH_STM32F4 || MACH_STM32F7 || MACH_STM32G0 || MACH_STM32H7
|
||||||
select HAVE_STRICT_TIMING
|
select HAVE_STRICT_TIMING
|
||||||
select HAVE_CHIPID
|
select HAVE_CHIPID
|
||||||
select HAVE_STEPPER_BOTH_EDGE
|
select HAVE_STEPPER_OPTIMIZED_BOTH_EDGE
|
||||||
select HAVE_BOOTLOADER_REQUEST
|
select HAVE_BOOTLOADER_REQUEST
|
||||||
select HAVE_LIMITED_CODE_SIZE if FLASH_SIZE < 0x10000
|
select HAVE_LIMITED_CODE_SIZE if FLASH_SIZE < 0x10000
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user