add temporal step algorithm for testing

This commit is contained in:
Michael Moon 2010-11-08 22:13:50 +11:00
parent ba7d9a082f
commit 98882e1c69
4 changed files with 189 additions and 75 deletions

View File

@ -48,6 +48,9 @@ F_CPU = 16000000L
# enables reprap-style acceleration #
# ACCELERATION_RAMPING #
# enables start/stop ramping #
# ACCELERATION_TEMPORAL #
# enables experimental temporal step algorithm - not technically a type of #
# acceleration, but since it controls step timing it seems appropriate #
# GEN3 #
# build for standard reprap electronics instead of your custom rig #
# HOST #

View File

@ -77,7 +77,21 @@
Each movement starts at (almost) no speed, linearly accelerates to target speed and decelerates just in time to smoothly stop at the target. alternative to ACCELERATION_REPRAP
Can also be set in Makefile
*/
#define ACCELERATION_RAMPING
// #define ACCELERATION_RAMPING
/*
temporal step algorithm
This algorithm causes the timer to fire when any axis needs to step, instead of synchronising to the axis with the most steps.
This algorithm is not a type of acceleration, and I haven't worked out how to integrate acceleration with it.
However it does control step timing, so acceleration algorithms seemed appropriate
The Bresenham algorithm is great for drawing lines, but not so good for steppers - In the case where X steps 3 times to Y's two, Y experiences massive jitter as it steps in sync with X every 2 out of 3 X steps. This is a worst-case, but the problem exists for most non-45/90 degree moves. At higher speeds, the jitter /will/ cause position loss and unnecessary vibration.
This algorithm instead calculates when a step occurs on any axis, and sets the timer to that value.
// TODO: figure out how to add acceleration to this algorithm
*/
// #define ACCELERATION_TEMPORAL
// how fast to accelerate when using ACCELERATION_RAMPING
// smaller values give quicker acceleration

239
dda.c
View File

