Mass reformat
No code changes other than what clang-format mandates. This is breaking
This commit is contained in:
parent
a1c041481c
commit
fe32d197f9
38 changed files with 7094 additions and 6574 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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--) {
|
||||
|
|
|
|||
533
grbl/gcode.c
533
grbl/gcode.c
|
|
@ -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:
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
25
grbl/grbl.h
25
grbl/grbl.h
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
110
grbl/limits.c
110
grbl/limits.c
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#ifndef limits_h
|
||||
#define limits_h
|
||||
|
||||
|
||||
// Initialize the limits module
|
||||
void limits_init();
|
||||
|
||||
|
|
|
|||
16
grbl/main.c
16
grbl/main.c
|
|
@ -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 */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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++) {
|
||||
|
|
|
|||
189
grbl/planner.c
189
grbl/planner.c
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
55
grbl/print.c
55
grbl/print.c
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@
|
|||
#ifndef print_h
|
||||
#define print_h
|
||||
|
||||
|
||||
void printString(const char *s);
|
||||
|
||||
void printPgmString(const char *s);
|
||||
|
|
|
|||
26
grbl/probe.c
26
grbl/probe.c
|
|
@ -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));
|
||||
|
|
|
|||
270
grbl/protocol.c
270
grbl/protocol.c
|
|
@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
265
grbl/report.c
265
grbl/report.c
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
181
grbl/settings.c
181
grbl/settings.c
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
258
grbl/stepper.c
258
grbl/stepper.c
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
175
grbl/system.c
175
grbl/system.c
|
|
@ -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, ¶meter)) { return(STATUS_BAD_NUMBER_FORMAT); }
|
||||
if(line[char_counter++] != '=') { return(STATUS_INVALID_STATEMENT); }
|
||||
if (!read_float(line, &char_counter, ¶meter)) {
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue