diff --git a/config.h b/config.h index 1e01df7..935a483 100644 --- a/config.h +++ b/config.h @@ -65,6 +65,10 @@ #define CMD_CYCLE_START '~' #define CMD_RESET 0x18 // ctrl-x +// Specifies the number of work coordinate systems grbl will support (G54 - G59). +// This parameter must be one or greater, currently supporting up to a value of 6. +#define N_COORDINATE_SYSTEM 1 + // This parameter sets the delay time before disabling the steppers after the final block of movement. // A short delay ensures the steppers come to a complete stop and the residual inertial force in the // CNC axes don't cause the axes to drift off position. This is particularly important when manually diff --git a/gcode.c b/gcode.c index 6a9372f..4a054e7 100644 --- a/gcode.c +++ b/gcode.c @@ -32,42 +32,53 @@ #include "errno.h" #include "protocol.h" -#define NEXT_ACTION_DEFAULT 0 -#define NEXT_ACTION_DWELL 1 -#define NEXT_ACTION_GO_HOME 2 -#define NEXT_ACTION_SET_COORDINATE_OFFSET 3 +// Define modal group internal numbers for checking multiple command violations and tracking the +// type of command that is called in the block. A modal group is a group of g-code commands that are +// mutually exclusive, or cannot exist on the same line, because they each toggle a state or execute +// a unique motion. These are defined in the NIST RS274-NGC v3 g-code standard, available online, +// and are similar/identical to other g-code interpreters by manufacturers (Haas,Fanuc,Mazak,etc). +#define MODAL_GROUP_NONE 0 +#define MODAL_GROUP_0 1 // [G4,G10,G28,G30,G53,G92,G92.1] Non-modal +#define MODAL_GROUP_1 2 // [G0,G1,G2,G3,G80] Motion +#define MODAL_GROUP_2 3 // [G17,G18,G19] Plane selection +#define MODAL_GROUP_3 4 // [G90,G91] Distance mode +#define MODAL_GROUP_4 5 // [M0,M1,M2,M30] Stopping +#define MODAL_GROUP_5 6 // [G93,G94] Feed rate mode +#define MODAL_GROUP_6 7 // [G20,G21] Units +#define MODAL_GROUP_7 8 // [M3,M4,M5] Spindle turning +#define MODAL_GROUP_12 9 // [G54,G55,G56,G57,G58,G59] Coordinate system selection +// Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used +// internally by the parser to know which command to execute. #define MOTION_MODE_SEEK 0 // G0 #define MOTION_MODE_LINEAR 1 // G1 #define MOTION_MODE_CW_ARC 2 // G2 #define MOTION_MODE_CCW_ARC 3 // G3 #define MOTION_MODE_CANCEL 4 // G80 -#define PATH_CONTROL_MODE_EXACT_PATH 0 -#define PATH_CONTROL_MODE_EXACT_STOP 1 -#define PATH_CONTROL_MODE_CONTINOUS 2 - #define PROGRAM_FLOW_RUNNING 0 -#define PROGRAM_FLOW_PAUSED 1 -#define PROGRAM_FLOW_OPT_PAUSED 2 -#define PROGRAM_FLOW_COMPLETED 3 +#define PROGRAM_FLOW_PAUSED 1 // M0, M1 +#define PROGRAM_FLOW_COMPLETED 2 // M2, M30 -#define SPINDLE_DIRECTION_CW 0 -#define SPINDLE_DIRECTION_CCW 1 +#define NON_MODAL_NONE 0 +#define NON_MODAL_DWELL 1 // G4 +#define NON_MODAL_SET_COORDINATE_DATA 2 // G10 +#define NON_MODAL_GO_HOME 3 // G28,G30 +#define NON_MODAL_SET_COORDINATE_OFFSET 4 // G92 +#define NON_MODAL_RESET_COORDINATE_OFFSET 5 //G92.1 typedef struct { - uint8_t status_code; - - uint8_t motion_mode; /* {G0, G1, G2, G3, G80} */ - uint8_t inverse_feed_rate_mode; /* G93, G94 */ - uint8_t inches_mode; /* 0 = millimeter mode, 1 = inches mode {G20, G21} */ - uint8_t absolute_mode; /* 0 = relative motion, 1 = absolute motion {G90, G91} */ - uint8_t program_flow; - int8_t spindle_direction; - double feed_rate, seek_rate; /* Millimeters/second */ - double position[3]; /* Where the interpreter considers the tool to be at this point in the code */ + uint8_t status_code; // Parser status for current block + uint8_t motion_mode; // {G0, G1, G2, G3, G80} + uint8_t inverse_feed_rate_mode; // {G93, G94} + uint8_t inches_mode; // 0 = millimeter mode, 1 = inches mode {G20, G21} + uint8_t absolute_mode; // 0 = relative motion, 1 = absolute motion {G90, G91} + uint8_t program_flow; // {M0, M1, M2, M30} + int8_t spindle_direction; // 1 = CW, -1 = CCW, 0 = Stop {M3, M4, M5} + double feed_rate, seek_rate; // Millimeters/second + double position[3]; // Where the interpreter considers the tool to be at this point in the code uint8_t tool; - int16_t spindle_speed; /* RPM/100 */ + int16_t spindle_speed; // RPM/100 uint8_t plane_axis_0, plane_axis_1, plane_axis_2; // The axes of the selected plane @@ -89,12 +100,11 @@ void gc_init() { memset(&gc, 0, sizeof(gc)); gc.feed_rate = settings.default_feed_rate; - gc.seek_rate = settings.default_seek_rate; select_plane(X_AXIS, Y_AXIS, Z_AXIS); gc.absolute_mode = true; } -// Set g-code parser position. Input in steps. +// Sets g-code parser position in mm. Input in steps. Called by the system abort routine. void gc_set_current_position(int32_t x, int32_t y, int32_t z) { gc.position[X_AXIS] = x/settings.steps_per_mm[X_AXIS]; @@ -102,63 +112,104 @@ void gc_set_current_position(int32_t x, int32_t y, int32_t z) gc.position[Z_AXIS] = z/settings.steps_per_mm[Z_AXIS]; } -static float to_millimeters(double value) { +static float to_millimeters(double value) +{ return(gc.inches_mode ? (value * MM_PER_INCH) : value); } // Executes one line of 0-terminated G-Code. The line is assumed to contain only uppercase // characters and signed floating point values (no whitespace). Comments and block delete -// characters have been removed. +// characters have been removed. All units and positions are converted and exported to grbl's +// internal functions in terms of (mm, mm/min) and absolute machine coordinates, respectively. uint8_t gc_execute_line(char *line) { uint8_t char_counter = 0; char letter; double value; - double unit_converted_value; - double inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified - uint8_t radius_mode = false; + int int_value; - uint8_t absolute_override = false; /* 1 = absolute motion for this block only {G53} */ - uint8_t next_action = NEXT_ACTION_DEFAULT; /* The action that will be taken by the parsed line */ + uint16_t modal_group_words = 0; // Bitflag variable to track and check modal group words in block + uint8_t axis_words = 0; // Bitflag to track which XYZ(ABC) parameters exist in block + + double inverse_feed_rate = -1; // negative inverse_feed_rate means no inverse_feed_rate specified + uint8_t absolute_override = false; // true(1) = absolute motion for this block only {G53} + uint8_t non_modal_action = NON_MODAL_NONE; // Tracks the actions of modal group 0 (non-modal) double target[3], offset[3]; - - double p = 0, r = 0; - int int_value; - + clear_vector(target); // XYZ(ABC) axes parameters. + clear_vector(offset); // IJK Arc offsets are incremental. Value of zero indicates no change. + gc.status_code = STATUS_OK; - // Pass 1: Commands + /* Pass 1: Commands and set all modes. Check for modal group violations. + NOTE: Modal group numbers are defined in Table 4 of NIST RS274-NGC v3, pg.20 */ + uint8_t group_number = MODAL_GROUP_NONE; while(next_statement(&letter, &value, line, &char_counter)) { int_value = trunc(value); switch(letter) { case 'G': + // Set modal group values + switch(int_value) { + case 4: case 10: case 28: case 30: case 53: case 92: group_number = MODAL_GROUP_0; break; + case 0: case 1: case 2: case 3: case 80: group_number = MODAL_GROUP_1; break; + case 17: case 18: case 19: group_number = MODAL_GROUP_2; break; + case 90: case 91: group_number = MODAL_GROUP_3; break; + case 93: case 94: group_number = MODAL_GROUP_5; break; + case 20: case 21: group_number = MODAL_GROUP_6; break; + case 54: case 55: case 56: case 57: case 58: case 59: group_number = MODAL_GROUP_12; break; + } + // Set 'G' commands switch(int_value) { case 0: gc.motion_mode = MOTION_MODE_SEEK; break; case 1: gc.motion_mode = MOTION_MODE_LINEAR; break; case 2: gc.motion_mode = MOTION_MODE_CW_ARC; break; case 3: gc.motion_mode = MOTION_MODE_CCW_ARC; break; - case 4: next_action = NEXT_ACTION_DWELL; break; + case 4: non_modal_action = NON_MODAL_DWELL; break; + case 10: non_modal_action = NON_MODAL_SET_COORDINATE_DATA; break; case 17: select_plane(X_AXIS, Y_AXIS, Z_AXIS); break; case 18: select_plane(X_AXIS, Z_AXIS, Y_AXIS); break; case 19: select_plane(Y_AXIS, Z_AXIS, X_AXIS); break; case 20: gc.inches_mode = true; break; case 21: gc.inches_mode = false; break; - case 28: case 30: next_action = NEXT_ACTION_GO_HOME; break; + case 28: case 30: non_modal_action = NON_MODAL_GO_HOME; break; case 53: absolute_override = true; break; - case 80: gc.motion_mode = MOTION_MODE_CANCEL; break; + case 54: case 55: case 56: case 57: case 58: case 59: + int_value -= 54; // Compute coordinate system row index (0=G54,1=G55,...) + if (int_value < N_COORDINATE_SYSTEM) { + sys.coord_select = int_value; + } else { + FAIL(STATUS_UNSUPPORTED_STATEMENT); + } + break; + case 80: gc.motion_mode = MOTION_MODE_CANCEL; break; case 90: gc.absolute_mode = true; break; case 91: gc.absolute_mode = false; break; - case 92: next_action = NEXT_ACTION_SET_COORDINATE_OFFSET; break; + case 92: + int_value = trunc(10*value); // Multiply by 10 to pick up G92.1 + switch(int_value) { + case 920: non_modal_action = NON_MODAL_SET_COORDINATE_OFFSET; break; + case 921: non_modal_action = NON_MODAL_RESET_COORDINATE_OFFSET; break; + default: FAIL(STATUS_UNSUPPORTED_STATEMENT); + } + break; case 93: gc.inverse_feed_rate_mode = true; break; case 94: gc.inverse_feed_rate_mode = false; break; default: FAIL(STATUS_UNSUPPORTED_STATEMENT); } - break; + break; case 'M': + // Set modal group values switch(int_value) { - case 0: case 60: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause - case 1: gc.program_flow = PROGRAM_FLOW_OPT_PAUSED; break; // Program pause with optional stop on + case 0: case 1: case 2: case 30: group_number = MODAL_GROUP_4; break; + case 3: case 4: case 5: group_number = MODAL_GROUP_7; break; + } + // Set 'M' commands + switch(int_value) { + case 0: gc.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause + case 1: // Program pause with optional stop on + // if (sys.opt_stop) { // TODO: Add system variable for optional stop. + gc.program_flow = PROGRAM_FLOW_PAUSED; break; + // } case 2: case 30: gc.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset case 3: gc.spindle_direction = 1; break; case 4: gc.spindle_direction = -1; break; @@ -166,75 +217,197 @@ uint8_t gc_execute_line(char *line) default: FAIL(STATUS_UNSUPPORTED_STATEMENT); } break; - case 'T': gc.tool = trunc(value); break; + } + // Check for modal group multiple command violations in the current block + if (group_number) { + if ( bit_istrue(modal_group_words,bit(group_number)) ) { + FAIL(STATUS_MODAL_GROUP_VIOLATION); + } else { + bit_true(modal_group_words,bit(group_number)); + } + group_number = MODAL_GROUP_NONE; // Reset for next command. } - if(gc.status_code) { break; } - } - + } + // If there were any errors parsing this line, we will return right away with the bad news if (gc.status_code) { return(gc.status_code); } - + + /* Pass 2: Parameters. All units converted according to current block commands. Position + parameters are converted and flagged to indicate a change. These can have multiple connotations + for different commands. Each will be converted to their proper value upon execution. */ + double p = 0, r = 0; + uint8_t l = 0; char_counter = 0; - clear_vector(target); - clear_vector(offset); - memcpy(target, gc.position, sizeof(target)); // i.e. target = gc.position - - // Pass 2: Parameters while(next_statement(&letter, &value, line, &char_counter)) { - int_value = trunc(value); - unit_converted_value = to_millimeters(value); switch(letter) { case 'F': - if (unit_converted_value <= 0) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // Must be greater than zero + if (value <= 0) { FAIL(STATUS_INVALID_COMMAND); } // Must be greater than zero if (gc.inverse_feed_rate_mode) { - inverse_feed_rate = unit_converted_value; // seconds per motion for this motion only + inverse_feed_rate = to_millimeters(value); // seconds per motion for this motion only } else { - if (gc.motion_mode == MOTION_MODE_SEEK) { - gc.seek_rate = unit_converted_value; - } else { - gc.feed_rate = unit_converted_value; // millimeters per minute - } + gc.feed_rate = to_millimeters(value); // millimeters per minute } break; - case 'I': case 'J': case 'K': offset[letter-'I'] = unit_converted_value; break; - case 'P': p = value; break; - case 'R': r = unit_converted_value; radius_mode = true; break; - case 'S': gc.spindle_speed = value; break; - case 'X': case 'Y': case 'Z': - if (gc.absolute_mode || absolute_override) { - target[letter - 'X'] = unit_converted_value; - } else { - target[letter - 'X'] += unit_converted_value; - } + case 'I': case 'J': case 'K': offset[letter-'I'] = to_millimeters(value); break; + case 'L': l = trunc(value); break; + case 'P': p = value; break; + case 'R': r = to_millimeters(value); break; + case 'S': + if (value < 0) { FAIL(STATUS_INVALID_COMMAND); } // Cannot be negative + gc.spindle_speed = value; break; + case 'T': + if (value < 0) { FAIL(STATUS_INVALID_COMMAND); } // Cannot be negative + gc.tool = trunc(value); + break; + case 'X': target[X_AXIS] = to_millimeters(value); bit_true(axis_words,bit(X_AXIS)); break; + case 'Y': target[Y_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Y_AXIS)); break; + case 'Z': target[Z_AXIS] = to_millimeters(value); bit_true(axis_words,bit(Z_AXIS)); break; } } // If there were any errors parsing this line, we will return right away with the bad news if (gc.status_code) { return(gc.status_code); } - - // Update spindle state + + + /* Execute Commands: Perform by order of execution defined in NIST RS274-NGC.v3, Table 8, pg.41. + NOTE: Independent non-motion/settings parameters are set out of this order for code efficiency + and simplicity purposes, but this should not affect proper g-code execution. */ + + // ([M6]: Tool change execution should be executed here.) + + // [M3,M4,M5]: Update spindle state spindle_run(gc.spindle_direction, gc.spindle_speed); - // Perform any physical actions - switch (next_action) { - case NEXT_ACTION_GO_HOME: mc_go_home(); clear_vector_double(target); break; - case NEXT_ACTION_DWELL: mc_dwell(p); break; - case NEXT_ACTION_SET_COORDINATE_OFFSET: - mc_set_coordinate_offset(target[X_AXIS],target[Y_AXIS],target[Z_AXIS]); + // ([M7,M8,M9]: Coolant state should be executed here.) + + // [G4,G10,G28,G30,G92,G92.1]: Perform dwell, set coordinate system data, homing, or set axis offsets. + // NOTE: These commands are in the same modal group, hence are mutually exclusive. G53 is in this + // modal group and do not effect these actions. + switch (non_modal_action) { + case NON_MODAL_DWELL: + if (p < 0) { // Time cannot be negative. + FAIL(STATUS_INVALID_COMMAND); + } else { + mc_dwell(p); + } break; - case NEXT_ACTION_DEFAULT: - switch (gc.motion_mode) { - case MOTION_MODE_CANCEL: break; - case MOTION_MODE_SEEK: - mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], gc.seek_rate, false); - break; - case MOTION_MODE_LINEAR: - mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], - (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); - break; - case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: - if (radius_mode) { + case NON_MODAL_SET_COORDINATE_DATA: + int_value = trunc(p); // Convert p value to int. + if (l != 2 || (int_value < 1 || int_value > N_COORDINATE_SYSTEM)) { // L2 only. P1=G54, P2=G55, ... + FAIL(STATUS_UNSUPPORTED_STATEMENT); + } else if (!axis_words) { // No axis words. + FAIL(STATUS_INVALID_COMMAND); + } else { + int_value--; // Adjust p to be inline with row array index. + // Update axes defined only in block. Always in machine coordinates. Can change non-active system. + uint8_t i; + for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used. + if ( bit_istrue(axis_words,bit(i)) ) { sys.coord_system[int_value][i] = target[i]; } + } + } + axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. + break; + case NON_MODAL_GO_HOME: + // Move to intermediate position before going home. Obeys current coordinate system and offsets + // and absolute and incremental modes. + if (axis_words) { + // Apply absolute mode coordinate offsets or incremental mode offsets. + uint8_t i; + for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used. + if ( bit_istrue(axis_words,bit(i)) ) { + if (gc.absolute_mode) { + target[i] += sys.coord_system[sys.coord_select][i] + sys.coord_offset[i]; + } else { + target[i] += gc.position[i]; + } + } else { + target[i] = gc.position[i]; + } + } + mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); + } + mc_go_home(); + clear_vector(gc.position); // Assumes home is at [0,0,0] + axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. + break; + case NON_MODAL_SET_COORDINATE_OFFSET: + if (!axis_words) { // No axis words + FAIL(STATUS_INVALID_COMMAND); + } else { + // Update axes defined only in block. Offsets current system to defined value. Does not update when + // active coordinate system is selected, but is still active unless G92.1 disables it. + uint8_t i; + for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used. + if (bit_istrue(axis_words,bit(i)) ) { + sys.coord_offset[i] = gc.position[i]-sys.coord_system[sys.coord_select][i]-target[i]; + } + } + } + axis_words = 0; // Axis words used. Lock out from motion modes by clearing flags. + break; + case NON_MODAL_RESET_COORDINATE_OFFSET: + clear_vector(sys.coord_offset); // Disable G92 offsets by zeroing offset vector. + break; + } + + // [G0,G1,G2,G3,G80]: Perform motion modes. + // NOTE: Commands G10,G28,G30,G92 lock out and prevent axis words from use in motion modes. + // Enter motion modes only if there are axis words or a motion mode command word in the block. + if ( bit_istrue(modal_group_words,bit(MODAL_GROUP_1)) || axis_words ) { + + // G1,G2,G3 require F word in inverse time mode. + if ( gc.inverse_feed_rate_mode ) { + if (inverse_feed_rate < 0 && gc.motion_mode != MOTION_MODE_CANCEL) { + FAIL(STATUS_INVALID_COMMAND); + } + } + // Absolute override G53 only valid with G0 and G1 active. + if ( absolute_override && !(gc.motion_mode == MOTION_MODE_SEEK || gc.motion_mode == MOTION_MODE_LINEAR)) { + FAIL(STATUS_INVALID_COMMAND); + } + // Report any errors. + if (gc.status_code) { return(gc.status_code); } + + // Convert all target position data to machine coordinates for executing motion. Apply + // absolute mode coordinate offsets or incremental mode offsets. + // NOTE: Tool offsets may be appended to these conversions when/if this feature is added. + uint8_t i; + for (i=0; i<=2; i++) { // Axes indices are consistent, so loop may be used to save flash space. + if ( bit_istrue(axis_words,bit(i)) ) { + if (!absolute_override) { // Do not update target in absolute override mode + if (gc.absolute_mode) { + target[i] += sys.coord_system[sys.coord_select][i] + sys.coord_offset[i]; // Absolute mode + } else { + target[i] += gc.position[i]; // Incremental mode + } + } + } else { + target[i] = gc.position[i]; // No axis word in block. Keep same axis position. + } + } + + switch (gc.motion_mode) { + case MOTION_MODE_CANCEL: + if (axis_words) { FAIL(STATUS_INVALID_COMMAND); } // No axis words allowed while active. + break; + case MOTION_MODE_SEEK: + if (!axis_words) { FAIL(STATUS_INVALID_COMMAND);} + else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], settings.default_seek_rate, false); } + break; + case MOTION_MODE_LINEAR: + if (!axis_words) { FAIL(STATUS_INVALID_COMMAND);} + else { mc_line(target[X_AXIS], target[Y_AXIS], target[Z_AXIS], + (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode); } + break; + case MOTION_MODE_CW_ARC: case MOTION_MODE_CCW_ARC: + // Check if at least one of the axes of the selected plane has been specified. If in center + // format arc mode, also check for at least one of the IJK axes of the selected plane was sent. + if ( !( bit_false(axis_words,bit(gc.plane_axis_2)) ) || + ( !r && !offset[gc.plane_axis_0] && !offset[gc.plane_axis_1] ) ) { + FAIL(STATUS_INVALID_COMMAND); + } else { + if (r != 0) { // Arc Radius Mode /* We need to calculate the center of the circle that has the designated radius and passes through both the current position and the target position. This method calculates the following @@ -325,11 +498,9 @@ uint8_t gc_execute_line(char *line) // Complete the operation by calculating the actual center of the arc offset[gc.plane_axis_0] = 0.5*(x-(y*h_x2_div_d)); offset[gc.plane_axis_1] = 0.5*(y+(x*h_x2_div_d)); - - } else { // Offset mode specific computations - + + } else { // Arc Center Format Offset Mode r = hypot(offset[gc.plane_axis_0], offset[gc.plane_axis_1]); // Compute arc radius for mc_arc - } // Set clockwise/counter-clockwise sign for mc_arc computations @@ -340,29 +511,28 @@ uint8_t gc_execute_line(char *line) mc_arc(gc.position, target, offset, gc.plane_axis_0, gc.plane_axis_1, gc.plane_axis_2, (gc.inverse_feed_rate_mode) ? inverse_feed_rate : gc.feed_rate, gc.inverse_feed_rate_mode, r, isclockwise); - - break; - } + } + break; + } + + // Report any errors. + if (gc.status_code) { return(gc.status_code); } + + // As far as the parser is concerned, the position is now == target. In reality the + // motion control system might still be processing the action and the real tool position + // in any intermediate location. + memcpy(gc.position, target, sizeof(double)*3); // gc.position[] = target[]; } - // As far as the parser is concerned, the position is now == target. In reality the - // motion control system might still be processing the action and the real tool position - // in any intermediate location. - memcpy(gc.position, target, sizeof(double)*3); // gc.position[] = target[]; - - // Perform non-running program flow actions. During a program pause, the buffer may refill and can - // only be resumed by the cycle start run-time command. - // TODO: Install optional stop setting and re-factor program flow actions. + // M0,M1,M2,M30: Perform non-running program flow actions. During a program pause, the buffer may + // refill and can only be resumed by the cycle start run-time command. if (gc.program_flow) { plan_synchronize(); // Finish all remaining buffered motions. Program paused when complete. sys.auto_start = false; // Disable auto cycle start. - switch (gc.program_flow) { - case PROGRAM_FLOW_PAUSED: case PROGRAM_FLOW_OPT_PAUSED: - gc.program_flow = PROGRAM_FLOW_RUNNING; // Re-enable program flow after pause complete. - break; - // Reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9) - case PROGRAM_FLOW_COMPLETED: sys.abort = true; break; - } + gc.program_flow = PROGRAM_FLOW_RUNNING; // Re-enable program flow after pause complete. + + // If complete, reset to reload defaults (G92.2,G54,G17,G90,G94,M48,G40,M5,M9) + if (gc.program_flow == PROGRAM_FLOW_COMPLETED) { sys.abort = true; } } return(gc.status_code); @@ -391,21 +561,24 @@ static int next_statement(char *letter, double *double_ptr, char *line, uint8_t } /* - Intentionally not supported: + Not supported: - Canned cycles - Tool radius compensation - A,B,C-axes - - Multiple coordinate systems - Evaluation of expressions - Variables - Multiple home locations + - Multiple coordinate systems (May be added in the future) - Probing - Override control + - Tool changes - group 0 = {G10, G28, G30, G92.1, G92.2, G92.3} (Non modal G-codes) + group 0 = {G92.2, G92.3} (Non modal: Cancel and re-enable G92 offsets) + group 1 = {G38.2, G81 - G89} (Motion modes: straight probe, canned cycles) + group 6 = {M6} (Tool change) group 8 = {M7, M8, M9} coolant (special case: M7 and M8 may be active at the same time) 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 12 = {G55, G56, G57, G58, G59, G59.1, G59.2, G59.3} coordinate system selection group 13 = {G61, G61.1, G64} path control mode */ diff --git a/gcode.h b/gcode.h index af9751a..f3d86f1 100644 --- a/gcode.h +++ b/gcode.h @@ -3,7 +3,8 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - + Copyright (c) 2011-2012 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 diff --git a/limits.c b/limits.c index a5c4a3e..be93b78 100644 --- a/limits.c +++ b/limits.c @@ -76,9 +76,9 @@ static void homing_cycle(bool x_axis, bool y_axis, bool z_axis, bool reverse_dir // Check if we are done if(!(x_axis || y_axis || z_axis)) { return; } STEPPING_PORT |= out_bits & STEP_MASK; - _delay_us(settings.pulse_microseconds); + delay_us(settings.pulse_microseconds); STEPPING_PORT ^= out_bits & STEP_MASK; - _delay_us(step_delay); + delay_us(step_delay); } return; } diff --git a/main.c b/main.c index 40441b8..fd75f3b 100644 --- a/main.c +++ b/main.c @@ -53,15 +53,17 @@ int main(void) // reset to finish the initialization process. if (sys.abort) { - // Retain last known machine position. If the system abort occurred while in motion, machine - // position is not guaranteed, since a hard stop can cause the steppers to lose steps. Always - // perform a feedhold before an abort, if maintaining accurate machine position is required. + // Retain last known machine position and work coordinate offset(s). If the system abort + // occurred while in motion, machine position is not guaranteed, since a hard stop can cause + // the steppers to lose steps. Always perform a feedhold before an abort, if maintaining + // accurate machine position is required. // TODO: Report last position and coordinate offset to users to help relocate origins. Future // releases will auto-reset the machine position back to [0,0,0] if an abort is used while // grbl is moving the machine. - int32_t last_position[3]; // last_coord_offset[3]; + int32_t last_position[3]; + double last_coord_system[N_COORDINATE_SYSTEM][3]; memcpy(last_position, sys.position, sizeof(sys.position)); // last_position[] = sys.position[] - // memcpy(last_coord_offset, sys.coord_offset, sizeof(sys.coord_offset)); // last_coord_offset[] = sys.coord_offset[] + memcpy(last_coord_system, sys.coord_system, sizeof(sys.coord_system)); // last_coord_system[] = sys.coord_system[] // Reset system. memset(&sys, 0, sizeof(sys)); // Clear all system variables @@ -74,8 +76,9 @@ int main(void) limits_init(); st_reset(); // Clear stepper subsystem variables. - // Reload last known machine position. Coordinate offsets are reset per NIST RS274-NGC protocol. + // Reload last known machine position and work systems. G92 coordinate offsets are reset. memcpy(sys.position, last_position, sizeof(last_position)); // sys.position[] = last_position[] + memcpy(sys.coord_system, last_coord_system, sizeof(last_coord_system)); // sys.coord_system[] = last_coord_system[] gc_set_current_position(last_position[X_AXIS],last_position[Y_AXIS],last_position[Z_AXIS]); plan_set_current_position(last_position[X_AXIS],last_position[Y_AXIS],last_position[Z_AXIS]); @@ -85,6 +88,7 @@ int main(void) #ifdef CYCLE_AUTO_START sys.auto_start = true; #endif + // TODO: Install G20/G21 unit default into settings and load appropriate settings. } protocol_execute_runtime(); diff --git a/motion_control.h b/motion_control.h index b5061e9..5f9fc35 100644 --- a/motion_control.h +++ b/motion_control.h @@ -25,10 +25,6 @@ #include #include "planner.h" -// NOTE: Although the following function structurally belongs in this module, there is nothing to do but -// to forward the request to the planner. -#define mc_set_coordinate_offset(x, y, z) plan_set_coordinate_offset(x, y, z) - // Execute linear motion in absolute millimeter coordinates. Feed rate given in millimeters/second // unless invert_feed_rate is true. Then the feed_rate means that the motion should be completed in // (1 minute)/feed_rate time. diff --git a/nuts_bolts.c b/nuts_bolts.c index 83b4e40..79cc41d 100644 --- a/nuts_bolts.c +++ b/nuts_bolts.c @@ -44,3 +44,10 @@ void delay_ms(uint16_t ms) { while ( ms-- ) { _delay_ms(1); } } + +// Delays variable defined microseconds. Compiler compatibility fix for _delay_us(), +// which only accepts constants in future compiler releases. +void delay_us(uint16_t us) +{ + while ( us-- ) { _delay_us(1); } +} diff --git a/nuts_bolts.h b/nuts_bolts.h index 3c4112b..35fc7c4 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -21,6 +21,7 @@ #ifndef nuts_bolts_h #define nuts_bolts_h +#include #include #include #include @@ -70,8 +71,13 @@ typedef struct { int32_t position[3]; // Real-time machine (aka home) position vector in steps. // NOTE: This may need to be a volatile variable, if problems arise. - int32_t coord_offset[3]; // Retains the G92 coordinate offset (work coordinates) relative to - // machine zero in steps. + + uint8_t coord_select; // Active work coordinate system number. Default: 0=G54. + double coord_system[N_COORDINATE_SYSTEM][3]; // Work coordinate systems (G54+). Stores offset from + // absolute machine position in mm. + // Rows: Work system number (0=G54,1=G55,...5=G59), Columns: XYZ Offsets + double coord_offset[3]; // Retains the G92 coordinate offset (work coordinates) relative to + // machine zero in mm. volatile uint8_t cycle_start; // Cycle start flag. Set by stepper subsystem or main program. volatile uint8_t execute; // Global system runtime executor bitflag variable. See EXEC bitmasks. @@ -86,4 +92,7 @@ int read_double(char *line, uint8_t *char_counter, double *double_ptr); // Delays variable-defined milliseconds. Compiler compatibility fix for _delay_ms(). void delay_ms(uint16_t ms); +// Delays variable-defined microseconds. Compiler compatibility fix for _delay_us(). +void delay_us(uint16_t us); + #endif diff --git a/planner.c b/planner.c index ff32b5e..dac2d62 100644 --- a/planner.c +++ b/planner.c @@ -344,6 +344,8 @@ void plan_synchronize() // Add a new linear movement to the buffer. x, y and z is the signed, absolute target position in // millimeters. Feed rate specifies the speed of the motion. If feed rate is inverted, the feed // rate is taken to mean "frequency" and would complete the operation in 1/feed_rate minutes. +// All position data passed to the planner must be in terms of machine position to keep the planner +// independent of any coordinate system changes and offsets, which are handled by the g-code parser. // NOTE: Assumes buffer is available. Buffer checks are handled at a higher level by motion_control. void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t invert_feed_rate) { @@ -471,36 +473,7 @@ void plan_buffer_line(double x, double y, double z, double feed_rate, uint8_t in planner_recalculate(); } -// Apply G92 coordinate offsets and update planner position vector. -void plan_set_coordinate_offset(double x, double y, double z) -{ - // To correlate status reporting work position correctly, the planner must force the steppers to - // empty the block buffer and synchronize with the planner, as the real-time machine position and - // the planner position at the end of the buffer can be and are usually different. This function is - // only called with a G92, which typically is used only at the beginning of a g-code program or - // between different operations. - // TODO: Find a robust way to avoid a planner synchronize, but this may require a bit of ingenuity. - plan_synchronize(); - - // Update the system coordinate offsets from machine zero - sys.coord_offset[X_AXIS] += pl.position[X_AXIS]; - sys.coord_offset[Y_AXIS] += pl.position[Y_AXIS]; - sys.coord_offset[Z_AXIS] += pl.position[Z_AXIS]; - - memset(&pl, 0, sizeof(pl)); // Clear planner variables. Assume start from rest. - - // Update planner position and coordinate offset vectors - int32_t new_position[3]; - new_position[X_AXIS] = lround(x*settings.steps_per_mm[X_AXIS]); - new_position[Y_AXIS] = lround(y*settings.steps_per_mm[Y_AXIS]); - new_position[Z_AXIS] = lround(z*settings.steps_per_mm[Z_AXIS]); - plan_set_current_position(new_position[X_AXIS],new_position[Y_AXIS],new_position[Z_AXIS]); - sys.coord_offset[X_AXIS] -= pl.position[X_AXIS]; - sys.coord_offset[Y_AXIS] -= pl.position[Y_AXIS]; - sys.coord_offset[Z_AXIS] -= pl.position[Z_AXIS]; -} - -// Reset the planner position vector (in steps) +// Reset the planner position vector (in steps). Called by the system abort routine. void plan_set_current_position(int32_t x, int32_t y, int32_t z) { pl.position[X_AXIS] = x; diff --git a/planner.h b/planner.h index 95dbd78..3f1ba01 100644 --- a/planner.h +++ b/planner.h @@ -69,9 +69,6 @@ block_t *plan_get_current_block(); // Reset the planner position vector (in steps) void plan_set_current_position(int32_t x, int32_t y, int32_t z); -// Apply G92 coordinate offsets and update planner position vector. Called by g-code parser. -void plan_set_coordinate_offset(double x, double y, double z); - // Reinitialize plan with a partially completed block void plan_cycle_reinitialize(int32_t step_events_remaining); diff --git a/protocol.c b/protocol.c index 3f92d1e..2646f49 100644 --- a/protocol.c +++ b/protocol.c @@ -53,6 +53,10 @@ static void status_message(int status_code) printPgmString(PSTR("Unsupported statement\r\n")); break; case STATUS_FLOATING_POINT_ERROR: printPgmString(PSTR("Floating point error\r\n")); break; + case STATUS_MODAL_GROUP_VIOLATION: + printPgmString(PSTR("Modal group violation\r\n")); break; + case STATUS_INVALID_COMMAND: + printPgmString(PSTR("Invalid command\r\n")); break; default: printInteger(status_code); printPgmString(PSTR("\r\n")); @@ -74,7 +78,7 @@ void protocol_status_report() // short line segments and interface setups that require real-time status reports (5-20Hz). // **Under construction** Bare-bones status report. Provides real-time machine position relative to - // the system power on location (0,0,0) and work coordinate position, updatable by the G92 command. + // the system power on location (0,0,0) and work coordinate position (G54 and G92 applied). // The following are still needed: user setting of output units (mm|inch), compressed (non-human // readable) data for interfaces?, save last known position in EEPROM?, code optimizations, solidify // the reporting schemes, move to a separate .c file for easy user accessibility, and setting the @@ -87,16 +91,16 @@ void protocol_status_report() printString("MPos:["); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); printString(","); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); printString(","); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); - printString("],WPos:["); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS]*MM_PER_INCH)); - printString(","); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS]*MM_PER_INCH)); - printString(","); printFloat((print_position[Z_AXIS]-sys.coord_offset[Z_AXIS])/(settings.steps_per_mm[Z_AXIS]*MM_PER_INCH)); + printString("],WPos:["); printFloat((print_position[X_AXIS]/settings.steps_per_mm[X_AXIS]-sys.coord_system[sys.coord_select][X_AXIS]-sys.coord_offset[X_AXIS])/MM_PER_INCH); + printString(","); printFloat((print_position[Y_AXIS]/settings.steps_per_mm[Y_AXIS]-sys.coord_system[sys.coord_select][Y_AXIS]-sys.coord_offset[Y_AXIS])/MM_PER_INCH); + printString(","); printFloat((print_position[Z_AXIS]/settings.steps_per_mm[Z_AXIS]-sys.coord_system[sys.coord_select][Z_AXIS]-sys.coord_offset[Z_AXIS])/MM_PER_INCH); #else printString("MPos:["); printFloat(print_position[X_AXIS]/(settings.steps_per_mm[X_AXIS])); printString(","); printFloat(print_position[Y_AXIS]/(settings.steps_per_mm[Y_AXIS])); printString(","); printFloat(print_position[Z_AXIS]/(settings.steps_per_mm[Z_AXIS])); - printString("],WPos:["); printFloat((print_position[X_AXIS]-sys.coord_offset[X_AXIS])/(settings.steps_per_mm[X_AXIS])); - printString(","); printFloat((print_position[Y_AXIS]-sys.coord_offset[Y_AXIS])/(settings.steps_per_mm[Y_AXIS])); - printString(","); printFloat((print_position[Z_AXIS]-sys.coord_offset[Z_AXIS])/(settings.steps_per_mm[Z_AXIS])); + printString("],WPos:["); printFloat(print_position[X_AXIS]/settings.steps_per_mm[X_AXIS]-sys.coord_system[sys.coord_select][X_AXIS]-sys.coord_offset[X_AXIS]); + printString(","); printFloat(print_position[Y_AXIS]/settings.steps_per_mm[Y_AXIS]-sys.coord_system[sys.coord_select][Y_AXIS]-sys.coord_offset[Y_AXIS]); + printString(","); printFloat(print_position[Z_AXIS]/settings.steps_per_mm[Z_AXIS]-sys.coord_system[sys.coord_select][Z_AXIS]-sys.coord_offset[Z_AXIS]); #endif printString("]\r\n"); } diff --git a/protocol.h b/protocol.h index ece1f1f..8c341fe 100644 --- a/protocol.h +++ b/protocol.h @@ -26,6 +26,8 @@ #define STATUS_EXPECTED_COMMAND_LETTER 2 #define STATUS_UNSUPPORTED_STATEMENT 3 #define STATUS_FLOATING_POINT_ERROR 4 +#define STATUS_MODAL_GROUP_VIOLATION 5 +#define STATUS_INVALID_COMMAND 6 // Initialize the serial protocol void protocol_init(); diff --git a/readme.textile b/readme.textile index 43b89f9..ad06c0f 100644 --- a/readme.textile +++ b/readme.textile @@ -14,9 +14,10 @@ Grbl includes full acceleration management with look ahead. That means the contr - New run-time command control: Feed hold (pause), Cycle start (resume), Reset (abort), Status reporting - Controlled feed hold with deceleration to ensure no skipped steps and loss of location. - After feed hold, cycle accelerations are re-planned and may be resumed. - - Work offset(G92) and machine coordinate system support. - - Program stop(M0,M1*,M2,M30) initial support. Pallet shuttle not supported. - - System reset re-initializes grbl without resetting the Arduino and retains machine/home position. + - Re-factored g-code parser with robust error-checking. + - Work coordinate system (G54), offsets(G92), and machine coordinate system support. G10 work coordinate settings support. (Up to 6 work coordinate systems(G54-G59) available as a compile-time option.) + - Program stop(M0,M1*,M2,M30) initial support. Optional stop to do. + - System reset re-initializes grbl without resetting the Arduino and retains machine/home position and work coordinates. - Restructured planner and stepper modules to become independent and ready for future features. - Planned features: Jog mode, status reporting, runtime settings such as toggling block delete, XON/XOFF flow control. - Reduce serial read buffer to 128 characters and increased write buffer to 64 characters. diff --git a/serial.h b/serial.h index 904c6c0..3e1cc77 100644 --- a/serial.h +++ b/serial.h @@ -3,7 +3,7 @@ Part of Grbl Copyright (c) 2009-2011 Simen Svale Skogsrud - Copyright (c) 2011 Sungeun K. Jeon + Copyright (c) 2011-2012 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 diff --git a/spindle_control.c b/spindle_control.c index cd99c9a..3b7f8ca 100644 --- a/spindle_control.c +++ b/spindle_control.c @@ -24,7 +24,7 @@ #include "config.h" #include "planner.h" -#include +#include // TODO: Deprecated. Need to update for new version.