diff --git a/README.md b/README.md index 45d68a2..7cc0bd9 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ Grbl includes full acceleration management with look ahead. That means the contr - **Laser Mode** : The new "laser" mode will cause Grbl to move continuously through consecutive G1, G2, and G3 commands with spindle speed changes. When "laser" mode is disabled, Grbl will instead come to a stop to ensure a spindle comes up to speed properly. Spindle speed overrides also work with laser mode so you can tweak the laser power, if you need to during the job. Switch between "laser" mode and "normal" mode via a `$` setting. + - **Dynamic Laser Power Scaling with Speed** : (Optional) If your machine has low accelerations, this option will automagically scale the laser power based on how fast Grbl is traveling, so you won't have burnt corners when your CNC has to make a turn! Currently available as a compile-time option until vetting is complete. Operation may be tweaked in future versions. + - **Sleep Mode** : Grbl may now be put to "sleep" via a `$SLP` command. This will disable everything, including the stepper drivers. Nice to have when you are leaving your machine unattended and want to power down everything automatically. Only a reset exits the sleep state. - **Significant Interface Improvements**: Tweaked to increase overall performance, include lots more real-time data, and to simplify maintaining and writing GUIs. Based on direct feedback from multiple GUI developers and bench performance testing. _NOTE: GUIs need to specifically update their code to be compatible with v1.1 and later._ diff --git a/doc/log/commit_log_v1.1.txt b/doc/log/commit_log_v1.1.txt index fed54e7..32b63f7 100644 --- a/doc/log/commit_log_v1.1.txt +++ b/doc/log/commit_log_v1.1.txt @@ -1,3 +1,32 @@ +---------------- +Date: 2016-10-27 +Author: Sonny Jeon +Subject: Spindle speed overrides behavior tweak. New experimental laser dynamic power mode. + +- Spindle speed overrides now update immediately if they are changed +while in a HOLD state. Previously, they would update after exiting the +HOLD, which isn’t correct. + +- New experimental dynamic laser power mode that adjusts laser power +based on current machine speed. Enabled by uncommenting +LASER_CONSTANT_POWER_PER_RATE in config.h + + - It assumes the programmed rate is the intended power/rate for the +motion. + - Feed rate overrides (FRO) do not effect the power/rate. Meaning +that spindle PWM will automatically lower with lower FRO and increase +with higher FRO to keep it the same. + - Spindle speed overrides (SSO) will directly increase and decrease +the power/rate. So 150% SSO will increase the PWM output by 150% for +the same speed. + - The combination of FRO and SSO behaviors should allow for subtle +and highly flexible tuning of how the laser cutter is operating in +real-time and during the job. + +- Re-factored planner block rapid rate handling for the dynamic laser +power feature. Should have had no effect on how Grbl operates. + + ---------------- Date: 2016-10-26 Author: Sonny Jeon diff --git a/doc/markdown/interface.md b/doc/markdown/interface.md index e62d41f..41716e8 100644 --- a/doc/markdown/interface.md +++ b/doc/markdown/interface.md @@ -411,6 +411,8 @@ Feedback messages provide non-critical information on what Grbl is doing, what i - `MPos:0.000,-10.000,5.000` machine position or - `WPos:-2.500,0.000,11.000` work position + - **NOTE: Grbl v1.1 sends only one position vector because a GUI can easily compute the other position vector with the work coordinate offset `WCO:` data. See WCO description for details.** + - Three position values are given in the order of X, Y, and Z. A fourth position value may exist in later versions for the A-axis. - `$13` report inches user setting effects these values and is given as either mm or inches. @@ -422,6 +424,10 @@ Feedback messages provide non-critical information on what Grbl is doing, what i - `WCO:0.000,1.551,5.664` is the current work coordinate offset of the g-code parser, which is the sum of the current work coordinate system, G92 offsets, and G43.1 tool length offset. - Machine position and work position are related by this simple equation per axis: `WPos = MPos - WCO` + + - **GUI Developers:** Simply track and retain the last `WCO:` vector and use the above equation to compute the other position vector for your position readouts. If Grbl's status reports show either `WPos` or `MPos`, just follow the equations below. It's as easy as that! + - If `WPos:` is given, use `MPos = WPos + WCO`. + - If `MPos:` is given, use `WPos = MPos - WCO`. - Values are given in the order of the X,Y, and Z axes offsets. A fourth offset value may exist in later versions for the A-axis. - `$13` report inches user setting effects these values and is given as either mm or inches. diff --git a/grbl/gcode.c b/grbl/gcode.c index c0d0bce..e1fcfa2 100644 --- a/grbl/gcode.c +++ b/grbl/gcode.c @@ -149,35 +149,12 @@ uint8_t gc_execute_line(char *line) // No break. Continues to next line. case 4: case 53: word_bit = MODAL_GROUP_G0; - switch(int_value) { - case 4: gc_block.non_modal_command = NON_MODAL_DWELL; break; // G4 - case 10: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_DATA; break; // G10 - case 28: - switch(mantissa) { - case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_0; break; // G28 - case 10: gc_block.non_modal_command = NON_MODAL_SET_HOME_0; break; // G28.1 - default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G28.x command] - } - mantissa = 0; // Set to zero to indicate valid non-integer G command. - break; - case 30: - switch(mantissa) { - case 0: gc_block.non_modal_command = NON_MODAL_GO_HOME_1; break; // G30 - case 10: gc_block.non_modal_command = NON_MODAL_SET_HOME_1; break; // G30.1 - default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G30.x command] - } - mantissa = 0; // Set to zero to indicate valid non-integer G command. - break; - case 53: gc_block.non_modal_command = NON_MODAL_ABSOLUTE_OVERRIDE; break; // G53 - case 92: - switch(mantissa) { - case 0: gc_block.non_modal_command = NON_MODAL_SET_COORDINATE_OFFSET; break; // G92 - case 10: gc_block.non_modal_command = NON_MODAL_RESET_COORDINATE_OFFSET; break; // G92.1 - default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G92.x command] - } - mantissa = 0; // Set to zero to indicate valid non-integer G command. - break; - } + gc_block.non_modal_command = int_value; + if ((int_value == 28) || (int_value == 30) || (int_value == 92)) { + if ((mantissa != 0) || (mantissa != 10)) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } + gc_block.non_modal_command += mantissa; + mantissa = 0; // Set to zero to indicate valid non-integer G command. + } break; case 0: case 1: case 2: case 3: case 38: // Check for G0/1/2/3/38 being called with G10/28/30/92 on same block. @@ -187,37 +164,22 @@ uint8_t gc_execute_line(char *line) // No break. Continues to next line. case 80: word_bit = MODAL_GROUP_G1; - switch(int_value) { - case 0: gc_block.modal.motion = MOTION_MODE_SEEK; break; // G0 - case 1: gc_block.modal.motion = MOTION_MODE_LINEAR; break; // G1 - case 2: gc_block.modal.motion = MOTION_MODE_CW_ARC; break; // G2 - case 3: gc_block.modal.motion = MOTION_MODE_CCW_ARC; break; // G3 - case 38: - switch(mantissa) { - case 20: gc_block.modal.motion = MOTION_MODE_PROBE_TOWARD; break; // G38.2 - case 30: gc_block.modal.motion = MOTION_MODE_PROBE_TOWARD_NO_ERROR; break; // G38.3 - case 40: gc_block.modal.motion = MOTION_MODE_PROBE_AWAY; break; // G38.4 - case 50: gc_block.modal.motion = MOTION_MODE_PROBE_AWAY_NO_ERROR; break; // G38.5 - default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G38.x command] - } - mantissa = 0; // Set to zero to indicate valid non-integer G command. - break; - case 80: gc_block.modal.motion = MOTION_MODE_NONE; break; // G80 - } + gc_block.modal.motion = int_value; + if (int_value == 38){ + if ((mantissa != 20) || (mantissa != 30) || (mantissa != 40) || (mantissa != 50)) { + FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G38.x command] + } + gc_block.modal.motion += (mantissa/10)+100; + } break; case 17: case 18: case 19: word_bit = MODAL_GROUP_G2; - switch(int_value) { - case 17: gc_block.modal.plane_select = PLANE_SELECT_XY; break; - case 18: gc_block.modal.plane_select = PLANE_SELECT_ZX; break; - case 19: gc_block.modal.plane_select = PLANE_SELECT_YZ; break; - } + gc_block.modal.plane_select = int_value - 17; break; case 90: case 91: if (mantissa == 0) { word_bit = MODAL_GROUP_G3; - if (int_value == 90) { gc_block.modal.distance = DISTANCE_MODE_ABSOLUTE; } // G90 - else { gc_block.modal.distance = DISTANCE_MODE_INCREMENTAL; } // G91 + gc_block.modal.distance = int_value - 90; } else { word_bit = MODAL_GROUP_G4; if ((mantissa != 10) || (int_value == 90)) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G90.1 not supported] @@ -227,13 +189,11 @@ uint8_t gc_execute_line(char *line) break; case 93: case 94: word_bit = MODAL_GROUP_G5; - if (int_value == 93) { gc_block.modal.feed_rate = FEED_RATE_MODE_INVERSE_TIME; } // G93 - else { gc_block.modal.feed_rate = FEED_RATE_MODE_UNITS_PER_MIN; } // G94 + gc_block.modal.feed_rate = 94 - int_value; break; case 20: case 21: word_bit = MODAL_GROUP_G6; - if (int_value == 20) { gc_block.modal.units = UNITS_MODE_INCHES; } // G20 - else { gc_block.modal.units = UNITS_MODE_MM; } // G21 + gc_block.modal.units = 21 - int_value; break; case 40: word_bit = MODAL_GROUP_G7; @@ -258,7 +218,7 @@ uint8_t gc_execute_line(char *line) case 54: case 55: case 56: case 57: case 58: case 59: // NOTE: G59.x are not supported. (But their int_values would be 60, 61, and 62.) word_bit = MODAL_GROUP_G12; - gc_block.modal.coord_select = int_value-54; // Shift to array indexing. + gc_block.modal.coord_select = int_value - 54; // Shift to array indexing. break; case 61: word_bit = MODAL_GROUP_G13; @@ -284,7 +244,7 @@ uint8_t gc_execute_line(char *line) switch(int_value) { case 0: gc_block.modal.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause case 1: break; // Optional stop not supported. Ignore. - case 2: case 30: gc_block.modal.program_flow = PROGRAM_FLOW_COMPLETED; break; // Program end and reset + default: gc_block.modal.program_flow = int_value; // Program end and reset } break; #ifndef USE_SPINDLE_DIR_AS_ENABLE_PIN @@ -891,7 +851,7 @@ uint8_t gc_execute_line(char *line) // [2. Set feed rate mode ]: gc_state.modal.feed_rate = gc_block.modal.feed_rate; - pl_data->condition |= gc_state.modal.feed_rate; // Set condition flag for planner use. + if (gc_state.modal.feed_rate) { pl_data->condition |= PL_COND_FLAG_INVERSE_TIME; } // Set condition flag for planner use. // [3. Set feed rate ]: gc_state.feed_rate = gc_block.values.f; // Always copy this value. See feed rate error-checking. diff --git a/grbl/gcode.h b/grbl/gcode.h index fa6ee5e..66d6737 100644 --- a/grbl/gcode.h +++ b/grbl/gcode.h @@ -51,54 +51,60 @@ // Define command actions for within execution-type modal groups (motion, stopping, non-modal). Used // internally by the parser to know which command to execute. +// NOTE: Some macro values are assigned specific values to make g-code state reporting and parsing +// compile a litte smaller. Necessary due to being completely out of flash on the 328p. Although not +// ideal, just be careful with values that state 'do not alter' and check both report.c and gcode.c +// to see how they are used, if you need to alter them. // Modal Group G0: Non-modal actions #define NON_MODAL_NO_ACTION 0 // (Default: Must be zero) -#define NON_MODAL_DWELL 1 // G4 -#define NON_MODAL_SET_COORDINATE_DATA 2 // G10 -#define NON_MODAL_GO_HOME_0 3 // G28 -#define NON_MODAL_SET_HOME_0 4 // G28.1 -#define NON_MODAL_GO_HOME_1 5 // G30 -#define NON_MODAL_SET_HOME_1 6 // G30.1 -#define NON_MODAL_ABSOLUTE_OVERRIDE 7 // G53 -#define NON_MODAL_SET_COORDINATE_OFFSET 8 // G92 -#define NON_MODAL_RESET_COORDINATE_OFFSET 9 //G92.1 +#define NON_MODAL_DWELL 4 // G4 (Do not alter value) +#define NON_MODAL_SET_COORDINATE_DATA 10 // G10 (Do not alter value) +#define NON_MODAL_GO_HOME_0 28 // G28 (Do not alter value) +#define NON_MODAL_SET_HOME_0 38 // G28.1 (Do not alter value) +#define NON_MODAL_GO_HOME_1 30 // G30 (Do not alter value) +#define NON_MODAL_SET_HOME_1 40 // G30.1 (Do not alter value) +#define NON_MODAL_ABSOLUTE_OVERRIDE 53 // G53 (Do not alter value) +#define NON_MODAL_SET_COORDINATE_OFFSET 92 // G92 (Do not alter value) +#define NON_MODAL_RESET_COORDINATE_OFFSET 102 //G92.1 (Do not alter value) // Modal Group G1: Motion modes #define MOTION_MODE_SEEK 0 // G0 (Default: Must be zero) -#define MOTION_MODE_LINEAR 1 // G1 -#define MOTION_MODE_CW_ARC 2 // G2 -#define MOTION_MODE_CCW_ARC 3 // G3 -#define MOTION_MODE_PROBE_TOWARD 4 // G38.2 NOTE: G38.2, G38.3, G38.4, G38.5 must be sequential. See report_gcode_modes(). -#define MOTION_MODE_PROBE_TOWARD_NO_ERROR 5 // G38.3 -#define MOTION_MODE_PROBE_AWAY 6 // G38.4 -#define MOTION_MODE_PROBE_AWAY_NO_ERROR 7 // G38.5 -#define MOTION_MODE_NONE 8 // G80 +#define MOTION_MODE_LINEAR 1 // G1 (Do not alter value) +#define MOTION_MODE_CW_ARC 2 // G2 (Do not alter value) +#define MOTION_MODE_CCW_ARC 3 // G3 (Do not alter value) +#define MOTION_MODE_PROBE_TOWARD 140 // G38.2 (Do not alter value) +#define MOTION_MODE_PROBE_TOWARD_NO_ERROR 141 // G38.3 (Do not alter value) +#define MOTION_MODE_PROBE_AWAY 142 // G38.4 (Do not alter value) +#define MOTION_MODE_PROBE_AWAY_NO_ERROR 143 // G38.5 (Do not alter value) +#define MOTION_MODE_NONE 80 // G80 (Do not alter value) // Modal Group G2: Plane select #define PLANE_SELECT_XY 0 // G17 (Default: Must be zero) -#define PLANE_SELECT_ZX 1 // G18 -#define PLANE_SELECT_YZ 2 // G19 +#define PLANE_SELECT_ZX 1 // G18 (Do not alter value) +#define PLANE_SELECT_YZ 2 // G19 (Do not alter value) // Modal Group G3: Distance mode #define DISTANCE_MODE_ABSOLUTE 0 // G90 (Default: Must be zero) -#define DISTANCE_MODE_INCREMENTAL 1 // G91 +#define DISTANCE_MODE_INCREMENTAL 1 // G91 (Do not alter value) // Modal Group G4: Arc IJK distance mode #define DISTANCE_ARC_MODE_INCREMENTAL 0 // G91.1 (Default: Must be zero) // Modal Group M4: Program flow #define PROGRAM_FLOW_RUNNING 0 // (Default: Must be zero) -#define PROGRAM_FLOW_PAUSED 1 // M0, M1 -#define PROGRAM_FLOW_COMPLETED 2 // M2, M30 +#define PROGRAM_FLOW_PAUSED 3 // M0 +#define PROGRAM_FLOW_OPTIONAL_STOP 1 // M1 NOTE: Not supported, but valid and ignored. +#define PROGRAM_FLOW_COMPLETED_M2 2 // M2 (Do not alter value) +#define PROGRAM_FLOW_COMPLETED_M30 30 // M30 (Do not alter value) // Modal Group G5: Feed rate mode #define FEED_RATE_MODE_UNITS_PER_MIN 0 // G94 (Default: Must be zero) -#define FEED_RATE_MODE_INVERSE_TIME PL_COND_FLAG_INVERSE_TIME // G93 (NOTE: Uses planner condition bit flag) +#define FEED_RATE_MODE_INVERSE_TIME 1 // G93 (Do not alter value) // Modal Group G6: Units mode #define UNITS_MODE_MM 0 // G21 (Default: Must be zero) -#define UNITS_MODE_INCHES 1 // G20 +#define UNITS_MODE_INCHES 1 // G20 (Do not alter value) // Modal Group G7: Cutter radius compensation mode #define CUTTER_COMP_DISABLE 0 // G40 (Default: Must be zero) diff --git a/grbl/grbl.h b/grbl/grbl.h index 4f91c6c..4e4edbd 100644 --- a/grbl/grbl.h +++ b/grbl/grbl.h @@ -23,7 +23,7 @@ // Grbl versioning system #define GRBL_VERSION "1.1d" -#define GRBL_VERSION_BUILD "20161027" +#define GRBL_VERSION_BUILD "20161104" // Define standard libraries used by Grbl. #include diff --git a/grbl/motion_control.c b/grbl/motion_control.c index 192cfa2..a4f1794 100644 --- a/grbl/motion_control.c +++ b/grbl/motion_control.c @@ -106,8 +106,11 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of // Multiply inverse feed_rate to compensate for the fact that this movement is approximated // by a number of discrete segments. The inverse feed_rate should be correct for the sum of // all segments. - if (pl_data->condition & PL_COND_FLAG_INVERSE_TIME) { pl_data->feed_rate *= segments; } - + if (pl_data->condition & PL_COND_FLAG_INVERSE_TIME) { + pl_data->feed_rate *= segments; + bit_false(pl_data->condition,PL_COND_FLAG_INVERSE_TIME); // Force as feed absolute mode over arc segments. + } + float theta_per_segment = angular_travel/segments; float linear_per_segment = (target[axis_linear] - position[axis_linear])/segments; diff --git a/grbl/planner.h b/grbl/planner.h index 0bac916..9379dc3 100644 --- a/grbl/planner.h +++ b/grbl/planner.h @@ -40,7 +40,7 @@ #define PL_COND_FLAG_RAPID_MOTION bit(0) #define PL_COND_FLAG_SYSTEM_MOTION bit(1) // Single motion. Circumvents planner state. Used by home/park. #define PL_COND_FLAG_NO_FEED_OVERRIDE bit(2) // Motion does not honor feed override. -#define PL_COND_FLAG_INVERSE_TIME bit(3) +#define PL_COND_FLAG_INVERSE_TIME bit(3) // Interprets feed rate value as inverse time when set. #define PL_COND_FLAG_SPINDLE_CW bit(4) #define PL_COND_FLAG_SPINDLE_CCW bit(5) #define PL_COND_FLAG_COOLANT_FLOOD bit(6) diff --git a/grbl/protocol.c b/grbl/protocol.c index 3f13c15..858acbb 100644 --- a/grbl/protocol.c +++ b/grbl/protocol.c @@ -716,7 +716,6 @@ static void protocol_exec_rt_suspend() bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); } else { spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed); - delay_sec(SAFETY_DOOR_SPINDLE_DELAY, DELAY_MODE_SYS_SUSPEND); } } if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_RESTORE_CYCLE) { diff --git a/grbl/report.c b/grbl/report.c index 4fe425e..5b004be 100644 --- a/grbl/report.c +++ b/grbl/report.c @@ -33,6 +33,8 @@ void report_util_setting_prefix(uint8_t n) { serial_write('$'); print_uint8_base10(n); serial_write('='); } static void report_util_line_feed() { printPgmString(PSTR("\r\n")); } static void report_util_feedback_line_feed() { serial_write(']'); report_util_line_feed(); } +static void report_util_gcode_modes_G() { printPgmString(PSTR(" G")); } +static void report_util_gcode_modes_M() { printPgmString(PSTR(" M")); } // static void report_util_comment_line_feed() { serial_write(')'); report_util_line_feed(); } static void report_util_axis_values(float *axis_value) { uint8_t idx; @@ -439,54 +441,48 @@ void report_gcode_modes() #else printPgmString(PSTR("[GC:G")); #endif - switch (gc_state.modal.motion) { - case MOTION_MODE_SEEK : serial_write('0'); break; - case MOTION_MODE_LINEAR : serial_write('1'); break; - case MOTION_MODE_CW_ARC : serial_write('2'); break; - case MOTION_MODE_CCW_ARC : serial_write('3'); break; - case MOTION_MODE_NONE : printPgmString(PSTR("80")); break; - default: - printPgmString(PSTR("38.")); - print_uint8_base10(gc_state.modal.motion - (MOTION_MODE_PROBE_TOWARD-2)); + if (gc_state.modal.motion >= MOTION_MODE_PROBE_TOWARD) { + printPgmString(PSTR("38.")); + print_uint8_base10(gc_state.modal.motion - (MOTION_MODE_PROBE_TOWARD-2)); + } else { + print_uint8_base10(gc_state.modal.motion); } - printPgmString(PSTR(" G")); + report_util_gcode_modes_G(); print_uint8_base10(gc_state.modal.coord_select+54); - printPgmString(PSTR(" G1")); - switch (gc_state.modal.plane_select) { - case PLANE_SELECT_XY : serial_write('7'); break; - case PLANE_SELECT_ZX : serial_write('8'); break; - case PLANE_SELECT_YZ : serial_write('9'); break; + report_util_gcode_modes_G(); + print_uint8_base10(gc_state.modal.plane_select+17); + + report_util_gcode_modes_G(); + print_uint8_base10(21-gc_state.modal.units); + + report_util_gcode_modes_G(); + print_uint8_base10(gc_state.modal.distance+90); + + report_util_gcode_modes_G(); + print_uint8_base10(94-gc_state.modal.feed_rate); + + if (gc_state.modal.program_flow) { + report_util_gcode_modes_M(); + switch (gc_state.modal.program_flow) { + case PROGRAM_FLOW_PAUSED : serial_write('0'); break; + // case PROGRAM_FLOW_OPTIONAL_STOP : serial_write('1'); break; // M1 is ignored and not supported. + case PROGRAM_FLOW_COMPLETED_M2 : + case PROGRAM_FLOW_COMPLETED_M30 : + print_uint8_base10(gc_state.modal.program_flow); + break; + } } - printPgmString(PSTR(" G2")); - if (gc_state.modal.units == UNITS_MODE_MM) { serial_write('1'); } - else { serial_write('0'); } - - printPgmString(PSTR(" G9")); - if (gc_state.modal.distance == DISTANCE_MODE_ABSOLUTE) { serial_write('0'); } - else { serial_write('1'); } - - printPgmString(PSTR(" G9")); - if (gc_state.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { serial_write('3'); } - else { serial_write('4'); } - - printPgmString(PSTR(" M")); - switch (gc_state.modal.program_flow) { - case PROGRAM_FLOW_RUNNING : serial_write('0'); break; - case PROGRAM_FLOW_PAUSED : serial_write('1'); break; - case PROGRAM_FLOW_COMPLETED : serial_write('2'); break; - } - - printPgmString(PSTR(" M")); + report_util_gcode_modes_M(); switch (gc_state.modal.spindle) { case SPINDLE_ENABLE_CW : serial_write('3'); break; case SPINDLE_ENABLE_CCW : serial_write('4'); break; case SPINDLE_DISABLE : serial_write('5'); break; } - printPgmString(PSTR(" M")); + report_util_gcode_modes_M(); #ifdef ENABLE_M7 if (gc_state.modal.coolant) { // Note: Multiple coolant states may be active at the same time. if (gc_state.modal.coolant & PL_COND_FLAG_COOLANT_MIST) { serial_write('7'); } diff --git a/grbl/spindle_control.c b/grbl/spindle_control.c index b24b11b..4749ad0 100644 --- a/grbl/spindle_control.c +++ b/grbl/spindle_control.c @@ -152,7 +152,7 @@ void spindle_stop() } } else { // Compute intermediate PWM value with linear spindle speed model. - // NOTE: A nonlinear model could be installed here, if required, but keep it light-weight. + // NOTE: A nonlinear model could be installed here, if required, but keep it VERY light-weight. sys.spindle_speed = rpm; pwm_value = floor( (rpm-settings.rpm_min)*pwm_gradient + (SPINDLE_PWM_MIN_VALUE+0.5) ); } diff --git a/grbl/stepper.c b/grbl/stepper.c index 57cc89d..4ea09bd 100644 --- a/grbl/stepper.c +++ b/grbl/stepper.c @@ -58,12 +58,16 @@ // discarded when entirely consumed and completed by the segment buffer. Also, AMASS alters this // data for its own use. typedef struct { - uint8_t direction_bits; - #ifdef VARIABLE_SPINDLE - uint8_t spindle_pwm; - #endif uint32_t steps[N_AXIS]; uint32_t step_event_count; + uint8_t direction_bits; + #ifdef VARIABLE_SPINDLE + #ifdef LASER_CONSTANT_POWER_PER_RATE + uint8_t is_pwm_rate_adjusted; // Tracks motions that require constant laser power/rate + #else + uint8_t spindle_pwm; + #endif + #endif } st_block_t; static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1]; @@ -72,14 +76,17 @@ static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1]; // planner buffer. Once "checked-out", the steps in the segments buffer cannot be modified by // the planner, where the remaining planner block steps still can. typedef struct { - uint16_t n_step; // Number of step events to be executed for this segment - uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment. - uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate. + uint16_t n_step; // Number of step events to be executed for this segment + uint16_t cycles_per_tick; // Step distance traveled per ISR tick, aka step rate. + uint8_t st_block_index; // Stepper block data index. Uses this information to execute this segment. #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING uint8_t amass_level; // Indicates AMASS level for the ISR to execute this segment #else uint8_t prescaler; // Without AMASS, a prescaler is required to adjust for slow timing. #endif + #ifdef LASER_CONSTANT_POWER_PER_RATE + uint8_t rate_adjusted_pwm; + #endif } segment_t; static segment_t segment_buffer[SEGMENT_BUFFER_SIZE]; @@ -153,7 +160,7 @@ typedef struct { float decelerate_after; // Deceleration ramp start measured from end of block (mm) #ifdef LASER_CONSTANT_POWER_PER_RATE - float inv_rate; // Used by PWM laser mode + float inv_rate; // Used by PWM laser mode to speed up segment calculations. #endif } st_prep_t; static st_prep_t prep; @@ -353,12 +360,20 @@ ISR(TIMER1_COMPA_vect) #ifdef VARIABLE_SPINDLE // Set real-time spindle output as segment is loaded, just prior to the first step. - spindle_set_speed(st.exec_block->spindle_pwm); + #ifdef LASER_CONSTANT_POWER_PER_RATE + spindle_set_speed(st.exec_segment->rate_adjusted_pwm); + #else + spindle_set_speed(st.exec_block->spindle_pwm); + #endif #endif } else { // Segment buffer empty. Shutdown. st_go_idle(); + #ifdef LASER_CONSTANT_POWER_PER_RATE + // Ensure pwm is set properly upon completion of rate-controlled motion. + if (st.exec_block->is_pwm_rate_adjusted) { spindle_set_speed(SPINDLE_PWM_OFF_VALUE); } + #endif system_set_exec_state_flag(EXEC_CYCLE_STOP); // Flag main program for cycle end return; // Nothing to do but exit. } @@ -650,8 +665,14 @@ void st_prep_buffer() } #ifdef LASER_CONSTANT_POWER_PER_RATE - // Pre-compute inverse programmed rate to speed up PWM updating per step segment. - prep.inv_rate = 1.0/pl_block->programmed_rate; + // Setup laser mode variables. PWM rate adjusted motions will always complete a motion with the + // spindle off. + st_prep_block->is_pwm_rate_adjusted = false; + if (settings.flags & BITFLAG_LASER_MODE) { + // Pre-compute inverse programmed rate to speed up PWM updating per step segment. + prep.inv_rate = 1.0/pl_block->programmed_rate; + if (!(pl_block->condition & PL_COND_MOTION_MASK)) { st_prep_block->is_pwm_rate_adjusted = true; } + } #endif } @@ -746,7 +767,7 @@ void st_prep_buffer() } } - #ifdef VARIABLE_SPINDLE + #ifdef VARIABLE_SPINDLE bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); // Force update whenever updating block. #endif } @@ -856,34 +877,36 @@ void st_prep_buffer() } } while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete. - /* ----------------------------------------------------------------------------------- - Compute spindle speed PWM output for step segment - */ #ifdef VARIABLE_SPINDLE - + /* ----------------------------------------------------------------------------------- + Compute spindle speed PWM output for step segment + */ #ifdef LASER_CONSTANT_POWER_PER_RATE - if ((settings.flags & BITFLAG_LASER_MODE) || (sys.step_control & STEP_CONTROL_UPDATE_SPINDLE_PWM)) { + if (st_prep_block->is_pwm_rate_adjusted || (sys.step_control & STEP_CONTROL_UPDATE_SPINDLE_PWM)) { + if (pl_block->condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)) { + float rpm = pl_block->spindle_speed; + // NOTE: Feed and rapid overrides are independent of PWM value and do not alter laser power/rate. + if (st_prep_block->is_pwm_rate_adjusted) { rpm *= (prep.current_speed * prep.inv_rate); } + // If current_speed is zero, then may need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE) + // but this would be instantaneous only and during a motion. May not matter at all. + prep_segment->rate_adjusted_pwm = spindle_compute_pwm_value(rpm); + } else { + sys.spindle_speed = 0.0; + prep_segment->rate_adjusted_pwm = SPINDLE_PWM_OFF_VALUE; + } + bit_false(sys.step_control,STEP_CONTROL_UPDATE_SPINDLE_PWM); + } #else if (sys.step_control & STEP_CONTROL_UPDATE_SPINDLE_PWM) { + if (pl_block->condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)) { + st_prep_block->spindle_pwm = spindle_compute_pwm_value(pl_block->spindle_speed); + } else { + sys.spindle_speed = 0.0; + st_prep_block->spindle_pwm = SPINDLE_PWM_OFF_VALUE; + } + bit_false(sys.step_control,STEP_CONTROL_UPDATE_SPINDLE_PWM); + } #endif - if (pl_block->condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)) { - float rpm = pl_block->spindle_speed; - #ifdef LASER_CONSTANT_POWER_PER_RATE - // NOTE: Feed and rapid overrides are independent of PWM value and do not alter laser power/rate. - if (settings.flags & BITFLAG_LASER_MODE) { - // If current_speed is zero, then may need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE) - // but this would be instantaneous only and during a motion. May not matter at all. - rpm *= (prep.current_speed * prep.inv_rate); - } - #endif - st_prep_block->spindle_pwm = spindle_compute_pwm_value(rpm); - } else { - sys.spindle_speed = 0.0; - st_prep_block->spindle_pwm = SPINDLE_PWM_OFF_VALUE; - } - bit_false(sys.step_control,STEP_CONTROL_UPDATE_SPINDLE_PWM); - } - #endif /* -----------------------------------------------------------------------------------