Implement Roland Brochards' new acceleration maths clock-based.
His implementation was done on every step and as it turns out, the very same maths works just fine in the clock interrupt. Reason for the clock interrupt is: it allows about 3 times higher step rates. This strategy is not only substantially faster, but also a bit smaller. One funny anecdote: the acceleration initialisation value, C0, was taken from elsewhere in the code as-is. Still it had to be adjusted by a factor of sqrt(2) to now(!) match the physics formulas and to get ramps reasonably matching the prediction (and my pocket calculator). Apparently the code before accumulated enough rounding errors to compensate for the wrong formula.
This commit is contained in:
parent
921348c474
commit
3988e47467
155
dda.c
155
dda.c
|
|
@ -53,11 +53,6 @@ void dda_init(void) {
|
||||||
// set up default feedrate
|
// set up default feedrate
|
||||||
if (startpoint.F == 0)
|
if (startpoint.F == 0)
|
||||||
startpoint.F = next_target.target.F = SEARCH_FEEDRATE_Z;
|
startpoint.F = next_target.target.F = SEARCH_FEEDRATE_Z;
|
||||||
|
|
||||||
#ifdef ACCELERATION_RAMPING
|
|
||||||
move_state.n = 1;
|
|
||||||
move_state.c = ((uint32_t)((double)F_CPU / sqrt((double)(STEPS_PER_M_X * ACCELERATION / 1000.)))) << 8;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*! Distribute a new startpoint to DDA's internal structures without any movement.
|
/*! Distribute a new startpoint to DDA's internal structures without any movement.
|
||||||
|
|
@ -337,7 +332,7 @@ void dda_create(DDA *dda, TARGET *target, DDA *prev_dda) {
|
||||||
// as the ramp length is calculated for XY, its incorrect for Z: apply the original
|
// as the ramp length is calculated for XY, its incorrect for Z: apply the original
|
||||||
// 'fix' to simply specify a large enough ramp for any speed.
|
// 'fix' to simply specify a large enough ramp for any speed.
|
||||||
if (x_delta_um == 0 && y_delta_um == 0) {
|
if (x_delta_um == 0 && y_delta_um == 0) {
|
||||||
dda->rampup_steps = 100000; // replace mis-calculation by a safe value
|
dda->rampup_steps = 1000000; // replace mis-calculation by a safe value
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dda->rampup_steps > dda->total_steps / 2)
|
if (dda->rampup_steps > dda->total_steps / 2)
|
||||||
|
|
@ -429,6 +424,8 @@ void dda_start(DDA *dda) {
|
||||||
memcpy(&move_state.x_steps, &dda->x_delta, sizeof(uint32_t) * 4);
|
memcpy(&move_state.x_steps, &dda->x_delta, sizeof(uint32_t) * 4);
|
||||||
#ifdef ACCELERATION_RAMPING
|
#ifdef ACCELERATION_RAMPING
|
||||||
move_state.step_no = 0;
|
move_state.step_no = 0;
|
||||||
|
move_state.n = 0;
|
||||||
|
move_state.c = C0;
|
||||||
#endif
|
#endif
|
||||||
#ifdef ACCELERATION_TEMPORAL
|
#ifdef ACCELERATION_TEMPORAL
|
||||||
move_state.x_time = move_state.y_time = \
|
move_state.x_time = move_state.y_time = \
|
||||||
|
|
@ -440,12 +437,9 @@ void dda_start(DDA *dda) {
|
||||||
|
|
||||||
// set timeout for first step
|
// set timeout for first step
|
||||||
#ifdef ACCELERATION_RAMPING
|
#ifdef ACCELERATION_RAMPING
|
||||||
if (dda->c_min > move_state.c) // can be true when look-ahead removed all deceleration steps
|
|
||||||
setTimer(dda->c_min >> 8);
|
|
||||||
else
|
|
||||||
setTimer(move_state.c >> 8);
|
setTimer(move_state.c >> 8);
|
||||||
#else
|
#else
|
||||||
setTimer(dda->c >> 8);
|
setTimer(dda->c >> 8);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
// else just a speed change, keep dda->live = 0
|
// else just a speed change, keep dda->live = 0
|
||||||
|
|
@ -537,7 +531,7 @@ void dda_step(DDA *dda) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if STEP_INTERRUPT_INTERRUPTIBLE
|
#if STEP_INTERRUPT_INTERRUPTIBLE && ! defined ACCELERATION_RAMPING
|
||||||
// Since we have sent steps to all the motors that will be stepping
|
// Since we have sent steps to all the motors that will be stepping
|
||||||
// and the rest of this function isn't so time critical, this interrupt
|
// and the rest of this function isn't so time critical, this interrupt
|
||||||
// can now be interruptible by other interrupts.
|
// can now be interruptible by other interrupts.
|
||||||
|
|
@ -573,60 +567,9 @@ void dda_step(DDA *dda) {
|
||||||
// else we are already at target speed
|
// else we are already at target speed
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ACCELERATION_RAMPING
|
#ifdef ACCELERATION_RAMPING
|
||||||
// - algorithm courtesy of http://www.embedded.com/columns/technicalinsights/56800129?printable=true
|
|
||||||
// - precalculate ramp lengths instead of counting them, see AVR446 tech note
|
|
||||||
uint8_t recalc_speed;
|
|
||||||
|
|
||||||
// debug ramping algorithm
|
|
||||||
//if (move_state.step_no == 0) {
|
|
||||||
// sersendf_P(PSTR("\r\nc %lu c_min %lu n %d"), dda->c, dda->c_min, move_state.n);
|
|
||||||
//}
|
|
||||||
|
|
||||||
recalc_speed = 0;
|
|
||||||
if (move_state.step_no < dda->rampup_steps) {
|
|
||||||
if (move_state.n < 0) // wrong ramp direction
|
|
||||||
move_state.n = -((int32_t)2) - move_state.n;
|
|
||||||
recalc_speed = 1;
|
|
||||||
}
|
|
||||||
else if (move_state.step_no >= dda->rampdown_steps) {
|
|
||||||
if (move_state.n > 0) // wrong ramp direction
|
|
||||||
move_state.n = -((int32_t)2) - move_state.n;
|
|
||||||
recalc_speed = 1;
|
|
||||||
}
|
|
||||||
if (recalc_speed) {
|
|
||||||
move_state.n += 4;
|
|
||||||
// be careful of signedness!
|
|
||||||
move_state.c = (int32_t)move_state.c - ((int32_t)(move_state.c * 2) / (int32_t)move_state.n);
|
|
||||||
//sersendf_P(PSTR("n:%ld; c:%ld; steps: %ld / %lu\n"), move_state.n,
|
|
||||||
// move_state.c, move_state.step_no, move_state.y_steps);
|
|
||||||
}
|
|
||||||
move_state.step_no++;
|
move_state.step_no++;
|
||||||
|
|
||||||
#ifdef ACCELERATION_RAMPING
|
|
||||||
// This is a hack which deals with movements with an unknown number of
|
|
||||||
// acceleration steps. dda_create() sets a very high number, then.
|
|
||||||
if (move_state.c < dda->c_min &&
|
|
||||||
dda->rampup_steps > move_state.step_no + 5) {
|
|
||||||
dda->rampup_steps = move_state.step_no;
|
|
||||||
dda->rampdown_steps = dda->total_steps - dda->rampup_steps;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Print the number of steps actually needed for ramping up
|
|
||||||
// Needed for comparing the number with the one calculated in dda_create()
|
|
||||||
//static char printed = 0;
|
|
||||||
//if (printed == 0 && dda->c_min >= move_state.c) {
|
|
||||||
// sersendf_P(PSTR("speedup %lu steps\n"), move_state.step_no);
|
|
||||||
// printed = 1;
|
|
||||||
//}
|
|
||||||
//if (move_state.step_no < 3) printed = 0;
|
|
||||||
|
|
||||||
// debug ramping algorithm
|
|
||||||
// raise this 10 for higher speeds to avoid flooding the serial line
|
|
||||||
//if (move_state.step_no % 10 /* 10, 50, 100, ...*/ == 0)
|
|
||||||
// sersendf_P(PSTR("\r\nc %lu c_min %lu n %ld"),
|
|
||||||
// move_state.c, dda->c_min, move_state.n);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef ACCELERATION_TEMPORAL
|
#ifdef ACCELERATION_TEMPORAL
|
||||||
|
|
@ -674,7 +617,7 @@ void dda_step(DDA *dda) {
|
||||||
if ((move_state.x_steps == 0 && move_state.y_steps == 0 &&
|
if ((move_state.x_steps == 0 && move_state.y_steps == 0 &&
|
||||||
move_state.z_steps == 0 && move_state.e_steps == 0)
|
move_state.z_steps == 0 && move_state.e_steps == 0)
|
||||||
#ifdef ACCELERATION_RAMPING
|
#ifdef ACCELERATION_RAMPING
|
||||||
|| (dda->endstop_check && move_state.n == -3)
|
|| (dda->endstop_check && move_state.n == 0)
|
||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
dda->live = 0;
|
dda->live = 0;
|
||||||
|
|
@ -693,13 +636,7 @@ void dda_step(DDA *dda) {
|
||||||
psu_timeout = 0;
|
psu_timeout = 0;
|
||||||
|
|
||||||
#ifdef ACCELERATION_RAMPING
|
#ifdef ACCELERATION_RAMPING
|
||||||
// we don't hit maximum speed exactly with acceleration calculation, so limit it here
|
setTimer(move_state.c >> 8);
|
||||||
// the nice thing about _not_ setting dda->c to dda->c_min is, the move stops at the exact same c as it started, so we have to calculate c only once for the time being
|
|
||||||
// TODO: set timer only if dda->c has changed
|
|
||||||
if (dda->c_min > move_state.c)
|
|
||||||
setTimer(dda->c_min >> 8);
|
|
||||||
else
|
|
||||||
setTimer(move_state.c >> 8);
|
|
||||||
#else
|
#else
|
||||||
setTimer(dda->c >> 8);
|
setTimer(dda->c >> 8);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -714,19 +651,26 @@ void dda_step(DDA *dda) {
|
||||||
|
|
||||||
This should be called pretty often, like once every 1 ot 2 milliseconds.
|
This should be called pretty often, like once every 1 ot 2 milliseconds.
|
||||||
|
|
||||||
Currently, this is checking the endstops. These don't need to be checked on
|
Currently, this is checking the endstops and doing acceleration maths. These
|
||||||
every single step, so this code can be moved out of the highly time critical
|
don't need to be checked/recalculated on every single step, so this code
|
||||||
dda_step(). At high precision (slow) searches of the endstop, this function
|
can be moved out of the highly time critical dda_step(). At high precision
|
||||||
is called more often than dda_step() anyways.
|
(slow) searches of the endstop, this function is called more often than
|
||||||
|
dda_step() anyways.
|
||||||
|
|
||||||
In the future, acceleration and arc movement calculations might go here, too.
|
In the future, arc movement calculations might go here, too. Updating
|
||||||
Updating speed 500 times a second is easily enough for smooth acceleration!
|
movement direction 500 times a second is easily enough for smooth and
|
||||||
|
accurate curves!
|
||||||
*/
|
*/
|
||||||
|
#ifdef LOOKAHEAD
|
||||||
|
#error Look-ahead currently not supported. Turn it off or use an earlier commit.
|
||||||
|
#endif
|
||||||
void dda_clock() {
|
void dda_clock() {
|
||||||
static volatile uint8_t busy = 0;
|
static volatile uint8_t busy = 0;
|
||||||
DDA *dda;
|
DDA *dda;
|
||||||
static DDA *last_dda = NULL;
|
static DDA *last_dda = NULL;
|
||||||
static uint8_t endstop_stop = 0; ///< Stop due to endstop trigger
|
static uint8_t endstop_stop = 0; ///< Stop due to endstop trigger
|
||||||
|
uint32_t move_step_no, move_c;
|
||||||
|
uint8_t recalc_speed;
|
||||||
|
|
||||||
dda = queue_current_movement();
|
dda = queue_current_movement();
|
||||||
if (dda != last_dda) {
|
if (dda != last_dda) {
|
||||||
|
|
@ -812,20 +756,67 @@ void dda_clock() {
|
||||||
// If an endstop is definitely triggered, stop the movement.
|
// If an endstop is definitely triggered, stop the movement.
|
||||||
if (endstop_stop) {
|
if (endstop_stop) {
|
||||||
#ifdef ACCELERATION_RAMPING
|
#ifdef ACCELERATION_RAMPING
|
||||||
// For always smooth operations, don't halt apruptly,
|
// For always smooth operations, don't halt apruptly,
|
||||||
// but start deceleration here.
|
// but start deceleration here.
|
||||||
ATOMIC_START
|
// Not atomic, because not used in dda_step().
|
||||||
dda->rampdown_steps = move_state.step_no;
|
dda->rampdown_steps = move_state.step_no;
|
||||||
dda->rampup_steps = 0; // in case we're still accelerating
|
dda->rampup_steps = 0; // in case we're still accelerating
|
||||||
ATOMIC_END
|
|
||||||
#else
|
#else
|
||||||
dda->live = 0;
|
dda->live = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
endstops_off();
|
endstops_off();
|
||||||
}
|
}
|
||||||
} /* if (endstop_stop == 0) */
|
} /* if (endstop_stop == 0) */
|
||||||
|
|
||||||
|
#ifdef ACCELERATION_RAMPING
|
||||||
|
// For maths about stepper speed profiles, see
|
||||||
|
// http://www.embedded.com/columns/technicalinsights/56800129?printable=true
|
||||||
|
// and http://www.atmel.com/images/doc8017.pdf (Atmel app note AVR446)
|
||||||
|
ATOMIC_START
|
||||||
|
move_step_no = move_state.step_no;
|
||||||
|
// All other variables are read-only or unused in dda_step(),
|
||||||
|
// so no need for atomic operations.
|
||||||
|
ATOMIC_END
|
||||||
|
|
||||||
|
recalc_speed = 0;
|
||||||
|
if (move_step_no < dda->rampup_steps) {
|
||||||
|
move_state.n = move_step_no;
|
||||||
|
recalc_speed = 1;
|
||||||
|
}
|
||||||
|
else if (move_step_no >= dda->rampdown_steps) {
|
||||||
|
move_state.n = dda->total_steps - move_step_no;
|
||||||
|
recalc_speed = 1;
|
||||||
|
}
|
||||||
|
if (recalc_speed) {
|
||||||
|
if (move_state.n == 0)
|
||||||
|
move_c = C0;
|
||||||
|
else
|
||||||
|
// Explicit formula: sqrt(n + 1) - sqrt(n),
|
||||||
|
// approximation here: 1 / (2 * sqrt(n)).
|
||||||
|
move_c = ((C0 >> 8) * int_inv_sqrt(move_state.n)) >> 5;
|
||||||
|
|
||||||
|
if (move_c < dda->c_min) {
|
||||||
|
// We hit max speed not always exactly.
|
||||||
|
move_c = dda->c_min;
|
||||||
|
|
||||||
|
#ifndef LOOKAHEAD
|
||||||
|
// This is a hack which deals with movements with an unknown number of
|
||||||
|
// acceleration steps. dda_create() sets a very high number, then,
|
||||||
|
// but we don't want to re-calculate all the time.
|
||||||
|
dda->rampup_steps = move_step_no;
|
||||||
|
dda->rampdown_steps = dda->total_steps - dda->rampup_steps;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write results.
|
||||||
|
ATOMIC_START
|
||||||
|
move_state.c = move_c;
|
||||||
|
ATOMIC_END
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
cli(); // Compensate sei() above.
|
cli(); // Compensate sei() above.
|
||||||
busy = 0;
|
busy = 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,4 +69,7 @@ const uint8_t msbloc (uint32_t v);
|
||||||
// Note: the floating point bit is optimized away during compilation
|
// Note: the floating point bit is optimized away during compilation
|
||||||
#define ACCELERATE_RAMP_LEN(speed) (((speed)*(speed)) / (uint32_t)((7200000.0f * ACCELERATION) / (float)STEPS_PER_M_X))
|
#define ACCELERATE_RAMP_LEN(speed) (((speed)*(speed)) / (uint32_t)((7200000.0f * ACCELERATION) / (float)STEPS_PER_M_X))
|
||||||
|
|
||||||
|
// Initialization constant for the ramping algorithm.
|
||||||
|
#define C0 (((uint32_t)((double)F_CPU / sqrt((double)(STEPS_PER_M_X * ACCELERATION / 2000.)))) << 8)
|
||||||
|
|
||||||
#endif /* _DDA_MATHS_H */
|
#endif /* _DDA_MATHS_H */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue