Mass reformat

No code changes other than what clang-format mandates. This is breaking
This commit is contained in:
Imbus 2025-12-28 07:09:00 +01:00
parent a1c041481c
commit fe32d197f9
38 changed files with 7094 additions and 6574 deletions

View file

@ -29,7 +29,6 @@
#define config_h
#include "grbl.h" // For Arduino IDE compatibility.
// Define CPU pin map and default settings.
// NOTE: OEMs can avoid the need to maintain/update the defaults.h and cpu_map.h files and use only
// one configuration file by placing their specific defaults and pin map at the bottom of this file.
@ -509,7 +508,8 @@
// written into the Arduino EEPROM via a seperate .INO sketch to contain product data. Altering this
// macro to not restore the build info EEPROM will ensure this data is retained after firmware upgrades.
// NOTE: Uncomment to override defaults in settings.h
// #define SETTINGS_RESTORE_ALL (SETTINGS_RESTORE_DEFAULTS | SETTINGS_RESTORE_PARAMETERS | SETTINGS_RESTORE_STARTUP_LINES | SETTINGS_RESTORE_BUILD_INFO)
// #define SETTINGS_RESTORE_ALL (SETTINGS_RESTORE_DEFAULTS | SETTINGS_RESTORE_PARAMETERS |
// SETTINGS_RESTORE_STARTUP_LINES | SETTINGS_RESTORE_BUILD_INFO)
// Enable the '$I=(string)' build info write command. If disabled, any existing build info data must
// be placed into EEPROM via external means with a valid checksum value. This macro option is useful
@ -568,7 +568,8 @@
#define PARKING_TARGET -5.0 // Parking axis target. In mm, as machine coordinate [-max_travel,0].
#define PARKING_RATE 500.0 // Parking fast rate after pull-out in mm/min.
#define PARKING_PULLOUT_RATE 100.0 // Pull-out/plunge slow feed rate in mm/min.
#define PARKING_PULLOUT_INCREMENT 5.0 // Spindle pull-out and plunge distance in mm. Incremental distance.
#define PARKING_PULLOUT_INCREMENT \
5.0 // Spindle pull-out and plunge distance in mm. Incremental distance.
// Must be positive value or equal to zero.
// Enables a special set of M-code commands that enables and disables the parking motion.
@ -674,7 +675,6 @@
// updating lots of code to ensure everything is running correctly.
// #define DUAL_AXIS_CONFIG_CNC_SHIELD_CLONE // Uncomment to select. Comment other configs.
/* ---------------------------------------------------------------------------------------
OEM Single File Configuration Option
@ -688,5 +688,4 @@
// Paste default settings definitions here.
#endif

View file

@ -20,9 +20,7 @@
#include "grbl.h"
void coolant_init()
{
void coolant_init() {
COOLANT_FLOOD_DDR |= (1 << COOLANT_FLOOD_BIT); // Configure as output pin
#ifdef ENABLE_M7
COOLANT_MIST_DDR |= (1 << COOLANT_MIST_BIT);
@ -30,10 +28,8 @@ void coolant_init()
coolant_stop();
}
// Returns current coolant output state. Overrides may alter it from programmed state.
uint8_t coolant_get_state()
{
uint8_t coolant_get_state() {
uint8_t cl_state = COOLANT_STATE_DISABLE;
#ifdef INVERT_COOLANT_FLOOD_PIN
if (bit_isfalse(COOLANT_FLOOD_PORT, (1 << COOLANT_FLOOD_BIT))) {
@ -54,11 +50,9 @@ uint8_t coolant_get_state()
return (cl_state);
}
// Directly called by coolant_init(), coolant_set_state(), and mc_reset(), which can be at
// an interrupt-level. No report flag set, but only called by routines that don't need it.
void coolant_stop()
{
void coolant_stop() {
#ifdef INVERT_COOLANT_FLOOD_PIN
COOLANT_FLOOD_PORT |= (1 << COOLANT_FLOOD_BIT);
#else
@ -73,14 +67,14 @@ void coolant_stop()
#endif
}
// Main program only. Immediately sets flood coolant running state and also mist coolant,
// if enabled. Also sets a flag to report an update to a coolant state.
// Called by coolant toggle override, parking restore, parking retract, sleep mode, g-code
// parser program end, and g-code parser coolant_sync().
void coolant_set_state(uint8_t mode)
{
if (sys.abort) { return; } // Block during abort.
void coolant_set_state(uint8_t mode) {
if (sys.abort) {
return;
} // Block during abort.
if (mode & COOLANT_FLOOD_ENABLE) {
#ifdef INVERT_COOLANT_FLOOD_PIN
@ -115,12 +109,12 @@ void coolant_set_state(uint8_t mode)
sys.report_ovr_counter = 0; // Set to report change immediately
}
// G-code parser entry-point for setting coolant state. Forces a planner buffer sync and bails
// if an abort or check-mode is active.
void coolant_sync(uint8_t mode)
{
if (sys.state == STATE_CHECK_MODE) { return; }
void coolant_sync(uint8_t mode) {
if (sys.state == STATE_CHECK_MODE) {
return;
}
protocol_buffer_synchronize(); // Ensure coolant turns on when specified in program.
coolant_set_state(mode);
}

View file

@ -28,7 +28,6 @@
#define COOLANT_STATE_FLOOD PL_COND_FLAG_COOLANT_FLOOD
#define COOLANT_STATE_MIST PL_COND_FLAG_COOLANT_MIST
// Initializes coolant control pins.
void coolant_init();

View file

@ -22,11 +22,9 @@
processor types or alternative pin layouts. This version of Grbl officially supports
only the Arduino Mega328p. */
#ifndef cpu_map_h
#define cpu_map_h
#ifdef CPU_MAP_ATMEGA328P // (Arduino Uno) Officially supported by Grbl.
// Define serial port pins and interrupt vectors.
@ -82,11 +80,14 @@
#define CONTROL_RESET_BIT 0 // Uno Analog Pin 0
#define CONTROL_FEED_HOLD_BIT 1 // Uno Analog Pin 1
#define CONTROL_CYCLE_START_BIT 2 // Uno Analog Pin 2
#define CONTROL_SAFETY_DOOR_BIT 1 // Uno Analog Pin 1 NOTE: Safety door is shared with feed hold. Enabled by config define.
#define CONTROL_SAFETY_DOOR_BIT \
1 // Uno Analog Pin 1 NOTE: Safety door is shared with feed hold. Enabled by config define.
#define CONTROL_INT PCIE1 // Pin change interrupt enable pin
#define CONTROL_INT_vect PCINT1_vect
#define CONTROL_PCMSK PCMSK1 // Pin change interrupt register
#define CONTROL_MASK ((1<<CONTROL_RESET_BIT)|(1<<CONTROL_FEED_HOLD_BIT)|(1<<CONTROL_CYCLE_START_BIT)|(1<<CONTROL_SAFETY_DOOR_BIT))
#define CONTROL_MASK \
((1 << CONTROL_RESET_BIT) | (1 << CONTROL_FEED_HOLD_BIT) | (1 << CONTROL_CYCLE_START_BIT) | \
(1 << CONTROL_SAFETY_DOOR_BIT))
#define CONTROL_INVERT_MASK CONTROL_MASK // May be re-defined to only invert certain control pins.
// Define probe switch input pin.

View file

