From 4acfd8d7c8973f6cc7b8a3eaa8ea9e82a92b018b Mon Sep 17 00:00:00 2001 From: Kevin O'Connor Date: Wed, 27 Oct 2021 19:10:36 -0400 Subject: stepper: Make step pulse duration customizable at run-time Remove the STEP_DELAY Kconfig option and replace it with a per-stepper step_pulse_duration printer.cfg config option. The AVR code will continue to have optimized code to step and "unstep" in the same function (which is automatically activated when the step delay is 40 ticks or less). This change removes the Kconfig option for single function step/unstep on 32bit processors. Signed-off-by: Kevin O'Connor --- src/stepper.c | 110 +++++++++++++++++++++++++--------------------------------- 1 file changed, 47 insertions(+), 63 deletions(-) (limited to 'src/stepper.c') diff --git a/src/stepper.c b/src/stepper.c index e4a12693..5d69d9df 100644 --- a/src/stepper.c +++ b/src/stepper.c @@ -14,12 +14,11 @@ #include "stepper.h" // stepper_event #include "trsync.h" // trsync_add_signal -DECL_CONSTANT("STEP_DELAY", CONFIG_STEP_DELAY); - - -/**************************************************************** - * Steppers - ****************************************************************/ +#if CONFIG_INLINE_STEPPER_HACK && CONFIG_MACH_AVR + #define HAVE_AVR_OPTIMIZATION 1 +#else + #define HAVE_AVR_OPTIMIZATION 0 +#endif struct stepper_move { struct move_node node; @@ -35,13 +34,8 @@ struct stepper { struct timer time; uint32_t interval; int16_t add; -#if CONFIG_STEP_DELAY <= 0 - uint_fast16_t count; -#define next_step_time time.waketime -#else uint32_t count; - uint32_t next_step_time; -#endif + uint32_t next_step_time, step_pulse_ticks; struct gpio_out step_pin, dir_pin; uint32_t position; struct move_queue_head mq; @@ -53,8 +47,8 @@ struct stepper { enum { POSITION_BIAS=0x40000000 }; enum { - SF_LAST_DIR=1<<0, SF_NEXT_DIR=1<<1, SF_INVERT_STEP=1<<2, SF_HAVE_ADD=1<<3, - SF_NEED_RESET=1<<4 + 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 }; // Setup a stepper for the next move in its queue @@ -70,18 +64,17 @@ stepper_load_next(struct stepper *s, uint32_t min_next_time) // Load next 'struct stepper_move' into 'struct stepper' struct move_node *mn = move_queue_pop(&s->mq); struct stepper_move *m = container_of(mn, struct stepper_move, node); - s->next_step_time += m->interval; s->add = m->add; s->interval = m->interval + m->add; - if (CONFIG_STEP_DELAY <= 0) { - if (CONFIG_MACH_AVR) - // On AVR see if the add can be optimized away - s->flags = m->add ? s->flags|SF_HAVE_ADD : s->flags & ~SF_HAVE_ADD; + if (HAVE_AVR_OPTIMIZATION && s->flags & SF_SINGLE_SCHED) { + s->time.waketime += m->interval; + 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 // and so there are twice as many events. Also check that the // next step event isn't too close to the last unstep. + s->next_step_time += m->interval; if (unlikely(timer_is_before(s->next_step_time, min_next_time))) { if ((int32_t)(s->next_step_time - min_next_time) < (int32_t)(-timer_from_us(1000))) @@ -104,14 +97,17 @@ stepper_load_next(struct stepper *s, uint32_t min_next_time) return SF_RESCHEDULE; } +#define AVR_STEP_INSNS 40 // minimum instructions between step gpio pulses + // AVR optimized step function static uint_fast8_t -stepper_event_avr(struct stepper *s) +stepper_event_avr(struct timer *t) { + struct stepper *s = container_of(t, struct stepper, time); gpio_out_toggle_noirq(s->step_pin); - uint_fast16_t count = s->count - 1; + uint16_t *pcount = (void*)&s->count, count = *pcount - 1; if (likely(count)) { - s->count = count; + *pcount = count; s->time.waketime += s->interval; gpio_out_toggle_noirq(s->step_pin); if (s->flags & SF_HAVE_ADD) @@ -123,42 +119,14 @@ stepper_event_avr(struct stepper *s) return ret; } -// Optimized step function for stepping and unstepping in same function -static uint_fast8_t -stepper_event_nodelay(struct stepper *s) -{ - gpio_out_toggle_noirq(s->step_pin); - uint_fast16_t count = s->count - 1; - if (likely(count)) { - s->count = count; - s->time.waketime += s->interval; - s->interval += s->add; - gpio_out_toggle_noirq(s->step_pin); - return SF_RESCHEDULE; - } - uint_fast8_t ret = stepper_load_next(s, 0); - gpio_out_toggle_noirq(s->step_pin); - return ret; -} - -// Timer callback - step the given stepper. +// Regular "double scheduled" step function uint_fast8_t -stepper_event(struct timer *t) +stepper_event_full(struct timer *t) { struct stepper *s = container_of(t, struct stepper, time); - if (CONFIG_STEP_DELAY <= 0 && CONFIG_MACH_AVR) - return stepper_event_avr(s); - if (CONFIG_STEP_DELAY <= 0) - return stepper_event_nodelay(s); - - // Normal step code - schedule the unstep event - if (!CONFIG_HAVE_STRICT_TIMING) - gpio_out_toggle_noirq(s->step_pin); - uint32_t step_delay = timer_from_us(CONFIG_STEP_DELAY); - uint32_t min_next_time = timer_read_time() + step_delay; - if (CONFIG_HAVE_STRICT_TIMING) - // Toggling gpio after reading the time is a micro-optimization - gpio_out_toggle_noirq(s->step_pin); + gpio_out_toggle_noirq(s->step_pin); + uint32_t curtime = timer_read_time(); + uint32_t min_next_time = curtime + s->step_pulse_ticks; s->count--; if (likely(s->count & 1)) // Schedule unstep event @@ -178,20 +146,36 @@ reschedule_min: return SF_RESCHEDULE; } +// Optimized entry point for step function (may be inlined into sched.c code) +uint_fast8_t +stepper_event(struct timer *t) +{ + if (HAVE_AVR_OPTIMIZATION) + return stepper_event_avr(t); + return stepper_event_full(t); +} + void command_config_stepper(uint32_t *args) { struct stepper *s = oid_alloc(args[0], command_config_stepper, sizeof(*s)); - if (!CONFIG_INLINE_STEPPER_HACK) - s->time.func = stepper_event; s->flags = args[3] ? 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 (s->step_pulse_ticks <= AVR_STEP_INSNS) + s->flags |= SF_SINGLE_SCHED; + else + s->time.func = stepper_event_full; + } else if (!CONFIG_INLINE_STEPPER_HACK) { + s->time.func = stepper_event_full; + } } -DECL_COMMAND(command_config_stepper, - "config_stepper oid=%c step_pin=%c dir_pin=%c invert_step=%c"); +DECL_COMMAND(command_config_stepper, "config_stepper oid=%c step_pin=%c" + " dir_pin=%c invert_step=%c step_pulse_ticks=%u"); // Return the 'struct stepper' for a given stepper oid static struct stepper * @@ -256,7 +240,7 @@ command_reset_step_clock(uint32_t *args) irq_disable(); if (s->count) shutdown("Can't reset time when stepper active"); - s->next_step_time = waketime; + s->next_step_time = s->time.waketime = waketime; s->flags &= ~SF_NEED_RESET; irq_enable(); } @@ -268,7 +252,7 @@ stepper_get_position(struct stepper *s) { uint32_t position = s->position; // If stepper is mid-move, subtract out steps not yet taken - if (CONFIG_STEP_DELAY <= 0) + if (HAVE_AVR_OPTIMIZATION && s->flags & SF_SINGLE_SCHED) position -= s->count; else position -= s->count / 2; @@ -297,10 +281,10 @@ stepper_stop(struct trsync_signal *tss, uint8_t reason) { struct stepper *s = container_of(tss, struct stepper, stop_signal); sched_del_timer(&s->time); - s->next_step_time = 0; + 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_NEED_RESET; + 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); while (!move_queue_empty(&s->mq)) { -- cgit v1.2.3-70-g09d2