diff --git a/config.h b/config.h index 952f7a2..89d9aea 100644 --- a/config.h +++ b/config.h @@ -3,6 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud + Copyright (c) 2011 Sungeun K. Jeon Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,6 +22,8 @@ #ifndef config_h #define config_h +// IMPORTANT: Any changes here requires a full re-compiling of the source code to propagate them. + #define BAUD_RATE 9600 // Updated default pin-assignments from 0.6 onwards @@ -55,12 +58,32 @@ // The temporal resolution of the acceleration management subsystem. Higher number // give smoother acceleration but may impact performance -// NOTE: Increasing this parameter will help remove the long slow motion bug at the end -// of very fast de/ac-celerations. This is due to the increased resolution of the -// acceleration steps that more accurately predicted by the planner exact integration -// of acceleration distance. An efficient solution to this bug is under investigation. -// In general, setting this parameter is high as your system will allow is suggested. -#define ACCELERATION_TICKS_PER_SECOND 40L +// NOTE: Increasing this parameter will help any resolution related issues, especially with machines +// requiring very high accelerations and/or very fast feedrates. In general, this will reduce the +// error between how the planner plans the motions and how the stepper program actually performs them. +// However, at some point, the resolution can be high enough, where the errors related to numerical +// round-off can be great enough to cause problems and/or it's too fast for the Arduino. The correct +// value for this parameter is machine dependent, so it's advised to set this only as high as needed. +// Approximate successful values can range from 30L to 100L or more. +#define ACCELERATION_TICKS_PER_SECOND 50L + +// Minimum planner junction speed. Sets the default minimum speed the planner plans for at the end +// of the buffer and all stops. This should not be much greater than zero and should only be changed +// if unwanted behavior is observed on a user's machine when running at very slow speeds. +#define MINIMUM_PLANNER_SPEED 0.0 // (mm/min) + +// Minimum stepper rate. Sets the absolute minimum stepper rate in the stepper program and never run +// slower than this value, except when sleeping. This parameter overrides the minimum planner speed. +// This is primarily used to guarantee that the end of a movement is always reached and not stop to +// never reach its target. This parameter should always be greater than zero. +#define MINIMUM_STEPS_PER_MINUTE 800 // (steps/min) - Integer value only + +// Number of arc generation iterations by small angle approximation before exact arc +// trajectory correction. Value must be 1-255. This parameter maybe decreased if there are issues +// with the accuracy of the arc generations. In general, the default value is more than enough for +// the intended CNC applications of grbl, and should be on the order or greater than the size of +// the buffer to help with the computational efficiency of generating arcs. +#define N_ARC_CORRECTION 25 #endif @@ -91,5 +114,4 @@ // // #define SPINDLE_DIRECTION_DDR DDRD // #define SPINDLE_DIRECTION_PORT PORTD -// #define SPINDLE_DIRECTION_BIT 7 - +// #define SPINDLE_DIRECTION_BIT 7 \ No newline at end of file diff --git a/gcode.c b/gcode.c index 1027a7f..1930389 100644 --- a/gcode.c +++ b/gcode.c @@ -388,5 +388,4 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t group 9 = {M48, M49} enable/disable feed and speed override switches group 12 = {G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3} coordinate system selection group 13 = {G61, G61.1, G64} path control mode -*/ - +*/ \ No newline at end of file diff --git a/motion_control.c b/motion_control.c index 98931f8..52a72c2 100644 --- a/motion_control.c +++ b/motion_control.c @@ -21,6 +21,7 @@ #include #include "settings.h" +#include "config.h" #include "motion_control.h" #include #include @@ -29,11 +30,6 @@ #include "stepper.h" #include "planner.h" -// Number of arc generation iterations with small angle approximation before exact arc -// trajectory correction. Value must be 1-255. -#define N_ARC_CORRECTION 25 - - // Execute dwell in seconds. Maximum time delay is > 18 hours, more than enough for any application. void mc_dwell(double seconds) { @@ -48,7 +44,7 @@ void mc_dwell(double seconds) // Execute an arc in offset mode format. position == current xyz, target == target xyz, // offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is -// the direction of helical travel, radius == circle radius, clockwise_sign == -1 or 1. Used +// the direction of helical travel, radius == circle radius, isclockwise boolean. Used // for vector transformation direction. // position, target, and offset are pointers to vectors from gcode.c diff --git a/planner.c b/planner.c index 4be9c88..b3abd6c 100644 --- a/planner.c +++ b/planner.c @@ -193,6 +193,7 @@ static void planner_forward_pass() { // Calculates trapezoid parameters so that the entry- and exit-speed is compensated by the provided factors. // The factors represent a factor of braking and must be in the range 0.0-1.0. // This converts the planner parameters to the data required by the stepper controller. +// NOTE: Final rates must be computed in terms of their respective blocks. static void calculate_trapezoid_for_block(block_t *block, double entry_factor, double exit_factor) { block->initial_rate = ceil(block->nominal_rate*entry_factor); // (step/min) @@ -212,6 +213,8 @@ static void calculate_trapezoid_for_block(block_t *block, double entry_factor, d if (plateau_steps < 0) { accelerate_steps = ceil( intersection_distance(block->initial_rate, block->final_rate, acceleration_per_minute, block->step_event_count)); + accelerate_steps = max(accelerate_steps,0); // Check limits due to numerical round-off + accelerate_steps = min(accelerate_steps,block->step_event_count); plateau_steps = 0; } @@ -251,8 +254,9 @@ static void planner_recalculate_trapezoids() { } block_index = next_block_index( block_index ); } - // Last/newest block in buffer. Exit speed is zero. Always recalculated. - calculate_trapezoid_for_block(next, next->entry_speed/next->nominal_speed, 0.0); + // Last/newest block in buffer. Exit speed is set with MINIMUM_PLANNER_SPEED. Always recalculated. + calculate_trapezoid_for_block(next, next->entry_speed/next->nominal_speed, + MINIMUM_PLANNER_SPEED/next->nominal_speed); next->recalculate_flag = false; } @@ -273,6 +277,9 @@ static void planner_recalculate_trapezoids() { // // 3. Recalculate trapezoids for all blocks using the recently updated junction speeds. Block trapezoids // with no updated junction speeds will not be recalculated and assumed ok as is. +// +// All planner computations are performed with doubles (float on Arduinos) to minimize numerical round- +// off errors. Only when planned values are converted to stepper rate parameters, these are integers. static void planner_recalculate() { planner_reverse_pass(); @@ -396,7 +403,7 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in // path width or max_jerk in the previous grbl version. This approach does not actually deviate // from path, but used as a robust way to compute cornering speeds, as it takes into account the // nonlinearities of both the junction angle and junction velocity. - double vmax_junction = 0.0; // Set default zero max junction speed + double vmax_junction = MINIMUM_PLANNER_SPEED; // Set default max junction speed // Skip first block or when previous_nominal_speed is used as a flag for homing and offset cycles. if ((block_buffer_head != block_buffer_tail) && (previous_nominal_speed > 0.0)) { @@ -420,8 +427,8 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in } block->max_entry_speed = vmax_junction; - // Initialize block entry speed. Compute based on deceleration to rest (zero speed). - double v_allowable = max_allowable_speed(-settings.acceleration,0.0,block->millimeters); + // Initialize block entry speed. Compute based on deceleration to user-defined MINIMUM_PLANNER_SPEED. + double v_allowable = max_allowable_speed(-settings.acceleration,MINIMUM_PLANNER_SPEED,block->millimeters); block->entry_speed = min(vmax_junction, v_allowable); // Initialize planner efficiency flags @@ -465,4 +472,4 @@ void plan_set_current_position(double x, double y, double z) { position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); previous_nominal_speed = 0.0; // Resets planner junction speeds. Assumes start from rest. clear_vector_double(previous_unit_vec); -} +} \ No newline at end of file diff --git a/settings.c b/settings.c index bcf14cf..f85e3c3 100644 --- a/settings.c +++ b/settings.c @@ -80,7 +80,7 @@ void settings_dump() { printPgmString(PSTR(" (step port invert mask. binary = ")); printIntegerInBase(settings.invert_mask, 2); printPgmString(PSTR(")\r\n$8 = ")); printFloat(settings.acceleration); printPgmString(PSTR(" (acceleration in mm/sec^2)\r\n$9 = ")); printFloat(settings.junction_deviation); - printPgmString(PSTR(" (junction deviation for cornering in mm)")); + printPgmString(PSTR(" (cornering junction deviation in mm)")); printPgmString(PSTR("\r\n'$x=value' to set parameter or just '$' to dump current settings\r\n")); } @@ -125,12 +125,18 @@ int read_settings() { return(false); } } else if (version == 1) { - // Migrate from old settings version + // Migrate from settings version 1 if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_v1_t)))) { return(false); } settings.acceleration = DEFAULT_ACCELERATION; settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; + } else if (version == 2) { + // Migrate from settings version 2 + if (!(memcpy_from_eeprom_with_checksum((char*)&settings, 1, sizeof(settings_t)))) { + return(false); + } + settings.junction_deviation = DEFAULT_JUNCTION_DEVIATION; } else { return(false); } diff --git a/stepper.c b/stepper.c index 164db81..ea6c17f 100644 --- a/stepper.c +++ b/stepper.c @@ -3,7 +3,8 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - + Modifications Copyright (c) 2011 Sungeun K. Jeon + Grbl is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or @@ -40,8 +41,6 @@ #define TICKS_PER_MICROSECOND (F_CPU/1000000) #define CYCLES_PER_ACCELERATION_TICK ((TICKS_PER_MICROSECOND*1000000)/ACCELERATION_TICKS_PER_SECOND) -#define MINIMUM_STEPS_PER_MINUTE 1200 // The stepper subsystem will never run slower than this, exept when sleeping - static block_t *current_block; // A pointer to the block currently being traced // Variables used by The Stepper Driver Interrupt @@ -95,41 +94,27 @@ static void st_go_idle() { // block begins. static void trapezoid_generator_reset() { trapezoid_adjusted_rate = current_block->initial_rate; - trapezoid_tick_cycle_counter = 0; // Always start a new trapezoid with a full acceleration tick - set_step_events_per_minute(trapezoid_adjusted_rate); + trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; // Start halfway for midpoint rule. + set_step_events_per_minute(trapezoid_adjusted_rate); // Initialize cycles_per_step_event } -// This is called ACCELERATION_TICKS_PER_SECOND times per second by the step_event -// interrupt. It can be assumed that the trapezoid-generator-parameters and the -// current_block stays untouched by outside handlers for the duration of this function call. -static void trapezoid_generator_tick() { - if (current_block) { - if (step_events_completed < current_block->accelerate_until) { - trapezoid_adjusted_rate += current_block->rate_delta; - set_step_events_per_minute(trapezoid_adjusted_rate); - } else if (step_events_completed > current_block->decelerate_after) { - // NOTE: We will only reduce speed if the result will be > 0. This catches small - // rounding errors that might leave steps hanging after the last trapezoid tick. - if (trapezoid_adjusted_rate > current_block->rate_delta) { - trapezoid_adjusted_rate -= current_block->rate_delta; - } - if (trapezoid_adjusted_rate < current_block->final_rate) { - trapezoid_adjusted_rate = current_block->final_rate; - } - set_step_events_per_minute(trapezoid_adjusted_rate); - } else { - // Make sure we cruise at exactly nominal rate - if (trapezoid_adjusted_rate != current_block->nominal_rate) { - trapezoid_adjusted_rate = current_block->nominal_rate; - set_step_events_per_minute(trapezoid_adjusted_rate); - } - } +// This function determines an acceleration velocity change every CYCLES_PER_ACCELERATION_TICK by +// keeping track of the number of elapsed cycles during a de/ac-celeration. The code assumes that +// step_events occur significantly more often than the acceleration velocity iterations. +static uint8_t iterate_trapezoid_cycle_counter() { + trapezoid_tick_cycle_counter += cycles_per_step_event; + if(trapezoid_tick_cycle_counter > CYCLES_PER_ACCELERATION_TICK) { + trapezoid_tick_cycle_counter -= CYCLES_PER_ACCELERATION_TICK; + return(true); + } else { + return(false); } -} +} // "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. It is executed at the rate set with // config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately. -// It is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port after each pulse. +// It is supported by The Stepper Port Reset Interrupt which it uses to reset the stepper port after each pulse. +// The bresenham line tracer algorithm controls all three stepper outputs simultaneously with these two interrupts. SIGNAL(TIMER1_COMPA_vect) { // TODO: Check if the busy-flag can be eliminated by just disabeling this interrupt while we are in it @@ -157,13 +142,14 @@ SIGNAL(TIMER1_COMPA_vect) counter_x = -(current_block->step_event_count >> 1); counter_y = counter_x; counter_z = counter_x; - step_events_completed = 0; + step_events_completed = 0; } else { st_go_idle(); } } if (current_block != NULL) { + // Execute step displacement profile by bresenham line algorithm out_bits = current_block->direction_bits; counter_x += current_block->steps_x; if (counter_x > 0) { @@ -180,26 +166,73 @@ SIGNAL(TIMER1_COMPA_vect) out_bits |= (1<step_event_count; } - // If current block is finished, reset pointer - step_events_completed += 1; - if (step_events_completed >= current_block->step_event_count) { + + step_events_completed += 1; // Iterate step events + + // While in block steps, check for de/ac-celeration events and execute them accordingly. + if (step_events_completed < current_block->step_event_count) { + + // Always check step event location to ensure de/ac-celerations are executed and terminated at + // exactly the right time. This helps prevent over/under-shooting the target position and speed. + // Trapezoid de/ac-celeration is approximated by discrete increases or decreases in velocity, + // defined by ACCELERATION_TICKS_PER_SECOND and block->rate_delta. The accelerations employ the + // midpoint rule to obtain an accurate representation of the exact acceleration curve. + + // NOTE: By increasing the ACCELERATION_TICKS_PER_SECOND in config.h, the resolution of the + // discrete velocity changes increase and accuracy can increase as well to a point. Numerical + // round-off errors can effect this, if set too high. This is important to note if a user has + // very high acceleration and/or feedrate requirements for their machine. + + if (step_events_completed < current_block->accelerate_until) { + // Iterate cycle counter and check if speeds need to be increased. + if ( iterate_trapezoid_cycle_counter() ) { + trapezoid_adjusted_rate += current_block->rate_delta; + if (trapezoid_adjusted_rate >= current_block->nominal_rate) { + // Reached nominal rate a little early. Cruise at nominal rate until decelerate_after. + trapezoid_adjusted_rate = current_block->nominal_rate; + } + set_step_events_per_minute(trapezoid_adjusted_rate); + } + } else if (step_events_completed > current_block->decelerate_after) { + // Iterate cycle counter and check if speeds need to be reduced. + if ( iterate_trapezoid_cycle_counter() ) { + // NOTE: We will only reduce speed if the result will be > 0. This catches small + // rounding errors that might leave steps hanging after the last trapezoid tick. + if (trapezoid_adjusted_rate > current_block->rate_delta) { + trapezoid_adjusted_rate -= current_block->rate_delta; + } + if (trapezoid_adjusted_rate < current_block->final_rate) { + // Reached final rate a little early. Cruise to end of block at final rate. + trapezoid_adjusted_rate = current_block->final_rate; + } + set_step_events_per_minute(trapezoid_adjusted_rate); + } + } else { + // No accelerations. Make sure we cruise exactly at nominal rate. + if (trapezoid_adjusted_rate != current_block->nominal_rate) { + trapezoid_adjusted_rate = current_block->nominal_rate; + set_step_events_per_minute(trapezoid_adjusted_rate); + } + // Check to reset trapezoid tick cycle counter to make sure that the deceleration is + // performed the same every time. Reset to CYCLES_PER_ACCELERATION_TICK/2 to follow the + // midpoint rule for an accurate approximation of the deceleration curve. + if (step_events_completed >= current_block-> decelerate_after) { + trapezoid_tick_cycle_counter = CYCLES_PER_ACCELERATION_TICK/2; + } + } + + } else { + // If current block is finished, reset pointer current_block = NULL; plan_discard_current_block(); } + } else { + // Still no block? Set the stepper pins to low before sleeping. out_bits = 0; } - out_bits ^= settings.invert_mask; - - // In average this generates a trapezoid_generator_tick every CYCLES_PER_ACCELERATION_TICK by keeping track - // of the number of elapsed cycles. The code assumes that step_events occur significantly more often than - // trapezoid_generator_ticks as they well should. - trapezoid_tick_cycle_counter += cycles_per_step_event; - if(trapezoid_tick_cycle_counter > CYCLES_PER_ACCELERATION_TICK) { - trapezoid_tick_cycle_counter -= CYCLES_PER_ACCELERATION_TICK; - trapezoid_generator_tick(); - } + out_bits ^= settings.invert_mask; // Apply stepper invert mask busy=false; }