@ -21,8 +21,8 @@
* $Revision: 1.6 $
* $Date: Friday, February 11, 2005 07:16:44 UTC $
****************************************************************************/
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/io.h>
/* These EEPROM bits have different names on different devices. */
#ifndef EEPE
@ -46,9 +46,9 @@
* \param addr EEPROM address to read from.
* \return The byte read from the EEPROM address.
*/
unsigned char eeprom_get_char( unsigned int addr )
{
do {} while( EECR & (1<<EEPE) ); // Wait for completion of previous write.
unsigned char eeprom_get_char(unsigned int addr) {
do {
} while (EECR & (1 << EEPE)); // Wait for completion of previous write.
EEAR = addr; // Set EEPROM address register.
EECR = (1 << EERE); // Start EEPROM read operation.
return EEDR; // Return the byte read from EEPROM.
@ -71,16 +71,17 @@ unsigned char eeprom_get_char( unsigned int addr )
* \param addr EEPROM address to write to.
* \param new_value New EEPROM value.
*/
void eeprom_put_char( unsigned int addr, unsigned char new_value )
{
void eeprom_put_char(unsigned int addr, unsigned char new_value) {
char old_value; // Old EEPROM value.
char diff_mask; // Difference mask, i.e. old value XOR new value.
cli(); // Ensure atomic operation for the write operation.
do {} while( EECR & (1<<EEPE) ); // Wait for completion of previous write.
do {
} while (EECR & (1 << EEPE)); // Wait for completion of previous write.
#ifndef EEPROM_IGNORE_SELFPROG
do {} while( SPMCSR & (1<<SELFPRGEN) ); // Wait for completion of SPM.
do {
} while (SPMCSR & (1 << SELFPRGEN)); // Wait for completion of SPM.
#endif
EEAR = addr; // Set EEPROM address register.
@ -126,7 +127,6 @@ void eeprom_put_char( unsigned int addr, unsigned char new_value )
// Extensions added as part of Grbl
void memcpy_to_eeprom_with_checksum(unsigned int destination, char *source, unsigned int size) {
unsigned char checksum = 0;
for (; size > 0; size--) {

View file

@ -38,9 +38,7 @@ parser_block_t gc_block;
#define FAIL(status) return (status);
void gc_init()
{
void gc_init() {
memset(&gc_state, 0, sizeof(parser_state_t));
// Load default G54 coordinate system.
@ -49,22 +47,18 @@ void gc_init()
}
}
// Sets g-code parser position in mm. Input in steps. Called by the system abort and hard
// limit pull-off routines.
void gc_sync_position()
{
void gc_sync_position() {
system_convert_array_steps_to_mpos(gc_state.position, sys_position);
}
// 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. In this function, 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 gc_execute_line(char *line) {
/* -------------------------------------------------------------------------------------
STEP 1: Initialize parser block struct and copy current g-code state modes. The parser
updates these modes and commands as the block line is parser and will only be used and
@ -111,16 +105,24 @@ uint8_t gc_execute_line(char *line)
float value;
uint8_t int_value = 0;
uint16_t mantissa = 0;
if (gc_parser_flags & GC_PARSER_JOG_MOTION) { char_counter = 3; } // Start parsing after `$J=`
else { char_counter = 0; }
if (gc_parser_flags & GC_PARSER_JOG_MOTION) {
char_counter = 3;
} // Start parsing after `$J=`
else {
char_counter = 0;
}
while (line[char_counter] != 0) { // Loop until no more g-code words in line.
// Import the next g-code word, expecting a letter followed by a value. Otherwise, error out.
letter = line[char_counter];
if((letter < 'A') || (letter > 'Z')) { FAIL(STATUS_EXPECTED_COMMAND_LETTER); } // [Expected word letter]
if ((letter < 'A') || (letter > 'Z')) {
FAIL(STATUS_EXPECTED_COMMAND_LETTER);
} // [Expected word letter]
char_counter++;
if (!read_float(line, &char_counter, &value)) { FAIL(STATUS_BAD_NUMBER_FORMAT); } // [Expected word value]
if (!read_float(line, &char_counter, &value)) {
FAIL(STATUS_BAD_NUMBER_FORMAT);
} // [Expected word value]
// Convert values to smaller uint8 significand and mantissa values for parsing this word.
// NOTE: Mantissa is multiplied by 100 to catch non-integer command values. This is more
@ -143,27 +145,41 @@ uint8_t gc_execute_line(char *line)
case 'G':
// Determine 'G' command and its modal group
switch (int_value) {
case 10: case 28: case 30: case 92:
case 10:
case 28:
case 30:
case 92:
// Check for G10/28/30/92 being called with G0/1/2/3/38 on same block.
// * G43.1 is also an axis command but is not explicitly defined this way.
if (mantissa == 0) { // Ignore G28.1, G30.1, and G92.1
if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict]
if (axis_command) {
FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT);
} // [Axis word/command conflict]
axis_command = AXIS_COMMAND_NON_MODAL;
}
// No break. Continues to next line.
case 4: case 53:
case 4:
case 53:
word_bit = MODAL_GROUP_G0;
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); }
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:
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.
// * G43.1 is also an axis command but is not explicitly defined this way.
if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict]
if (axis_command) {
FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT);
} // [Axis word/command conflict]
axis_command = AXIS_COMMAND_MOTION_MODE;
// No break. Continues to next line.
case 80:
@ -177,26 +193,33 @@ uint8_t gc_execute_line(char *line)
mantissa = 0; // Set to zero to indicate valid non-integer G command.
}
break;
case 17: case 18: case 19:
case 17:
case 18:
case 19:
word_bit = MODAL_GROUP_G2;
gc_block.modal.plane_select = int_value - 17;
break;
case 90: case 91:
case 90:
case 91:
if (mantissa == 0) {
word_bit = MODAL_GROUP_G3;
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]
if ((mantissa != 10) || (int_value == 90)) {
FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND);
} // [G90.1 not supported]
mantissa = 0; // Set to zero to indicate valid non-integer G command.
// Otherwise, arc IJK incremental mode is default. G91.1 does nothing.
}
break;
case 93: case 94:
case 93:
case 94:
word_bit = MODAL_GROUP_G5;
gc_block.modal.feed_rate = 94 - int_value;
break;
case 20: case 21:
case 20:
case 21:
word_bit = MODAL_GROUP_G6;
gc_block.modal.units = 21 - int_value;
break;
@ -206,45 +229,66 @@ uint8_t gc_execute_line(char *line)
// to support G40 commands that often appear in g-code program headers to setup defaults.
// gc_block.modal.cutter_comp = CUTTER_COMP_DISABLE; // G40
break;
case 43: case 49:
case 43:
case 49:
word_bit = MODAL_GROUP_G8;
// NOTE: The NIST g-code standard vaguely states that when a tool length offset is changed,
// there cannot be any axis motion or coordinate offsets updated. Meaning G43, G43.1, and G49
// all are explicit axis commands, regardless if they require axis words or not.
if (axis_command) { FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT); } // [Axis word/command conflict] }
if (axis_command) {
FAIL(STATUS_GCODE_AXIS_COMMAND_CONFLICT);
} // [Axis word/command conflict] }
axis_command = AXIS_COMMAND_TOOL_LENGTH_OFFSET;
if (int_value == 49) { // G49
gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_CANCEL;
} else if (mantissa == 10) { // G43.1
gc_block.modal.tool_length = TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC;
} else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported G43.x command]
} else {
FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND);
} // [Unsupported G43.x command]
mantissa = 0; // Set to zero to indicate valid non-integer G command.
break;
case 54: case 55: case 56: case 57: case 58: case 59:
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.
break;
case 61:
word_bit = MODAL_GROUP_G13;
if (mantissa != 0) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G61.1 not supported]
if (mantissa != 0) {
FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND);
} // [G61.1 not supported]
// gc_block.modal.control = CONTROL_MODE_EXACT_PATH; // G61
break;
default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); // [Unsupported G command]
}
if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [Unsupported or invalid Gxx.x command]
if (mantissa > 0) {
FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER);
} // [Unsupported or invalid Gxx.x command]
// Check for more than one command per modal group violations in the current block
// NOTE: Variable 'word_bit' is always assigned, if the command is valid.
if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); }
if (bit_istrue(command_words, bit(word_bit))) {
FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION);
}
command_words |= bit(word_bit);
break;
case 'M':
// Determine 'M' command and its modal group
if (mantissa > 0) { FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER); } // [No Mxx.x commands]
if (mantissa > 0) {
FAIL(STATUS_GCODE_COMMAND_VALUE_NOT_INTEGER);
} // [No Mxx.x commands]
switch (int_value) {
case 0: case 1: case 2: case 30:
case 0:
case 1:
case 2:
case 30:
word_bit = MODAL_GROUP_M4;
switch (int_value) {
case 0: gc_block.modal.program_flow = PROGRAM_FLOW_PAUSED; break; // Program pause
@ -252,7 +296,9 @@ uint8_t gc_execute_line(char *line)
default: gc_block.modal.program_flow = int_value; // Program end and reset
}
break;
case 3: case 4: case 5:
case 3:
case 4:
case 5:
word_bit = MODAL_GROUP_M7;
switch (int_value) {
case 3: gc_block.modal.spindle = SPINDLE_ENABLE_CW; break;
@ -261,9 +307,12 @@ uint8_t gc_execute_line(char *line)
}
break;
#ifdef ENABLE_M7
case 7: case 8: case 9:
case 7:
case 8:
case 9:
#else
case 8: case 9:
case 8:
case 9:
#endif
word_bit = MODAL_GROUP_M8;
switch (int_value) {
@ -285,7 +334,9 @@ uint8_t gc_execute_line(char *line)
// Check for more than one command per modal group violations in the current block
// NOTE: Variable 'word_bit' is always assigned, if the command is valid.
if ( bit_istrue(command_words,bit(word_bit)) ) { FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION); }
if (bit_istrue(command_words, bit(word_bit))) {
FAIL(STATUS_GCODE_MODAL_GROUP_VIOLATION);
}
command_words |= bit(word_bit);
break;
@ -300,42 +351,89 @@ uint8_t gc_execute_line(char *line)
// case 'B': // Not supported
// case 'C': // Not supported
// case 'D': // Not supported
case 'F': word_bit = WORD_F; gc_block.values.f = value; break;
case 'F':
word_bit = WORD_F;
gc_block.values.f = value;
break;
// case 'H': // Not supported
case 'I': word_bit = WORD_I; gc_block.values.ijk[X_AXIS] = value; ijk_words |= (1<<X_AXIS); break;
case 'J': word_bit = WORD_J; gc_block.values.ijk[Y_AXIS] = value; ijk_words |= (1<<Y_AXIS); break;
case 'K': word_bit = WORD_K; gc_block.values.ijk[Z_AXIS] = value; ijk_words |= (1<<Z_AXIS); break;
case 'L': word_bit = WORD_L; gc_block.values.l = int_value; break;
case 'N': word_bit = WORD_N; gc_block.values.n = trunc(value); break;
case 'P': word_bit = WORD_P; gc_block.values.p = value; break;
case 'I':
word_bit = WORD_I;
gc_block.values.ijk[X_AXIS] = value;
ijk_words |= (1 << X_AXIS);
break;
case 'J':
word_bit = WORD_J;
gc_block.values.ijk[Y_AXIS] = value;
ijk_words |= (1 << Y_AXIS);
break;
case 'K':
word_bit = WORD_K;
gc_block.values.ijk[Z_AXIS] = value;
ijk_words |= (1 << Z_AXIS);
break;
case 'L':
word_bit = WORD_L;
gc_block.values.l = int_value;
break;
case 'N':
word_bit = WORD_N;
gc_block.values.n = trunc(value);
break;
case 'P':
word_bit = WORD_P;
gc_block.values.p = value;
break;
// NOTE: For certain commands, P value must be an integer, but none of these commands are supported.
// case 'Q': // Not supported
case 'R': word_bit = WORD_R; gc_block.values.r = value; break;
case 'S': word_bit = WORD_S; gc_block.values.s = value; break;
case 'T': word_bit = WORD_T;
if (value > MAX_TOOL_NUMBER) { FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED); }
case 'R':
word_bit = WORD_R;
gc_block.values.r = value;
break;
case 'S':
word_bit = WORD_S;
gc_block.values.s = value;
break;
case 'T':
word_bit = WORD_T;
if (value > MAX_TOOL_NUMBER) {
FAIL(STATUS_GCODE_MAX_VALUE_EXCEEDED);
}
gc_block.values.t = int_value;
break;
case 'X': word_bit = WORD_X; gc_block.values.xyz[X_AXIS] = value; axis_words |= (1<<X_AXIS); break;
case 'Y': word_bit = WORD_Y; gc_block.values.xyz[Y_AXIS] = value; axis_words |= (1<<Y_AXIS); break;
case 'Z': word_bit = WORD_Z; gc_block.values.xyz[Z_AXIS] = value; axis_words |= (1<<Z_AXIS); break;
case 'X':
word_bit = WORD_X;
gc_block.values.xyz[X_AXIS] = value;
axis_words |= (1 << X_AXIS);
break;
case 'Y':
word_bit = WORD_Y;
gc_block.values.xyz[Y_AXIS] = value;
axis_words |= (1 << Y_AXIS);
break;
case 'Z':
word_bit = WORD_Z;
gc_block.values.xyz[Z_AXIS] = value;
axis_words |= (1 << Z_AXIS);
break;
default: FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND);
}
// NOTE: Variable 'word_bit' is always assigned, if the non-command letter is valid.
if (bit_istrue(value_words,bit(word_bit))) { FAIL(STATUS_GCODE_WORD_REPEATED); } // [Word repeated]
if (bit_istrue(value_words, bit(word_bit))) {
FAIL(STATUS_GCODE_WORD_REPEATED);
} // [Word repeated]
// Check for invalid negative values for words F, N, P, T, and S.
// NOTE: Negative value check is done here simply for code-efficiency.
if (bit(word_bit) & (bit(WORD_F) | bit(WORD_N) | bit(WORD_P) | bit(WORD_T) | bit(WORD_S))) {
if (value < 0.0) { FAIL(STATUS_NEGATIVE_VALUE); } // [Word value cannot be negative]
if (value < 0.0) {
FAIL(STATUS_NEGATIVE_VALUE);
} // [Word value cannot be negative]
}
value_words |= bit(word_bit); // Flag to indicate parameter assigned.
}
}
// Parsing complete!
/* -------------------------------------------------------------------------------------
STEP 3: Error-check all commands and values passed in this block. This step ensures all of
the commands are valid for execution and follows the NIST standard as closely as possible.
@ -367,13 +465,17 @@ uint8_t gc_execute_line(char *line)
// Determine implicit axis command conditions. Axis words have been passed, but no explicit axis
// command has been sent. If so, set axis command to current motion mode.
if (axis_words) {
if (!axis_command) { axis_command = AXIS_COMMAND_MOTION_MODE; } // Assign implicit motion-mode
if (!axis_command) {
axis_command = AXIS_COMMAND_MOTION_MODE;
} // Assign implicit motion-mode
}
// Check for valid line number N value.
if (bit_istrue(value_words, bit(WORD_N))) {
// Line number value cannot be less than zero (done) or greater than max line number.
if (gc_block.values.n > MAX_LINE_NUMBER) { FAIL(STATUS_GCODE_INVALID_LINE_NUMBER); } // [Exceeds max line number]
if (gc_block.values.n > MAX_LINE_NUMBER) {
FAIL(STATUS_GCODE_INVALID_LINE_NUMBER);
} // [Exceeds max line number]
}
// bit_false(value_words,bit(WORD_N)); // NOTE: Single-meaning value word. Set at end of error-checking.
@ -390,14 +492,21 @@ uint8_t gc_execute_line(char *line)
// is not defined after switching to G94 from G93.
// NOTE: For jogging, ignore prior feed rate mode. Enforce G94 and check for required F word.
if (gc_parser_flags & GC_PARSER_JOG_MOTION) {
if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); }
if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; }
if (bit_isfalse(value_words, bit(WORD_F))) {
FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE);
}
if (gc_block.modal.units == UNITS_MODE_INCHES) {
gc_block.values.f *= MM_PER_INCH;
}
} else {
if (gc_block.modal.feed_rate == FEED_RATE_MODE_INVERSE_TIME) { // = G93
// NOTE: G38 can also operate in inverse time, but is undefined as an error. Missing F word check added here.
// NOTE: G38 can also operate in inverse time, but is undefined as an error. Missing F word check added
// here.
if (axis_command == AXIS_COMMAND_MOTION_MODE) {
if ((gc_block.modal.motion != MOTION_MODE_NONE) && (gc_block.modal.motion != MOTION_MODE_SEEK)) {
if (bit_isfalse(value_words,bit(WORD_F))) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [F word missing]
if (bit_isfalse(value_words, bit(WORD_F))) {
FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE);
} // [F word missing]
}
}
// NOTE: It seems redundant to check for an F word to be passed after switching from G94 to G93. We would
@ -409,28 +518,34 @@ uint8_t gc_execute_line(char *line)
// [3. Set feed rate ]: F is negative (done.)
// - In inverse time mode: Always implicitly zero the feed rate value before and after block completion.
// NOTE: If in G93 mode or switched into it from G94, just keep F value as initialized zero or passed F word
// value in the block. If no F word is passed with a motion command that requires a feed rate, this will error
// out in the motion modes error-checking. However, if no F word is passed with NO motion command that requires
// a feed rate, we simply move on and the state feed rate value gets updated to zero and remains undefined.
// value in the block. If no F word is passed with a motion command that requires a feed rate, this will
// error out in the motion modes error-checking. However, if no F word is passed with NO motion command that
// requires a feed rate, we simply move on and the state feed rate value gets updated to zero and remains
// undefined.
} else { // = G94
// - In units per mm mode: If F word passed, ensure value is in mm/min, otherwise push last state value.
if (gc_state.modal.feed_rate == FEED_RATE_MODE_UNITS_PER_MIN) { // Last state is also G94
if (bit_istrue(value_words, bit(WORD_F))) {
if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.f *= MM_PER_INCH; }
if (gc_block.modal.units == UNITS_MODE_INCHES) {
gc_block.values.f *= MM_PER_INCH;
}
} else {
gc_block.values.f = gc_state.feed_rate; // Push last state feed rate
}
} // Else, switching to G94 from G93, so don't push last state feed rate. Its undefined or the passed F word value.
} // Else, switching to G94 from G93, so don't push last state feed rate. Its undefined or the passed F word
// value.
}
}
// bit_false(value_words,bit(WORD_F)); // NOTE: Single-meaning value word. Set at end of error-checking.
// [4. Set spindle speed ]: S is negative (done.)
if (bit_isfalse(value_words,bit(WORD_S))) { gc_block.values.s = gc_state.spindle_speed; }
if (bit_isfalse(value_words, bit(WORD_S))) {
gc_block.values.s = gc_state.spindle_speed;
}
// bit_false(value_words,bit(WORD_S)); // NOTE: Single-meaning value word. Set at end of error-checking.
// [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool value.
// bit_false(value_words,bit(WORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking.
// [5. Select tool ]: NOT SUPPORTED. Only tracks value. T is negative (done.) Not an integer. Greater than max tool
// value. bit_false(value_words,bit(WORD_T)); // NOTE: Single-meaning value word. Set at end of error-checking.
// [6. Change tool ]: N/A
// [7. Spindle control ]: N/A
@ -439,7 +554,9 @@ uint8_t gc_execute_line(char *line)
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
if (bit_istrue(command_words, bit(MODAL_GROUP_M9))) { // Already set as enabled in parser.
if (bit_istrue(value_words, bit(WORD_P))) {
if (gc_block.values.p == 0.0) { gc_block.modal.override = OVERRIDE_DISABLED; }
if (gc_block.values.p == 0.0) {
gc_block.modal.override = OVERRIDE_DISABLED;
}
bit_false(value_words, bit(WORD_P));
}
}
@ -447,7 +564,9 @@ uint8_t gc_execute_line(char *line)
// [10. Dwell ]: P value missing. P is negative (done.) NOTE: See below.
if (gc_block.non_modal_command == NON_MODAL_DWELL) {
if (bit_isfalse(value_words,bit(WORD_P))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P word missing]
if (bit_isfalse(value_words, bit(WORD_P))) {
FAIL(STATUS_GCODE_VALUE_WORD_MISSING);
} // [P word missing]
bit_false(value_words, bit(WORD_P));
}
@ -492,7 +611,9 @@ uint8_t gc_execute_line(char *line)
// is absent or if any of the other axis words are present.
if (axis_command == AXIS_COMMAND_TOOL_LENGTH_OFFSET) { // Indicates called in block.
if (gc_block.modal.tool_length == TOOL_LENGTH_OFFSET_ENABLE_DYNAMIC) {
if (axis_words ^ (1<<TOOL_LENGTH_OFFSET_AXIS)) { FAIL(STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR); }
if (axis_words ^ (1 << TOOL_LENGTH_OFFSET_AXIS)) {
FAIL(STATUS_GCODE_G43_DYNAMIC_AXIS_ERROR);
}
}
}
@ -504,9 +625,13 @@ uint8_t gc_execute_line(char *line)
float block_coord_system[N_AXIS];
memcpy(block_coord_system, gc_state.coord_system, sizeof(gc_state.coord_system));
if (bit_istrue(command_words, bit(MODAL_GROUP_G12))) { // Check if called in block
if (gc_block.modal.coord_select > N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys]
if (gc_block.modal.coord_select > N_COORDINATE_SYSTEM) {
FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS);
} // [Greater than N sys]
if (gc_state.modal.coord_select != gc_block.modal.coord_select) {
if (!(settings_read_coord_data(gc_block.modal.coord_select,block_coord_system))) { FAIL(STATUS_SETTING_READ_FAIL); }
if (!(settings_read_coord_data(gc_block.modal.coord_select, block_coord_system))) {
FAIL(STATUS_SETTING_READ_FAIL);
}
}
}
@ -524,23 +649,39 @@ uint8_t gc_execute_line(char *line)
// [G10 Errors]: L missing and is not 2 or 20. P word missing. (Negative P value done.)
// [G10 L2 Errors]: R word NOT SUPPORTED. P value not 0 to nCoordSys(max 9). Axis words missing.
// [G10 L20 Errors]: P must be 0 to nCoordSys(max 9). Axis words missing.
if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS) }; // [No axis words]
if (bit_isfalse(value_words,((1<<WORD_P)|(1<<WORD_L)))) { FAIL(STATUS_GCODE_VALUE_WORD_MISSING); } // [P/L word missing]
if (!axis_words) {
FAIL(STATUS_GCODE_NO_AXIS_WORDS)
}; // [No axis words]
if (bit_isfalse(value_words, ((1 << WORD_P) | (1 << WORD_L)))) {
FAIL(STATUS_GCODE_VALUE_WORD_MISSING);
} // [P/L word missing]
coord_select = trunc(gc_block.values.p); // Convert p value to int.
if (coord_select > N_COORDINATE_SYSTEM) { FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS); } // [Greater than N sys]
if (coord_select > N_COORDINATE_SYSTEM) {
FAIL(STATUS_GCODE_UNSUPPORTED_COORD_SYS);
} // [Greater than N sys]
if (gc_block.values.l != 20) {
if (gc_block.values.l == 2) {
if (bit_istrue(value_words,bit(WORD_R))) { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [G10 L2 R not supported]
} else { FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND); } // [Unsupported L]
if (bit_istrue(value_words, bit(WORD_R))) {
FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND);
} // [G10 L2 R not supported]
} else {
FAIL(STATUS_GCODE_UNSUPPORTED_COMMAND);
} // [Unsupported L]
}
bit_false(value_words, (bit(WORD_L) | bit(WORD_P)));
// Determine coordinate system to change and try to load from EEPROM.
if (coord_select > 0) { coord_select--; } // Adjust P1-P6 index to EEPROM coordinate data indexing.
else { coord_select = gc_block.modal.coord_select; } // Index P0 as the active coordinate system
if (coord_select > 0) {
coord_select--;
} // Adjust P1-P6 index to EEPROM coordinate data indexing.
else {
coord_select = gc_block.modal.coord_select;
} // Index P0 as the active coordinate system
// NOTE: Store parameter data in IJK values. By rule, they are not in use with this command.
if (!settings_read_coord_data(coord_select,gc_block.values.ijk)) { FAIL(STATUS_SETTING_READ_FAIL); } // [EEPROM read fail]
if (!settings_read_coord_data(coord_select, gc_block.values.ijk)) {
FAIL(STATUS_SETTING_READ_FAIL);
} // [EEPROM read fail]
// Pre-calculate the coordinate data changes.
for (idx = 0; idx < N_AXIS; idx++) { // Axes indices are consistent, so loop may be used.
@ -549,8 +690,11 @@ uint8_t gc_execute_line(char *line)
if (gc_block.values.l == 20) {
// L20: Update coordinate system axis at current position (with modifiers) with programmed value
// WPos = MPos - WCS - G92 - TLO -> WCS = MPos - G92 - TLO - WPos
gc_block.values.ijk[idx] = gc_state.position[idx]-gc_state.coord_offset[idx]-gc_block.values.xyz[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { gc_block.values.ijk[idx] -= gc_state.tool_length_offset; }
gc_block.values.ijk[idx] =
gc_state.position[idx] - gc_state.coord_offset[idx] - gc_block.values.xyz[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) {
gc_block.values.ijk[idx] -= gc_state.tool_length_offset;
}
} else {
// L2: Update coordinate system axis to programmed value.
gc_block.values.ijk[idx] = gc_block.values.xyz[idx];
@ -560,7 +704,9 @@ uint8_t gc_execute_line(char *line)
break;
case NON_MODAL_SET_COORDINATE_OFFSET:
// [G92 Errors]: No axis words.
if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words]
if (!axis_words) {
FAIL(STATUS_GCODE_NO_AXIS_WORDS);
} // [No axis words]
// 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.
@ -568,7 +714,9 @@ uint8_t gc_execute_line(char *line)
if (bit_istrue(axis_words, bit(idx))) {
// WPos = MPos - WCS - G92 - TLO -> G92 = MPos - WCS - TLO - WPos
gc_block.values.xyz[idx] = gc_state.position[idx] - block_coord_system[idx] - gc_block.values.xyz[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { gc_block.values.xyz[idx] -= gc_state.tool_length_offset; }
if (idx == TOOL_LENGTH_OFFSET_AXIS) {
gc_block.values.xyz[idx] -= gc_state.tool_length_offset;
}
} else {
gc_block.values.xyz[idx] = gc_state.coord_offset[idx];
}
@ -583,9 +731,11 @@ uint8_t gc_execute_line(char *line)
// NOTE: Tool offsets may be appended to these conversions when/if this feature is added.
if (axis_command != AXIS_COMMAND_TOOL_LENGTH_OFFSET) { // TLO block any axis command.
if (axis_words) {
for (idx=0; idx<N_AXIS; idx++) { // Axes indices are consistent, so loop may be used to save flash space.
for (idx = 0; idx < N_AXIS;
idx++) { // Axes indices are consistent, so loop may be used to save flash space.
if (bit_isfalse(axis_words, bit(idx))) {
gc_block.values.xyz[idx] = gc_state.position[idx]; // No axis word in block. Keep same axis position.
gc_block.values.xyz[idx] =
gc_state.position[idx]; // No axis word in block. Keep same axis position.
} else {
// Update specified value according to distance mode or ignore if absolute override is active.
// NOTE: G53 is never active with G28/30 since they are in the same modal group.
@ -593,7 +743,9 @@ uint8_t gc_execute_line(char *line)
// Apply coordinate offsets based on distance mode.
if (gc_block.modal.distance == DISTANCE_MODE_ABSOLUTE) {
gc_block.values.xyz[idx] += block_coord_system[idx] + gc_state.coord_offset[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { gc_block.values.xyz[idx] += gc_state.tool_length_offset; }
if (idx == TOOL_LENGTH_OFFSET_AXIS) {
gc_block.values.xyz[idx] += gc_state.tool_length_offset;
}
} else { // Incremental mode
gc_block.values.xyz[idx] += gc_state.position[idx];
}
@ -611,14 +763,20 @@ uint8_t gc_execute_line(char *line)
// Retreive G28/30 go-home position data (in machine coordinates) from EEPROM
// NOTE: Store parameter data in IJK values. By rule, they are not in use with this command.
if (gc_block.non_modal_command == NON_MODAL_GO_HOME_0) {
if (!settings_read_coord_data(SETTING_INDEX_G28,gc_block.values.ijk)) { FAIL(STATUS_SETTING_READ_FAIL); }
if (!settings_read_coord_data(SETTING_INDEX_G28, gc_block.values.ijk)) {
FAIL(STATUS_SETTING_READ_FAIL);
}
} else { // == NON_MODAL_GO_HOME_1
if (!settings_read_coord_data(SETTING_INDEX_G30,gc_block.values.ijk)) { FAIL(STATUS_SETTING_READ_FAIL); }
if (!settings_read_coord_data(SETTING_INDEX_G30, gc_block.values.ijk)) {
FAIL(STATUS_SETTING_READ_FAIL);
}
}
if (axis_words) {
// Move only the axes specified in secondary move.
for (idx = 0; idx < N_AXIS; idx++) {
if (!(axis_words & (1<<idx))) { gc_block.values.ijk[idx] = gc_state.position[idx]; }
if (!(axis_words & (1 << idx))) {
gc_block.values.ijk[idx] = gc_state.position[idx];
}
}
} else {
axis_command = AXIS_COMMAND_NONE; // Set to none if no intermediate motion.
@ -646,7 +804,9 @@ uint8_t gc_execute_line(char *line)
if (gc_block.modal.motion == MOTION_MODE_NONE) {
// [G80 Errors]: Axis word are programmed while G80 is active.
// NOTE: Even non-modal commands or TLO that use axis words will throw this strict error.
if (axis_words) { FAIL(STATUS_GCODE_AXIS_WORDS_EXIST); } // [No axis words allowed]
if (axis_words) {
FAIL(STATUS_GCODE_AXIS_WORDS_EXIST);
} // [No axis words allowed]
// Check remaining motion modes, if axis word are implicit (exist and not used by G10/28/30/92), or
// was explicitly commanded in the g-code block.
@ -655,51 +815,68 @@ uint8_t gc_execute_line(char *line)
if (gc_block.modal.motion == MOTION_MODE_SEEK) {
// [G0 Errors]: Axis letter not configured or without real value (done.)
// Axis words are optional. If missing, set axis command flag to ignore execution.
if (!axis_words) { axis_command = AXIS_COMMAND_NONE; }
if (!axis_words) {
axis_command = AXIS_COMMAND_NONE;
}
// All remaining motion modes (all but G0 and G80), require a valid feed rate value. In units per mm mode,
// the value must be positive. In inverse time mode, a positive value must be passed with each block.
} else {
// Check if feed rate is defined for the motion modes that require it.
if (gc_block.values.f == 0.0) { FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE); } // [Feed rate undefined]
if (gc_block.values.f == 0.0) {
FAIL(STATUS_GCODE_UNDEFINED_FEED_RATE);
} // [Feed rate undefined]
switch (gc_block.modal.motion) {
case MOTION_MODE_LINEAR:
// [G1 Errors]: Feed rate undefined. Axis letter not configured or without real value.
// Axis words are optional. If missing, set axis command flag to ignore execution.
if (!axis_words) { axis_command = AXIS_COMMAND_NONE; }
if (!axis_words) {
axis_command = AXIS_COMMAND_NONE;
}
break;
case MOTION_MODE_CW_ARC:
gc_parser_flags |= GC_PARSER_ARC_IS_CLOCKWISE; // No break intentional.
case MOTION_MODE_CW_ARC: gc_parser_flags |= GC_PARSER_ARC_IS_CLOCKWISE; // No break intentional.
case MOTION_MODE_CCW_ARC:
// [G2/3 Errors All-Modes]: Feed rate undefined.
// [G2/3 Radius-Mode Errors]: No axis words in selected plane. Target point is same as current.
// [G2/3 Offset-Mode Errors]: No axis words and/or offsets in selected plane. The radius to the current
// point and the radius to the target point differs more than 0.002mm (EMC def. 0.5mm OR 0.005mm and 0.1% radius).
// [G2/3 Full-Circle-Mode Errors]: NOT SUPPORTED. Axis words exist. No offsets programmed. P must be an integer.
// NOTE: Both radius and offsets are required for arc tracing and are pre-computed with the error-checking.
// point and the radius to the target point differs more than 0.002mm (EMC def. 0.5mm OR 0.005mm and
// 0.1% radius).
// [G2/3 Full-Circle-Mode Errors]: NOT SUPPORTED. Axis words exist. No offsets programmed. P must be an
// integer. NOTE: Both radius and offsets are required for arc tracing and are pre-computed with the
// error-checking.
if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words]
if (!(axis_words & (bit(axis_0)|bit(axis_1)))) { FAIL(STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE); } // [No axis words in plane]
if (!axis_words) {
FAIL(STATUS_GCODE_NO_AXIS_WORDS);
} // [No axis words]
if (!(axis_words & (bit(axis_0) | bit(axis_1)))) {
FAIL(STATUS_GCODE_NO_AXIS_WORDS_IN_PLANE);
} // [No axis words in plane]
// Calculate the change in position along each selected axis
float x, y;
x = gc_block.values.xyz[axis_0]-gc_state.position[axis_0]; // Delta x between current position and target
y = gc_block.values.xyz[axis_1]-gc_state.position[axis_1]; // Delta y between current position and target
x = gc_block.values.xyz[axis_0] -
gc_state.position[axis_0]; // Delta x between current position and target
y = gc_block.values.xyz[axis_1] -
gc_state.position[axis_1]; // Delta y between current position and target
if (value_words & bit(WORD_R)) { // Arc Radius Mode
bit_false(value_words, bit(WORD_R));
if (isequal_position_vector(gc_state.position, gc_block.values.xyz)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Invalid target]
if (isequal_position_vector(gc_state.position, gc_block.values.xyz)) {
FAIL(STATUS_GCODE_INVALID_TARGET);
} // [Invalid target]
// Convert radius value to proper units.
if (gc_block.modal.units == UNITS_MODE_INCHES) { gc_block.values.r *= MM_PER_INCH; }
if (gc_block.modal.units == UNITS_MODE_INCHES) {
gc_block.values.r *= MM_PER_INCH;
}
/* 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
set of equations where [x,y] is the vector from current to target position, d == magnitude of
that vector, h == hypotenuse of the triangle formed by the radius of the circle, the distance to
the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to the
length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form the new point
[i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc.
the center of the travel vector. A vector perpendicular to the travel vector [-y,x] is scaled to
the length of h [-y/d*h, x/d*h] and added to the center of the travel vector [x/2,y/2] to form
the new point [i,j] at [x/2-y/d*h, y/2+x/d*h] which will be the center of our arc.
d^2 == x^2 + y^2
h^2 == r^2 - (d/2)^2
@ -745,12 +922,16 @@ uint8_t gc_execute_line(char *line)
// than d. If so, the sqrt of a negative number is complex and error out.
float h_x2_div_d = 4.0 * gc_block.values.r * gc_block.values.r - x * x - y * y;
if (h_x2_div_d < 0) { FAIL(STATUS_GCODE_ARC_RADIUS_ERROR); } // [Arc radius error]
if (h_x2_div_d < 0) {
FAIL(STATUS_GCODE_ARC_RADIUS_ERROR);
} // [Arc radius error]
// Finish computing h_x2_div_d.
h_x2_div_d = -sqrt(h_x2_div_d) / hypot_f(x, y); // == -(h * 2 / d)
// Invert the sign of h_x2_div_d if the circle is counter clockwise (see sketch below)
if (gc_block.modal.motion == MOTION_MODE_CCW_ARC) { h_x2_div_d = -h_x2_div_d; }
if (gc_block.modal.motion == MOTION_MODE_CCW_ARC) {
h_x2_div_d = -h_x2_div_d;
}
/* The counter clockwise circle lies to the left of the target direction. When offset is positive,
the left hand circle will be generated - when it is negative the right hand circle is generated.
@ -758,19 +939,21 @@ uint8_t gc_execute_line(char *line)
T <-- Target position
^
Clockwise circles with this center | Clockwise circles with this center will have
will have > 180 deg of angular travel | < 180 deg of angular travel, which is a good thing!
Clockwise circles with this center | Clockwise circles with this center
will have will have > 180 deg of angular travel | < 180 deg of angular travel, which
is a good thing!
\ | /
center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d is negative
center of arc when h_x2_div_d is positive -> x <----- | -----> x <- center of arc when h_x2_div_d
is negative
|
|
C <-- Current position
*/
// Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go figure!),
// even though it is advised against ever generating such circles in a single line of g-code. By
// inverting the sign of h_x2_div_d the center of the circles is placed on the opposite side of the line of
// travel and thus we get the unadvisably long arcs as prescribed.
// Negative R is g-code-alese for "I want a circle with more than 180 degrees of travel" (go
// figure!), even though it is advised against ever generating such circles in a single line of
// g-code. By inverting the sign of h_x2_div_d the center of the circles is placed on the opposite
// side of the line of travel and thus we get the unadvisably long arcs as prescribed.
if (gc_block.values.r < 0) {
h_x2_div_d = -h_x2_div_d;
gc_block.values.r = -gc_block.values.r; // Finished with r. Set to positive for mc_arc
@ -780,13 +963,18 @@ uint8_t gc_execute_line(char *line)
gc_block.values.ijk[axis_1] = 0.5 * (y + (x * h_x2_div_d));
} else { // Arc Center Format Offset Mode
if (!(ijk_words & (bit(axis_0)|bit(axis_1)))) { FAIL(STATUS_GCODE_NO_OFFSETS_IN_PLANE); } // [No offsets in plane]
if (!(ijk_words & (bit(axis_0) | bit(axis_1)))) {
FAIL(STATUS_GCODE_NO_OFFSETS_IN_PLANE);
} // [No offsets in plane]
bit_false(value_words, (bit(WORD_I) | bit(WORD_J) | bit(WORD_K)));
// Convert IJK values to proper units.
if (gc_block.modal.units == UNITS_MODE_INCHES) {
for (idx=0; idx<N_AXIS; idx++) { // Axes indices are consistent, so loop may be used to save flash space.
if (ijk_words & bit(idx)) { gc_block.values.ijk[idx] *= MM_PER_INCH; }
for (idx = 0; idx < N_AXIS;
idx++) { // Axes indices are consistent, so loop may be used to save flash space.
if (ijk_words & bit(idx)) {
gc_block.values.ijk[idx] *= MM_PER_INCH;
}
}
}
@ -801,22 +989,34 @@ uint8_t gc_execute_line(char *line)
// Compute difference between current location and target radii for final error-checks.
float delta_r = fabs(target_r - gc_block.values.r);
if (delta_r > 0.005) {
if (delta_r > 0.5) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Arc definition error] > 0.5mm
if (delta_r > (0.001*gc_block.values.r)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Arc definition error] > 0.005mm AND 0.1% radius
if (delta_r > 0.5) {
FAIL(STATUS_GCODE_INVALID_TARGET);
} // [Arc definition error] > 0.5mm
if (delta_r > (0.001 * gc_block.values.r)) {
FAIL(STATUS_GCODE_INVALID_TARGET);
} // [Arc definition error] > 0.005mm AND 0.1% radius
}
}
break;
case MOTION_MODE_PROBE_TOWARD_NO_ERROR: case MOTION_MODE_PROBE_AWAY_NO_ERROR:
case MOTION_MODE_PROBE_TOWARD_NO_ERROR:
case MOTION_MODE_PROBE_AWAY_NO_ERROR:
gc_parser_flags |= GC_PARSER_PROBE_IS_NO_ERROR; // No break intentional.
case MOTION_MODE_PROBE_TOWARD: case MOTION_MODE_PROBE_AWAY:
case MOTION_MODE_PROBE_TOWARD:
case MOTION_MODE_PROBE_AWAY:
if ((gc_block.modal.motion == MOTION_MODE_PROBE_AWAY) ||
(gc_block.modal.motion == MOTION_MODE_PROBE_AWAY_NO_ERROR)) { gc_parser_flags |= GC_PARSER_PROBE_IS_AWAY; }
(gc_block.modal.motion == MOTION_MODE_PROBE_AWAY_NO_ERROR)) {
gc_parser_flags |= GC_PARSER_PROBE_IS_AWAY;
}
// [G38 Errors]: Target is same current. No axis words. Cutter compensation is enabled. Feed rate
// is undefined. Probe is triggered. NOTE: Probe check moved to probe cycle. Instead of returning
// an error, it issues an alarm to prevent further motion to the probe. It's also done there to
// allow the planner buffer to empty and move off the probe trigger before another probing cycle.
if (!axis_words) { FAIL(STATUS_GCODE_NO_AXIS_WORDS); } // [No axis words]
if (isequal_position_vector(gc_state.position, gc_block.values.xyz)) { FAIL(STATUS_GCODE_INVALID_TARGET); } // [Invalid target]
if (!axis_words) {
FAIL(STATUS_GCODE_NO_AXIS_WORDS);
} // [No axis words]
if (isequal_position_vector(gc_state.position, gc_block.values.xyz)) {
FAIL(STATUS_GCODE_INVALID_TARGET);
} // [Invalid target]
break;
}
}
@ -830,10 +1030,15 @@ uint8_t gc_execute_line(char *line)
// Jogging only uses the F feed rate and XYZ value words. N is valid, but S and T are invalid.
bit_false(value_words, (bit(WORD_N) | bit(WORD_F)));
} else {
bit_false(value_words,(bit(WORD_N)|bit(WORD_F)|bit(WORD_S)|bit(WORD_T))); // Remove single-meaning value words.
bit_false(value_words,
(bit(WORD_N) | bit(WORD_F) | bit(WORD_S) | bit(WORD_T))); // Remove single-meaning value words.
}
if (axis_command) { bit_false(value_words,(bit(WORD_X)|bit(WORD_Y)|bit(WORD_Z))); } // Remove axis words.
if (value_words) { FAIL(STATUS_GCODE_UNUSED_WORDS); } // [Unused words]
if (axis_command) {
bit_false(value_words, (bit(WORD_X) | bit(WORD_Y) | bit(WORD_Z)));
} // Remove axis words.
if (value_words) {
FAIL(STATUS_GCODE_UNUSED_WORDS);
} // [Unused words]
/* -------------------------------------------------------------------------------------
STEP 4: EXECUTE!!
@ -853,22 +1058,29 @@ uint8_t gc_execute_line(char *line)
if (gc_parser_flags & GC_PARSER_JOG_MOTION) {
// Only distance and unit modal commands and G53 absolute override command are allowed.
// NOTE: Feed rate word and axis word checks have already been performed in STEP 3.
if (command_words & ~(bit(MODAL_GROUP_G3) | bit(MODAL_GROUP_G6) | bit(MODAL_GROUP_G0)) ) { FAIL(STATUS_INVALID_JOG_COMMAND) };
if (!(gc_block.non_modal_command == NON_MODAL_ABSOLUTE_OVERRIDE || gc_block.non_modal_command == NON_MODAL_NO_ACTION)) { FAIL(STATUS_INVALID_JOG_COMMAND); }
if (command_words & ~(bit(MODAL_GROUP_G3) | bit(MODAL_GROUP_G6) | bit(MODAL_GROUP_G0))) {
FAIL(STATUS_INVALID_JOG_COMMAND)
};
if (!(gc_block.non_modal_command == NON_MODAL_ABSOLUTE_OVERRIDE ||
gc_block.non_modal_command == NON_MODAL_NO_ACTION)) {
FAIL(STATUS_INVALID_JOG_COMMAND);
}
// Initialize planner data to current spindle and coolant modal state.
pl_data->spindle_speed = gc_state.spindle_speed;
plan_data.condition = (gc_state.modal.spindle | gc_state.modal.coolant);
uint8_t status = jog_execute(&plan_data, &gc_block);
if (status == STATUS_OK) { memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); }
if (status == STATUS_OK) {
memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz));
}
return (status);
}
// If in laser mode, setup laser power based on current and past parser conditions.
if (bit_istrue(settings.flags, BITFLAG_LASER_MODE)) {
if ( !((gc_block.modal.motion == MOTION_MODE_LINEAR) || (gc_block.modal.motion == MOTION_MODE_CW_ARC)
|| (gc_block.modal.motion == MOTION_MODE_CCW_ARC)) ) {
if (!((gc_block.modal.motion == MOTION_MODE_LINEAR) || (gc_block.modal.motion == MOTION_MODE_CW_ARC) ||
(gc_block.modal.motion == MOTION_MODE_CCW_ARC))) {
gc_parser_flags |= GC_PARSER_LASER_DISABLE;
}
@ -881,8 +1093,8 @@ uint8_t gc_execute_line(char *line)
// M3 constant power laser requires planner syncs to update the laser when changing between
// a G1/2/3 motion mode state and vice versa when there is no motion in the line.
if (gc_state.modal.spindle == SPINDLE_ENABLE_CW) {
if ((gc_state.modal.motion == MOTION_MODE_LINEAR) || (gc_state.modal.motion == MOTION_MODE_CW_ARC)
|| (gc_state.modal.motion == MOTION_MODE_CCW_ARC)) {
if ((gc_state.modal.motion == MOTION_MODE_LINEAR) || (gc_state.modal.motion == MOTION_MODE_CW_ARC) ||
(gc_state.modal.motion == MOTION_MODE_CCW_ARC)) {
if (bit_istrue(gc_parser_flags, GC_PARSER_LASER_DISABLE)) {
gc_parser_flags |= GC_PARSER_LASER_FORCE_SYNC; // Change from G1/2/3 motion mode.
}
@ -907,7 +1119,9 @@ uint8_t gc_execute_line(char *line)
// [2. Set feed rate mode ]:
gc_state.modal.feed_rate = gc_block.modal.feed_rate;
if (gc_state.modal.feed_rate) { pl_data->condition |= PL_COND_FLAG_INVERSE_TIME; } // 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.
@ -920,7 +1134,9 @@ uint8_t gc_execute_line(char *line)
if (bit_isfalse(gc_parser_flags, GC_PARSER_LASER_ISMOTION)) {
if (bit_istrue(gc_parser_flags, GC_PARSER_LASER_DISABLE)) {
spindle_sync(gc_state.modal.spindle, 0.0);
} else { spindle_sync(gc_state.modal.spindle, gc_block.values.s); }
} else {
spindle_sync(gc_state.modal.spindle, gc_block.values.s);
}
}
#else
spindle_sync(gc_state.modal.spindle, 0.0);
@ -966,7 +1182,9 @@ uint8_t gc_execute_line(char *line)
#endif
// [10. Dwell ]:
if (gc_block.non_modal_command == NON_MODAL_DWELL) { mc_dwell(gc_block.values.p); }
if (gc_block.non_modal_command == NON_MODAL_DWELL) {
mc_dwell(gc_block.values.p);
}
// [11. Set active plane ]:
gc_state.modal.plane_select = gc_block.modal.plane_select;
@ -1017,20 +1235,19 @@ uint8_t gc_execute_line(char *line)
system_flag_wco_change();
}
break;
case NON_MODAL_GO_HOME_0: case NON_MODAL_GO_HOME_1:
case NON_MODAL_GO_HOME_0:
case NON_MODAL_GO_HOME_1:
// Move to intermediate position before going home. Obeys current coordinate system and offsets
// and absolute and incremental modes.
pl_data->condition |= PL_COND_FLAG_RAPID_MOTION; // Set rapid motion condition flag.
if (axis_command) { mc_line(gc_block.values.xyz, pl_data); }
if (axis_command) {
mc_line(gc_block.values.xyz, pl_data);
}
mc_line(gc_block.values.ijk, pl_data);
memcpy(gc_state.position, gc_block.values.ijk, N_AXIS * sizeof(float));
break;
case NON_MODAL_SET_HOME_0:
settings_write_coord_data(SETTING_INDEX_G28,gc_state.position);
break;
case NON_MODAL_SET_HOME_1:
settings_write_coord_data(SETTING_INDEX_G30,gc_state.position);
break;
case NON_MODAL_SET_HOME_0: settings_write_coord_data(SETTING_INDEX_G28, gc_state.position); break;
case NON_MODAL_SET_HOME_1: settings_write_coord_data(SETTING_INDEX_G30, gc_state.position); break;
case NON_MODAL_SET_COORDINATE_OFFSET:
memcpy(gc_state.coord_offset, gc_block.values.xyz, sizeof(gc_block.values.xyz));
system_flag_wco_change();
@ -1041,7 +1258,6 @@ uint8_t gc_execute_line(char *line)
break;
}
// [20. 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.
@ -1054,9 +1270,10 @@ uint8_t gc_execute_line(char *line)
} else if (gc_state.modal.motion == MOTION_MODE_SEEK) {
pl_data->condition |= PL_COND_FLAG_RAPID_MOTION; // Set rapid motion condition flag.
mc_line(gc_block.values.xyz, pl_data);
} else if ((gc_state.modal.motion == MOTION_MODE_CW_ARC) || (gc_state.modal.motion == MOTION_MODE_CCW_ARC)) {
mc_arc(gc_block.values.xyz, pl_data, gc_state.position, gc_block.values.ijk, gc_block.values.r,
axis_0, axis_1, axis_linear, bit_istrue(gc_parser_flags,GC_PARSER_ARC_IS_CLOCKWISE));
} else if ((gc_state.modal.motion == MOTION_MODE_CW_ARC) ||
(gc_state.modal.motion == MOTION_MODE_CCW_ARC)) {
mc_arc(gc_block.values.xyz, pl_data, gc_state.position, gc_block.values.ijk, gc_block.values.r, axis_0,
axis_1, axis_linear, bit_istrue(gc_parser_flags, GC_PARSER_ARC_IS_CLOCKWISE));
} else {
// NOTE: gc_block.values.xyz is returned from mc_probe_cycle with the updated position value. So
// upon a successful probing cycle, the machine position and the returned value should be the same.
@ -1070,7 +1287,8 @@ uint8_t gc_execute_line(char *line)
// motion control system might still be processing the action and the real tool position
// in any intermediate location.
if (gc_update_pos == GC_UPDATE_POS_TARGET) {
memcpy(gc_state.position, gc_block.values.xyz, sizeof(gc_block.values.xyz)); // gc_state.position[] = gc_block.values.xyz[]
memcpy(gc_state.position, gc_block.values.xyz,
sizeof(gc_block.values.xyz)); // gc_state.position[] = gc_block.values.xyz[]
} else if (gc_update_pos == GC_UPDATE_POS_SYSTEM) {
gc_sync_position(); // gc_state.position[] = sys_position
} // == GC_UPDATE_POS_NONE
@ -1117,7 +1335,9 @@ uint8_t gc_execute_line(char *line)
// Execute coordinate change and spindle/coolant stop.
if (sys.state != STATE_CHECK_MODE) {
if (!(settings_read_coord_data(gc_state.modal.coord_select,gc_state.coord_system))) { FAIL(STATUS_SETTING_READ_FAIL); }
if (!(settings_read_coord_data(gc_state.modal.coord_select, gc_state.coord_system))) {
FAIL(STATUS_SETTING_READ_FAIL);
}
system_flag_wco_change(); // Set to refresh immediately just in case something altered.
spindle_set_state(SPINDLE_DISABLE, 0.0);
coolant_set_state(COOLANT_DISABLE);
@ -1132,7 +1352,6 @@ uint8_t gc_execute_line(char *line)
return (STATUS_OK);
}
/*
Not supported:

View file

@ -22,7 +22,6 @@
#ifndef gcode_h
#define gcode_h
// 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
@ -177,7 +176,6 @@
#define GC_PARSER_LASER_DISABLE bit(6)
#define GC_PARSER_LASER_ISMOTION bit(7)
// NOTE: When this struct is zeroed, the above defines set the defaults for the system.
typedef struct {
uint8_t motion; // {G0,G1,G2,G3,G38.2,G80}
@ -209,7 +207,6 @@ typedef struct {
float xyz[3]; // X,Y,Z Translational axes
} gc_values_t;
typedef struct {
gc_modal_t modal;
@ -226,8 +223,8 @@ typedef struct {
// machine zero in mm. Non-persistent. Cleared upon reset and boot.
float tool_length_offset; // Tracks tool length offset value when enabled.
} parser_state_t;
extern parser_state_t gc_state;
extern parser_state_t gc_state;
typedef struct {
uint8_t non_modal_command;
@ -235,7 +232,6 @@ typedef struct {
gc_values_t values;
} parser_block_t;
// Initialize the parser
void gc_init();

View file

@ -26,40 +26,39 @@
#define GRBL_VERSION_BUILD "20190830"
// Define standard libraries used by Grbl.
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <util/delay.h>
#include <math.h>
#include <inttypes.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <util/delay.h>
// Define the Grbl system include files. NOTE: Do not alter organization.
#include "config.h"
#include "nuts_bolts.h"
#include "settings.h"
#include "system.h"
#include "defaults.h"
#include "cpu_map.h"
#include "planner.h"
#include "coolant_control.h"
#include "cpu_map.h"
#include "defaults.h"
#include "eeprom.h"
#include "gcode.h"
#include "jog.h"
#include "limits.h"
#include "motion_control.h"
#include "nuts_bolts.h"
#include "planner.h"
#include "print.h"
#include "probe.h"
#include "protocol.h"
#include "report.h"
#include "serial.h"
#include "settings.h"
#include "spindle_control.h"
#include "stepper.h"
#include "jog.h"
#include "system.h"
// ---------------------------------------------------------------------------------------
// COMPILE-TIME ERROR CHECKING OF DEFINE VALUES:

View file

@ -20,10 +20,8 @@
#include "grbl.h"
// Sets up valid jog motion received from g-code parser, checks for soft-limits, and executes the jog.
uint8_t jog_execute(plan_line_data_t *pl_data, parser_block_t *gc_block)
{
uint8_t jog_execute(plan_line_data_t *pl_data, parser_block_t *gc_block) {
// Initialize planner data struct for jogging motions.
// NOTE: Spindle and coolant are allowed to fully function with overrides during a jog.
pl_data->feed_rate = gc_block->values.f;
@ -33,7 +31,9 @@ uint8_t jog_execute(plan_line_data_t *pl_data, parser_block_t *gc_block)
#endif
if (bit_istrue(settings.flags, BITFLAG_SOFT_LIMIT_ENABLE)) {
if (system_check_travel_limits(gc_block->values.xyz)) { return(STATUS_TRAVEL_EXCEEDED); }
if (system_check_travel_limits(gc_block->values.xyz)) {
return (STATUS_TRAVEL_EXCEEDED);
}
}
// Valid jog command. Plan, set state, and execute.

View file

@ -21,7 +21,6 @@
#include "grbl.h"
// Homing axis search distance multiplier. Computed by this value times the cycle travel.
#ifndef HOMING_AXIS_SEARCH_SCALAR
#define HOMING_AXIS_SEARCH_SCALAR 1.5 // Must be > 1 to ensure limit switch will be engaged.
@ -38,8 +37,7 @@
#define DUAL_AXIS_CHECK_TRIGGER_2 bit(2)
#endif
void limits_init()
{
void limits_init() {
LIMIT_DDR &= ~(LIMIT_MASK); // Set as input pins
#ifdef DISABLE_LIMIT_PIN_PULL_UP
@ -62,39 +60,40 @@ void limits_init()
#endif
}
// Disables hard limits.
void limits_disable()
{
void limits_disable() {
LIMIT_PCMSK &= ~LIMIT_MASK; // Disable specific pins of the Pin Change Interrupt
PCICR &= ~(1 << LIMIT_INT); // Disable Pin Change Interrupt
}
// Returns limit state as a bit-wise uint8 variable. Each bit indicates an axis limit, where
// triggered is 1 and not triggered is 0. Invert mask is applied. Axes are defined by their
// number in bit position, i.e. Z_AXIS is (1<<2) or bit 2, and Y_AXIS is (1<<1) or bit 1.
uint8_t limits_get_state()
{
uint8_t limits_get_state() {
uint8_t limit_state = 0;
uint8_t pin = (LIMIT_PIN & LIMIT_MASK);
#ifdef INVERT_LIMIT_PIN_MASK
pin ^= INVERT_LIMIT_PIN_MASK;
#endif
if (bit_isfalse(settings.flags,BITFLAG_INVERT_LIMIT_PINS)) { pin ^= LIMIT_MASK; }
if (bit_isfalse(settings.flags, BITFLAG_INVERT_LIMIT_PINS)) {
pin ^= LIMIT_MASK;
}
if (pin) {
uint8_t idx;
for (idx = 0; idx < N_AXIS; idx++) {
if (pin & get_limit_pin_mask(idx)) { limit_state |= (1 << idx); }
if (pin & get_limit_pin_mask(idx)) {
limit_state |= (1 << idx);
}
}
#ifdef ENABLE_DUAL_AXIS
if (pin & (1<<DUAL_LIMIT_BIT)) { limit_state |= (1 << N_AXIS); }
if (pin & (1 << DUAL_LIMIT_BIT)) {
limit_state |= (1 << N_AXIS);
}
#endif
}
return (limit_state);
}
// This is the Limit Pin Change Interrupt, which handles the hard limit feature. A bouncing
// limit switch can cause a lot of problems, like false readings and multiple interrupt calls.
// If a switch is triggered at all, something bad has happened and treat it as such, regardless
@ -131,7 +130,12 @@ uint8_t limits_get_state()
}
#else // OPTIONAL: Software debounce limit pin routine.
// Upon limit pin change, enable watchdog timer to create a short delay.
ISR(LIMIT_INT_vect) { if (!(WDTCSR & (1<<WDIE))) { WDTCSR |= (1<<WDIE); } }
ISR(LIMIT_INT_vect) {
if (!(WDTCSR & (1 << WDIE))) {
WDTCSR |= (1 << WDIE);
}
}
ISR(WDT_vect) // Watchdog timer ISR
{
WDTCSR &= ~(1 << WDIE); // Disable watchdog timer.
@ -154,9 +158,10 @@ uint8_t limits_get_state()
// circumvent the processes for executing motions in normal operation.
// NOTE: Only the abort realtime command can interrupt this process.
// TODO: Move limit pin-specific calls to a general function for portability.
void limits_go_home(uint8_t cycle_mask)
{
if (sys.abort) { return; } // Block if system reset has been issued.
void limits_go_home(uint8_t cycle_mask) {
if (sys.abort) {
return;
} // Block if system reset has been issued.
// Initialize plan data struct for homing motion. Spindle and coolant are disabled.
plan_line_data_t plan_data;
@ -182,7 +187,8 @@ void limits_go_home(uint8_t cycle_mask)
fail_distance = min(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MAX);
fail_distance = max(fail_distance, DUAL_AXIS_HOMING_FAIL_DISTANCE_MIN);
int32_t dual_fail_distance = trunc(fail_distance * settings.steps_per_mm[DUAL_AXIS_SELECT]);
// int32_t dual_fail_distance = trunc((DUAL_AXIS_HOMING_TRIGGER_FAIL_DISTANCE)*settings.steps_per_mm[DUAL_AXIS_SELECT]);
// int32_t dual_fail_distance =
// trunc((DUAL_AXIS_HOMING_TRIGGER_FAIL_DISTANCE)*settings.steps_per_mm[DUAL_AXIS_SELECT]);
#endif
float target[N_AXIS];
float max_travel = 0.0;
@ -191,7 +197,9 @@ void limits_go_home(uint8_t cycle_mask)
// Initialize step pin masks
step_pin[idx] = get_step_pin_mask(idx);
#ifdef COREXY
if ((idx==A_MOTOR)||(idx==B_MOTOR)) { step_pin[idx] = (get_step_pin_mask(X_AXIS)|get_step_pin_mask(Y_AXIS)); }
if ((idx == A_MOTOR) || (idx == B_MOTOR)) {
step_pin[idx] = (get_step_pin_mask(X_AXIS) | get_step_pin_mask(Y_AXIS));
}
#endif
if (bit_istrue(cycle_mask, bit(idx))) {
@ -242,19 +250,26 @@ void limits_go_home(uint8_t cycle_mask)
// Set target direction based on cycle mask and homing cycle approach state.
// NOTE: This happens to compile smaller than any other implementation tried.
if (bit_istrue(settings.homing_dir_mask, bit(idx))) {
if (approach) { target[idx] = -max_travel; }
else { target[idx] = max_travel; }
if (approach) {
target[idx] = -max_travel;
} else {
if (approach) { target[idx] = max_travel; }
else { target[idx] = -max_travel; }
target[idx] = max_travel;
}
} else {
if (approach) {
target[idx] = max_travel;
} else {
target[idx] = -max_travel;
}
}
// Apply axislock to the step port pins active in this cycle.
axislock |= step_pin[idx];
#ifdef ENABLE_DUAL_AXIS
if (idx == DUAL_AXIS_SELECT) { sys.homing_axis_lock_dual = step_pin_dual; }
if (idx == DUAL_AXIS_SELECT) {
sys.homing_axis_lock_dual = step_pin_dual;
}
#endif
}
}
homing_rate *= sqrt(n_active_axis); // [sqrt(N_AXIS)] Adjust so individual axes all move at homing rate.
sys.homing_axis_lock = axislock;
@ -274,12 +289,17 @@ void limits_go_home(uint8_t cycle_mask)
if (axislock & step_pin[idx]) {
if (limit_state & (1 << idx)) {
#ifdef COREXY
if (idx==Z_AXIS) { axislock &= ~(step_pin[Z_AXIS]); }
else { axislock &= ~(step_pin[A_MOTOR]|step_pin[B_MOTOR]); }
if (idx == Z_AXIS) {
axislock &= ~(step_pin[Z_AXIS]);
} else {
axislock &= ~(step_pin[A_MOTOR] | step_pin[B_MOTOR]);
}
#else
axislock &= ~(step_pin[idx]);
#ifdef ENABLE_DUAL_AXIS
if (idx == DUAL_AXIS_SELECT) { dual_axis_async_check |= DUAL_AXIS_CHECK_TRIGGER_1; }
if (idx == DUAL_AXIS_SELECT) {
dual_axis_async_check |= DUAL_AXIS_CHECK_TRIGGER_1;
}
#endif
#endif
}
@ -294,10 +314,12 @@ void limits_go_home(uint8_t cycle_mask)
}
}
// When first dual axis limit triggers, record position and begin checking distance until other limit triggers. Bail upon failure.
// When first dual axis limit triggers, record position and begin checking distance until other limit
// triggers. Bail upon failure.
if (dual_axis_async_check) {
if (dual_axis_async_check & DUAL_AXIS_CHECK_ENABLE) {
if (( dual_axis_async_check & (DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) == (DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) {
if ((dual_axis_async_check & (DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) ==
(DUAL_AXIS_CHECK_TRIGGER_1 | DUAL_AXIS_CHECK_TRIGGER_2)) {
dual_axis_async_check = DUAL_AXIS_CHECK_DISABLE;
} else {
if (abs(dual_trigger_position - sys_position[DUAL_AXIS_SELECT]) > dual_fail_distance) {
@ -321,13 +343,21 @@ void limits_go_home(uint8_t cycle_mask)
if (sys_rt_exec_state & (EXEC_SAFETY_DOOR | EXEC_RESET | EXEC_CYCLE_STOP)) {
uint8_t rt_exec = sys_rt_exec_state;
// Homing failure condition: Reset issued during cycle.
if (rt_exec & EXEC_RESET) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
if (rt_exec & EXEC_RESET) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET);
}
// Homing failure condition: Safety door was opened.
if (rt_exec & EXEC_SAFETY_DOOR) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR); }
if (rt_exec & EXEC_SAFETY_DOOR) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_DOOR);
}
// Homing failure condition: Limit switch still engaged after pull-off motion
if (!approach && (limits_get_state() & cycle_mask)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF); }
if (!approach && (limits_get_state() & cycle_mask)) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_PULLOFF);
}
// Homing failure condition: Limit switch not found during approach.
if (approach && (rt_exec & EXEC_CYCLE_STOP)) { system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH); }
if (approach && (rt_exec & EXEC_CYCLE_STOP)) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_APPROACH);
}
if (sys_rt_exec_alarm) {
mc_reset(); // Stop motors, if they are running.
protocol_execute_realtime();
@ -377,7 +407,8 @@ void limits_go_home(uint8_t cycle_mask)
set_axis_position = 0;
#else
if (bit_istrue(settings.homing_dir_mask, bit(idx))) {
set_axis_position = lround((settings.max_travel[idx]+settings.homing_pulloff)*settings.steps_per_mm[idx]);
set_axis_position =
lround((settings.max_travel[idx] + settings.homing_pulloff) * settings.steps_per_mm[idx]);
} else {
set_axis_position = lround(-settings.homing_pulloff * settings.steps_per_mm[idx]);
}
@ -398,18 +429,15 @@ void limits_go_home(uint8_t cycle_mask)
#else
sys_position[idx] = set_axis_position;
#endif
}
}
sys.step_control = STEP_CONTROL_NORMAL_OP; // Return step control to normal operation.
}
// Performs a soft limit check. Called from mc_line() only. Assumes the machine has been homed,
// the workspace volume is in all negative space, and the system is in normal operation.
// NOTE: Used by jogging to limit travel within soft-limit volume.
void limits_soft_check(float *target)
{
void limits_soft_check(float *target) {
if (system_check_travel_limits(target)) {
sys.soft_limit = true;
// Force feed hold if cycle is active. All buffered blocks are guaranteed to be within
@ -419,7 +447,9 @@ void limits_soft_check(float *target)
system_set_exec_state_flag(EXEC_FEED_HOLD);
do {
protocol_execute_realtime();
if (sys.abort) { return; }
if (sys.abort) {
return;
}
} while (sys.state != STATE_IDLE);
}
mc_reset(); // Issue system reset and ensure spindle and coolant are shutdown.

View file

@ -22,7 +22,6 @@
#ifndef limits_h
#define limits_h
// Initialize the limits module
void limits_init();

View file

@ -21,23 +21,22 @@
#include "grbl.h"
// Declare system global variable structure
system_t sys;
int32_t sys_position[N_AXIS]; // Real-time machine (aka home) position vector in steps.
int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
volatile uint8_t
sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides.
volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
volatile uint8_t
sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
#ifdef DEBUG
volatile uint8_t sys_rt_exec_debug;
#endif
int main(void)
{
int main(void) {
// Initialize system upon power-up.
serial_init(); // Setup serial baud rate and interrupts
settings_init(); // Load Grbl settings from EEPROM
@ -63,7 +62,9 @@ int main(void)
// not after disabling the alarm locks. Prevents motion startup blocks from crashing into
// things uncontrollably. Very bad.
#ifdef HOMING_INIT_LOCK
if (bit_istrue(settings.flags,BITFLAG_HOMING_ENABLE)) { sys.state = STATE_ALARM; }
if (bit_istrue(settings.flags, BITFLAG_HOMING_ENABLE)) {
sys.state = STATE_ALARM;
}
#endif
// Grbl initialization loop upon power-up or a system abort. For the latter, all processes
@ -103,7 +104,6 @@ int main(void)
// Start Grbl main loop. Processes program inputs and executes them.
protocol_main_loop();
}
return 0; /* Never reached */
}

View file

@ -21,7 +21,6 @@
#include "grbl.h"
// 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.
@ -29,17 +28,20 @@
// segments, must pass through this routine before being passed to the planner. The seperation of
// mc_line and plan_buffer_line is done primarily to place non-planner-type functions from being
// in the planner and to let backlash compensation or canned cycle integration simple and direct.
void mc_line(float *target, plan_line_data_t *pl_data)
{
void mc_line(float *target, plan_line_data_t *pl_data) {
// If enabled, check for soft limit violations. Placed here all line motions are picked up
// from everywhere in Grbl.
if (bit_istrue(settings.flags, BITFLAG_SOFT_LIMIT_ENABLE)) {
// NOTE: Block jog state. Jogging is a special case and soft limits are handled independently.
if (sys.state != STATE_JOG) { limits_soft_check(target); }
if (sys.state != STATE_JOG) {
limits_soft_check(target);
}
}
// If in check gcode mode, prevent motion by blocking planner. Soft limits still work.
if (sys.state == STATE_CHECK_MODE) { return; }
if (sys.state == STATE_CHECK_MODE) {
return;
}
// NOTE: Backlash compensation may be installed here. It will need direction info to track when
// to insert a backlash line motion(s) before the intended line motion and will require its own
@ -59,9 +61,15 @@ void mc_line(float *target, plan_line_data_t *pl_data)
// Remain in this loop until there is room in the buffer.
do {
protocol_execute_realtime(); // Check for any run-time commands
if (sys.abort) { return; } // Bail, if system abort.
if ( plan_check_full_buffer() ) { protocol_auto_cycle_start(); } // Auto-cycle start when buffer is full.
else { break; }
if (sys.abort) {
return;
} // Bail, if system abort.
if (plan_check_full_buffer()) {
protocol_auto_cycle_start();
} // Auto-cycle start when buffer is full.
else {
break;
}
} while (1);
// Plan and queue motion into planner buffer
@ -76,7 +84,6 @@ void mc_line(float *target, plan_line_data_t *pl_data)
}
}
// Execute an arc in offset mode format. position == current xyz, target == target xyz,
// offset == offset from current xyz, axis_X defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, isclockwise boolean. Used
@ -84,9 +91,8 @@ void mc_line(float *target, plan_line_data_t *pl_data)
// The arc is approximated by generating a huge number of tiny, linear segments. The chordal tolerance
// of each segment is configured in settings.arc_tolerance, which is defined to be the maximum normal
// distance from segment to the circle when the end points both lie on the circle.
void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius,
uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc)
{
void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, uint8_t axis_0,
uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc) {
float center_axis0 = position[axis_0] + offset[axis_0];
float center_axis1 = position[axis_1] + offset[axis_1];
float r_axis0 = -offset[axis_0]; // Radius vector from center to current location
@ -97,9 +103,13 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of
// CCW angle between position and target from circle center. Only one atan2() trig computation required.
float angular_travel = atan2(r_axis0 * rt_axis1 - r_axis1 * rt_axis0, r_axis0 * rt_axis0 + r_axis1 * rt_axis1);
if (is_clockwise_arc) { // Correct atan2 output per direction
if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel -= 2*M_PI; }
if (angular_travel >= -ARC_ANGULAR_TRAVEL_EPSILON) {
angular_travel -= 2 * M_PI;
}
} else {
if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) { angular_travel += 2*M_PI; }
if (angular_travel <= ARC_ANGULAR_TRAVEL_EPSILON) {
angular_travel += 2 * M_PI;
}
}
// NOTE: Segment end points are on the arc, which can lead to the arc diameter being smaller by up to
@ -183,28 +193,28 @@ void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *of
mc_line(position, pl_data);
// Bail mid-circle on system abort. Runtime command check already performed by mc_line.
if (sys.abort) { return; }
if (sys.abort) {
return;
}
}
}
// Ensure last segment arrives at target location.
mc_line(target, pl_data);
}
// Execute dwell in seconds.
void mc_dwell(float seconds)
{
if (sys.state == STATE_CHECK_MODE) { return; }
void mc_dwell(float seconds) {
if (sys.state == STATE_CHECK_MODE) {
return;
}
protocol_buffer_synchronize();
delay_sec(seconds, DELAY_MODE_DWELL);
}
// Perform homing cycle to locate and set machine zero. Only '$H' executes this command.
// NOTE: There should be no motions in the buffer and Grbl must be in an idle state before
// executing the homing cycle. This prevents incorrect buffered plans after homing.
void mc_homing_cycle(uint8_t cycle_mask)
{
void mc_homing_cycle(uint8_t cycle_mask) {
// Check and abort homing cycle, if hard limits are already enabled. Helps prevent problems
// with machines with limits wired on both ends of travel to one limit pin.
// TODO: Move the pin-specific LIMIT_PIN call to limits.c as a function.
@ -222,7 +232,9 @@ void mc_homing_cycle(uint8_t cycle_mask)
// Perform homing routine. NOTE: Special motion case. Only system reset works.
#ifdef HOMING_SINGLE_AXIS_COMMANDS
if (cycle_mask) { limits_go_home(cycle_mask); } // Perform homing cycle based on mask.
if (cycle_mask) {
limits_go_home(cycle_mask);
} // Perform homing cycle based on mask.
else
#endif
{
@ -237,7 +249,9 @@ void mc_homing_cycle(uint8_t cycle_mask)
}
protocol_execute_realtime(); // Check for reset and set system abort.
if (sys.abort) { return; } // Did not complete. Alarm state set by mc_alarm.
if (sys.abort) {
return;
} // Did not complete. Alarm state set by mc_alarm.
// Homing cycle complete! Setup system for normal operation.
// -------------------------------------------------------------------------------------
@ -250,17 +264,19 @@ void mc_homing_cycle(uint8_t cycle_mask)
limits_init();
}
// Perform tool length probe cycle. Requires probe switch.
// NOTE: Upon probe failure, the program will be stopped and placed into ALARM state.
uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_flags)
{
uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_flags) {
// TODO: Need to update this cycle so it obeys a non-auto cycle start.
if (sys.state == STATE_CHECK_MODE) { return(GC_PROBE_CHECK_MODE); }
if (sys.state == STATE_CHECK_MODE) {
return (GC_PROBE_CHECK_MODE);
}
// Finish all queued commands and empty planner buffer before starting probe cycle.
protocol_buffer_synchronize();
if (sys.abort) { return(GC_PROBE_ABORT); } // Return if system reset has been issued.
if (sys.abort) {
return (GC_PROBE_ABORT);
} // Return if system reset has been issued.
// Initialize probing control variables
uint8_t is_probe_away = bit_istrue(parser_flags, GC_PARSER_PROBE_IS_AWAY);
@ -287,15 +303,20 @@ uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_
system_set_exec_state_flag(EXEC_CYCLE_START);
do {
protocol_execute_realtime();
if (sys.abort) { return(GC_PROBE_ABORT); } // Check for system abort
if (sys.abort) {
return (GC_PROBE_ABORT);
} // Check for system abort
} while (sys.state != STATE_IDLE);
// Probing cycle complete!
// Set state variables and error out, if the probe failed and cycle with error is enabled.
if (sys_probe_state == PROBE_ACTIVE) {
if (is_no_error) { memcpy(sys_probe_position, sys_position, sizeof(sys_position)); }
else { system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_CONTACT); }
if (is_no_error) {
memcpy(sys_probe_position, sys_position, sizeof(sys_position));
} else {
system_set_exec_alarm(EXEC_ALARM_PROBE_FAIL_CONTACT);
}
} else {
sys.probe_succeeded = true; // Indicate to system the probing cycle completed successfully.
}
@ -313,58 +334,62 @@ uint8_t mc_probe_cycle(float *target, plan_line_data_t *pl_data, uint8_t parser_
report_probe_parameters();
#endif
if (sys.probe_succeeded) { return(GC_PROBE_FOUND); } // Successful probe cycle.
else { return(GC_PROBE_FAIL_END); } // Failed to trigger probe within travel. With or without error.
if (sys.probe_succeeded) {
return (GC_PROBE_FOUND);
} // Successful probe cycle.
else {
return (GC_PROBE_FAIL_END);
} // Failed to trigger probe within travel. With or without error.
}
// Plans and executes the single special motion case for parking. Independent of main planner buffer.
// NOTE: Uses the always free planner ring buffer head to store motion parameters for execution.
#ifdef PARKING_ENABLE
void mc_parking_motion(float *parking_target, plan_line_data_t *pl_data)
{
if (sys.abort) { return; } // Block during abort.
void mc_parking_motion(float *parking_target, plan_line_data_t *pl_data) {
if (sys.abort) {
return;
} // Block during abort.
uint8_t plan_status = plan_buffer_line(parking_target, pl_data);
if (plan_status) {
bit_true(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION);
bit_false(sys.step_control, STEP_CONTROL_END_MOTION); // Allow parking motion to execute, if feed hold is active.
bit_false(sys.step_control,
STEP_CONTROL_END_MOTION); // Allow parking motion to execute, if feed hold is active.
st_parking_setup_buffer(); // Setup step segment buffer for special parking motion case
st_prep_buffer();
st_wake_up();
do {
protocol_exec_rt_system();
if (sys.abort) { return; }
if (sys.abort) {
return;
}
} while (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION);
st_parking_restore_buffer(); // Restore step segment buffer to normal run state.
} else {
bit_false(sys.step_control, STEP_CONTROL_EXECUTE_SYS_MOTION);
protocol_exec_rt_system();
}
}
#endif
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
void mc_override_ctrl_update(uint8_t override_state)
{
void mc_override_ctrl_update(uint8_t override_state) {
// Finish all queued commands before altering override control state
protocol_buffer_synchronize();
if (sys.abort) { return; }
if (sys.abort) {
return;
}
sys.override_ctrl = override_state;
}
#endif
// Method to ready the system to reset by setting the realtime reset command and killing any
// active processes in the system. This also checks if a system reset is issued while Grbl
// is in a motion state. If so, kills the steppers and sets the system alarm to flag position
// lost, since there was an abrupt uncontrolled deceleration. Called at an interrupt level by
// realtime abort command and hard limits. So, keep to a minimum.
void mc_reset()
{
void mc_reset() {
// Only this function can set the system reset. Helps prevent multiple kill calls.
if (bit_isfalse(sys_rt_exec_state, EXEC_RESET)) {
system_set_exec_state_flag(EXEC_RESET);
@ -380,8 +405,12 @@ void mc_reset()
if ((sys.state & (STATE_CYCLE | STATE_HOMING | STATE_JOG)) ||
(sys.step_control & (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION))) {
if (sys.state == STATE_HOMING) {
if (!sys_rt_exec_alarm) {system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET); }
} else { system_set_exec_alarm(EXEC_ALARM_ABORT_CYCLE); }
if (!sys_rt_exec_alarm) {
system_set_exec_alarm(EXEC_ALARM_HOMING_FAIL_RESET);
}
} else {
system_set_exec_alarm(EXEC_ALARM_ABORT_CYCLE);
}
st_go_idle(); // Force kill steppers. Position has likely been lost.
}
}

View file

@ -22,7 +22,6 @@
#ifndef motion_control_h
#define motion_control_h
// System motion commands must have a line number of zero.
#define HOMING_CYCLE_LINE_NUMBER 0
#define PARKING_MOTION_LINE_NUMBER 0
@ -32,7 +31,6 @@
#define HOMING_CYCLE_Y bit(Y_AXIS)
#define HOMING_CYCLE_Z bit(Z_AXIS)
// 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.
@ -42,8 +40,8 @@ void mc_line(float *target, plan_line_data_t *pl_data);
// offset == offset from current xyz, axis_XXX defines circle plane in tool space, axis_linear is
// the direction of helical travel, radius == circle radius, is_clockwise_arc boolean. Used
// for vector transformation direction.
void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius,
uint8_t axis_0, uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc);
void mc_arc(float *target, plan_line_data_t *pl_data, float *position, float *offset, float radius, uint8_t axis_0,
uint8_t axis_1, uint8_t axis_linear, uint8_t is_clockwise_arc);
// Dwell for a specific number of seconds
void mc_dwell(float seconds);

View file

@ -21,10 +21,8 @@
#include "grbl.h"
#define MAX_INT_DIGITS 8 // Maximum number of digits in int32 (and float)
// Extracts a floating point value from a string. The following code is based loosely on
// the avr-libc strtod() function by Michael Stumpf and Dmitry Xmelkov and many freely
// available conversion method examples, but has been highly optimized for Grbl. For known
@ -32,8 +30,7 @@
// Scientific notation is officially not supported by g-code, and the 'E' character may
// be a g-code word on some CNC systems. So, 'E' notation will not be recognized.
// NOTE: Thanks to Radu-Eosif Mihailescu for identifying the issues with using strtod().
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
{
uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr) {
char *ptr = line + *char_counter;
unsigned char c;
@ -59,10 +56,14 @@ uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
if (c <= 9) {
ndigit++;
if (ndigit <= MAX_INT_DIGITS) {
if (isdecimal) { exp--; }
if (isdecimal) {
exp--;
}
intval = (((intval << 2) + intval) << 1) + c; // intval*10 + c
} else {
if (!(isdecimal)) { exp++; } // Drop overflow digits
if (!(isdecimal)) {
exp++;
} // Drop overflow digits
}
} else if (c == (('.' - '0') & 0xff) && !(isdecimal)) {
isdecimal = true;
@ -73,7 +74,9 @@ uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
}
// Return if no digits have been read.
if (!ndigit) { return(false); };
if (!ndigit) {
return (false);
};
// Convert integer into floating point.
float fval;
@ -107,38 +110,38 @@ uint8_t read_float(char *line, uint8_t *char_counter, float *float_ptr)
return (true);
}
// Non-blocking delay function used for general operation and suspend features.
void delay_sec(float seconds, uint8_t mode)
{
void delay_sec(float seconds, uint8_t mode) {
uint16_t i = ceil(1000 / DWELL_TIME_STEP * seconds);
while (i-- > 0) {
if (sys.abort) { return; }
if (sys.abort) {
return;
}
if (mode == DELAY_MODE_DWELL) {
protocol_execute_realtime();
} else { // DELAY_MODE_SYS_SUSPEND
// Execute rt_system() only to avoid nesting suspend loops.
protocol_exec_rt_system();
if (sys.suspend & SUSPEND_RESTART_RETRACT) { return; } // Bail, if safety door reopens.
if (sys.suspend & SUSPEND_RESTART_RETRACT) {
return;
} // Bail, if safety door reopens.
}
_delay_ms(DWELL_TIME_STEP); // Delay DWELL_TIME_STEP increment
}
}
// Delays variable defined milliseconds. Compiler compatibility fix for _delay_ms(),
// which only accepts constants in future compiler releases.
void delay_ms(uint16_t ms)
{
while ( ms-- ) { _delay_ms(1); }
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. Written to perform more
// efficiently with larger delays, as the counter adds parasitic time in each iteration.
void delay_us(uint32_t us)
{
void delay_us(uint32_t us) {
while (us) {
if (us < 10) {
_delay_us(1);
@ -156,13 +159,12 @@ void delay_us(uint32_t us)
}
}
// Simple hypotenuse computation function.
float hypot_f(float x, float y) { return(sqrt(x*x + y*y)); }
float hypot_f(float x, float y) {
return (sqrt(x * x + y * y));
}
float convert_delta_vector_to_unit_vector(float *vector)
{
float convert_delta_vector_to_unit_vector(float *vector) {
uint8_t idx;
float magnitude = 0.0;
for (idx = 0; idx < N_AXIS; idx++) {
@ -172,13 +174,13 @@ float convert_delta_vector_to_unit_vector(float *vector)
}
magnitude = sqrt(magnitude);
float inv_magnitude = 1.0 / magnitude;
for (idx=0; idx<N_AXIS; idx++) { vector[idx] *= inv_magnitude; }
for (idx = 0; idx < N_AXIS; idx++) {
vector[idx] *= inv_magnitude;
}
return (magnitude);
}
float limit_value_by_axis_maximum(float *max_value, float *unit_vec)
{
float limit_value_by_axis_maximum(float *max_value, float *unit_vec) {
uint8_t idx;
float limit_value = SOME_LARGE_VALUE;
for (idx = 0; idx < N_AXIS; idx++) {

View file

@ -22,7 +22,6 @@
#include "grbl.h"
static plan_block_t block_buffer[BLOCK_BUFFER_SIZE]; // A ring buffer for motion instructions
static uint8_t block_buffer_tail; // Index of the block to process now
static uint8_t block_buffer_head; // Index of the next block to be pushed
@ -37,27 +36,27 @@ typedef struct {
float previous_unit_vec[N_AXIS]; // Unit vector of previous path line segment
float previous_nominal_speed; // Nominal speed of previous path line segment
} planner_t;
static planner_t pl;
// Returns the index of the next block in the ring buffer. Also called by stepper segment buffer.
uint8_t plan_next_block_index(uint8_t block_index)
{
uint8_t plan_next_block_index(uint8_t block_index) {
block_index++;
if (block_index == BLOCK_BUFFER_SIZE) { block_index = 0; }
if (block_index == BLOCK_BUFFER_SIZE) {
block_index = 0;
}
return (block_index);
}
// Returns the index of the previous block in the ring buffer
static uint8_t plan_prev_block_index(uint8_t block_index)
{
if (block_index == 0) { block_index = BLOCK_BUFFER_SIZE; }
static uint8_t plan_prev_block_index(uint8_t block_index) {
if (block_index == 0) {
block_index = BLOCK_BUFFER_SIZE;
}
block_index--;
return (block_index);
}
/* PLANNER SPEED DEFINITION
+--------+ <- current->nominal_speed
/ \
@ -123,13 +122,14 @@ static uint8_t plan_prev_block_index(uint8_t block_index)
ARM versions should have enough memory and speed for look-ahead blocks numbering up to a hundred or more.
*/
static void planner_recalculate()
{
static void planner_recalculate() {
// Initialize block index to the last block in the planner buffer.
uint8_t block_index = plan_prev_block_index(block_buffer_head);
// Bail. Can't do anything with one only one plan-able block.
if (block_index == block_buffer_planned) { return; }
if (block_index == block_buffer_planned) {
return;
}
// Reverse Pass: Coarsely maximize all possible deceleration curves back-planning from the last
// block in buffer. Cease planning when the last optimal planned or tail pointer is reached.
@ -144,7 +144,9 @@ static void planner_recalculate()
block_index = plan_prev_block_index(block_index);
if (block_index == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete.
// Check if the first block is the tail. If so, notify stepper to update its current parameters.
if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
if (block_index == block_buffer_tail) {
st_update_plan_block_parameters();
}
} else { // Three or more plan-able blocks
while (block_index != block_buffer_planned) {
next = current;
@ -152,7 +154,9 @@ static void planner_recalculate()
block_index = plan_prev_block_index(block_index);
// Check if next block is the tail block(=planned block). If so, update current stepper parameters.
if (block_index == block_buffer_tail) { st_update_plan_block_parameters(); }
if (block_index == block_buffer_tail) {
st_update_plan_block_parameters();
}
// Compute maximum entry speed decelerating over the current block from its exit speed.
if (current->entry_speed_sqr != current->max_entry_speed_sqr) {
@ -190,99 +194,101 @@ static void planner_recalculate()
// point in the buffer. When the plan is bracketed by either the beginning of the
// buffer and a maximum entry speed or two maximum entry speeds, every block in between
// cannot logically be further improved. Hence, we don't have to recompute them anymore.
if (next->entry_speed_sqr == next->max_entry_speed_sqr) { block_buffer_planned = block_index; }
if (next->entry_speed_sqr == next->max_entry_speed_sqr) {
block_buffer_planned = block_index;
}
block_index = plan_next_block_index(block_index);
}
}
void plan_reset()
{
void plan_reset() {
memset(&pl, 0, sizeof(planner_t)); // Clear planner struct
plan_reset_buffer();
}
void plan_reset_buffer()
{
void plan_reset_buffer() {
block_buffer_tail = 0;
block_buffer_head = 0; // Empty = tail
next_buffer_head = 1; // plan_next_block_index(block_buffer_head)
block_buffer_planned = 0; // = block_buffer_tail;
}
void plan_discard_current_block()
{
void plan_discard_current_block() {
if (block_buffer_head != block_buffer_tail) { // Discard non-empty buffer.
uint8_t block_index = plan_next_block_index(block_buffer_tail);
// Push block_buffer_planned pointer, if encountered.
if (block_buffer_tail == block_buffer_planned) { block_buffer_planned = block_index; }
if (block_buffer_tail == block_buffer_planned) {
block_buffer_planned = block_index;
}
block_buffer_tail = block_index;
}
}
// Returns address of planner buffer block used by system motions. Called by segment generator.
plan_block_t *plan_get_system_motion_block()
{
plan_block_t *plan_get_system_motion_block() {
return (&block_buffer[block_buffer_head]);
}
// Returns address of first planner block, if available. Called by various main program functions.
plan_block_t *plan_get_current_block()
{
if (block_buffer_head == block_buffer_tail) { return(NULL); } // Buffer empty
plan_block_t *plan_get_current_block() {
if (block_buffer_head == block_buffer_tail) {
return (NULL);
} // Buffer empty
return (&block_buffer[block_buffer_tail]);
}
float plan_get_exec_block_exit_speed_sqr()
{
float plan_get_exec_block_exit_speed_sqr() {
uint8_t block_index = plan_next_block_index(block_buffer_tail);
if (block_index == block_buffer_head) { return( 0.0 ); }
if (block_index == block_buffer_head) {
return (0.0);
}
return (block_buffer[block_index].entry_speed_sqr);
}
// Returns the availability status of the block ring buffer. True, if full.
uint8_t plan_check_full_buffer()
{
if (block_buffer_tail == next_buffer_head) { return(true); }
uint8_t plan_check_full_buffer() {
if (block_buffer_tail == next_buffer_head) {
return (true);
}
return (false);
}
// Computes and returns block nominal speed based on running condition and override values.
// NOTE: All system motion commands, such as homing/parking, are not subject to overrides.
float plan_compute_profile_nominal_speed(plan_block_t *block)
{
float plan_compute_profile_nominal_speed(plan_block_t *block) {
float nominal_speed = block->programmed_rate;
if (block->condition & PL_COND_FLAG_RAPID_MOTION) { nominal_speed *= (0.01*sys.r_override); }
else {
if (!(block->condition & PL_COND_FLAG_NO_FEED_OVERRIDE)) { nominal_speed *= (0.01*sys.f_override); }
if (nominal_speed > block->rapid_rate) { nominal_speed = block->rapid_rate; }
if (block->condition & PL_COND_FLAG_RAPID_MOTION) {
nominal_speed *= (0.01 * sys.r_override);
} else {
if (!(block->condition & PL_COND_FLAG_NO_FEED_OVERRIDE)) {
nominal_speed *= (0.01 * sys.f_override);
}
if (nominal_speed > block->rapid_rate) {
nominal_speed = block->rapid_rate;
}
}
if (nominal_speed > MINIMUM_FEED_RATE) {
return (nominal_speed);
}
if (nominal_speed > MINIMUM_FEED_RATE) { return(nominal_speed); }
return (MINIMUM_FEED_RATE);
}
// Computes and updates the max entry speed (sqr) of the block, based on the minimum of the junction's
// previous and current nominal speeds and max junction speed.
static void plan_compute_profile_parameters(plan_block_t *block, float nominal_speed, float prev_nominal_speed)
{
static void plan_compute_profile_parameters(plan_block_t *block, float nominal_speed, float prev_nominal_speed) {
// Compute the junction maximum entry based on the minimum of the junction speed and neighboring nominal speeds.
if (nominal_speed > prev_nominal_speed) { block->max_entry_speed_sqr = prev_nominal_speed*prev_nominal_speed; }
else { block->max_entry_speed_sqr = nominal_speed*nominal_speed; }
if (block->max_entry_speed_sqr > block->max_junction_speed_sqr) { block->max_entry_speed_sqr = block->max_junction_speed_sqr; }
if (nominal_speed > prev_nominal_speed) {
block->max_entry_speed_sqr = prev_nominal_speed * prev_nominal_speed;
} else {
block->max_entry_speed_sqr = nominal_speed * nominal_speed;
}
if (block->max_entry_speed_sqr > block->max_junction_speed_sqr) {
block->max_entry_speed_sqr = block->max_junction_speed_sqr;
}
}
// Re-calculates buffered motions profile parameters upon a motion-based override change.
void plan_update_velocity_profile_parameters()
{
void plan_update_velocity_profile_parameters() {
uint8_t block_index = block_buffer_tail;
plan_block_t *block;
float nominal_speed;
@ -297,7 +303,6 @@ void plan_update_velocity_profile_parameters()
pl.previous_nominal_speed = prev_nominal_speed; // Update prev nominal speed for next incoming block.
}
/* Add a new linear movement to the buffer. target[N_AXIS] 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.
@ -312,8 +317,7 @@ void plan_update_velocity_profile_parameters()
head. It avoids changing the planner state and preserves the buffer to ensure subsequent gcode
motions are still planned correctly, while the stepper module only points to the block buffer head
to execute the special system motion. */
uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
{
uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data) {
// Prepare and initialize new block. Copy relevant pl_data for block execution.
plan_block_t *block = &block_buffer[block_buffer_head];
memset(block, 0, sizeof(plan_block_t)); // Zero all block values.
@ -339,13 +343,17 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
#else
memcpy(position_steps, sys_position, sizeof(sys_position));
#endif
} else { memcpy(position_steps, pl.position, sizeof(pl.position)); }
} else {
memcpy(position_steps, pl.position, sizeof(pl.position));
}
#ifdef COREXY
target_steps[A_MOTOR] = lround(target[A_MOTOR] * settings.steps_per_mm[A_MOTOR]);
target_steps[B_MOTOR] = lround(target[B_MOTOR] * settings.steps_per_mm[B_MOTOR]);
block->steps[A_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) + (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
block->steps[B_MOTOR] = labs((target_steps[X_AXIS]-position_steps[X_AXIS]) - (target_steps[Y_AXIS]-position_steps[Y_AXIS]));
block->steps[A_MOTOR] =
labs((target_steps[X_AXIS] - position_steps[X_AXIS]) + (target_steps[Y_AXIS] - position_steps[Y_AXIS]));
block->steps[B_MOTOR] =
labs((target_steps[X_AXIS] - position_steps[X_AXIS]) - (target_steps[Y_AXIS] - position_steps[Y_AXIS]));
#endif
for (idx = 0; idx < N_AXIS; idx++) {
@ -359,9 +367,11 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
}
block->step_event_count = max(block->step_event_count, block->steps[idx]);
if (idx == A_MOTOR) {
delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] + target_steps[Y_AXIS]-position_steps[Y_AXIS])/settings.steps_per_mm[idx];
delta_mm = (target_steps[X_AXIS] - position_steps[X_AXIS] + target_steps[Y_AXIS] - position_steps[Y_AXIS]) /
settings.steps_per_mm[idx];
} else if (idx == B_MOTOR) {
delta_mm = (target_steps[X_AXIS]-position_steps[X_AXIS] - target_steps[Y_AXIS]+position_steps[Y_AXIS])/settings.steps_per_mm[idx];
delta_mm = (target_steps[X_AXIS] - position_steps[X_AXIS] - target_steps[Y_AXIS] + position_steps[Y_AXIS]) /
settings.steps_per_mm[idx];
} else {
delta_mm = (target_steps[idx] - position_steps[idx]) / settings.steps_per_mm[idx];
}
@ -374,11 +384,15 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
unit_vec[idx] = delta_mm; // Store unit vector numerator
// Set direction bits. Bit enabled always means direction is negative.
if (delta_mm < 0.0 ) { block->direction_bits |= get_direction_pin_mask(idx); }
if (delta_mm < 0.0) {
block->direction_bits |= get_direction_pin_mask(idx);
}
}
// Bail if this is a zero-length block. Highly unlikely to occur.
if (block->step_event_count == 0) { return(PLAN_EMPTY_BLOCK); }
if (block->step_event_count == 0) {
return (PLAN_EMPTY_BLOCK);
}
// Calculate the unit vector of the line move and the block maximum feed rate and acceleration scaled
// down such that no individual axes maximum values are exceeded with respect to the line direction.
@ -389,10 +403,13 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
block->rapid_rate = limit_value_by_axis_maximum(settings.max_rate, unit_vec);
// Store programmed rate.
if (block->condition & PL_COND_FLAG_RAPID_MOTION) { block->programmed_rate = block->rapid_rate; }
else {
if (block->condition & PL_COND_FLAG_RAPID_MOTION) {
block->programmed_rate = block->rapid_rate;
} else {
block->programmed_rate = pl_data->feed_rate;
if (block->condition & PL_COND_FLAG_INVERSE_TIME) { block->programmed_rate *= block->millimeters; }
if (block->condition & PL_COND_FLAG_INVERSE_TIME) {
block->programmed_rate *= block->millimeters;
}
}
// TODO: Need to check this method handling zero junction speeds when starting from rest.
@ -444,8 +461,10 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
} else {
convert_delta_vector_to_unit_vector(junction_unit_vec);
float junction_acceleration = limit_value_by_axis_maximum(settings.acceleration, junction_unit_vec);
float sin_theta_d2 = sqrt(0.5*(1.0-junction_cos_theta)); // Trig half angle identity. Always positive.
block->max_junction_speed_sqr = max( MINIMUM_JUNCTION_SPEED*MINIMUM_JUNCTION_SPEED,
float sin_theta_d2 =
sqrt(0.5 * (1.0 - junction_cos_theta)); // Trig half angle identity. Always positive.
block->max_junction_speed_sqr =
max(MINIMUM_JUNCTION_SPEED * MINIMUM_JUNCTION_SPEED,
(junction_acceleration * settings.junction_deviation * sin_theta_d2) / (1.0 - sin_theta_d2));
}
}
@ -471,10 +490,8 @@ uint8_t plan_buffer_line(float *target, plan_line_data_t *pl_data)
return (PLAN_OK);
}
// Reset the planner position vectors. Called by the system abort/initialization routine.
void plan_sync_position()
{
void plan_sync_position() {
// TODO: For motor configurations not in the same coordinate frame as the machine position,
// this function needs to be updated to accomodate the difference.
uint8_t idx;
@ -493,28 +510,26 @@ void plan_sync_position()
}
}
// Returns the number of available blocks are in the planner buffer.
uint8_t plan_get_block_buffer_available()
{
if (block_buffer_head >= block_buffer_tail) { return((BLOCK_BUFFER_SIZE-1)-(block_buffer_head-block_buffer_tail)); }
uint8_t plan_get_block_buffer_available() {
if (block_buffer_head >= block_buffer_tail) {
return ((BLOCK_BUFFER_SIZE - 1) - (block_buffer_head - block_buffer_tail));
}
return ((block_buffer_tail - block_buffer_head - 1));
}
// Returns the number of active blocks are in the planner buffer.
// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h
uint8_t plan_get_block_buffer_count()
{
if (block_buffer_head >= block_buffer_tail) { return(block_buffer_head-block_buffer_tail); }
uint8_t plan_get_block_buffer_count() {
if (block_buffer_head >= block_buffer_tail) {
return (block_buffer_head - block_buffer_tail);
}
return (BLOCK_BUFFER_SIZE - (block_buffer_tail - block_buffer_head));
}
// Re-initialize buffer plan with a partially completed block, assumed to exist at the buffer tail.
// Called after a steppers have come to a complete stop for a feed hold and the cycle is stopped.
void plan_cycle_reinitialize()
{
void plan_cycle_reinitialize() {
// Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer.
st_update_plan_block_parameters();
block_buffer_planned = block_buffer_tail;

View file

@ -22,7 +22,6 @@
#ifndef planner_h
#define planner_h
// The number of linear motions that can be in the plan at any give time
#ifndef BLOCK_BUFFER_SIZE
#ifdef USE_LINE_NUMBERS
@ -47,8 +46,8 @@
#define PL_COND_FLAG_COOLANT_MIST bit(7)
#define PL_COND_MOTION_MASK (PL_COND_FLAG_RAPID_MOTION | PL_COND_FLAG_SYSTEM_MOTION | PL_COND_FLAG_NO_FEED_OVERRIDE)
#define PL_COND_SPINDLE_MASK (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)
#define PL_COND_ACCESSORY_MASK (PL_COND_FLAG_SPINDLE_CW|PL_COND_FLAG_SPINDLE_CCW|PL_COND_FLAG_COOLANT_FLOOD|PL_COND_FLAG_COOLANT_MIST)
#define PL_COND_ACCESSORY_MASK \
(PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW | PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_MIST)
// This struct stores a linear movement of a g-code block motion with its critical "nominal" values
// are as specified in the source g-code.
@ -85,7 +84,6 @@ typedef struct {
#endif
} plan_block_t;
// Planner data prototype. Must be used when passing new motions to the planner.
typedef struct {
float feed_rate; // Desired feed rate for line motion. Value is ignored, if rapid motion.
@ -96,7 +94,6 @@ typedef struct {
#endif
} plan_line_data_t;
// Initialize and reset the motion plan subsystem
void plan_reset(); // Reset all
void plan_reset_buffer(); // Reset buffer only.
@ -146,5 +143,4 @@ uint8_t plan_check_full_buffer();
void plan_get_planner_mpos(float *target);
#endif

View file

@ -21,23 +21,16 @@
#include "grbl.h"
void printString(const char *s)
{
while (*s)
serial_write(*s++);
void printString(const char *s) {
while (*s) serial_write(*s++);
}
// Print a string stored in PGM-memory
void printPgmString(const char *s)
{
void printPgmString(const char *s) {
char c;
while ((c = pgm_read_byte_near(s++)))
serial_write(c);
while ((c = pgm_read_byte_near(s++))) serial_write(c);
}
// void printIntegerInBase(unsigned long n, unsigned long base)
// {
// unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars.
@ -59,10 +52,8 @@ void printPgmString(const char *s)
// 'A' + buf[i - 1] - 10);
// }
// Prints an uint8 variable in base 10.
void print_uint8_base10(uint8_t n)
{
void print_uint8_base10(uint8_t n) {
uint8_t digit_a = 0;
uint8_t digit_b = 0;
if (n >= 100) { // 100-255
@ -74,10 +65,13 @@ void print_uint8_base10(uint8_t n)
n /= 10;
}
serial_write('0' + n);
if (digit_b) { serial_write(digit_b); }
if (digit_a) { serial_write(digit_a); }
if (digit_b) {
serial_write(digit_b);
}
if (digit_a) {
serial_write(digit_a);
}
}
// Prints an uint8 variable in base 2 with desired number of desired digits.
void print_uint8_base2_ndigit(uint8_t n, uint8_t digits) {
@ -89,13 +83,10 @@ void print_uint8_base2_ndigit(uint8_t n, uint8_t digits) {
n /= 2;
}
for (; i > 0; i--)
serial_write('0' + buf[i - 1]);
for (; i > 0; i--) serial_write('0' + buf[i - 1]);
}
void print_uint32_base10(uint32_t n)
{
void print_uint32_base10(uint32_t n) {
if (n == 0) {
serial_write('0');
return;
@ -109,13 +100,10 @@ void print_uint32_base10(uint32_t n)
n /= 10;
}
for (; i > 0; i--)
serial_write('0' + buf[i-1]);
for (; i > 0; i--) serial_write('0' + buf[i - 1]);
}
void printInteger(long n)
{
void printInteger(long n) {
if (n < 0) {
serial_write('-');
print_uint32_base10(-n);
@ -124,14 +112,12 @@ void printInteger(long n)
}
}
// Convert float to string by immediately converting to a long integer, which contains
// more digits than a float. Number of decimal places, which are tracked by a counter,
// may be set by the user. The integer is then efficiently converted to a string.
// NOTE: AVR '%' and '/' integer operations are very efficient. Bitshifting speed-up
// techniques are actually just slightly slower. Found this out the hard way.
void printFloat(float n, uint8_t decimal_places)
{
void printFloat(float n, uint8_t decimal_places) {
if (n < 0) {
serial_write('-');
n = -n;
@ -142,7 +128,9 @@ void printFloat(float n, uint8_t decimal_places)
n *= 100;
decimals -= 2;
}
if (decimals) { n *= 10; }
if (decimals) {
n *= 10;
}
n += 0.5; // Add rounding factor. Ensures carryover through entire value.
// Generate digits backwards and store in string.
@ -162,12 +150,13 @@ void printFloat(float n, uint8_t decimal_places)
// Print the generated string.
for (; i > 0; i--) {
if (i == decimal_places) { serial_write('.'); } // Insert decimal point in right place.
if (i == decimal_places) {
serial_write('.');
} // Insert decimal point in right place.
serial_write(buf[i - 1]);
}
}
// Floating value printing handlers for special variables types used in Grbl and are defined
// in the config.h.
// - CoordValue: Handles all position or coordinate values in inches or mm reporting.

View file

@ -22,7 +22,6 @@
#ifndef print_h
#define print_h
void printString(const char *s);
void printPgmString(const char *s);

View file

@ -20,14 +20,11 @@
#include "grbl.h"
// Inverts the probe pin state depending on user settings and probing cycle mode.
uint8_t probe_invert_mask;
// Probe pin initialization routine.
void probe_init()
{
void probe_init() {
PROBE_DDR &= ~(PROBE_MASK); // Configure as input pins
#ifdef DISABLE_PROBE_PIN_PULL_UP
PROBE_PORT &= ~(PROBE_MASK); // Normal low operation. Requires external pull-down.
@ -37,27 +34,28 @@ void probe_init()
probe_configure_invert_mask(false); // Initialize invert mask.
}
// Called by probe_init() and the mc_probe() routines. Sets up the probe pin invert mask to
// appropriately set the pin logic according to setting for normal-high/normal-low operation
// and the probing cycle modes for toward-workpiece/away-from-workpiece.
void probe_configure_invert_mask(uint8_t is_probe_away)
{
void probe_configure_invert_mask(uint8_t is_probe_away) {
probe_invert_mask = 0; // Initialize as zero.
if (bit_isfalse(settings.flags,BITFLAG_INVERT_PROBE_PIN)) { probe_invert_mask ^= PROBE_MASK; }
if (is_probe_away) { probe_invert_mask ^= PROBE_MASK; }
if (bit_isfalse(settings.flags, BITFLAG_INVERT_PROBE_PIN)) {
probe_invert_mask ^= PROBE_MASK;
}
if (is_probe_away) {
probe_invert_mask ^= PROBE_MASK;
}
}
// Returns the probe pin state. Triggered = true. Called by gcode parser and probe state monitor.
uint8_t probe_get_state() { return((PROBE_PIN & PROBE_MASK) ^ probe_invert_mask); }
uint8_t probe_get_state() {
return ((PROBE_PIN & PROBE_MASK) ^ probe_invert_mask);
}
// Monitors probe pin state and records the system position when detected. Called by the
// stepper ISR per ISR tick.
// NOTE: This function must be extremely efficient as to not bog down the stepper ISR.
void probe_state_monitor()
{
void probe_state_monitor() {
if (probe_get_state()) {
sys_probe_state = PROBE_OFF;
memcpy(sys_probe_position, sys_position, sizeof(sys_position));

View file

@ -26,17 +26,14 @@
#define LINE_FLAG_COMMENT_PARENTHESES bit(1)
#define LINE_FLAG_COMMENT_SEMICOLON bit(2)
static char line[LINE_BUFFER_SIZE]; // Line to be executed. Zero-terminated.
static void protocol_exec_rt_suspend();
/*
GRBL PRIMARY LOOP:
*/
void protocol_main_loop()
{
void protocol_main_loop() {
// Perform some machine checks to make sure everything is good to go.
#ifdef CHECK_LIMITS_AT_INIT
if (bit_istrue(settings.flags, BITFLAG_HARD_LIMIT_ENABLE)) {
@ -79,7 +76,9 @@ void protocol_main_loop()
if ((c == '\n') || (c == '\r')) { // End of line reached
protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to calling function upon system abort
if (sys.abort) {
return;
} // Bail to calling function upon system abort
line[char_counter] = 0; // Set string termination character.
#ifdef REPORT_ECHO_LINE_RECEIVED
@ -114,7 +113,9 @@ void protocol_main_loop()
// Throw away all (except EOL) comment characters and overflow characters.
if (c == ')') {
// End of '()' comment. Resume line allowed.
if (line_flags & LINE_FLAG_COMMENT_PARENTHESES) { line_flags &= ~(LINE_FLAG_COMMENT_PARENTHESES); }
if (line_flags & LINE_FLAG_COMMENT_PARENTHESES) {
line_flags &= ~(LINE_FLAG_COMMENT_PARENTHESES);
}
}
} else {
if (c <= ' ') {
@ -147,7 +148,6 @@ void protocol_main_loop()
line[char_counter++] = c;
}
}
}
}
@ -157,40 +157,39 @@ void protocol_main_loop()
protocol_auto_cycle_start();
protocol_execute_realtime(); // Runtime command check point.
if (sys.abort) { return; } // Bail to main() program loop to reset system.
if (sys.abort) {
return;
} // Bail to main() program loop to reset system.
}
return; /* Never reached */
}
// Block until all buffered steps are executed or in a cycle state. Works with feed hold
// during a synchronize call, if it should happen. Also, waits for clean cycle end.
void protocol_buffer_synchronize()
{
void protocol_buffer_synchronize() {
// If system is queued, ensure cycle resumes if the auto start flag is present.
protocol_auto_cycle_start();
do {
protocol_execute_realtime(); // Check and execute run-time commands
if (sys.abort) { return; } // Check for system abort
if (sys.abort) {
return;
} // Check for system abort
} while (plan_get_current_block() || (sys.state == STATE_CYCLE));
}
// Auto-cycle start triggers when there is a motion ready to execute and if the main program is not
// actively parsing commands.
// NOTE: This function is called from the main loop, buffer sync, and mc_line() only and executes
// when one of these conditions exist respectively: There are no more blocks sent (i.e. streaming
// is finished, single commands), a command that needs to wait for the motions in the buffer to
// execute calls a buffer sync, or the planner buffer is full and ready to go.
void protocol_auto_cycle_start()
{
void protocol_auto_cycle_start() {
if (plan_get_current_block() != NULL) { // Check if there are any blocks in the buffer.
system_set_exec_state_flag(EXEC_CYCLE_START); // If so, execute them!
}
}
// This function is the general interface to Grbl's real-time command execution system. It is called
// from various check points in the main program, primarily where there may be a while loop waiting
// for a buffer to clear space or any point where the execution time from the last check point may
@ -202,18 +201,17 @@ void protocol_auto_cycle_start()
// the same task, such as the planner recalculating the buffer upon a feedhold or overrides.
// NOTE: The sys_rt_exec_state variable flags are set by any process, step or serial interrupts, pinouts,
// limit switches, or the main program.
void protocol_execute_realtime()
{
void protocol_execute_realtime() {
protocol_exec_rt_system();
if (sys.suspend) { protocol_exec_rt_suspend(); }
if (sys.suspend) {
protocol_exec_rt_suspend();
}
}
// Executes run-time commands, when required. This function primarily operates as Grbl's state
// machine and controls the various real-time features Grbl has to offer.
// NOTE: Do not alter this unless you know exactly what you are doing!
void protocol_exec_rt_system()
{
void protocol_exec_rt_system() {
uint8_t rt_exec; // Temp variable to avoid calling volatile multiple times.
rt_exec = sys_rt_exec_alarm; // Copy volatile sys_rt_exec_alarm.
if (rt_exec) { // Enter only if any bit flag is true
@ -265,26 +263,34 @@ void protocol_exec_rt_system()
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
sys.step_control = STEP_CONTROL_EXECUTE_HOLD; // Initiate suspend state with active flag.
if (sys.state == STATE_JOG) { // Jog cancelled upon any hold event, except for sleeping.
if (!(rt_exec & EXEC_SLEEP)) { sys.suspend |= SUSPEND_JOG_CANCEL; }
if (!(rt_exec & EXEC_SLEEP)) {
sys.suspend |= SUSPEND_JOG_CANCEL;
}
}
}
}
// If IDLE, Grbl is not in motion. Simply indicate suspend state and hold is complete.
if (sys.state == STATE_IDLE) { sys.suspend = SUSPEND_HOLD_COMPLETE; }
if (sys.state == STATE_IDLE) {
sys.suspend = SUSPEND_HOLD_COMPLETE;
}
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing cycle
// to halt and cancel the remainder of the motion.
// Execute and flag a motion cancel with deceleration and return to idle. Used primarily by probing
// cycle to halt and cancel the remainder of the motion.
if (rt_exec & EXEC_MOTION_CANCEL) {
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated beforehand
// to hold the CYCLE. Motion cancel is valid for a single planner block motion only, while jog cancel
// will handle and clear multiple planner block motions.
if (!(sys.state & STATE_JOG)) { sys.suspend |= SUSPEND_MOTION_CANCEL; } // NOTE: State is STATE_CYCLE.
// MOTION_CANCEL only occurs during a CYCLE, but a HOLD and SAFETY_DOOR may been initiated
// beforehand to hold the CYCLE. Motion cancel is valid for a single planner block motion only,
// while jog cancel will handle and clear multiple planner block motions.
if (!(sys.state & STATE_JOG)) {
sys.suspend |= SUSPEND_MOTION_CANCEL;
} // NOTE: State is STATE_CYCLE.
}
// Execute a feed hold with deceleration, if required. Then, suspend system.
if (rt_exec & EXEC_FEED_HOLD) {
// Block SAFETY_DOOR, JOG, and SLEEP states from changing to HOLD state.
if (!(sys.state & (STATE_SAFETY_DOOR | STATE_JOG | STATE_SLEEP))) { sys.state = STATE_HOLD; }
if (!(sys.state & (STATE_SAFETY_DOOR | STATE_JOG | STATE_SLEEP))) {
sys.state = STATE_HOLD;
}
}
// Execute a safety door stop with a feed hold and disable spindle/coolant.
@ -301,26 +307,31 @@ void protocol_exec_rt_system()
#ifdef PARKING_ENABLE
// Set hold and reset appropriate control flags to restart parking sequence.
if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) {
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration.
st_update_plan_block_parameters(); // Notify stepper module to recompute for hold
// deceleration.
sys.step_control = (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION);
sys.suspend &= ~(SUSPEND_HOLD_COMPLETE);
} // else NO_MOTION is active.
#endif
sys.suspend &= ~(SUSPEND_RETRACT_COMPLETE | SUSPEND_INITIATE_RESTORE | SUSPEND_RESTORE_COMPLETE);
sys.suspend &=
~(SUSPEND_RETRACT_COMPLETE | SUSPEND_INITIATE_RESTORE | SUSPEND_RESTORE_COMPLETE);
sys.suspend |= SUSPEND_RESTART_RETRACT;
}
}
if (sys.state != STATE_SLEEP) { sys.state = STATE_SAFETY_DOOR; }
if (sys.state != STATE_SLEEP) {
sys.state = STATE_SAFETY_DOOR;
}
// NOTE: This flag doesn't change when the door closes, unlike sys.state. Ensures any parking motions
// are executed if the door switch closes and the state returns to HOLD.
}
// NOTE: This flag doesn't change when the door closes, unlike sys.state. Ensures any parking
// motions are executed if the door switch closes and the state returns to HOLD.
sys.suspend |= SUSPEND_SAFETY_DOOR_AJAR;
}
}
if (rt_exec & EXEC_SLEEP) {
if (sys.state == STATE_ALARM) { sys.suspend |= (SUSPEND_RETRACT_COMPLETE|SUSPEND_HOLD_COMPLETE); }
if (sys.state == STATE_ALARM) {
sys.suspend |= (SUSPEND_RETRACT_COMPLETE | SUSPEND_HOLD_COMPLETE);
}
sys.state = STATE_SLEEP;
}
@ -337,18 +348,20 @@ void protocol_exec_rt_system()
if (sys.suspend & SUSPEND_RESTORE_COMPLETE) {
sys.state = STATE_IDLE; // Set to IDLE to immediately resume the cycle.
} else if (sys.suspend & SUSPEND_RETRACT_COMPLETE) {
// Flag to re-energize powered components and restore original position, if disabled by SAFETY_DOOR.
// NOTE: For a safety door to resume, the switch must be closed, as indicated by HOLD state, and
// the retraction execution is complete, which implies the initial feed hold is not active. To
// restore normal operation, the restore procedures must be initiated by the following flag. Once,
// they are complete, it will call CYCLE_START automatically to resume and exit the suspend.
// Flag to re-energize powered components and restore original position, if disabled by
// SAFETY_DOOR. NOTE: For a safety door to resume, the switch must be closed, as indicated by
// HOLD state, and the retraction execution is complete, which implies the initial feed hold is
// not active. To restore normal operation, the restore procedures must be initiated by the
// following flag. Once, they are complete, it will call CYCLE_START automatically to resume and
// exit the suspend.
sys.suspend |= SUSPEND_INITIATE_RESTORE;
}
}
// Cycle start only when IDLE or when a hold is complete and ready to resume.
if ((sys.state == STATE_IDLE) || ((sys.state & STATE_HOLD) && (sys.suspend & SUSPEND_HOLD_COMPLETE))) {
if (sys.state == STATE_HOLD && sys.spindle_stop_ovr) {
sys.spindle_stop_ovr |= SPINDLE_STOP_OVR_RESTORE_CYCLE; // Set to restore in suspend routine and cycle start after.
sys.spindle_stop_ovr |=
SPINDLE_STOP_OVR_RESTORE_CYCLE; // Set to restore in suspend routine and cycle start after.
} else {
// Start cycle only if queued motions exist in planner buffer and the motion is not canceled.
sys.step_control = STEP_CONTROL_NORMAL_OP; // Restore step control to normal operation
@ -373,11 +386,14 @@ void protocol_exec_rt_system()
// NOTE: Bresenham algorithm variables are still maintained through both the planner and stepper
// cycle reinitializations. The stepper path should continue exactly as if nothing has happened.
// NOTE: EXEC_CYCLE_STOP is set by the stepper subsystem when a cycle or feed hold completes.
if ((sys.state & (STATE_HOLD|STATE_SAFETY_DOOR|STATE_SLEEP)) && !(sys.soft_limit) && !(sys.suspend & SUSPEND_JOG_CANCEL)) {
if ((sys.state & (STATE_HOLD | STATE_SAFETY_DOOR | STATE_SLEEP)) && !(sys.soft_limit) &&
!(sys.suspend & SUSPEND_JOG_CANCEL)) {
// Hold complete. Set to indicate ready to resume. Remain in HOLD or DOOR states until user
// has issued a resume command or reset.
plan_cycle_reinitialize();
if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) { sys.suspend |= SUSPEND_HOLD_COMPLETE; }
if (sys.step_control & STEP_CONTROL_EXECUTE_HOLD) {
sys.suspend |= SUSPEND_HOLD_COMPLETE;
}
bit_false(sys.step_control, (STEP_CONTROL_EXECUTE_HOLD | STEP_CONTROL_EXECUTE_SYS_MOTION));
} else {
// Motion complete. Includes CYCLE/JOG/HOMING states and jog cancel/motion cancel/soft limit events.
@ -408,18 +424,34 @@ void protocol_exec_rt_system()
system_clear_exec_motion_overrides(); // Clear all motion override flags.
uint8_t new_f_override = sys.f_override;
if (rt_exec & EXEC_FEED_OVR_RESET) { new_f_override = DEFAULT_FEED_OVERRIDE; }
if (rt_exec & EXEC_FEED_OVR_COARSE_PLUS) { new_f_override += FEED_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_COARSE_MINUS) { new_f_override -= FEED_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_FINE_PLUS) { new_f_override += FEED_OVERRIDE_FINE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_FINE_MINUS) { new_f_override -= FEED_OVERRIDE_FINE_INCREMENT; }
if (rt_exec & EXEC_FEED_OVR_RESET) {
new_f_override = DEFAULT_FEED_OVERRIDE;
}
if (rt_exec & EXEC_FEED_OVR_COARSE_PLUS) {
new_f_override += FEED_OVERRIDE_COARSE_INCREMENT;
}
if (rt_exec & EXEC_FEED_OVR_COARSE_MINUS) {
new_f_override -= FEED_OVERRIDE_COARSE_INCREMENT;
}
if (rt_exec & EXEC_FEED_OVR_FINE_PLUS) {
new_f_override += FEED_OVERRIDE_FINE_INCREMENT;
}
if (rt_exec & EXEC_FEED_OVR_FINE_MINUS) {
new_f_override -= FEED_OVERRIDE_FINE_INCREMENT;
}
new_f_override = min(new_f_override, MAX_FEED_RATE_OVERRIDE);
new_f_override = max(new_f_override, MIN_FEED_RATE_OVERRIDE);
uint8_t new_r_override = sys.r_override;
if (rt_exec & EXEC_RAPID_OVR_RESET) { new_r_override = DEFAULT_RAPID_OVERRIDE; }
if (rt_exec & EXEC_RAPID_OVR_MEDIUM) { new_r_override = RAPID_OVERRIDE_MEDIUM; }
if (rt_exec & EXEC_RAPID_OVR_LOW) { new_r_override = RAPID_OVERRIDE_LOW; }
if (rt_exec & EXEC_RAPID_OVR_RESET) {
new_r_override = DEFAULT_RAPID_OVERRIDE;
}
if (rt_exec & EXEC_RAPID_OVR_MEDIUM) {
new_r_override = RAPID_OVERRIDE_MEDIUM;
}
if (rt_exec & EXEC_RAPID_OVR_LOW) {
new_r_override = RAPID_OVERRIDE_LOW;
}
if ((new_f_override != sys.f_override) || (new_r_override != sys.r_override)) {
sys.f_override = new_f_override;
@ -436,19 +468,32 @@ void protocol_exec_rt_system()
// NOTE: Unlike motion overrides, spindle overrides do not require a planner reinitialization.
uint8_t last_s_override = sys.spindle_speed_ovr;
if (rt_exec & EXEC_SPINDLE_OVR_RESET) { last_s_override = DEFAULT_SPINDLE_SPEED_OVERRIDE; }
if (rt_exec & EXEC_SPINDLE_OVR_COARSE_PLUS) { last_s_override += SPINDLE_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_COARSE_MINUS) { last_s_override -= SPINDLE_OVERRIDE_COARSE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_FINE_PLUS) { last_s_override += SPINDLE_OVERRIDE_FINE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_FINE_MINUS) { last_s_override -= SPINDLE_OVERRIDE_FINE_INCREMENT; }
if (rt_exec & EXEC_SPINDLE_OVR_RESET) {
last_s_override = DEFAULT_SPINDLE_SPEED_OVERRIDE;
}
if (rt_exec & EXEC_SPINDLE_OVR_COARSE_PLUS) {
last_s_override += SPINDLE_OVERRIDE_COARSE_INCREMENT;
}
if (rt_exec & EXEC_SPINDLE_OVR_COARSE_MINUS) {
last_s_override -= SPINDLE_OVERRIDE_COARSE_INCREMENT;
}
if (rt_exec & EXEC_SPINDLE_OVR_FINE_PLUS) {
last_s_override += SPINDLE_OVERRIDE_FINE_INCREMENT;
}
if (rt_exec & EXEC_SPINDLE_OVR_FINE_MINUS) {
last_s_override -= SPINDLE_OVERRIDE_FINE_INCREMENT;
}
last_s_override = min(last_s_override, MAX_SPINDLE_SPEED_OVERRIDE);
last_s_override = max(last_s_override, MIN_SPINDLE_SPEED_OVERRIDE);
if (last_s_override != sys.spindle_speed_ovr) {
sys.spindle_speed_ovr = last_s_override;
// NOTE: Spindle speed overrides during HOLD state are taken care of by suspend function.
if (sys.state == STATE_IDLE) { spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed); }
else { bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM); }
if (sys.state == STATE_IDLE) {
spindle_set_state(gc_state.modal.spindle, gc_state.spindle_speed);
} else {
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
}
sys.report_ovr_counter = 0; // Set to report change immediately
}
@ -456,8 +501,11 @@ void protocol_exec_rt_system()
// Spindle stop override allowed only while in HOLD state.
// NOTE: Report counters are set in spindle_set_state() when spindle stop is executed.
if (sys.state == STATE_HOLD) {
if (!(sys.spindle_stop_ovr)) { sys.spindle_stop_ovr = SPINDLE_STOP_OVR_INITIATE; }
else if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_ENABLED) { sys.spindle_stop_ovr |= SPINDLE_STOP_OVR_RESTORE; }
if (!(sys.spindle_stop_ovr)) {
sys.spindle_stop_ovr = SPINDLE_STOP_OVR_INITIATE;
} else if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_ENABLED) {
sys.spindle_stop_ovr |= SPINDLE_STOP_OVR_RESTORE;
}
}
}
@ -469,16 +517,25 @@ void protocol_exec_rt_system()
uint8_t coolant_state = gc_state.modal.coolant;
#ifdef ENABLE_M7
if (rt_exec & EXEC_COOLANT_MIST_OVR_TOGGLE) {
if (coolant_state & COOLANT_MIST_ENABLE) { bit_false(coolant_state,COOLANT_MIST_ENABLE); }
else { coolant_state |= COOLANT_MIST_ENABLE; }
if (coolant_state & COOLANT_MIST_ENABLE) {
bit_false(coolant_state, COOLANT_MIST_ENABLE);
} else {
coolant_state |= COOLANT_MIST_ENABLE;
}
}
if (rt_exec & EXEC_COOLANT_FLOOD_OVR_TOGGLE) {
if (coolant_state & COOLANT_FLOOD_ENABLE) { bit_false(coolant_state,COOLANT_FLOOD_ENABLE); }
else { coolant_state |= COOLANT_FLOOD_ENABLE; }
if (coolant_state & COOLANT_FLOOD_ENABLE) {
bit_false(coolant_state, COOLANT_FLOOD_ENABLE);
} else {
coolant_state |= COOLANT_FLOOD_ENABLE;
}
}
#else
if (coolant_state & COOLANT_FLOOD_ENABLE) { bit_false(coolant_state,COOLANT_FLOOD_ENABLE); }
else { coolant_state |= COOLANT_FLOOD_ENABLE; }
if (coolant_state & COOLANT_FLOOD_ENABLE) {
bit_false(coolant_state, COOLANT_FLOOD_ENABLE);
} else {
coolant_state |= COOLANT_FLOOD_ENABLE;
}
#endif
coolant_set_state(coolant_state); // Report counter set in coolant_set_state().
gc_state.modal.coolant = coolant_state;
@ -497,17 +554,14 @@ void protocol_exec_rt_system()
if (sys.state & (STATE_CYCLE | STATE_HOLD | STATE_SAFETY_DOOR | STATE_HOMING | STATE_SLEEP | STATE_JOG)) {
st_prep_buffer();
}
}
// Handles Grbl system suspend procedures, such as feed hold, safety door, and parking motion.
// The system will enter this loop, create local variables for suspend tasks, and return to
// whatever function that invoked the suspend, such that Grbl resumes normal operation.
// This function is written in a way to promote custom parking motions. Simply use this as a
// template
static void protocol_exec_rt_suspend()
{
static void protocol_exec_rt_suspend() {
#ifdef PARKING_ENABLE
// Declare and initialize parking local variables
float restore_target[N_AXIS];
@ -539,13 +593,18 @@ static void protocol_exec_rt_suspend()
}
#endif
#else
if (block == NULL) { restore_condition = (gc_state.modal.spindle | gc_state.modal.coolant); }
else { restore_condition = (block->condition & PL_COND_SPINDLE_MASK) | coolant_get_state(); }
if (block == NULL) {
restore_condition = (gc_state.modal.spindle | gc_state.modal.coolant);
} else {
restore_condition = (block->condition & PL_COND_SPINDLE_MASK) | coolant_get_state();
}
#endif
while (sys.suspend) {
if (sys.abort) { return; }
if (sys.abort) {
return;
}
// Block until initial hold is complete and the machine has stopped motion.
if (sys.suspend & SUSPEND_HOLD_COMPLETE) {
@ -593,7 +652,8 @@ static void protocol_exec_rt_suspend()
if (parking_target[PARKING_AXIS] < retract_waypoint) {
parking_target[PARKING_AXIS] = retract_waypoint;
pl_data->feed_rate = PARKING_PULLOUT_RATE;
pl_data->condition |= (restore_condition & PL_COND_ACCESSORY_MASK); // Retain accessory state
pl_data->condition |=
(restore_condition & PL_COND_ACCESSORY_MASK); // Retain accessory state
pl_data->spindle_speed = restore_spindle_speed;
mc_parking_motion(parking_target, pl_data);
}
@ -617,7 +677,6 @@ static void protocol_exec_rt_suspend()
// NOTE: Laser mode does not start a parking motion to ensure the laser stops immediately.
spindle_set_state(SPINDLE_DISABLE, 0.0); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
}
#endif
@ -627,21 +686,24 @@ static void protocol_exec_rt_suspend()
} else {
if (sys.state == STATE_SLEEP) {
report_feedback_message(MESSAGE_SLEEP_MODE);
// Spindle and coolant should already be stopped, but do it again just to be sure.
spindle_set_state(SPINDLE_DISABLE, 0.0); // De-energize
coolant_set_state(COOLANT_DISABLE); // De-energize
st_go_idle(); // Disable steppers
while (!(sys.abort)) { protocol_exec_rt_system(); } // Do nothing until reset.
while (!(sys.abort)) {
protocol_exec_rt_system();
} // Do nothing until reset.
return; // Abort received. Return to re-initialize.
}
// Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to resume.
// Allows resuming from parking/safety door. Actively checks if safety door is closed and ready to
// resume.
if (sys.state == STATE_SAFETY_DOOR) {
if (!(system_check_safety_door_ajar())) {
sys.suspend &= ~(SUSPEND_SAFETY_DOOR_AJAR); // Reset door ajar flag to denote ready to resume.
sys.suspend &=
~(SUSPEND_SAFETY_DOOR_AJAR); // Reset door ajar flag to denote ready to resume.
}
}
@ -652,7 +714,8 @@ static void protocol_exec_rt_suspend()
// Execute fast restore motion to the pull-out position. Parking requires homing enabled.
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
if (((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) &&
if (((settings.flags & (BITFLAG_HOMING_ENABLE | BITFLAG_LASER_MODE)) ==
BITFLAG_HOMING_ENABLE) &&
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
#else
if ((settings.flags & (BITFLAG_HOMING_ENABLE | BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) {
@ -671,10 +734,13 @@ static void protocol_exec_rt_suspend()
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) {
if (bit_istrue(settings.flags, BITFLAG_LASER_MODE)) {
// When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
// When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle
// starts.
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);
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);
}
}
@ -682,8 +748,10 @@ static void protocol_exec_rt_suspend()
if (gc_state.modal.coolant != COOLANT_DISABLE) {
// Block if safety door re-opened during prior restore actions.
if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) {
// NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this pin.
coolant_set_state((restore_condition & (PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_MIST)));
// NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this
// pin.
coolant_set_state(
(restore_condition & (PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_MIST)));
delay_sec(SAFETY_DOOR_COOLANT_DELAY, DELAY_MODE_SYS_SUSPEND);
}
}
@ -691,7 +759,8 @@ static void protocol_exec_rt_suspend()
#ifdef PARKING_ENABLE
// Execute slow plunge motion from pull-out position to resume position.
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
if (((settings.flags & (BITFLAG_HOMING_ENABLE|BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) &&
if (((settings.flags & (BITFLAG_HOMING_ENABLE | BITFLAG_LASER_MODE)) ==
BITFLAG_HOMING_ENABLE) &&
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
#else
if ((settings.flags & (BITFLAG_HOMING_ENABLE | BITFLAG_LASER_MODE)) == BITFLAG_HOMING_ENABLE) {
@ -702,7 +771,8 @@ static void protocol_exec_rt_suspend()
// restore parking motion should logically be valid, either by returning to the
// original position through valid machine space or by not moving at all.
pl_data->feed_rate = PARKING_PULLOUT_RATE;
pl_data->condition |= (restore_condition & PL_COND_ACCESSORY_MASK); // Restore accessory state
pl_data->condition |=
(restore_condition & PL_COND_ACCESSORY_MASK); // Restore accessory state
pl_data->spindle_speed = restore_spindle_speed;
mc_parking_motion(restore_target, pl_data);
}
@ -714,10 +784,8 @@ static void protocol_exec_rt_suspend()
system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.
}
}
}
} else {
// Feed hold manager. Controls spindle stop override states.
@ -727,7 +795,8 @@ static void protocol_exec_rt_suspend()
if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_INITIATE) {
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
spindle_set_state(SPINDLE_DISABLE, 0.0); // De-energize
sys.spindle_stop_ovr = SPINDLE_STOP_OVR_ENABLED; // Set stop override state to enabled, if de-energized.
sys.spindle_stop_ovr =
SPINDLE_STOP_OVR_ENABLED; // Set stop override state to enabled, if de-energized.
} else {
sys.spindle_stop_ovr = SPINDLE_STOP_OVR_DISABLED; // Clear stop override state
}
@ -736,10 +805,13 @@ static void protocol_exec_rt_suspend()
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
report_feedback_message(MESSAGE_SPINDLE_RESTORE);
if (bit_istrue(settings.flags, BITFLAG_LASER_MODE)) {
// When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
// When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle
// starts.
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);
spindle_set_state(
(restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)),
restore_spindle_speed);
}
}
if (sys.spindle_stop_ovr & SPINDLE_STOP_OVR_RESTORE_CYCLE) {
@ -748,18 +820,18 @@ static void protocol_exec_rt_suspend()
sys.spindle_stop_ovr = SPINDLE_STOP_OVR_DISABLED; // Clear stop override state
}
} else {
// Handles spindle state during hold. NOTE: Spindle speed overrides may be altered during hold state.
// NOTE: STEP_CONTROL_UPDATE_SPINDLE_PWM is automatically reset upon resume in step generator.
// Handles spindle state during hold. NOTE: Spindle speed overrides may be altered during hold
// state. NOTE: STEP_CONTROL_UPDATE_SPINDLE_PWM is automatically reset upon resume in step
// generator.
if (bit_istrue(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM)) {
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)), restore_spindle_speed);
spindle_set_state((restore_condition & (PL_COND_FLAG_SPINDLE_CW | PL_COND_FLAG_SPINDLE_CCW)),
restore_spindle_speed);
bit_false(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_PWM);
}
}
}
}
protocol_exec_rt_system();
}
}

View file

@ -28,19 +28,38 @@
#include "grbl.h"
// Internal report utilities to reduce flash with repetitive tasks turned into functions.
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")); }
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;
for (idx = 0; idx < N_AXIS; idx++) {
printFloat_CoordValue(axis_value[idx]);
if (idx < (N_AXIS-1)) { serial_write(','); }
if (idx < (N_AXIS - 1)) {
serial_write(',');
}
}
}
@ -96,24 +115,24 @@ static void report_util_uint8_setting(uint8_t n, int val) {
print_uint8_base10(val);
report_util_line_feed(); // report_util_setting_string(n);
}
static void report_util_float_setting(uint8_t n, float val, uint8_t n_decimal) {
report_util_setting_prefix(n);
printFloat(val, n_decimal);
report_util_line_feed(); // report_util_setting_string(n);
}
// Handles the primary confirmation protocol response for streaming interfaces and human-feedback.
// For every incoming line, this method responds with an 'ok' for a successful command or an
// 'error:' to indicate some error event with the line or some critical system error during
// operation. Errors events can originate from the g-code parser, settings module, or asynchronously
// from a critical error, such as a triggered hard limit. Interface should always monitor for these
// responses.
void report_status_message(uint8_t status_code)
{
void report_status_message(uint8_t status_code) {
switch (status_code) {
case STATUS_OK: // STATUS_OK
printPgmString(PSTR("ok\r\n")); break;
printPgmString(PSTR("ok\r\n"));
break;
default:
printPgmString(PSTR("error:"));
print_uint8_base10(status_code);
@ -122,8 +141,7 @@ void report_status_message(uint8_t status_code)
}
// Prints alarm messages.
void report_alarm_message(uint8_t alarm_code)
{
void report_alarm_message(uint8_t alarm_code) {
printPgmString(PSTR("ALARM:"));
print_uint8_base10(alarm_code);
report_util_line_feed();
@ -135,40 +153,26 @@ void report_alarm_message(uint8_t alarm_code)
// messages such as setup warnings, switch toggling, and how to exit alarms.
// NOTE: For interfaces, messages are always placed within brackets. And if silent mode
// is installed, the message number codes are less than zero.
void report_feedback_message(uint8_t message_code)
{
void report_feedback_message(uint8_t message_code) {
printPgmString(PSTR("[MSG:"));
switch (message_code) {
case MESSAGE_CRITICAL_EVENT:
printPgmString(PSTR("Reset to continue")); break;
case MESSAGE_ALARM_LOCK:
printPgmString(PSTR("'$H'|'$X' to unlock")); break;
case MESSAGE_ALARM_UNLOCK:
printPgmString(PSTR("Caution: Unlocked")); break;
case MESSAGE_ENABLED:
printPgmString(PSTR("Enabled")); break;
case MESSAGE_DISABLED:
printPgmString(PSTR("Disabled")); break;
case MESSAGE_SAFETY_DOOR_AJAR:
printPgmString(PSTR("Check Door")); break;
case MESSAGE_CHECK_LIMITS:
printPgmString(PSTR("Check Limits")); break;
case MESSAGE_PROGRAM_END:
printPgmString(PSTR("Pgm End")); break;
case MESSAGE_RESTORE_DEFAULTS:
printPgmString(PSTR("Restoring defaults")); break;
case MESSAGE_SPINDLE_RESTORE:
printPgmString(PSTR("Restoring spindle")); break;
case MESSAGE_SLEEP_MODE:
printPgmString(PSTR("Sleeping")); break;
case MESSAGE_CRITICAL_EVENT: printPgmString(PSTR("Reset to continue")); break;
case MESSAGE_ALARM_LOCK: printPgmString(PSTR("'$H'|'$X' to unlock")); break;
case MESSAGE_ALARM_UNLOCK: printPgmString(PSTR("Caution: Unlocked")); break;
case MESSAGE_ENABLED: printPgmString(PSTR("Enabled")); break;
case MESSAGE_DISABLED: printPgmString(PSTR("Disabled")); break;
case MESSAGE_SAFETY_DOOR_AJAR: printPgmString(PSTR("Check Door")); break;
case MESSAGE_CHECK_LIMITS: printPgmString(PSTR("Check Limits")); break;
case MESSAGE_PROGRAM_END: printPgmString(PSTR("Pgm End")); break;
case MESSAGE_RESTORE_DEFAULTS: printPgmString(PSTR("Restoring defaults")); break;
case MESSAGE_SPINDLE_RESTORE: printPgmString(PSTR("Restoring spindle")); break;
case MESSAGE_SLEEP_MODE: printPgmString(PSTR("Sleeping")); break;
}
report_util_feedback_line_feed();
}
// Welcome message
void report_init_message()
{
void report_init_message() {
printPgmString(PSTR("\r\nGrbl " GRBL_VERSION " ['$' for help]\r\n"));
}
@ -177,7 +181,6 @@ void report_grbl_help() {
printPgmString(PSTR("[HLP:$$ $# $G $I $N $x=val $Nx=line $J=line $SLP $C $X $H ~ ! ? ctrl-x]\r\n"));
}
// Grbl global settings print out.
// NOTE: The numbering scheme here must correlate to storing in settings.c
void report_grbl_settings() {
@ -216,7 +219,9 @@ void report_grbl_settings() {
switch (set_idx) {
case 0: report_util_float_setting(val + idx, settings.steps_per_mm[idx], N_DECIMAL_SETTINGVALUE); break;
case 1: report_util_float_setting(val + idx, settings.max_rate[idx], N_DECIMAL_SETTINGVALUE); break;
case 2: report_util_float_setting(val+idx,settings.acceleration[idx]/(60*60),N_DECIMAL_SETTINGVALUE); break;
case 2:
report_util_float_setting(val + idx, settings.acceleration[idx] / (60 * 60), N_DECIMAL_SETTINGVALUE);
break;
case 3: report_util_float_setting(val + idx, -settings.max_travel[idx], N_DECIMAL_SETTINGVALUE); break;
}
}
@ -224,12 +229,10 @@ void report_grbl_settings() {
}
}
// Prints current probe parameters. Upon a probe command, these parameters are updated upon a
// successful probe or upon a failed probe with the G38.3 without errors command (if supported).
// These values are retained until Grbl is power-cycled, whereby they will be re-zeroed.
void report_probe_parameters()
{
void report_probe_parameters() {
// Report in terms of machine position.
printPgmString(PSTR("[PRB:"));
float print_position[N_AXIS];
@ -240,10 +243,8 @@ void report_probe_parameters()
report_util_feedback_line_feed();
}
// Prints Grbl NGC parameters (coordinate offsets, probing)
void report_ngc_parameters()
{
void report_ngc_parameters() {
float coord_data[N_AXIS];
uint8_t coord_select;
for (coord_select = 0; coord_select <= SETTING_INDEX_NCOORD; coord_select++) {
@ -270,10 +271,8 @@ void report_ngc_parameters()
report_probe_parameters(); // Print probe parameters. Not persistent in memory.
}
// Print current gcode parser mode state
void report_gcode_modes()
{
void report_gcode_modes() {
printPgmString(PSTR("[GC:G"));
if (gc_state.modal.motion >= MOTION_MODE_PROBE_TOWARD) {
printPgmString(PSTR("38."));
@ -303,9 +302,7 @@ void report_gcode_modes()
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;
case PROGRAM_FLOW_COMPLETED_M30: print_uint8_base10(gc_state.modal.program_flow); break;
}
}
@ -318,13 +315,25 @@ void report_gcode_modes()
#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) { report_util_gcode_modes_M(); serial_write('7'); }
if (gc_state.modal.coolant & PL_COND_FLAG_COOLANT_FLOOD) { report_util_gcode_modes_M(); serial_write('8'); }
} else { report_util_gcode_modes_M(); serial_write('9'); }
if (gc_state.modal.coolant & PL_COND_FLAG_COOLANT_MIST) {
report_util_gcode_modes_M();
serial_write('7');
}
if (gc_state.modal.coolant & PL_COND_FLAG_COOLANT_FLOOD) {
report_util_gcode_modes_M();
serial_write('8');
}
} else {
report_util_gcode_modes_M();
serial_write('9');
}
#else
report_util_gcode_modes_M();
if (gc_state.modal.coolant) { serial_write('8'); }
else { serial_write('9'); }
if (gc_state.modal.coolant) {
serial_write('8');
} else {
serial_write('9');
}
#endif
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
@ -349,8 +358,7 @@ void report_gcode_modes()
}
// Prints specified startup line
void report_startup_line(uint8_t n, char *line)
{
void report_startup_line(uint8_t n, char *line) {
printPgmString(PSTR("$N"));
print_uint8_base10(n);
serial_write('=');
@ -358,8 +366,7 @@ void report_startup_line(uint8_t n, char *line)
report_util_line_feed();
}
void report_execute_startup_message(char *line, uint8_t status_code)
{
void report_execute_startup_message(char *line, uint8_t status_code) {
serial_write('>');
printString(line);
serial_write(':');
@ -367,8 +374,7 @@ void report_execute_startup_message(char *line, uint8_t status_code)
}
// Prints build info line
void report_build_info(char *line)
{
void report_build_info(char *line) {
printPgmString(PSTR("[VER:" GRBL_VERSION "." GRBL_VERSION_BUILD ":"));
printString(line);
report_util_feedback_line_feed();
@ -448,23 +454,20 @@ void report_build_info(char *line)
report_util_feedback_line_feed();
}
// Prints the character string line Grbl has received from the user, which has been pre-parsed,
// and has been sent into protocol_execute_line() routine to be executed by Grbl.
void report_echo_line_received(char *line)
{
printPgmString(PSTR("[echo: ")); printString(line);
void report_echo_line_received(char *line) {
printPgmString(PSTR("[echo: "));
printString(line);
report_util_feedback_line_feed();
}
// Prints real-time data. This function grabs a real-time snapshot of the stepper subprogram
// and the actual location of the CNC machine. Users may change the following function to their
// specific needs, but the desired real-time data report must be as short as possible. This is
// requires as it minimizes the computational overhead and allows grbl to keep running smoothly,
// especially during g-code programs with fast, short line segments and high frequency reports (5-20Hz).
void report_realtime_status()
{
void report_realtime_status() {
uint8_t idx;
int32_t current_position[N_AXIS]; // Copy current state of the system position variable
memcpy(current_position, sys_position, sizeof(sys_position));
@ -479,8 +482,12 @@ void report_realtime_status()
case STATE_HOLD:
if (!(sys.suspend & SUSPEND_JOG_CANCEL)) {
printPgmString(PSTR("Hold:"));
if (sys.suspend & SUSPEND_HOLD_COMPLETE) { serial_write('0'); } // Ready to resume
else { serial_write('1'); } // Actively holding
if (sys.suspend & SUSPEND_HOLD_COMPLETE) {
serial_write('0');
} // Ready to resume
else {
serial_write('1');
} // Actively holding
break;
} // Continues to print jog state during jog cancel.
case STATE_JOG: printPgmString(PSTR("Jog")); break;
@ -507,12 +514,13 @@ void report_realtime_status()
}
float wco[N_AXIS];
if (bit_isfalse(settings.status_report_mask,BITFLAG_RT_STATUS_POSITION_TYPE) ||
(sys.report_wco_counter == 0) ) {
if (bit_isfalse(settings.status_report_mask, BITFLAG_RT_STATUS_POSITION_TYPE) || (sys.report_wco_counter == 0)) {
for (idx = 0; idx < N_AXIS; idx++) {
// Apply work coordinate offsets and tool length offset to current position.
wco[idx] = gc_state.coord_system[idx] + gc_state.coord_offset[idx];
if (idx == TOOL_LENGTH_OFFSET_AXIS) { wco[idx] += gc_state.tool_length_offset; }
if (idx == TOOL_LENGTH_OFFSET_AXIS) {
wco[idx] += gc_state.tool_length_offset;
}
if (bit_isfalse(settings.status_report_mask, BITFLAG_RT_STATUS_POSITION_TYPE)) {
print_position[idx] -= wco[idx];
}
@ -570,53 +578,87 @@ void report_realtime_status()
uint8_t prb_pin_state = probe_get_state();
if (lim_pin_state | ctrl_pin_state | prb_pin_state) {
printPgmString(PSTR("|Pn:"));
if (prb_pin_state) { serial_write('P'); }
if (prb_pin_state) {
serial_write('P');
}
if (lim_pin_state) {
#ifdef ENABLE_DUAL_AXIS
#if (DUAL_AXIS_SELECT == X_AXIS)
if (bit_istrue(lim_pin_state,(bit(X_AXIS)|bit(N_AXIS)))) { serial_write('X'); }
if (bit_istrue(lim_pin_state,bit(Y_AXIS))) { serial_write('Y'); }
if (bit_istrue(lim_pin_state, (bit(X_AXIS) | bit(N_AXIS)))) {
serial_write('X');
}
if (bit_istrue(lim_pin_state, bit(Y_AXIS))) {
serial_write('Y');
}
#endif
#if (DUAL_AXIS_SELECT == Y_AXIS)
if (bit_istrue(lim_pin_state,bit(X_AXIS))) { serial_write('X'); }
if (bit_istrue(lim_pin_state,(bit(Y_AXIS)|bit(N_AXIS)))) { serial_write('Y'); }
if (bit_istrue(lim_pin_state, bit(X_AXIS))) {
serial_write('X');
}
if (bit_istrue(lim_pin_state, (bit(Y_AXIS) | bit(N_AXIS)))) {
serial_write('Y');
}
#endif
if (bit_istrue(lim_pin_state,bit(Z_AXIS))) { serial_write('Z'); }
if (bit_istrue(lim_pin_state, bit(Z_AXIS))) {
serial_write('Z');
}
#else
if (bit_istrue(lim_pin_state,bit(X_AXIS))) { serial_write('X'); }
if (bit_istrue(lim_pin_state,bit(Y_AXIS))) { serial_write('Y'); }
if (bit_istrue(lim_pin_state,bit(Z_AXIS))) { serial_write('Z'); }
if (bit_istrue(lim_pin_state, bit(X_AXIS))) {
serial_write('X');
}
if (bit_istrue(lim_pin_state, bit(Y_AXIS))) {
serial_write('Y');
}
if (bit_istrue(lim_pin_state, bit(Z_AXIS))) {
serial_write('Z');
}
#endif
}
if (ctrl_pin_state) {
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_SAFETY_DOOR)) { serial_write('D'); }
if (bit_istrue(ctrl_pin_state, CONTROL_PIN_INDEX_SAFETY_DOOR)) {
serial_write('D');
}
#endif
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_RESET)) { serial_write('R'); }
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_FEED_HOLD)) { serial_write('H'); }
if (bit_istrue(ctrl_pin_state,CONTROL_PIN_INDEX_CYCLE_START)) { serial_write('S'); }
if (bit_istrue(ctrl_pin_state, CONTROL_PIN_INDEX_RESET)) {
serial_write('R');
}
if (bit_istrue(ctrl_pin_state, CONTROL_PIN_INDEX_FEED_HOLD)) {
serial_write('H');
}
if (bit_istrue(ctrl_pin_state, CONTROL_PIN_INDEX_CYCLE_START)) {
serial_write('S');
}
}
}
#endif
#ifdef REPORT_FIELD_WORK_COORD_OFFSET
if (sys.report_wco_counter > 0) { sys.report_wco_counter--; }
else {
if (sys.report_wco_counter > 0) {
sys.report_wco_counter--;
} else {
if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
sys.report_wco_counter = (REPORT_WCO_REFRESH_BUSY_COUNT - 1); // Reset counter for slow refresh
} else { sys.report_wco_counter = (REPORT_WCO_REFRESH_IDLE_COUNT-1); }
if (sys.report_ovr_counter == 0) { sys.report_ovr_counter = 1; } // Set override on next report.
} else {
sys.report_wco_counter = (REPORT_WCO_REFRESH_IDLE_COUNT - 1);
}
if (sys.report_ovr_counter == 0) {
sys.report_ovr_counter = 1;
} // Set override on next report.
printPgmString(PSTR("|WCO:"));
report_util_axis_values(wco);
}
#endif
#ifdef REPORT_FIELD_OVERRIDES
if (sys.report_ovr_counter > 0) { sys.report_ovr_counter--; }
else {
if (sys.report_ovr_counter > 0) {
sys.report_ovr_counter--;
} else {
if (sys.state & (STATE_HOMING | STATE_CYCLE | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
sys.report_ovr_counter = (REPORT_OVR_REFRESH_BUSY_COUNT - 1); // Reset counter for slow refresh
} else { sys.report_ovr_counter = (REPORT_OVR_REFRESH_IDLE_COUNT-1); }
} else {
sys.report_ovr_counter = (REPORT_OVR_REFRESH_IDLE_COUNT - 1);
}
printPgmString(PSTR("|Ov:"));
print_uint8_base10(sys.f_override);
serial_write(',');
@ -633,17 +675,29 @@ void report_realtime_status()
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
serial_write('S'); // CW
#else
if (sp_state == SPINDLE_STATE_CW) { serial_write('S'); } // CW
else { serial_write('C'); } // CCW
if (sp_state == SPINDLE_STATE_CW) {
serial_write('S');
} // CW
else {
serial_write('C');
} // CCW
#endif
#else
if (sp_state & SPINDLE_STATE_CW) { serial_write('S'); } // CW
else { serial_write('C'); } // CCW
if (sp_state & SPINDLE_STATE_CW) {
serial_write('S');
} // CW
else {
serial_write('C');
} // CCW
#endif
}
if (cl_state & COOLANT_STATE_FLOOD) { serial_write('F'); }
if (cl_state & COOLANT_STATE_FLOOD) {
serial_write('F');
}
#ifdef ENABLE_M7
if (cl_state & COOLANT_STATE_MIST) { serial_write('M'); }
if (cl_state & COOLANT_STATE_MIST) {
serial_write('M');
}
#endif
}
}
@ -653,10 +707,7 @@ void report_realtime_status()
report_util_line_feed();
}
#ifdef DEBUG
void report_realtime_debug()
{
void report_realtime_debug() {
}
#endif

View file

@ -32,38 +32,36 @@ uint8_t serial_tx_buffer[TX_RING_BUFFER];
uint8_t serial_tx_buffer_head = 0;
volatile uint8_t serial_tx_buffer_tail = 0;
// Returns the number of bytes available in the RX serial buffer.
uint8_t serial_get_rx_buffer_available()
{
uint8_t serial_get_rx_buffer_available() {
uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
if (serial_rx_buffer_head >= rtail) { return(RX_BUFFER_SIZE - (serial_rx_buffer_head-rtail)); }
if (serial_rx_buffer_head >= rtail) {
return (RX_BUFFER_SIZE - (serial_rx_buffer_head - rtail));
}
return ((rtail - serial_rx_buffer_head - 1));
}
// Returns the number of bytes used in the RX serial buffer.
// NOTE: Deprecated. Not used unless classic status reports are enabled in config.h.
uint8_t serial_get_rx_buffer_count()
{
uint8_t serial_get_rx_buffer_count() {
uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
if (serial_rx_buffer_head >= rtail) { return(serial_rx_buffer_head-rtail); }
if (serial_rx_buffer_head >= rtail) {
return (serial_rx_buffer_head - rtail);
}
return (RX_BUFFER_SIZE - (rtail - serial_rx_buffer_head));
}
// Returns the number of bytes used in the TX serial buffer.
// NOTE: Not used except for debugging and ensuring no TX bottlenecks.
uint8_t serial_get_tx_buffer_count()
{
uint8_t serial_get_tx_buffer_count() {
uint8_t ttail = serial_tx_buffer_tail; // Copy to limit multiple calls to volatile
if (serial_tx_buffer_head >= ttail) { return(serial_tx_buffer_head-ttail); }
if (serial_tx_buffer_head >= ttail) {
return (serial_tx_buffer_head - ttail);
}
return (TX_RING_BUFFER - (ttail - serial_tx_buffer_head));
}
void serial_init()
{
void serial_init() {
// Set baud rate
#if BAUD_RATE < 57600
uint16_t UBRR0_value = ((F_CPU / (8L * BAUD_RATE)) - 1) / 2;
@ -81,17 +79,20 @@ void serial_init()
// defaults to 8-bit, no parity, 1 stop bit
}
// Writes one byte to the TX serial buffer. Called by main program.
void serial_write(uint8_t data) {
// Calculate next head
uint8_t next_head = serial_tx_buffer_head + 1;
if (next_head == TX_RING_BUFFER) { next_head = 0; }
if (next_head == TX_RING_BUFFER) {
next_head = 0;
}
// Wait until there is space in the buffer
while (next_head == serial_tx_buffer_tail) {
// TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
if (sys_rt_exec_state & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop.
if (sys_rt_exec_state & EXEC_RESET) {
return;
} // Only check for abort to avoid an endless loop.
}
// Store data and advance head
@ -102,10 +103,8 @@ void serial_write(uint8_t data) {
UCSR0B |= (1 << UDRIE0);
}
// Data Register Empty Interrupt handler
ISR(SERIAL_UDRE)
{
ISR(SERIAL_UDRE) {
uint8_t tail = serial_tx_buffer_tail; // Temporary serial_tx_buffer_tail (to optimize for volatile)
// Send a byte from the buffer
@ -113,18 +112,20 @@ ISR(SERIAL_UDRE)
// Update tail position
tail++;
if (tail == TX_RING_BUFFER) { tail = 0; }
if (tail == TX_RING_BUFFER) {
tail = 0;
}
serial_tx_buffer_tail = tail;
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
if (tail == serial_tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
if (tail == serial_tx_buffer_head) {
UCSR0B &= ~(1 << UDRIE0);
}
}
// Fetches the first byte in the serial read buffer. Called by main program.
uint8_t serial_read()
{
uint8_t serial_read() {
uint8_t tail = serial_rx_buffer_tail; // Temporary serial_rx_buffer_tail (to optimize for volatile)
if (serial_rx_buffer_head == tail) {
return SERIAL_NO_DATA;
@ -132,16 +133,16 @@ uint8_t serial_read()
uint8_t data = serial_rx_buffer[tail];
tail++;
if (tail == RX_RING_BUFFER) { tail = 0; }
if (tail == RX_RING_BUFFER) {
tail = 0;
}
serial_rx_buffer_tail = tail;
return data;
}
}
ISR(SERIAL_RX)
{
ISR(SERIAL_RX) {
uint8_t data = UDR0;
uint8_t next_head;
@ -162,7 +163,12 @@ ISR(SERIAL_RX)
}
break;
#ifdef DEBUG
case CMD_DEBUG_REPORT: {uint8_t sreg = SREG; cli(); bit_true(sys_rt_exec_debug,EXEC_DEBUG_REPORT); SREG = sreg;} break;
case CMD_DEBUG_REPORT: {
uint8_t sreg = SREG;
cli();
bit_true(sys_rt_exec_debug, EXEC_DEBUG_REPORT);
SREG = sreg;
} break;
#endif
case CMD_FEED_OVR_RESET: system_set_exec_motion_override_flag(EXEC_FEED_OVR_RESET); break;
case CMD_FEED_OVR_COARSE_PLUS: system_set_exec_motion_override_flag(EXEC_FEED_OVR_COARSE_PLUS); break;
@ -173,20 +179,32 @@ ISR(SERIAL_RX)
case CMD_RAPID_OVR_MEDIUM: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_MEDIUM); break;
case CMD_RAPID_OVR_LOW: system_set_exec_motion_override_flag(EXEC_RAPID_OVR_LOW); break;
case CMD_SPINDLE_OVR_RESET: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_RESET); break;
case CMD_SPINDLE_OVR_COARSE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS); break;
case CMD_SPINDLE_OVR_COARSE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS); break;
case CMD_SPINDLE_OVR_COARSE_PLUS:
system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_PLUS);
break;
case CMD_SPINDLE_OVR_COARSE_MINUS:
system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_COARSE_MINUS);
break;
case CMD_SPINDLE_OVR_FINE_PLUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_PLUS); break;
case CMD_SPINDLE_OVR_FINE_MINUS: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS); break;
case CMD_SPINDLE_OVR_FINE_MINUS:
system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_FINE_MINUS);
break;
case CMD_SPINDLE_OVR_STOP: system_set_exec_accessory_override_flag(EXEC_SPINDLE_OVR_STOP); break;
case CMD_COOLANT_FLOOD_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE); break;
case CMD_COOLANT_FLOOD_OVR_TOGGLE:
system_set_exec_accessory_override_flag(EXEC_COOLANT_FLOOD_OVR_TOGGLE);
break;
#ifdef ENABLE_M7
case CMD_COOLANT_MIST_OVR_TOGGLE: system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE); break;
case CMD_COOLANT_MIST_OVR_TOGGLE:
system_set_exec_accessory_override_flag(EXEC_COOLANT_MIST_OVR_TOGGLE);
break;
#endif
}
// Throw away any unfound extended-ASCII character by not passing it to the serial buffer.
} else { // Write character to buffer
next_head = serial_rx_buffer_head + 1;
if (next_head == RX_RING_BUFFER) { next_head = 0; }
if (next_head == RX_RING_BUFFER) {
next_head = 0;
}
// Write data to buffer unless it is full.
if (next_head != serial_rx_buffer_tail) {
@ -197,8 +215,6 @@ ISR(SERIAL_RX)
}
}
void serial_reset_read_buffer()
{
void serial_reset_read_buffer() {
serial_rx_buffer_tail = serial_rx_buffer_head;
}

View file

@ -22,7 +22,6 @@
#ifndef serial_h
#define serial_h
#ifndef RX_BUFFER_SIZE
#define RX_BUFFER_SIZE 128
#endif
@ -36,7 +35,6 @@
#define SERIAL_NO_DATA 0xff
void serial_init();
// Writes one byte to the TX serial buffer. Called by main program.

View file

@ -23,7 +23,7 @@
settings_t settings;
const __flash settings_t defaults = {\
const __flash settings_t defaults = {
.pulse_microseconds = DEFAULT_STEP_PULSE_MICROSECONDS,
.stepper_idle_lock_time = DEFAULT_STEPPER_IDLE_LOCK_TIME,
.step_invert_mask = DEFAULT_STEPPING_INVERT_MASK,
@ -38,14 +38,10 @@ const __flash settings_t defaults = {\
.homing_seek_rate = DEFAULT_HOMING_SEEK_RATE,
.homing_debounce_delay = DEFAULT_HOMING_DEBOUNCE_DELAY,
.homing_pulloff = DEFAULT_HOMING_PULLOFF,
.flags = (DEFAULT_REPORT_INCHES << BIT_REPORT_INCHES) | \
(DEFAULT_LASER_MODE << BIT_LASER_MODE) | \
(DEFAULT_INVERT_ST_ENABLE << BIT_INVERT_ST_ENABLE) | \
(DEFAULT_HARD_LIMIT_ENABLE << BIT_HARD_LIMIT_ENABLE) | \
(DEFAULT_HOMING_ENABLE << BIT_HOMING_ENABLE) | \
(DEFAULT_SOFT_LIMIT_ENABLE << BIT_SOFT_LIMIT_ENABLE) | \
(DEFAULT_INVERT_LIMIT_PINS << BIT_INVERT_LIMIT_PINS) | \
(DEFAULT_INVERT_PROBE_PIN << BIT_INVERT_PROBE_PIN),
.flags = (DEFAULT_REPORT_INCHES << BIT_REPORT_INCHES) | (DEFAULT_LASER_MODE << BIT_LASER_MODE) |
(DEFAULT_INVERT_ST_ENABLE << BIT_INVERT_ST_ENABLE) | (DEFAULT_HARD_LIMIT_ENABLE << BIT_HARD_LIMIT_ENABLE) |
(DEFAULT_HOMING_ENABLE << BIT_HOMING_ENABLE) | (DEFAULT_SOFT_LIMIT_ENABLE << BIT_SOFT_LIMIT_ENABLE) |
(DEFAULT_INVERT_LIMIT_PINS << BIT_INVERT_LIMIT_PINS) | (DEFAULT_INVERT_PROBE_PIN << BIT_INVERT_PROBE_PIN),
.steps_per_mm[X_AXIS] = DEFAULT_X_STEPS_PER_MM,
.steps_per_mm[Y_AXIS] = DEFAULT_Y_STEPS_PER_MM,
.steps_per_mm[Z_AXIS] = DEFAULT_Z_STEPS_PER_MM,
@ -59,10 +55,8 @@ const __flash settings_t defaults = {\
.max_travel[Y_AXIS] = (-DEFAULT_Y_MAX_TRAVEL),
.max_travel[Z_AXIS] = (-DEFAULT_Z_MAX_TRAVEL)};
// Method to store startup lines into EEPROM
void settings_store_startup_line(uint8_t n, char *line)
{
void settings_store_startup_line(uint8_t n, char *line) {
#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
protocol_buffer_synchronize(); // A startup line may contain a motion and be executing.
#endif
@ -70,19 +64,15 @@ void settings_store_startup_line(uint8_t n, char *line)
memcpy_to_eeprom_with_checksum(addr, (char *)line, LINE_BUFFER_SIZE);
}
// Method to store build info into EEPROM
// NOTE: This function can only be called in IDLE state.
void settings_store_build_info(char *line)
{
void settings_store_build_info(char *line) {
// Build info can only be stored when state is IDLE.
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_BUILD_INFO, (char *)line, LINE_BUFFER_SIZE);
}
// Method to store coord data parameters into EEPROM
void settings_write_coord_data(uint8_t coord_select, float *coord_data)
{
void settings_write_coord_data(uint8_t coord_select, float *coord_data) {
#ifdef FORCE_BUFFER_SYNC_DURING_EEPROM_WRITE
protocol_buffer_synchronize();
#endif
@ -90,16 +80,13 @@ void settings_write_coord_data(uint8_t coord_select, float *coord_data)
memcpy_to_eeprom_with_checksum(addr, (char *)coord_data, sizeof(float) * N_AXIS);
}
// Method to store Grbl global settings struct and version number into EEPROM
// NOTE: This function can only be called in IDLE state.
void write_global_settings()
{
void write_global_settings() {
eeprom_put_char(0, SETTINGS_VERSION);
memcpy_to_eeprom_with_checksum(EEPROM_ADDR_GLOBAL, (char *)&settings, sizeof(settings_t));
}
// Method to restore EEPROM-saved Grbl global settings back to defaults.
void settings_restore(uint8_t restore_flag) {
if (restore_flag & SETTINGS_RESTORE_DEFAULTS) {
@ -111,7 +98,9 @@ void settings_restore(uint8_t restore_flag) {
uint8_t idx;
float coord_data[N_AXIS];
memset(&coord_data, 0, sizeof(coord_data));
for (idx=0; idx <= SETTING_INDEX_NCOORD; idx++) { settings_write_coord_data(idx, coord_data); }
for (idx = 0; idx <= SETTING_INDEX_NCOORD; idx++) {
settings_write_coord_data(idx, coord_data);
}
}
if (restore_flag & SETTINGS_RESTORE_STARTUP_LINES) {
@ -131,10 +120,8 @@ void settings_restore(uint8_t restore_flag) {
}
}
// Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_startup_line(uint8_t n, char *line)
{
uint8_t settings_read_startup_line(uint8_t n, char *line) {
uint32_t addr = n * (LINE_BUFFER_SIZE + 1) + EEPROM_ADDR_STARTUP_BLOCK;
if (!(memcpy_from_eeprom_with_checksum((char *)line, addr, LINE_BUFFER_SIZE))) {
// Reset line with default value
@ -145,10 +132,8 @@ uint8_t settings_read_startup_line(uint8_t n, char *line)
return (true);
}
// Reads startup line from EEPROM. Updated pointed line string data.
uint8_t settings_read_build_info(char *line)
{
uint8_t settings_read_build_info(char *line) {
if (!(memcpy_from_eeprom_with_checksum((char *)line, EEPROM_ADDR_BUILD_INFO, LINE_BUFFER_SIZE))) {
// Reset line with default value
line[0] = 0; // Empty line
@ -158,10 +143,8 @@ uint8_t settings_read_build_info(char *line)
return (true);
}
// Read selected coordinate data from EEPROM. Updates pointed coord_data value.
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data)
{
uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data) {
uint32_t addr = coord_select * (sizeof(float) * N_AXIS + 1) + EEPROM_ADDR_PARAMETERS;
if (!(memcpy_from_eeprom_with_checksum((char *)coord_data, addr, sizeof(float) * N_AXIS))) {
// Reset with default zero vector
@ -172,7 +155,6 @@ uint8_t settings_read_coord_data(uint8_t coord_select, float *coord_data)
return (true);
}
// Reads Grbl global settings struct from EEPROM.
uint8_t read_global_settings() {
// Check version-byte of eeprom
@ -188,10 +170,11 @@ uint8_t read_global_settings() {
return (true);
}
// A helper method to set settings from command line
uint8_t settings_store_global_setting(uint8_t parameter, float value) {
if (value < 0.0) { return(STATUS_NEGATIVE_VALUE); }
if (value < 0.0) {
return (STATUS_NEGATIVE_VALUE);
}
if (parameter >= AXIS_SETTINGS_START_VAL) {
// Store axis configuration. Axis numbering sequence set by AXIS_SETTING defines.
// NOTE: Ensure the setting index corresponds to the report.c settings printout.
@ -203,24 +186,32 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
switch (set_idx) {
case 0:
#ifdef MAX_STEP_RATE_HZ
if (value*settings.max_rate[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); }
if (value * settings.max_rate[parameter] > (MAX_STEP_RATE_HZ * 60.0)) {
return (STATUS_MAX_STEP_RATE_EXCEEDED);
}
#endif
settings.steps_per_mm[parameter] = value;
break;
case 1:
#ifdef MAX_STEP_RATE_HZ
if (value*settings.steps_per_mm[parameter] > (MAX_STEP_RATE_HZ*60.0)) { return(STATUS_MAX_STEP_RATE_EXCEEDED); }
if (value * settings.steps_per_mm[parameter] > (MAX_STEP_RATE_HZ * 60.0)) {
return (STATUS_MAX_STEP_RATE_EXCEEDED);
}
#endif
settings.max_rate[parameter] = value;
break;
case 2: settings.acceleration[parameter] = value*60*60; break; // Convert to mm/min^2 for grbl internal use.
case 2:
settings.acceleration[parameter] = value * 60 * 60;
break; // Convert to mm/min^2 for grbl internal use.
case 3: settings.max_travel[parameter] = -value; break; // Store as negative for grbl internal use.
}
break; // Exit while-loop after setting has been configured and proceed to the EEPROM write call.
} else {
set_idx++;
// If axis index greater than N_AXIS or setting index greater than number of axis settings, error out.
if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) { return(STATUS_INVALID_STATEMENT); }
if ((parameter < AXIS_SETTINGS_INCREMENT) || (set_idx == AXIS_N_SETTINGS)) {
return (STATUS_INVALID_STATEMENT);
}
parameter -= AXIS_SETTINGS_INCREMENT;
}
}
@ -229,8 +220,11 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
uint8_t int_value = trunc(value);
switch (parameter) {
case 0:
if (int_value < 3) { return(STATUS_SETTING_STEP_PULSE_MIN); }
settings.pulse_microseconds = int_value; break;
if (int_value < 3) {
return (STATUS_SETTING_STEP_PULSE_MIN);
}
settings.pulse_microseconds = int_value;
break;
case 1: settings.stepper_idle_lock_time = int_value; break;
case 2:
settings.step_invert_mask = int_value;
@ -241,40 +235,60 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
st_generate_step_dir_invert_masks(); // Regenerate step and direction port invert masks.
break;
case 4: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) { settings.flags |= BITFLAG_INVERT_ST_ENABLE; }
else { settings.flags &= ~BITFLAG_INVERT_ST_ENABLE; }
if (int_value) {
settings.flags |= BITFLAG_INVERT_ST_ENABLE;
} else {
settings.flags &= ~BITFLAG_INVERT_ST_ENABLE;
}
break;
case 5: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) { settings.flags |= BITFLAG_INVERT_LIMIT_PINS; }
else { settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS; }
if (int_value) {
settings.flags |= BITFLAG_INVERT_LIMIT_PINS;
} else {
settings.flags &= ~BITFLAG_INVERT_LIMIT_PINS;
}
break;
case 6: // Reset to ensure change. Immediate re-init may cause problems.
if (int_value) { settings.flags |= BITFLAG_INVERT_PROBE_PIN; }
else { settings.flags &= ~BITFLAG_INVERT_PROBE_PIN; }
if (int_value) {
settings.flags |= BITFLAG_INVERT_PROBE_PIN;
} else {
settings.flags &= ~BITFLAG_INVERT_PROBE_PIN;
}
probe_configure_invert_mask(false);
break;
case 10: settings.status_report_mask = int_value; break;
case 11: settings.junction_deviation = value; break;
case 12: settings.arc_tolerance = value; break;
case 13:
if (int_value) { settings.flags |= BITFLAG_REPORT_INCHES; }
else { settings.flags &= ~BITFLAG_REPORT_INCHES; }
if (int_value) {
settings.flags |= BITFLAG_REPORT_INCHES;
} else {
settings.flags &= ~BITFLAG_REPORT_INCHES;
}
system_flag_wco_change(); // Make sure WCO is immediately updated.
break;
case 20:
if (int_value) {
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) { return(STATUS_SOFT_LIMIT_ERROR); }
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) {
return (STATUS_SOFT_LIMIT_ERROR);
}
settings.flags |= BITFLAG_SOFT_LIMIT_ENABLE;
} else { settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; }
} else {
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE;
}
break;
case 21:
if (int_value) { settings.flags |= BITFLAG_HARD_LIMIT_ENABLE; }
else { settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE; }
if (int_value) {
settings.flags |= BITFLAG_HARD_LIMIT_ENABLE;
} else {
settings.flags &= ~BITFLAG_HARD_LIMIT_ENABLE;
}
limits_init(); // Re-init to immediately change. NOTE: Nice to have but could be problematic later.
break;
case 22:
if (int_value) { settings.flags |= BITFLAG_HOMING_ENABLE; }
else {
if (int_value) {
settings.flags |= BITFLAG_HOMING_ENABLE;
} else {
settings.flags &= ~BITFLAG_HOMING_ENABLE;
settings.flags &= ~BITFLAG_SOFT_LIMIT_ENABLE; // Force disable soft-limits.
}
@ -284,25 +298,32 @@ uint8_t settings_store_global_setting(uint8_t parameter, float value) {
case 25: settings.homing_seek_rate = value; break;
case 26: settings.homing_debounce_delay = int_value; break;
case 27: settings.homing_pulloff = value; break;
case 30: settings.rpm_max = value; spindle_init(); break; // Re-initialize spindle rpm calibration
case 31: settings.rpm_min = value; spindle_init(); break; // Re-initialize spindle rpm calibration
case 30:
settings.rpm_max = value;
spindle_init();
break; // Re-initialize spindle rpm calibration
case 31:
settings.rpm_min = value;
spindle_init();
break; // Re-initialize spindle rpm calibration
case 32:
#ifdef VARIABLE_SPINDLE
if (int_value) { settings.flags |= BITFLAG_LASER_MODE; }
else { settings.flags &= ~BITFLAG_LASER_MODE; }
if (int_value) {
settings.flags |= BITFLAG_LASER_MODE;
} else {
settings.flags &= ~BITFLAG_LASER_MODE;
}
#else
return (STATUS_SETTING_DISABLED_LASER);
#endif
break;
default:
return(STATUS_INVALID_STATEMENT);
default: return (STATUS_INVALID_STATEMENT);
}
}
write_global_settings();
return (STATUS_OK);
}
// Initialize the config subsystem
void settings_init() {
if (!read_global_settings()) {
@ -312,29 +333,35 @@ void settings_init() {
}
}
// Returns step pin mask according to Grbl internal axis indexing.
uint8_t get_step_pin_mask(uint8_t axis_idx)
{
if ( axis_idx == X_AXIS ) { return((1<<X_STEP_BIT)); }
if ( axis_idx == Y_AXIS ) { return((1<<Y_STEP_BIT)); }
uint8_t get_step_pin_mask(uint8_t axis_idx) {
if (axis_idx == X_AXIS) {
return ((1 << X_STEP_BIT));
}
if (axis_idx == Y_AXIS) {
return ((1 << Y_STEP_BIT));
}
return ((1 << Z_STEP_BIT));
}
// Returns direction pin mask according to Grbl internal axis indexing.
uint8_t get_direction_pin_mask(uint8_t axis_idx)
{
if ( axis_idx == X_AXIS ) { return((1<<X_DIRECTION_BIT)); }
if ( axis_idx == Y_AXIS ) { return((1<<Y_DIRECTION_BIT)); }
uint8_t get_direction_pin_mask(uint8_t axis_idx) {
if (axis_idx == X_AXIS) {
return ((1 << X_DIRECTION_BIT));
}
if (axis_idx == Y_AXIS) {
return ((1 << Y_DIRECTION_BIT));
}
return ((1 << Z_DIRECTION_BIT));
}
// Returns limit pin mask according to Grbl internal axis indexing.
uint8_t get_limit_pin_mask(uint8_t axis_idx)
{
if ( axis_idx == X_AXIS ) { return((1<<X_LIMIT_BIT)); }
if ( axis_idx == Y_AXIS ) { return((1<<Y_LIMIT_BIT)); }
uint8_t get_limit_pin_mask(uint8_t axis_idx) {
if (axis_idx == X_AXIS) {
return ((1 << X_LIMIT_BIT));
}
if (axis_idx == Y_AXIS) {
return ((1 << Y_LIMIT_BIT));
}
return ((1 << Z_LIMIT_BIT));
}

View file

@ -24,7 +24,6 @@
#include "grbl.h"
// Version of the EEPROM data. Will be used to migrate existing data from older versions of Grbl
// when firmware is upgraded. Always stored in byte 0 of eeprom
#define SETTINGS_VERSION 10 // NOTE: Check settings_reset() when moving to next version.
@ -111,6 +110,7 @@ typedef struct {
uint16_t homing_debounce_delay;
float homing_pulloff;
} settings_t;
extern settings_t settings;
// Initialize the configuration subsystem (load settings from EEPROM)
@ -149,5 +149,4 @@ uint8_t get_direction_pin_mask(uint8_t i);
// Returns the limit pin mask according to Grbl's internal axis numbering
uint8_t get_limit_pin_mask(uint8_t i);
#endif

View file

@ -21,14 +21,11 @@
#include "grbl.h"
#ifdef VARIABLE_SPINDLE
static float pwm_gradient; // Precalulated value to speed up rpm to PWM conversions.
#endif
void spindle_init()
{
void spindle_init() {
#ifdef VARIABLE_SPINDLE
// Configure variable spindle PWM and enable pin, if requried. On the Uno, PWM and enable are
// combined unless configured otherwise.
@ -53,24 +50,29 @@ void spindle_init()
spindle_stop();
}
uint8_t spindle_get_state()
{
uint8_t spindle_get_state() {
#ifdef VARIABLE_SPINDLE
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
// No spindle direction output pin.
#ifdef INVERT_SPINDLE_ENABLE_PIN
if (bit_isfalse(SPINDLE_ENABLE_PORT,(1<<SPINDLE_ENABLE_BIT))) { return(SPINDLE_STATE_CW); }
if (bit_isfalse(SPINDLE_ENABLE_PORT, (1 << SPINDLE_ENABLE_BIT))) {
return (SPINDLE_STATE_CW);
}
#else
if (bit_istrue(SPINDLE_ENABLE_PORT,(1<<SPINDLE_ENABLE_BIT))) { return(SPINDLE_STATE_CW); }
if (bit_istrue(SPINDLE_ENABLE_PORT, (1 << SPINDLE_ENABLE_BIT))) {
return (SPINDLE_STATE_CW);
}
#endif
#else
if (SPINDLE_TCCRA_REGISTER & (1 << SPINDLE_COMB_BIT)) { // Check if PWM is enabled.
#ifdef ENABLE_DUAL_AXIS
return (SPINDLE_STATE_CW);
#else
if (SPINDLE_DIRECTION_PORT & (1<<SPINDLE_DIRECTION_BIT)) { return(SPINDLE_STATE_CCW); }
else { return(SPINDLE_STATE_CW); }
if (SPINDLE_DIRECTION_PORT & (1 << SPINDLE_DIRECTION_BIT)) {
return (SPINDLE_STATE_CCW);
} else {
return (SPINDLE_STATE_CW);
}
#endif
}
#endif
@ -83,20 +85,21 @@ uint8_t spindle_get_state()
#ifdef ENABLE_DUAL_AXIS
return (SPINDLE_STATE_CW);
#else
if (SPINDLE_DIRECTION_PORT & (1<<SPINDLE_DIRECTION_BIT)) { return(SPINDLE_STATE_CCW); }
else { return(SPINDLE_STATE_CW); }
if (SPINDLE_DIRECTION_PORT & (1 << SPINDLE_DIRECTION_BIT)) {
return (SPINDLE_STATE_CCW);
} else {
return (SPINDLE_STATE_CW);
}
#endif
}
#endif
return (SPINDLE_STATE_DISABLE);
}
// Disables the spindle and sets PWM output to zero when PWM variable spindle speed is enabled.
// Called by various main program and ISR routines. Keep routine small, fast, and efficient.
// Called by spindle_init(), spindle_set_speed(), spindle_set_state(), and mc_reset().
void spindle_stop()
{
void spindle_stop() {
#ifdef VARIABLE_SPINDLE
SPINDLE_TCCRA_REGISTER &= ~(1 << SPINDLE_COMB_BIT); // Disable PWM. Output voltage is zero.
#ifdef USE_SPINDLE_DIR_AS_ENABLE_PIN
@ -115,12 +118,10 @@ void spindle_stop()
#endif
}
#ifdef VARIABLE_SPINDLE
// Sets spindle speed PWM output and enable pin, if configured. Called by spindle_set_state()
// and stepper ISR. Keep routine small and efficient.
void spindle_set_speed(uint8_t pwm_value)
{
void spindle_set_speed(uint8_t pwm_value) {
SPINDLE_OCR_REGISTER = pwm_value; // Set PWM output level.
#ifdef SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED
if (pwm_value == SPINDLE_PWM_OFF_VALUE) {
@ -142,7 +143,6 @@ void spindle_stop()
#endif
}
#ifdef ENABLE_PIECEWISE_LINEAR_SPINDLE
// Called by spindle_set_state() and step segment generator. Keep routine small and efficient.
@ -218,7 +218,6 @@ void spindle_stop()
#endif
#endif
// Immediately sets spindle running state with direction and spindle rpm via PWM, if enabled.
// Called by g-code parser spindle_sync(), parking retract and restore, g-code program end,
// sleep, and spindle stop override.
@ -228,7 +227,9 @@ void spindle_stop()
void _spindle_set_state(uint8_t state)
#endif
{
if (sys.abort) { return; } // Block during abort.
if (sys.abort) {
return;
} // Block during abort.
if (state == SPINDLE_DISABLE) { // Halt or set spindle direction and rpm.
@ -250,12 +251,14 @@ void spindle_stop()
#ifdef VARIABLE_SPINDLE
// NOTE: Assumes all calls to this function is when Grbl is not moving or must remain off.
if (settings.flags & BITFLAG_LASER_MODE) {
if (state == SPINDLE_ENABLE_CCW) { rpm = 0.0; } // TODO: May need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE);
if (state == SPINDLE_ENABLE_CCW) {
rpm = 0.0;
} // TODO: May need to be rpm_min*(100/MAX_SPINDLE_SPEED_OVERRIDE);
}
spindle_set_speed(spindle_compute_pwm_value(rpm));
#endif
#if (defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && \
!defined(SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED)) || !defined(VARIABLE_SPINDLE)
#if (defined(USE_SPINDLE_DIR_AS_ENABLE_PIN) && !defined(SPINDLE_ENABLE_OFF_WITH_ZERO_SPEED)) || \
!defined(VARIABLE_SPINDLE)
// NOTE: Without variable spindle, the enable bit should just turn on or off, regardless
// if the spindle speed value is zero, as its ignored anyhow.
#ifdef INVERT_SPINDLE_ENABLE_PIN
@ -264,26 +267,26 @@ void spindle_stop()
SPINDLE_ENABLE_PORT |= (1 << SPINDLE_ENABLE_BIT);
#endif
#endif
}
sys.report_ovr_counter = 0; // Set to report change immediately
}
// G-code parser entry-point for setting spindle state. Forces a planner buffer sync and bails
// if an abort or check-mode is active.
#ifdef VARIABLE_SPINDLE
void spindle_sync(uint8_t state, float rpm)
{
if (sys.state == STATE_CHECK_MODE) { return; }
void spindle_sync(uint8_t state, float rpm) {
if (sys.state == STATE_CHECK_MODE) {
return;
}
protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed.
spindle_set_state(state, rpm);
}
#else
void _spindle_sync(uint8_t state)
{
if (sys.state == STATE_CHECK_MODE) { return; }
void _spindle_sync(uint8_t state) {
if (sys.state == STATE_CHECK_MODE) {
return;
}
protocol_buffer_synchronize(); // Empty planner buffer to ensure spindle is set when programmed.
_spindle_set_state(state);
}

View file

@ -29,7 +29,6 @@
#define SPINDLE_STATE_CW bit(0)
#define SPINDLE_STATE_CCW bit(1)
// Initializes spindle pins and hardware PWM, if enabled.
void spindle_init();
@ -69,5 +68,4 @@ uint8_t spindle_get_state();
// Stop and start spindle routines. Called by all spindle routines and stepper ISR.
void spindle_stop();
#endif

View file

@ -21,7 +21,6 @@
#include "grbl.h"
// Some useful constants.
#define DT_SEGMENT (1.0 / (ACCELERATION_TICKS_PER_SECOND * 60.0)) // min/segment
#define REQ_MM_INCREMENT_SCALAR 1.25
@ -56,7 +55,6 @@
#endif
#endif
// Stores the planner block Bresenham algorithm execution data for the segments in the segment
// buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will
// never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1).
@ -74,6 +72,7 @@ typedef struct {
uint8_t is_pwm_rate_adjusted; // Tracks motions that require constant laser power/rate
#endif
} st_block_t;
static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE - 1];
// Primary stepper segment ring buffer. Contains small, short line segments for the stepper
@ -93,14 +92,14 @@ typedef struct {
uint8_t spindle_pwm;
#endif
} segment_t;
static segment_t segment_buffer[SEGMENT_BUFFER_SIZE];
// Stepper ISR data struct. Contains the running data for the main stepper ISR.
typedef struct {
// Used by the bresenham line algorithm
uint32_t counter_x, // Counter variables for the bresenham line tracer
counter_y,
counter_z;
counter_y, counter_z;
#ifdef STEP_PULSE_DELAY
uint8_t step_bits; // Stores out_bits output to complete the step pulse delay
#endif
@ -122,6 +121,7 @@ typedef struct {
st_block_t *exec_block; // Pointer to the block data for the segment being executed
segment_t *exec_segment; // Pointer to the segment being executed
} stepper_t;
static stepper_t st;
// Step segment ring buffer indices
@ -177,8 +177,8 @@ typedef struct {
uint8_t current_spindle_pwm;
#endif
} st_prep_t;
static st_prep_t prep;
static st_prep_t prep;
/* BLOCK VELOCITY PROFILE DEFINITION
__________________________
@ -218,14 +218,15 @@ static st_prep_t prep;
are shown and defined in the above illustration.
*/
// Stepper state initialization. Cycle should only start if the st.cycle_start flag is
// enabled. Startup init and limits call this function but shouldn't start the cycle.
void st_wake_up()
{
void st_wake_up() {
// Enable stepper drivers.
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT); }
else { STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT); }
if (bit_istrue(settings.flags, BITFLAG_INVERT_ST_ENABLE)) {
STEPPERS_DISABLE_PORT |= (1 << STEPPERS_DISABLE_BIT);
} else {
STEPPERS_DISABLE_PORT &= ~(1 << STEPPERS_DISABLE_BIT);
}
// Initialize stepper output bits to ensure first ISR call does not step.
st.step_outbits = step_port_invert_mask;
@ -245,10 +246,8 @@ void st_wake_up()
TIMSK1 |= (1 << OCIE1A);
}
// Stepper shutdown
void st_go_idle()
{
void st_go_idle() {
// Disable Stepper Driver Interrupt. Allow Stepper Port Reset Interrupt to finish, if active.
TIMSK1 &= ~(1 << OCIE1A); // Disable Timer1 interrupt
TCCR1B = (TCCR1B & ~((1 << CS12) | (1 << CS11))) | (1 << CS10); // Reset clock to no prescaling.
@ -256,17 +255,22 @@ void st_go_idle()
// Set stepper driver idle state, disabled or enabled, depending on settings and circumstances.
bool pin_state = false; // Keep enabled.
if (((settings.stepper_idle_lock_time != 0xff) || sys_rt_exec_alarm || sys.state == STATE_SLEEP) && sys.state != STATE_HOMING) {
if (((settings.stepper_idle_lock_time != 0xff) || sys_rt_exec_alarm || sys.state == STATE_SLEEP) &&
sys.state != STATE_HOMING) {
// Force stepper dwell to lock axes for a defined amount of time to ensure the axes come to a complete
// stop and not drift from residual inertial forces at the end of the last movement.
delay_ms(settings.stepper_idle_lock_time);
pin_state = true; // Override. Disable steppers.
}
if (bit_istrue(settings.flags,BITFLAG_INVERT_ST_ENABLE)) { pin_state = !pin_state; } // Apply pin invert.
if (pin_state) { STEPPERS_DISABLE_PORT |= (1<<STEPPERS_DISABLE_BIT); }
else { STEPPERS_DISABLE_PORT &= ~(1<<STEPPERS_DISABLE_BIT); }
if (bit_istrue(settings.flags, BITFLAG_INVERT_ST_ENABLE)) {
pin_state = !pin_state;
} // Apply pin invert.
if (pin_state) {
STEPPERS_DISABLE_PORT |= (1 << STEPPERS_DISABLE_BIT);
} else {
STEPPERS_DISABLE_PORT &= ~(1 << STEPPERS_DISABLE_BIT);
}
}
/* "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Grbl. Grbl employs
the venerable Bresenham line algorithm to manage and exactly synchronize multi-axis moves.
@ -316,9 +320,10 @@ void st_go_idle()
// TODO: Replace direct updating of the int32 position counters in the ISR somehow. Perhaps use smaller
// int8 variables and update position counters only when a segment completes. This can get complicated
// with probing and homing cycles that require true real-time positions.
ISR(TIMER1_COMPA_vect)
{
if (busy) { return; } // The busy-flag is used to avoid reentering this interrupt
ISR(TIMER1_COMPA_vect) {
if (busy) {
return;
} // The busy-flag is used to avoid reentering this interrupt
// Set the direction pins a couple of nanoseconds before we step the steppers
DIRECTION_PORT = (DIRECTION_PORT & ~DIRECTION_MASK) | (st.dir_outbits & DIRECTION_MASK);
@ -394,16 +399,19 @@ ISR(TIMER1_COMPA_vect)
st_go_idle();
#ifdef VARIABLE_SPINDLE
// 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); }
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.
}
}
// Check probing state.
if (sys_probe_state == PROBE_ACTIVE) { probe_state_monitor(); }
if (sys_probe_state == PROBE_ACTIVE) {
probe_state_monitor();
}
// Reset step out bits.
st.step_outbits = 0;
@ -423,8 +431,11 @@ ISR(TIMER1_COMPA_vect)
st.step_outbits_dual = (1 << DUAL_STEP_BIT);
#endif
st.counter_x -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<X_DIRECTION_BIT)) { sys_position[X_AXIS]--; }
else { sys_position[X_AXIS]++; }
if (st.exec_block->direction_bits & (1 << X_DIRECTION_BIT)) {
sys_position[X_AXIS]--;
} else {
sys_position[X_AXIS]++;
}
}
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_y += st.steps[Y_AXIS];
@ -437,8 +448,11 @@ ISR(TIMER1_COMPA_vect)
st.step_outbits_dual = (1 << DUAL_STEP_BIT);
#endif
st.counter_y -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<Y_DIRECTION_BIT)) { sys_position[Y_AXIS]--; }
else { sys_position[Y_AXIS]++; }
if (st.exec_block->direction_bits & (1 << Y_DIRECTION_BIT)) {
sys_position[Y_AXIS]--;
} else {
sys_position[Y_AXIS]++;
}
}
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
st.counter_z += st.steps[Z_AXIS];
@ -448,8 +462,11 @@ ISR(TIMER1_COMPA_vect)
if (st.counter_z > st.exec_block->step_event_count) {
st.step_outbits |= (1 << Z_STEP_BIT);
st.counter_z -= st.exec_block->step_event_count;
if (st.exec_block->direction_bits & (1<<Z_DIRECTION_BIT)) { sys_position[Z_AXIS]--; }
else { sys_position[Z_AXIS]++; }
if (st.exec_block->direction_bits & (1 << Z_DIRECTION_BIT)) {
sys_position[Z_AXIS]--;
} else {
sys_position[Z_AXIS]++;
}
}
// During a homing cycle, lock out and prevent desired axes from moving.
@ -464,7 +481,9 @@ ISR(TIMER1_COMPA_vect)
if (st.step_count == 0) {
// Segment is complete. Discard current segment and advance segment indexing.
st.exec_segment = NULL;
if ( ++segment_buffer_tail == SEGMENT_BUFFER_SIZE) { segment_buffer_tail = 0; }
if (++segment_buffer_tail == SEGMENT_BUFFER_SIZE) {
segment_buffer_tail = 0;
}
}
st.step_outbits ^= step_port_invert_mask; // Apply step port invert mask
@ -474,7 +493,6 @@ ISR(TIMER1_COMPA_vect)
busy = false;
}
/* The Stepper Port Reset Interrupt: Timer0 OVF interrupt handles the falling edge of the step
pulse. This should always trigger before the next Timer1 COMPA interrupt and independently
finish, if Timer1 is disabled after completing a move.
@ -486,8 +504,7 @@ ISR(TIMER1_COMPA_vect)
// This interrupt is enabled by ISR_TIMER1_COMPAREA when it sets the motor port bits to execute
// a step. This ISR resets the motor port after a short period (settings.pulse_microseconds)
// completing one step cycle.
ISR(TIMER0_OVF_vect)
{
ISR(TIMER0_OVF_vect) {
// Reset stepping pins (leave the direction pins)
STEP_PORT = (STEP_PORT & ~STEP_MASK) | (step_port_invert_mask & STEP_MASK);
#ifdef ENABLE_DUAL_AXIS
@ -501,8 +518,7 @@ ISR(TIMER0_OVF_vect)
// will then trigger after the appropriate settings.pulse_microseconds, as in normal operation.
// The new timing between direction, step pulse, and step complete events are setup in the
// st_wake_up() routine.
ISR(TIMER0_COMPA_vect)
{
ISR(TIMER0_COMPA_vect) {
STEP_PORT = st.step_bits; // Begin step pulse.
#ifdef ENABLE_DUAL_AXIS
STEP_PORT_DUAL = st.step_bits_dual;
@ -510,30 +526,34 @@ ISR(TIMER0_OVF_vect)
}
#endif
// Generates the step and direction port invert masks used in the Stepper Interrupt Driver.
void st_generate_step_dir_invert_masks()
{
void st_generate_step_dir_invert_masks() {
uint8_t idx;
step_port_invert_mask = 0;
dir_port_invert_mask = 0;
for (idx = 0; idx < N_AXIS; idx++) {
if (bit_istrue(settings.step_invert_mask,bit(idx))) { step_port_invert_mask |= get_step_pin_mask(idx); }
if (bit_istrue(settings.dir_invert_mask,bit(idx))) { dir_port_invert_mask |= get_direction_pin_mask(idx); }
if (bit_istrue(settings.step_invert_mask, bit(idx))) {
step_port_invert_mask |= get_step_pin_mask(idx);
}
if (bit_istrue(settings.dir_invert_mask, bit(idx))) {
dir_port_invert_mask |= get_direction_pin_mask(idx);
}
}
#ifdef ENABLE_DUAL_AXIS
step_port_invert_mask_dual = 0;
dir_port_invert_mask_dual = 0;
// NOTE: Dual axis invert uses the N_AXIS bit to set step and direction invert pins.
if (bit_istrue(settings.step_invert_mask,bit(N_AXIS))) { step_port_invert_mask_dual = (1<<DUAL_STEP_BIT); }
if (bit_istrue(settings.dir_invert_mask,bit(N_AXIS))) { dir_port_invert_mask_dual = (1<<DUAL_DIRECTION_BIT); }
if (bit_istrue(settings.step_invert_mask, bit(N_AXIS))) {
step_port_invert_mask_dual = (1 << DUAL_STEP_BIT);
}
if (bit_istrue(settings.dir_invert_mask, bit(N_AXIS))) {
dir_port_invert_mask_dual = (1 << DUAL_DIRECTION_BIT);
}
#endif
}
// Reset and clear stepper subsystem variables
void st_reset()
{
void st_reset() {
// Initialize stepper driver idle state.
st_go_idle();
@ -561,10 +581,8 @@ void st_reset()
#endif
}
// Initialize and start the stepper motor subsystem
void stepper_init()
{
void stepper_init() {
// Configure step and direction interface pins
STEP_DDR |= STEP_MASK;
STEPPERS_DISABLE_DDR |= 1 << STEPPERS_DISABLE_BIT;
@ -593,10 +611,8 @@ void stepper_init()
#endif
}
// Called by planner_recalculate() when the executing block is updated by the new plan.
void st_update_plan_block_parameters()
{
void st_update_plan_block_parameters() {
if (pl_block != NULL) { // Ignore if at start of a new block.
prep.recalculate_flag |= PREP_FLAG_RECALCULATE;
pl_block->entry_speed_sqr = prep.current_speed * prep.current_speed; // Update entry speed.
@ -604,20 +620,18 @@ void st_update_plan_block_parameters()
}
}
// Increments the step segment buffer block data ring buffer.
static uint8_t st_next_block_index(uint8_t block_index)
{
static uint8_t st_next_block_index(uint8_t block_index) {
block_index++;
if ( block_index == (SEGMENT_BUFFER_SIZE-1) ) { return(0); }
if (block_index == (SEGMENT_BUFFER_SIZE - 1)) {
return (0);
}
return (block_index);
}
#ifdef PARKING_ENABLE
// Changes the run state of the step segment buffer to execute the special parking motion.
void st_parking_setup_buffer()
{
void st_parking_setup_buffer() {
// Store step execution data of partially completed block, if necessary.
if (prep.recalculate_flag & PREP_FLAG_HOLD_PARTIAL_BLOCK) {
prep.last_st_block_index = prep.st_block_index;
@ -631,10 +645,8 @@ static uint8_t st_next_block_index(uint8_t block_index)
pl_block = NULL; // Always reset parking motion to reload new block.
}
// Restores the step segment buffer to the normal run state after a parking motion.
void st_parking_restore_buffer()
{
void st_parking_restore_buffer() {
// Restore step execution data and flags of partially completed block, if necessary.
if (prep.recalculate_flag & PREP_FLAG_HOLD_PARTIAL_BLOCK) {
st_prep_block = &st_block_buffer[prep.last_st_block_index];
@ -651,7 +663,6 @@ static uint8_t st_next_block_index(uint8_t block_index)
}
#endif
/* Prepares step segment buffer. Continuously called from main program.
The segment buffer is an intermediary buffer interface between the execution of steps
@ -665,10 +676,11 @@ static uint8_t st_next_block_index(uint8_t block_index)
Currently, the segment buffer conservatively holds roughly up to 40-50 msec of steps.
NOTE: Computation units are in steps, millimeters, and minutes.
*/
void st_prep_buffer()
{
void st_prep_buffer() {
// Block step prep buffer, while in a suspend state and there is no suspend motion to execute.
if (bit_istrue(sys.step_control,STEP_CONTROL_END_MOTION)) { return; }
if (bit_istrue(sys.step_control, STEP_CONTROL_END_MOTION)) {
return;
}
while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer.
@ -676,16 +688,24 @@ void st_prep_buffer()
if (pl_block == NULL) {
// Query planner for a queued block
if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) { pl_block = plan_get_system_motion_block(); }
else { pl_block = plan_get_current_block(); }
if (pl_block == NULL) { return; } // No planner blocks. Exit.
if (sys.step_control & STEP_CONTROL_EXECUTE_SYS_MOTION) {
pl_block = plan_get_system_motion_block();
} else {
pl_block = plan_get_current_block();
}
if (pl_block == NULL) {
return;
} // No planner blocks. Exit.
// Check if we need to only recompute the velocity profile or load a new block.
if (prep.recalculate_flag & PREP_FLAG_RECALCULATE) {
#ifdef PARKING_ENABLE
if (prep.recalculate_flag & PREP_FLAG_PARKING) { prep.recalculate_flag &= ~(PREP_FLAG_RECALCULATE); }
else { prep.recalculate_flag = false; }
if (prep.recalculate_flag & PREP_FLAG_PARKING) {
prep.recalculate_flag &= ~(PREP_FLAG_RECALCULATE);
} else {
prep.recalculate_flag = false;
}
#else
prep.recalculate_flag = false;
#endif
@ -707,17 +727,23 @@ void st_prep_buffer()
if (st_prep_block->direction_bits & (1 << Y_DIRECTION_BIT)) {
#endif
st_prep_block->direction_bits_dual = (1 << DUAL_DIRECTION_BIT);
} else { st_prep_block->direction_bits_dual = 0; }
} else {
st_prep_block->direction_bits_dual = 0;
}
#endif
uint8_t idx;
#ifndef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps[idx] = (pl_block->steps[idx] << 1); }
for (idx = 0; idx < N_AXIS; idx++) {
st_prep_block->steps[idx] = (pl_block->steps[idx] << 1);
}
st_prep_block->step_event_count = (pl_block->step_event_count << 1);
#else
// With AMASS enabled, simply bit-shift multiply all Bresenham data by the max AMASS
// level, such that we never divide beyond the original data anywhere in the algorithm.
// If the original data is divided, we can lose a step from integer roundoff.
for (idx=0; idx<N_AXIS; idx++) { st_prep_block->steps[idx] = pl_block->steps[idx] << MAX_AMASS_LEVEL; }
for (idx = 0; idx < N_AXIS; idx++) {
st_prep_block->steps[idx] = pl_block->steps[idx] << MAX_AMASS_LEVEL;
}
st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL;
#endif
@ -727,7 +753,8 @@ void st_prep_buffer()
prep.req_mm_increment = REQ_MM_INCREMENT_SCALAR / prep.step_per_mm;
prep.dt_remainder = 0.0; // Reset for new segment block
if ((sys.step_control & STEP_CONTROL_EXECUTE_HOLD) || (prep.recalculate_flag & PREP_FLAG_DECEL_OVERRIDE)) {
if ((sys.step_control & STEP_CONTROL_EXECUTE_HOLD) ||
(prep.recalculate_flag & PREP_FLAG_DECEL_OVERRIDE)) {
// New block loaded mid-hold. Override planner block entry speed to enforce deceleration.
prep.current_speed = prep.exit_speed;
pl_block->entry_speed_sqr = prep.exit_speed * prep.exit_speed;
@ -766,7 +793,8 @@ void st_prep_buffer()
float decel_dist = pl_block->millimeters - inv_2_accel * pl_block->entry_speed_sqr;
if (decel_dist < 0.0) {
// Deceleration through entire planner block. End of feed hold is not in this block.
prep.exit_speed = sqrt(pl_block->entry_speed_sqr-2*pl_block->acceleration*pl_block->millimeters);
prep.exit_speed =
sqrt(pl_block->entry_speed_sqr - 2 * pl_block->acceleration * pl_block->millimeters);
} else {
prep.mm_complete = decel_dist; // End of feed hold.
prep.exit_speed = 0.0;
@ -791,15 +819,18 @@ void st_prep_buffer()
0.5 * (pl_block->millimeters + inv_2_accel * (pl_block->entry_speed_sqr - exit_speed_sqr));
if (pl_block->entry_speed_sqr > nominal_speed_sqr) { // Only occurs during override reductions.
prep.accelerate_until = pl_block->millimeters - inv_2_accel*(pl_block->entry_speed_sqr-nominal_speed_sqr);
prep.accelerate_until =
pl_block->millimeters - inv_2_accel * (pl_block->entry_speed_sqr - nominal_speed_sqr);
if (prep.accelerate_until <= 0.0) { // Deceleration-only.
prep.ramp_type = RAMP_DECEL;
// prep.decelerate_after = pl_block->millimeters;
// prep.maximum_speed = prep.current_speed;
// Compute override block exit speed since it doesn't match the planner exit speed.
prep.exit_speed = sqrt(pl_block->entry_speed_sqr - 2*pl_block->acceleration*pl_block->millimeters);
prep.recalculate_flag |= PREP_FLAG_DECEL_OVERRIDE; // Flag to load next block as deceleration override.
prep.exit_speed =
sqrt(pl_block->entry_speed_sqr - 2 * pl_block->acceleration * pl_block->millimeters);
prep.recalculate_flag |=
PREP_FLAG_DECEL_OVERRIDE; // Flag to load next block as deceleration override.
// TODO: Determine correct handling of parameters in deceleration-only.
// Can be tricky since entry speed will be current speed, as in feed holds.
@ -807,7 +838,9 @@ void st_prep_buffer()
} else {
// Decelerate to cruise or cruise-decelerate types. Guaranteed to intersect updated plan.
prep.decelerate_after = inv_2_accel*(nominal_speed_sqr-exit_speed_sqr); // Should always be >= 0.0 due to planner reinit.
prep.decelerate_after =
inv_2_accel *
(nominal_speed_sqr - exit_speed_sqr); // Should always be >= 0.0 due to planner reinit.
prep.maximum_speed = nominal_speed;
prep.ramp_type = RAMP_DECEL_OVERRIDE;
}
@ -827,7 +860,8 @@ void st_prep_buffer()
} else { // Triangle type
prep.accelerate_until = intersect_distance;
prep.decelerate_after = intersect_distance;
prep.maximum_speed = sqrt(2.0*pl_block->acceleration*intersect_distance+exit_speed_sqr);
prep.maximum_speed =
sqrt(2.0 * pl_block->acceleration * intersect_distance + exit_speed_sqr);
}
} else { // Deceleration-only type
prep.ramp_type = RAMP_DECEL;
@ -873,7 +907,9 @@ void st_prep_buffer()
float speed_var; // Speed worker variable
float mm_remaining = pl_block->millimeters; // New segment distance from end of block.
float minimum_mm = mm_remaining - prep.req_mm_increment; // Guarantee at least one step.
if (minimum_mm < 0.0) { minimum_mm = 0.0; }
if (minimum_mm < 0.0) {
minimum_mm = 0.0;
}
do {
switch (prep.ramp_type) {
@ -898,8 +934,11 @@ void st_prep_buffer()
// Acceleration-cruise, acceleration-deceleration ramp junction, or end of block.
mm_remaining = prep.accelerate_until; // NOTE: 0.0 at EOB
time_var = 2.0 * (pl_block->millimeters - mm_remaining) / (prep.current_speed + prep.maximum_speed);
if (mm_remaining == prep.decelerate_after) { prep.ramp_type = RAMP_DECEL; }
else { prep.ramp_type = RAMP_CRUISE; }
if (mm_remaining == prep.decelerate_after) {
prep.ramp_type = RAMP_DECEL;
} else {
prep.ramp_type = RAMP_CRUISE;
}
prep.current_speed = prep.maximum_speed;
} else { // Acceleration only.
prep.current_speed += speed_var;
@ -937,7 +976,9 @@ void st_prep_buffer()
prep.current_speed = prep.exit_speed;
}
dt += time_var; // Add computed ramp time to total segment time.
if (dt < dt_max) { time_var = dt_max - dt; } // **Incomplete** At ramp junction.
if (dt < dt_max) {
time_var = dt_max - dt;
} // **Incomplete** At ramp junction.
else {
if (mm_remaining > minimum_mm) { // Check for very slow segments with zero steps.
// Increase segment time to ensure at least one step in segment. Override and loop
@ -959,7 +1000,9 @@ void st_prep_buffer()
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 (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.current_spindle_pwm = spindle_compute_pwm_value(rpm);
@ -995,7 +1038,9 @@ void st_prep_buffer()
// requires full steps to execute. So, just bail.
bit_true(sys.step_control, STEP_CONTROL_END_MOTION);
#ifdef PARKING_ENABLE
if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) { prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK; }
if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) {
prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK;
}
#endif
return; // Segment not generated, but current step data still retained.
}
@ -1013,21 +1058,31 @@ void st_prep_buffer()
float inv_rate = dt / (last_n_steps_remaining - step_dist_remaining); // Compute adjusted step rate inverse
// Compute CPU cycles per step for the prepped segment.
uint32_t cycles = ceil( (TICKS_PER_MICROSECOND*1000000*60)*inv_rate ); // (cycles/step)
uint32_t cycles = ceil((TICKS_PER_MICROSECOND * 1000000 * 60) *
inv_rate); // (cycles/step)
#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING
// Compute step timing and multi-axis smoothing level.
// NOTE: AMASS overdrives the timer with each level, so only one prescalar is required.
if (cycles < AMASS_LEVEL1) { prep_segment->amass_level = 0; }
else {
if (cycles < AMASS_LEVEL2) { prep_segment->amass_level = 1; }
else if (cycles < AMASS_LEVEL3) { prep_segment->amass_level = 2; }
else { prep_segment->amass_level = 3; }
if (cycles < AMASS_LEVEL1) {
prep_segment->amass_level = 0;
} else {
if (cycles < AMASS_LEVEL2) {
prep_segment->amass_level = 1;
} else if (cycles < AMASS_LEVEL3) {
prep_segment->amass_level = 2;
} else {
prep_segment->amass_level = 3;
}
cycles >>= prep_segment->amass_level;
prep_segment->n_step <<= prep_segment->amass_level;
}
if (cycles < (1UL << 16)) { prep_segment->cycles_per_tick = cycles; } // < 65536 (4.1ms @ 16MHz)
else { prep_segment->cycles_per_tick = 0xffff; } // Just set the slowest speed possible.
if (cycles < (1UL << 16)) {
prep_segment->cycles_per_tick = cycles;
} // < 65536 (4.1ms @ 16MHz)
else {
prep_segment->cycles_per_tick = 0xffff;
} // Just set the slowest speed possible.
#else
// Compute step timing and timer prescalar for normal step generation.
if (cycles < (1UL << 16)) { // < 65536 (4.1ms @ 16MHz)
@ -1048,7 +1103,9 @@ void st_prep_buffer()
// Segment complete! Increment segment buffer indices, so stepper ISR can immediately execute it.
segment_buffer_head = segment_next_head;
if ( ++segment_next_head == SEGMENT_BUFFER_SIZE ) { segment_next_head = 0; }
if (++segment_next_head == SEGMENT_BUFFER_SIZE) {
segment_next_head = 0;
}
// Update the appropriate planner and segment data.
pl_block->millimeters = mm_remaining;
@ -1064,7 +1121,9 @@ void st_prep_buffer()
// cycle stop flag from the ISR. Prep_segment is blocked until then.
bit_true(sys.step_control, STEP_CONTROL_END_MOTION);
#ifdef PARKING_ENABLE
if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) { prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK; }
if (!(prep.recalculate_flag & PREP_FLAG_PARKING)) {
prep.recalculate_flag |= PREP_FLAG_HOLD_PARTIAL_BLOCK;
}
#endif
return; // Bail!
} else { // End of planner block
@ -1077,17 +1136,14 @@ void st_prep_buffer()
plan_discard_current_block();
}
}
}
}
// Called by realtime status reporting to fetch the current speed being executed. This value
// however is not exactly the current speed, but the speed computed in the last step segment
// in the segment buffer. It will always be behind by up to the number of segment blocks (-1)
// divided by the ACCELERATION TICKS PER SECOND in seconds.
float st_get_realtime_rate()
{
float st_get_realtime_rate() {
if (sys.state & (STATE_CYCLE | STATE_HOMING | STATE_HOLD | STATE_JOG | STATE_SAFETY_DOOR)) {
return prep.current_speed;
}

View file

@ -20,9 +20,7 @@
#include "grbl.h"
void system_init()
{
void system_init() {
CONTROL_DDR &= ~(CONTROL_MASK); // Configure as input pins
#ifdef DISABLE_CONTROL_PIN_PULL_UP
CONTROL_PORT &= ~(CONTROL_MASK); // Normal low operation. Requires external pull-down.
@ -33,12 +31,10 @@ void system_init()
PCICR |= (1 << CONTROL_INT); // Enable Pin Change Interrupt
}
// Returns control pin state as a uint8 bitfield. Each bit indicates the input pin state, where
// triggered is 1 and not triggered is 0. Invert mask is applied. Bitfield organization is
// defined by the CONTROL_PIN_INDEX in the header file.
uint8_t system_control_get_state()
{
uint8_t system_control_get_state() {
uint8_t control_state = 0;
uint8_t pin = (CONTROL_PIN & CONTROL_MASK) ^ CONTROL_MASK;
#ifdef INVERT_CONTROL_PIN_MASK
@ -46,23 +42,29 @@ uint8_t system_control_get_state()
#endif
if (pin) {
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
if (bit_istrue(pin,(1<<CONTROL_SAFETY_DOOR_BIT))) { control_state |= CONTROL_PIN_INDEX_SAFETY_DOOR; }
if (bit_istrue(pin, (1 << CONTROL_SAFETY_DOOR_BIT))) {
control_state |= CONTROL_PIN_INDEX_SAFETY_DOOR;
}
#else
if (bit_istrue(pin,(1<<CONTROL_FEED_HOLD_BIT))) { control_state |= CONTROL_PIN_INDEX_FEED_HOLD; }
if (bit_istrue(pin, (1 << CONTROL_FEED_HOLD_BIT))) {
control_state |= CONTROL_PIN_INDEX_FEED_HOLD;
}
#endif
if (bit_istrue(pin,(1<<CONTROL_RESET_BIT))) { control_state |= CONTROL_PIN_INDEX_RESET; }
if (bit_istrue(pin,(1<<CONTROL_CYCLE_START_BIT))) { control_state |= CONTROL_PIN_INDEX_CYCLE_START; }
if (bit_istrue(pin, (1 << CONTROL_RESET_BIT))) {
control_state |= CONTROL_PIN_INDEX_RESET;
}
if (bit_istrue(pin, (1 << CONTROL_CYCLE_START_BIT))) {
control_state |= CONTROL_PIN_INDEX_CYCLE_START;
}
}
return (control_state);
}
// Pin change interrupt for pin-out commands, i.e. cycle start, feed hold, and reset. Sets
// only the realtime command execute variable to have the main program execute these when
// its ready. This works exactly like the character-based realtime commands when picked off
// directly from the incoming serial data stream.
ISR(CONTROL_INT_vect)
{
ISR(CONTROL_INT_vect) {
uint8_t pin = system_control_get_state();
if (pin) {
if (bit_istrue(pin, CONTROL_PIN_INDEX_RESET)) {
@ -82,10 +84,8 @@ ISR(CONTROL_INT_vect)
}
}
// Returns if safety door is ajar(T) or closed(F), based on pin state.
uint8_t system_check_safety_door_ajar()
{
uint8_t system_check_safety_door_ajar() {
#ifdef ENABLE_SAFETY_DOOR_INPUT_PIN
return (system_control_get_state() & CONTROL_PIN_INDEX_SAFETY_DOOR);
#else
@ -93,10 +93,8 @@ uint8_t system_check_safety_door_ajar()
#endif
}
// Executes user startup script, if stored.
void system_execute_startup(char *line)
{
void system_execute_startup(char *line) {
uint8_t n;
for (n = 0; n < N_STARTUP_LINE; n++) {
if (!(settings_read_startup_line(n, line))) {
@ -111,7 +109,6 @@ void system_execute_startup(char *line)
}
}
// Directs and executes one line of formatted input from protocol_process. While mostly
// incoming streaming g-code blocks, this also executes Grbl internal commands, such as
// settings, initiating the homing cycle, and toggling switch states. This differs from
@ -120,8 +117,7 @@ void system_execute_startup(char *line)
// the lines that are processed afterward, not necessarily real-time during a cycle,
// since there are motions already stored in the buffer. However, this 'lag' should not
// be an issue, since these commands are not typically used during a cycle.
uint8_t system_execute_line(char *line)
{
uint8_t system_execute_line(char *line) {
uint8_t char_counter = 1;
uint8_t helper_var = 0; // Helper variable
float parameter, value;
@ -129,16 +125,29 @@ uint8_t system_execute_line(char *line)
case 0: report_grbl_help(); break;
case 'J': // Jogging
// Execute only if in IDLE or JOG states.
if (sys.state != STATE_IDLE && sys.state != STATE_JOG) { return(STATUS_IDLE_ERROR); }
if(line[2] != '=') { return(STATUS_INVALID_STATEMENT); }
if (sys.state != STATE_IDLE && sys.state != STATE_JOG) {
return (STATUS_IDLE_ERROR);
}
if (line[2] != '=') {
return (STATUS_INVALID_STATEMENT);
}
return (gc_execute_line(line)); // NOTE: $J= is ignored inside g-code parser and used to detect jog motions.
break;
case '$': case 'G': case 'C': case 'X':
if ( line[2] != 0 ) { return(STATUS_INVALID_STATEMENT); }
case '$':
case 'G':
case 'C':
case 'X':
if (line[2] != 0) {
return (STATUS_INVALID_STATEMENT);
}
switch (line[1]) {
case '$': // Prints Grbl settings
if ( sys.state & (STATE_CYCLE | STATE_HOLD) ) { return(STATUS_IDLE_ERROR); } // Block during cycle. Takes too long to print.
else { report_grbl_settings(); }
if (sys.state & (STATE_CYCLE | STATE_HOLD)) {
return (STATUS_IDLE_ERROR);
} // Block during cycle. Takes too long to print.
else {
report_grbl_settings();
}
break;
case 'G': // Prints gcode parser state
// TODO: Move this to realtime commands for GUIs to request this data during suspend-state.
@ -152,7 +161,9 @@ uint8_t system_execute_line(char *line)
mc_reset();
report_feedback_message(MESSAGE_DISABLED);
} else {
if (sys.state) { return(STATUS_IDLE_ERROR); } // Requires no alarm mode.
if (sys.state) {
return (STATUS_IDLE_ERROR);
} // Requires no alarm mode.
sys.state = STATE_CHECK_MODE;
report_feedback_message(MESSAGE_ENABLED);
}
@ -160,7 +171,9 @@ uint8_t system_execute_line(char *line)
case 'X': // Disable alarm lock [ALARM]
if (sys.state == STATE_ALARM) {
// Block if safety door is ajar.
if (system_check_safety_door_ajar()) { return(STATUS_CHECK_DOOR); }
if (system_check_safety_door_ajar()) {
return (STATUS_CHECK_DOOR);
}
report_feedback_message(MESSAGE_ALARM_UNLOCK);
sys.state = STATE_IDLE;
// Don't run startup script. Prevents stored moves in startup from causing accidents.
@ -170,15 +183,24 @@ uint8_t system_execute_line(char *line)
break;
default:
// Block any system command that requires the state as IDLE/ALARM. (i.e. EEPROM, homing)
if ( !(sys.state == STATE_IDLE || sys.state == STATE_ALARM) ) { return(STATUS_IDLE_ERROR); }
if (!(sys.state == STATE_IDLE || sys.state == STATE_ALARM)) {
return (STATUS_IDLE_ERROR);
}
switch (line[1]) {
case '#': // Print Grbl NGC parameters
if ( line[2] != 0 ) { return(STATUS_INVALID_STATEMENT); }
else { report_ngc_parameters(); }
if (line[2] != 0) {
return (STATUS_INVALID_STATEMENT);
} else {
report_ngc_parameters();
}
break;
case 'H': // Perform homing cycle [IDLE/ALARM]
if (bit_isfalse(settings.flags,BITFLAG_HOMING_ENABLE)) {return(STATUS_SETTING_DISABLED); }
if (system_check_safety_door_ajar()) { return(STATUS_CHECK_DOOR); } // Block if safety door is ajar.
if (bit_isfalse(settings.flags, BITFLAG_HOMING_ENABLE)) {
return (STATUS_SETTING_DISABLED);
}
if (system_check_safety_door_ajar()) {
return (STATUS_CHECK_DOOR);
} // Block if safety door is ajar.
sys.state = STATE_HOMING; // Set system state variable
if (line[2] == 0) {
mc_homing_cycle(HOMING_CYCLE_ALL);
@ -191,15 +213,21 @@ uint8_t system_execute_line(char *line)
default: return (STATUS_INVALID_STATEMENT);
}
#endif
} else { return(STATUS_INVALID_STATEMENT); }
} else {
return (STATUS_INVALID_STATEMENT);
}
if (!sys.abort) { // Execute startup scripts after successful homing.
sys.state = STATE_IDLE; // Set to IDLE when complete.
st_go_idle(); // Set steppers to the settings idle state before returning.
if (line[2] == 0) { system_execute_startup(line); }
if (line[2] == 0) {
system_execute_startup(line);
}
}
break;
case 'S': // Puts Grbl to sleep [IDLE/ALARM]
if ((line[2] != 'L') || (line[3] != 'P') || (line[4] != 0)) { return(STATUS_INVALID_STATEMENT); }
if ((line[2] != 'L') || (line[3] != 'P') || (line[4] != 0)) {
return (STATUS_INVALID_STATEMENT);
}
system_set_exec_state_flag(EXEC_SLEEP); // Set to execute sleep mode immediately
break;
case 'I': // Print or store build info. [IDLE/ALARM]
@ -208,7 +236,9 @@ uint8_t system_execute_line(char *line)
report_build_info(line);
#ifdef ENABLE_BUILD_INFO_WRITE_COMMAND
} else { // Store startup line [IDLE/ALARM]
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
if (line[char_counter++] != '=') {
return (STATUS_INVALID_STATEMENT);
}
helper_var = char_counter; // Set helper variable as counter to start of user info line.
do {
line[char_counter - helper_var] = line[char_counter];
@ -218,7 +248,9 @@ uint8_t system_execute_line(char *line)
}
break;
case 'R': // Restore defaults [IDLE/ALARM]
if ((line[2] != 'S') || (line[3] != 'T') || (line[4] != '=') || (line[6] != 0)) { return(STATUS_INVALID_STATEMENT); }
if ((line[2] != 'S') || (line[3] != 'T') || (line[4] != '=') || (line[6] != 0)) {
return (STATUS_INVALID_STATEMENT);
}
switch (line[5]) {
#ifdef ENABLE_RESTORE_EEPROM_DEFAULT_SETTINGS
case '$': settings_restore(SETTINGS_RESTORE_DEFAULTS); break;
@ -245,13 +277,19 @@ uint8_t system_execute_line(char *line)
}
break;
} else { // Store startup line [IDLE Only] Prevents motion during ALARM.
if (sys.state != STATE_IDLE) { return(STATUS_IDLE_ERROR); } // Store only when idle.
if (sys.state != STATE_IDLE) {
return (STATUS_IDLE_ERROR);
} // Store only when idle.
helper_var = true; // Set helper_var to flag storing method.
// No break. Continues into default: to read remaining command characters.
}
default: // Storing setting methods [IDLE/ALARM]
if(!read_float(line, &char_counter, &parameter)) { return(STATUS_BAD_NUMBER_FORMAT); }
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
if (!read_float(line, &char_counter, &parameter)) {
return (STATUS_BAD_NUMBER_FORMAT);
}
if (line[char_counter++] != '=') {
return (STATUS_INVALID_STATEMENT);
}
if (helper_var) { // Store startup line
// Prepare sending gcode block to gcode parser by shifting all characters
helper_var = char_counter; // Set helper variable as counter to start of gcode block
@ -260,14 +298,19 @@ uint8_t system_execute_line(char *line)
} while (line[char_counter++] != 0);
// Execute gcode block to ensure block is valid.
helper_var = gc_execute_line(line); // Set helper_var to returned status code.
if (helper_var) { return(helper_var); }
else {
if (helper_var) {
return (helper_var);
} else {
helper_var = trunc(parameter); // Set helper_var to int value of parameter
settings_store_startup_line(helper_var, line);
}
} else { // Store global setting.
if(!read_float(line, &char_counter, &value)) { return(STATUS_BAD_NUMBER_FORMAT); }
if((line[char_counter] != 0) || (parameter > 255)) { return(STATUS_INVALID_STATEMENT); }
if (!read_float(line, &char_counter, &value)) {
return (STATUS_BAD_NUMBER_FORMAT);
}
if ((line[char_counter] != 0) || (parameter > 255)) {
return (STATUS_INVALID_STATEMENT);
}
return (settings_store_global_setting((uint8_t)parameter, value));
}
}
@ -275,22 +318,17 @@ uint8_t system_execute_line(char *line)
return (STATUS_OK); // If '$' command makes it to here, then everything's ok.
}
void system_flag_wco_change()
{
void system_flag_wco_change() {
#ifdef FORCE_BUFFER_SYNC_DURING_WCO_CHANGE
protocol_buffer_synchronize();
#endif
sys.report_wco_counter = 0;
}
// Returns machine position of axis 'idx'. Must be sent a 'step' array.
// NOTE: If motor steps and machine position are not in the same coordinate frame, this function
// serves as a central place to compute the transformation.
float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx)
{
float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx) {
float pos;
#ifdef COREXY
if (idx == X_AXIS) {
@ -306,9 +344,7 @@ float system_convert_axis_steps_to_mpos(int32_t *steps, uint8_t idx)
return (pos);
}
void system_convert_array_steps_to_mpos(float *position, int32_t *steps)
{
void system_convert_array_steps_to_mpos(float *position, int32_t *steps) {
uint8_t idx;
for (idx = 0; idx < N_AXIS; idx++) {
position[idx] = system_convert_axis_steps_to_mpos(steps, idx);
@ -316,42 +352,43 @@ void system_convert_array_steps_to_mpos(float *position, int32_t *steps)
return;
}
// CoreXY calculation only. Returns x or y-axis "steps" based on CoreXY motor steps.
#ifdef COREXY
int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps)
{
int32_t system_convert_corexy_to_x_axis_steps(int32_t *steps) {
return ((steps[A_MOTOR] + steps[B_MOTOR]) / 2);
}
int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps)
{
int32_t system_convert_corexy_to_y_axis_steps(int32_t *steps) {
return ((steps[A_MOTOR] - steps[B_MOTOR]) / 2);
}
#endif
// Checks and reports if target array exceeds machine travel limits.
uint8_t system_check_travel_limits(float *target)
{
uint8_t system_check_travel_limits(float *target) {
uint8_t idx;
for (idx = 0; idx < N_AXIS; idx++) {
#ifdef HOMING_FORCE_SET_ORIGIN
// When homing forced set origin is enabled, soft limits checks need to account for directionality.
// NOTE: max_travel is stored as negative
if (bit_istrue(settings.homing_dir_mask, bit(idx))) {
if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) { return(true); }
if (target[idx] < 0 || target[idx] > -settings.max_travel[idx]) {
return (true);
}
} else {
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); }
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) {
return (true);
}
}
#else
// NOTE: max_travel is stored as negative
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) { return(true); }
if (target[idx] > 0 || target[idx] < settings.max_travel[idx]) {
return (true);
}
#endif
}
return (false);
}
// Special handlers for setting and clearing Grbl's real-time execution flags.
void system_set_exec_state_flag(uint8_t mask) {
uint8_t sreg = SREG;

View file

@ -122,7 +122,6 @@
#define SPINDLE_STOP_OVR_RESTORE bit(2)
#define SPINDLE_STOP_OVR_RESTORE_CYCLE bit(3)
// Define global system variables
typedef struct {
uint8_t state; // Tracks the current system state of Grbl.
@ -148,6 +147,7 @@ typedef struct {
float spindle_speed;
#endif
} system_t;
extern system_t sys;
// NOTE: These position variables may need to be declared as volatiles, if problems arise.
@ -155,10 +155,13 @@ extern int32_t sys_position[N_AXIS]; // Real-time machine (aka home) positi
extern int32_t sys_probe_position[N_AXIS]; // Last probe position in machine coordinates and steps.
extern volatile uint8_t sys_probe_state; // Probing state value. Used to coordinate the probing cycle with stepper ISR.
extern volatile uint8_t sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
extern volatile uint8_t
sys_rt_exec_state; // Global realtime executor bitflag variable for state management. See EXEC bitmasks.
extern volatile uint8_t sys_rt_exec_alarm; // Global realtime executor bitflag variable for setting various alarms.
extern volatile uint8_t sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides.
extern volatile uint8_t sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
extern volatile uint8_t
sys_rt_exec_motion_override; // Global realtime executor bitflag variable for motion-based overrides.
extern volatile uint8_t
sys_rt_exec_accessory_override; // Global realtime executor bitflag variable for spindle/coolant overrides.
#ifdef DEBUG
#define EXEC_DEBUG_REPORT bit(0)
@ -180,7 +183,6 @@ uint8_t system_execute_line(char *line);
// Execute the startup script lines stored in EEPROM upon initialization
void system_execute_startup(char *line);
void system_flag_wco_change();
// Returns machine position of axis 'idx'. Must be sent a 'step' array.
@ -208,5 +210,4 @@ void system_set_exec_accessory_override_flag(uint8_t mask);
void system_clear_exec_motion_overrides();
void system_clear_exec_accessory_overrides();
#endif