diff --git a/Makefile b/Makefile index ffac8f0..afede04 100644 --- a/Makefile +++ b/Makefile @@ -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 # diff --git a/config.h.dist b/config.h.dist index fb11c23..2f6c9b9 100644 --- a/config.h.dist +++ b/config.h.dist @@ -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 diff --git a/dda.c b/dda.c index ad96dac..2baa768 100644 --- a/dda.c +++ b/dda.c @@ -1,6 +1,7 @@ #include "dda.h" #include +#include #include #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 diff --git a/dda.h b/dda.h index a7f374b..48f6808 100644 --- a/dda.h +++ b/dda.h @@ -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; /*