diff --git a/dda.c b/dda.c index 4ec4ff0..a7c5412 100644 --- a/dda.c +++ b/dda.c @@ -527,7 +527,7 @@ void dda_start(DDA *dda) { dda->live = 1; // set timeout for first step - timer_set(dda->c); + timer_set(dda->c, 0); } // else just a speed change, keep dda->live = 0 @@ -644,47 +644,63 @@ void dda_step(DDA *dda) { later. In turn we promise here to send a maximum of four such short-delays consecutively and to give sufficient time on average. */ - uint32_t c_candidate; - uint8_t i; + // This is the time which led to this call of dda_step(). + move_state.last_time = move_state.time[dda->axis_to_step] + + dda->step_interval[dda->axis_to_step]; - if (dda->axis_to_step == X) { - x_step(); - move_state.steps[X]--; - move_state.time[X] += dda->step_interval[X]; - move_state.last_time = move_state.time[X]; - } - if (dda->axis_to_step == Y) { - y_step(); - move_state.steps[Y]--; - move_state.time[Y] += dda->step_interval[Y]; - move_state.last_time = move_state.time[Y]; - } - if (dda->axis_to_step == Z) { - z_step(); - move_state.steps[Z]--; - move_state.time[Z] += dda->step_interval[Z]; - move_state.last_time = move_state.time[Z]; - } - if (dda->axis_to_step == E) { - e_step(); - move_state.steps[E]--; - move_state.time[E] += dda->step_interval[E]; - move_state.last_time = move_state.time[E]; - } + do { + uint32_t c_candidate; + enum axis_e i; - dda->c = 0xFFFFFFFF; - for (i = X; i < AXIS_COUNT; i++) { - if (move_state.steps[i]) { - c_candidate = move_state.time[i] + dda->step_interval[i] - move_state.last_time; - if (c_candidate < dda->c) { - dda->axis_to_step = i; - dda->c = c_candidate; + if (dda->axis_to_step == X) { + x_step(); + move_state.steps[X]--; + move_state.time[X] += dda->step_interval[X]; + } + if (dda->axis_to_step == Y) { + y_step(); + move_state.steps[Y]--; + move_state.time[Y] += dda->step_interval[Y]; + } + if (dda->axis_to_step == Z) { + z_step(); + move_state.steps[Z]--; + move_state.time[Z] += dda->step_interval[Z]; + } + if (dda->axis_to_step == E) { + e_step(); + move_state.steps[E]--; + move_state.time[E] += dda->step_interval[E]; + } + unstep(); + + // Find the next stepper to step. + dda->c = 0xFFFFFFFF; + for (i = X; i < AXIS_COUNT; i++) { + if (move_state.steps[i]) { + c_candidate = move_state.time[i] + dda->step_interval[i] - + move_state.last_time; + if (c_candidate < dda->c) { + dda->axis_to_step = i; + dda->c = c_candidate; + } } } - } - #endif + + // No stepper to step found? Then we're done. + if (dda->c == 0xFFFFFFFF) { + dda->live = 0; + dda->done = 1; + break; + } + } while (timer_set(dda->c, 1)); + + #endif /* ACCELERATION_TEMPORAL */ // If there are no steps left or an endstop stop happened, we have finished. + // + // TODO: with ACCELERATION_TEMPORAL this duplicates some code. See where + // dda->live is zero'd, about 10 lines above. if ((move_state.steps[X] == 0 && move_state.steps[Y] == 0 && move_state.steps[Z] == 0 && move_state.steps[E] == 0) #ifdef ACCELERATION_RAMPING @@ -709,7 +725,9 @@ void dda_step(DDA *dda) { } else { psu_timeout = 0; - timer_set(dda->c); + #ifndef ACCELERATION_TEMPORAL + timer_set(dda->c, 0); + #endif } // turn off step outputs, hopefully they've been on long enough by now to register with the drivers diff --git a/dda_queue.c b/dda_queue.c index a3580bb..1e149a8 100644 --- a/dda_queue.c +++ b/dda_queue.c @@ -82,7 +82,7 @@ void queue_step() { DDA* current_movebuffer = &movebuffer[mb_tail]; if (current_movebuffer->live) { if (current_movebuffer->waitfor_temp) { - timer_set(HEATER_WAIT_TIMEOUT); + timer_set(HEATER_WAIT_TIMEOUT, 0); if (temp_achieved()) { current_movebuffer->live = current_movebuffer->done = 0; serial_writestr_P(PSTR("Temp achieved\n")); @@ -165,7 +165,7 @@ void next_move() { if (current_movebuffer->waitfor_temp) { serial_writestr_P(PSTR("Waiting for target temp\n")); current_movebuffer->live = 1; - timer_set(HEATER_WAIT_TIMEOUT); + timer_set(HEATER_WAIT_TIMEOUT, 0); } else { dda_start(current_movebuffer); diff --git a/timer.c b/timer.c index 0342bad..db09ef9 100644 --- a/timer.c +++ b/timer.c @@ -101,16 +101,39 @@ void timer_init() { /*! Specify how long until the step timer should fire. \param delay in CPU ticks + \param check_short tells wether to check for impossibly short requests. This + should be set to 1 for calls from the step interrupt. Short requests + then return 1 and do not schedule a timer interrupt. The calling code + usually wants to handle this case. + + Calls from elsewhere should set it to 0. In this case a timer + interrupt is always scheduled. At the risk that this scheduling + doesn't delay the requested time, but up to a full timer counter + overflow ( = 65536 / F_CPU = 3 to 4 milliseconds). + + \return a flag wether the requested time was too short to allow scheduling + an interrupt. This is meaningful for ACCELERATION_TEMPORAL, where + requested delays can be zero or even negative. In this case, the + calling code should repeat the stepping code immediately and also + assume the timer to not change his idea of when the last step + happened. + + Strategy of this timer is to schedule timer interrupts not starting at the + time of the call, but starting at the time of the previous timer interrupt + fired. This ignores the processing time taken in the step interrupt so far, + offering smooth and even step distribution. Flipside of this coin is, + schedules issued at an arbitrary time can result in drastically wrong delays. + See also discussion of parameter check_short and the return value. + This enables the step interrupt, but also disables interrupts globally. So, if you use it from inside the step interrupt, make sure to do so as late as possible. If you use it from outside the step interrupt, do a sei() after it to make the interrupt actually fire. */ -void timer_set(uint32_t delay) { +char timer_set(int32_t delay, char check_short) { uint16_t step_start = 0; #ifdef ACCELERATION_TEMPORAL uint16_t current_time; - uint32_t earliest_time, actual_time; #endif /* ACCELERATION_TEMPORAL */ // An interrupt would make all our timing calculations invalid, @@ -126,33 +149,22 @@ void timer_set(uint32_t delay) { next_step_time = delay; #ifdef ACCELERATION_TEMPORAL - // 300 = safe number of cpu cycles until the interrupt actually happens - current_time = TCNT1; - earliest_time = (uint32_t)current_time + 300; - if (current_time < step_start) // timer counter did overflow recently - earliest_time += 0x00010000; - actual_time = (uint32_t)step_start + next_step_time; + if (check_short) { + current_time = TCNT1; - // Setting the interrupt earlier than it can happen obviously doesn't - // make sense. To keep the "belongs to one move" idea, add an extra, - // remember this extra and compensate the extra if a longer delay comes in. - if (earliest_time > actual_time) { - step_extra_time += (earliest_time - actual_time); - next_step_time = earliest_time - (uint32_t)step_start; - } - else if (step_extra_time) { - if (step_extra_time < actual_time - earliest_time) { - next_step_time -= step_extra_time; - step_extra_time = 0; - } - else { - step_extra_time -= (actual_time - earliest_time); - next_step_time -= (actual_time - earliest_time); - } - } + // 200 = safe number of cpu cycles after current_time to allow a new + // interrupt happening. This is mostly the time needed to complete the + // current interrupt. + if ((current_time - step_start) + 200 > delay) + return 1; + } #endif /* ACCELERATION_TEMPORAL */ - // Now we know how long we actually want to delay, so set the timer. + // From here on we assume the requested delay is long enough to allow + // completion of the current interrupt before the next one is about to + // happen. + + // Now we know how long we actually want to delay, so set the timer. if (next_step_time < 65536) { // set the comparator directly to the next real step OCR1A = (next_step_time + step_start) & 0xFFFF; @@ -177,6 +189,8 @@ void timer_set(uint32_t delay) { // Tell simulator sim_timer_set(); #endif + + return 0; } /// stop timers - emergency stop diff --git a/timer.h b/timer.h index c185ef3..a7118e1 100644 --- a/timer.h +++ b/timer.h @@ -26,7 +26,7 @@ timer stuff */ void timer_init(void) __attribute__ ((cold)); -void timer_set(uint32_t delay); +char timer_set(int32_t delay, char check_short); void timer_stop(void);