Improved constant laser power per rate mode. Re-factored for flash size. Minor bug fixes.

- NOTE: This commit has largely been untested.

- Constant laser power per rate mode has been improved. Altered its
implementation to be more responsive and accurate.

- Based on LaserWeb dev feedback, only G1, G2, and G3 moves operate
with constant laser power mode. Meaning that G0, G38.x, and $J jogging
motions operate without it and will keep a constant power output. This
was specifically requested as a way to focus the laser by keeping the
laser on when not moving. Operationally, this shouldn’t alter how the
laser mode operates.

- Re-factored parts of the g-code parser and g-code state reports to
save a few hundred bytes of flash. What was done makes the code a bit
more unreadable (bad), but the flash space was in dire need. So, I’m
willing to live with it for now.

- Fixed a problem with $G g-code state reports. Showed `M0` program
pause during a run state. Now fixed to show nothing during a run state.
Also, `M30` program end was shown as `M2`. This was also corrected.

- Improved spindle stop override responsiveness by removing the
enforced spindle restoring delay. It’s not needed for a feature that is
user controlled.

- Fixed a bug with G2/3 arcs in inverse time mode.

- Updated the interface.md document to make it more clear how WPos: or
MPos: can be calculated from WCO:. Some GUI devs have failed to catch
this in the documentation.
This commit is contained in:
Sonny Jeon 2016-11-04 09:15:34 -06:00
parent e8b717604b
commit 6e3fb6bd13
12 changed files with 185 additions and 161 deletions

View File

@ -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._

View File

@ -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 isnt 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

View File

@ -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.
@ -423,6 +425,10 @@ Feedback messages provide non-critical information on what Grbl is doing, what i
- 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.

View File

@ -149,34 +149,11 @@ 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]
}
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 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;
}
break;
case 0: case 1: case 2: case 3: case 38:
@ -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]
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]
}
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 += (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.

View File

@ -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)

View File

@ -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 <avr/io.h>

View File

@ -106,7 +106,10 @@ 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;

View File

@ -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)

View File

@ -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) {

View File

@ -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:
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);
printPgmString(PSTR(" G2"));
if (gc_state.modal.units == UNITS_MODE_MM) { serial_write('1'); }
else { serial_write('0'); }
report_util_gcode_modes_G();
print_uint8_base10(21-gc_state.modal.units);
printPgmString(PSTR(" G9"));
if (gc_state.modal.distance == DISTANCE_MODE_ABSOLUTE) { serial_write('0'); }
else { serial_write('1'); }
report_util_gcode_modes_G();
print_uint8_base10(gc_state.modal.distance+90);
printPgmString(PSTR(" G9"));
if (gc_state.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { serial_write('3'); }
else { serial_write('4'); }
report_util_gcode_modes_G();
print_uint8_base10(94-gc_state.modal.feed_rate);
printPgmString(PSTR(" M"));
if (gc_state.modal.program_flow) {
report_util_gcode_modes_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;
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(" 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'); }

View File

@ -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) );
}

View File

@ -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];
@ -73,13 +77,16 @@ static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE-1];
// 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.
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.
#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
// 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
}
@ -856,34 +877,36 @@ void st_prep_buffer()
}
} while (mm_remaining > prep.mm_complete); // **Complete** Exit loop. Profile complete.
#ifdef VARIABLE_SPINDLE
/* -----------------------------------------------------------------------------------
Compute spindle speed PWM output for step segment
*/
#ifdef VARIABLE_SPINDLE
#ifdef LASER_CONSTANT_POWER_PER_RATE
if ((settings.flags & BITFLAG_LASER_MODE) || (sys.step_control & STEP_CONTROL_UPDATE_SPINDLE_PWM)) {
#else
if (sys.step_control & STEP_CONTROL_UPDATE_SPINDLE_PWM) {
#endif
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;
#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 (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.
rpm *= (prep.current_speed * prep.inv_rate);
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;
}
#endif
st_prep_block->spindle_pwm = spindle_compute_pwm_value(rpm);
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
#endif
/* -----------------------------------------------------------------------------------