stepper: Improve performance of scheduled unsteps

On faster MCUs where a delay is needed between step and unstep use a
"busy loop" in the scheduler instead of trying to schedule to the
unstep time.  This reduces the chance of jitter in the scheduler
accumulating.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-03-29 11:56:13 -04:00
parent 18f4d343f5
commit f8b0c884b0
1 changed files with 26 additions and 25 deletions

View File

@ -35,9 +35,10 @@ struct stepper {
#if CONFIG_NO_UNSTEP_DELAY
uint16_t count;
#define next_step_time time.waketime
#define min_step_time time.waketime
#else
uint32_t count;
uint32_t next_step_time;
uint32_t next_step_time, min_step_time;
#endif
struct gpio_out step_pin, dir_pin;
uint32_t position;
@ -54,7 +55,7 @@ enum { SF_LAST_DIR=1<<0, SF_NEXT_DIR=1<<1, SF_INVERT_STEP=1<<2, SF_HAVE_ADD=1<<3
// Setup a stepper for the next move in its queue
static uint_fast8_t
stepper_load_next(struct stepper *s, uint32_t min_next_time)
stepper_load_next(struct stepper *s)
{
struct stepper_move *m = s->first;
if (!m) {
@ -66,6 +67,7 @@ stepper_load_next(struct stepper *s, uint32_t min_next_time)
}
s->next_step_time += m->interval;
s->time.waketime = s->next_step_time;
s->add = m->add;
s->interval = m->interval + m->add;
if (CONFIG_NO_UNSTEP_DELAY) {
@ -74,17 +76,11 @@ stepper_load_next(struct stepper *s, uint32_t min_next_time)
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.
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)))
shutdown("stepper too far in past");
s->time.waketime = min_next_time;
} else {
s->time.waketime = s->next_step_time;
}
// and so there are twice as many events.
s->count = m->count * 2;
if (timer_is_before(s->next_step_time + timer_from_us(1000)
, s->min_step_time))
shutdown("stepper too far in past");
}
if (m->flags & MF_DIR) {
s->position = -s->position + m->count;
@ -118,30 +114,32 @@ stepper_event(struct timer *t)
s->interval += s->add;
return SF_RESCHEDULE;
}
uint_fast8_t ret = stepper_load_next(s, 0);
uint_fast8_t ret = stepper_load_next(s);
gpio_out_toggle(s->step_pin);
return ret;
}
// On faster mcus, it is necessary to schedule the unstep event
uint32_t min_next_time = timer_read_time() + UNSTEP_TIME;
// On faster mcus, it is necessary to wait between step and unstep
uint32_t curtime = timer_read_time();
if (unlikely(timer_is_before(curtime, s->min_step_time)))
// Can't step/unstep yet - busy wait in scheduler
goto busy_wait;
gpio_out_toggle(s->step_pin);
s->min_step_time = curtime + UNSTEP_TIME;
s->count--;
if (likely(s->count & 1))
// Schedule unstep event
goto reschedule_min;
if (s->count & 1)
// Started step - now busy wait in scheduler until unstep ready
goto busy_wait;
if (likely(s->count)) {
s->next_step_time += s->interval;
s->interval += s->add;
if (unlikely(timer_is_before(s->next_step_time, min_next_time)))
// The next step event is too close - push it back
goto reschedule_min;
s->time.waketime = s->next_step_time;
return SF_RESCHEDULE;
}
return stepper_load_next(s, min_next_time);
reschedule_min:
s->time.waketime = min_next_time;
return stepper_load_next(s);
busy_wait:
s->time.waketime = curtime;
return SF_RESCHEDULE;
}
@ -202,7 +200,9 @@ command_queue_step(uint32_t *args)
s->plast = &m->next;
} else {
s->first = m;
stepper_load_next(s, s->next_step_time + m->interval);
if (!CONFIG_NO_UNSTEP_DELAY)
s->min_step_time = s->next_step_time + m->interval;
stepper_load_next(s);
sched_add_timer(&s->time);
}
irq_enable();
@ -232,6 +232,7 @@ command_reset_step_clock(uint32_t *args)
if (s->count)
shutdown("Can't reset time when stepper active");
s->next_step_time = waketime;
s->min_step_time = s->next_step_time;
s->flags |= SF_LAST_RESET;
irq_enable();
}