@ -1,6 +1,7 @@
#include "dda.h"
#include <string.h>
#include <stdlib.h>
#include <avr/interrupt.h>
#include "timer.h"
@ -10,6 +11,7 @@
#include "debug.h"
#include "sersendf.h"
#include "pinio.h"
#include "config.h"
/*
X Stepper
@ -189,22 +191,8 @@ void dda_create(DDA *dda, TARGET *target) {
dda->z_direction = (target->Z >= startpoint.Z)?1:0;
dda->e_direction = (target->E >= startpoint.E)?1:0;
if (debug_flags & DEBUG_DDA) {
if (dda->x_direction == 0)
serial_writechar('-');
serwrite_uint32(dda->x_delta); serial_writechar(',');
if (dda->y_direction == 0)
serial_writechar('-');
serwrite_uint32(dda->y_delta); serial_writechar(',');
if (dda->z_direction == 0)
serial_writechar('-');
serwrite_uint32(dda->z_delta); serial_writechar(',');
if (dda->e_direction == 0)
serial_writechar('-');
serwrite_uint32(dda->e_delta);
serial_writestr_P(PSTR("] ["));
}
if (debug_flags & DEBUG_DDA)
sersendf_P(PSTR("%ld,%ld,%ld,%ld] ["), target->X - startpoint.X, target->Y - startpoint.Y, target->Z - startpoint.Z, target->E - startpoint.E);
dda->total_steps = dda->x_delta;
if (dda->y_delta > dda->total_steps)
@ -229,9 +217,6 @@ void dda_create(DDA *dda, TARGET *target) {
if (dda->z_delta)
z_enable();
dda->x_counter = dda->y_counter = dda->z_counter = dda->e_counter =
-(dda->total_steps >> 1);
// since it's unusual to combine X, Y and Z changes in a single move on reprap, check if we can use simpler approximations before trying the full 3d approximation.
if (dda->z_delta == 0)
distance = approx_distance(dda->x_delta * UM_PER_STEP_X, dda->y_delta * UM_PER_STEP_Y);
@ -239,30 +224,38 @@ void dda_create(DDA *dda, TARGET *target) {
distance = dda->z_delta * UM_PER_STEP_Z;
else
distance = approx_distance_3(dda->x_delta * UM_PER_STEP_X, dda->y_delta * UM_PER_STEP_Y, dda->z_delta * UM_PER_STEP_Z);
if (distance < 2)
distance = dda->e_delta * UM_PER_STEP_E;
if (debug_flags & DEBUG_DDA)
sersendf_P(PSTR(",ds:%lu"), distance);
#ifdef ACCELERATION_TEMPORAL
// bracket part of this equation in an attempt to avoid overflow: 60 * 16MHz * 5mm is >32 bits
uint32_t move_duration = distance * (60 * F_CPU / startpoint.F);
#else
dda->x_counter = dda->y_counter = dda->z_counter = dda->e_counter =
-(dda->total_steps >> 1);
// pre-calculate move speed in millimeter microseconds per step minute for less math in interrupt context
// mm (distance) * 60000000 us/min / step (total_steps) = mm.us per step.min
// note: um (distance) * 60000 == mm * 60000000
// so in the interrupt we must simply calculate
// mm.us per step.min / mm per min (F) = us per step
// pre-calculate move speed in millimeter microseconds per step minute for less math in interrupt context
// mm (distance) * 60000000 us/min / step (total_steps) = mm.us per step.min
// note: um (distance) * 60000 == mm * 60000000
// so in the interrupt we must simply calculate
// mm.us per step.min / mm per min (F) = us per step
// break this calculation up a bit and lose some precision because 300,000um * 60000 is too big for a uint32
// calculate this with a uint64 if you need the precision, but it'll take longer so routines with lots of short moves may suffer
// 2^32/6000 is about 715mm which should be plenty
// break this calculation up a bit and lose some precision because 300,000um * 60000 is too big for a uint32
// calculate this with a uint64 if you need the precision, but it'll take longer so routines with lots of short moves may suffer
// 2^32/6000 is about 715mm which should be plenty
// changed * 10 to * (F_CPU / 100000) so we can work in cpu_ticks rather than microseconds.
// timer.c setTimer() routine altered for same reason
// changed distance * 6000 .. * F_CPU / 100000 to
// distance * 2400 .. * F_CPU / 40000 so we can move a distance of up to 1800mm without overflowing
uint32_t move_duration = ((distance * 2400) / dda->total_steps) * (F_CPU / 40000);
// changed * 10 to * (F_CPU / 100000) so we can work in cpu_ticks rather than microseconds.
// timer.c setTimer() routine altered for same reason
// changed distance * 6000 .. * F_CPU / 100000 to
// distance * 2400 .. * F_CPU / 40000 so we can move a distance of up to 1800mm without overflowing
uint32_t move_duration = ((distance * 2400) / dda->total_steps) * (F_CPU / 40000);
#endif
// similarly, find out how fast we can run our axes.
// do this for each axis individually, as the combined speed of two or more axes can be higher than the capabilities of a single one.
c_limit = 0;
@ -337,7 +330,7 @@ void dda_create(DDA *dda, TARGET *target) {
else
dda->accel = 0;
#elif defined ACCELERATION_RAMPING
// add the last bit of dda->total_steps to always round up
// add the last bit of dda->total_steps to always round up
dda->ramp_steps = dda->total_steps / 2 + (dda->total_steps & 1);
dda->step_no = 0;
// c is initial step time in IOclk ticks
@ -347,6 +340,26 @@ void dda_create(DDA *dda, TARGET *target) {
dda->c_min = c_limit;
dda->n = 1;
dda->ramp_state = RAMP_UP;
#elif defined ACCELERATION_TEMPORAL
dda->x_step_interval = move_duration / dda->x_delta;
dda->y_step_interval = move_duration / dda->y_delta;
dda->z_step_interval = move_duration / dda->z_delta;
dda->e_step_interval = move_duration / dda->e_delta;
dda->x_counter = 0;
dda->y_counter = 0;
dda->z_counter = 0;
dda->e_counter = 0;
dda->c = dda->x_step_interval;
if (dda->y_step_interval < dda->c)
dda->c = dda->y_step_interval;
if (dda->z_step_interval < dda->c)
dda->c = dda->z_step_interval;
if (dda->e_step_interval < dda->c)
dda->c = dda->e_step_interval;
dda->c <<= 8;
#else
dda->c = (move_duration / target->F) << 8;
if (dda->c < c_limit)
@ -412,64 +425,115 @@ void dda_step(DDA *dda) {
// called from interrupt context! keep it as simple as possible
uint8_t did_step = 0;
if ((current_position.X != dda->endpoint.X) /* &&
(x_max() != dda->x_direction) && (x_min() == dda->x_direction) */) {
dda->x_counter -= dda->x_delta;
if (dda->x_counter < 0) {
x_step();
did_step = 1;
#ifdef ACCELERATION_TEMPORAL
if (dda->x_counter > dda->x_step_interval) {
if ((current_position.X != dda->endpoint.X) /* &&
(x_max() != dda->x_direction) && (x_min() == dda->x_direction) */) {
x_step();
if (dda->x_direction)
current_position.X++;
else
current_position.X--;
dda->x_counter += dda->total_steps;
}
dda->x_counter -= dda->x_step_interval;
dda->x_delta--;
}
}
if ((current_position.Y != dda->endpoint.Y) /* &&
(y_max() != dda->y_direction) && (y_min() == dda->y_direction) */) {
dda->y_counter -= dda->y_delta;
if (dda->y_counter < 0) {
y_step();
did_step = 1;
if (dda->y_counter > dda->y_step_interval) {
if ((current_position.Y != dda->endpoint.Y) /* &&
(y_max() != dda->y_direction) && (y_min() == dda->y_direction) */) {
y_step();
if (dda->y_direction)
current_position.Y++;
else
current_position.Y--;
dda->y_counter += dda->total_steps;
}
dda->y_counter -= dda->y_step_interval;
dda->y_delta--;
}
}
if ((current_position.Z != dda->endpoint.Z) /* &&
(z_max() != dda->z_direction) && (z_min() == dda->z_direction) */) {
dda->z_counter -= dda->z_delta;
if (dda->z_counter < 0) {
z_step();
did_step = 1;
if (dda->z_counter > dda->z_step_interval) {
if ((current_position.Z != dda->endpoint.Z) /* &&
(z_max() != dda->z_direction) && (z_min() == dda->z_direction) */) {
z_step();
if (dda->z_direction)
current_position.Z++;
else
current_position.Z--;
dda->z_counter += dda->total_steps;
}
dda->z_counter -= dda->z_step_interval;
dda->z_delta--;
}
}
if (current_position.E != dda->endpoint.E) {
dda->e_counter -= dda->e_delta;
if (dda->e_counter < 0) {
e_step();
did_step = 1;
if (dda->e_counter > dda->e_step_interval) {
if ((current_position.E != dda->endpoint.E) /* &&
(e_max() != dda->e_direction) && (e_min() == dda->e_direction) */) {
e_step();
if (dda->e_direction)
current_position.E++;
else
current_position.E--;
dda->e_counter += dda->total_steps;
}
dda->e_counter -= dda->e_step_interval;
dda->e_delta--;
}
}
#else
if ((current_position.X != dda->endpoint.X) /* &&
(x_max() != dda->x_direction) && (x_min() == dda->x_direction) */) {
dda->x_counter -= dda->x_delta;
if (dda->x_counter < 0) {
x_step();
did_step = 1;
if (dda->x_direction)
current_position.X++;
else
current_position.X--;
dda->x_counter += dda->total_steps;
}
}
if ((current_position.Y != dda->endpoint.Y) /* &&
(y_max() != dda->y_direction) && (y_min() == dda->y_direction) */) {
dda->y_counter -= dda->y_delta;
if (dda->y_counter < 0) {
y_step();
did_step = 1;
if (dda->y_direction)
current_position.Y++;
else
current_position.Y--;
dda->y_counter += dda->total_steps;
}
}
if ((current_position.Z != dda->endpoint.Z) /* &&
(z_max() != dda->z_direction) && (z_min() == dda->z_direction) */) {
dda->z_counter -= dda->z_delta;
if (dda->z_counter < 0) {
z_step();
did_step = 1;
if (dda->z_direction)
current_position.Z++;
else
current_position.Z--;
dda->z_counter += dda->total_steps;
}
}
if (current_position.E != dda->endpoint.E) {
dda->e_counter -= dda->e_delta;
if (dda->e_counter < 0) {
e_step();
did_step = 1;
if (dda->e_direction)
current_position.E++;
else
current_position.E--;
dda->e_counter += dda->total_steps;
}
}
#endif
#if STEP_INTERRUPT_INTERRUPTIBLE
// since we have sent steps to all the motors that will be stepping and the rest of this function isn't so time critical,
@ -524,6 +588,32 @@ void dda_step(DDA *dda) {
}
dda->step_no++;
#endif
#ifdef ACCELERATION_TEMPORAL
dda->c = dda->x_step_interval - dda->x_counter;
if ((dda->y_step_interval - dda->y_counter) < dda->c)
dda->c = dda->y_step_interval - dda->y_counter;
if ((dda->z_step_interval - dda->z_counter) < dda->c)
dda->c = dda->z_step_interval - dda->z_counter;
if ((dda->e_step_interval - dda->e_counter) < dda->c)
dda->c = dda->e_step_interval - dda->e_counter;
if (dda->x_delta)
dda->x_counter += dda->c;
if (dda->y_delta)
dda->y_counter += dda->c;
if (dda->z_delta)
dda->z_counter += dda->c;
if (dda->e_delta)
dda->e_counter += dda->c;
if (
(dda->x_delta > 0) ||
(dda->y_delta > 0) ||
(dda->z_delta > 0) ||
(dda->e_delta > 0))
did_step = 1;
dda->c <<= 8;
#endif
if (did_step) {
// we stepped, reset timeout
@ -540,6 +630,7 @@ void dda_step(DDA *dda) {
// in theory, we *could* update F every step, but that would require a divide in interrupt context which should be avoided if at all possible
current_position.F = dda->endpoint.F;
}
setTimer(dda->c >> 8);
// turn off step outputs, hopefully they've been on long enough by now to register with the drivers

6
dda.h
View File

@ -86,6 +86,12 @@ typedef struct {
int32_t n;
ramp_state_t ramp_state;
#endif
#ifdef ACCELERATION_TEMPORAL
uint32_t x_step_interval;
uint32_t y_step_interval;
uint32_t z_step_interval;
uint32_t e_step_interval;
#endif
} DDA;
/*