diff --git a/README.md b/README.md
index 62f502f..81ddca8 100644
--- a/README.md
+++ b/README.md
@@ -13,11 +13,8 @@ It has been written to complement grblHAL and has features such as proper keyboa
---
-Latest build date is 20231005, see the [changelog](changelog.md) for details.
+Latest build date is 20231210, see the [changelog](changelog.md) for details.
__NOTE:__ A settings reset will be performed on an update of builds earlier than 20230125. Backup and restore of settings is recommended.
-__IMPORTANT!__ A new setting has been introduced for ganged axes motors in build 20211121.
-I have only bench tested this for a couple of drivers, correct function should be verified after updating by those who have more than three motors configured.
-More details in the [changelog](changelog.md).
---
@@ -80,7 +77,7 @@ This is a port/rewrite of [grbl 1.1f](https://github.com/gnea/grbl) and should b
- Return from macro*****: M99
- Valid Non-Command Words: A*, B*, C*, D, E*, F, H*, I, J, K, L, N, P, Q*, R, S, T, U*, V*, W*, X, Y, Z
- * driver/configuration dependent. W axis only available when ABC axes are remapped to UVW.
+ * driver/configuration dependent. W axis only available when ABC axes are remapped to UVW or when lathe UVW mode is enabled.
** requires compatible GCode sender due to protocol extensions, new state and RT command.
*** number of inputs and outputs supported dependent on driver implementation.
**** supports multi turn arcs from build 20220718.
@@ -92,4 +89,4 @@ G/M-codes not supported by [legacy Grbl](https://github.com/gnea/grbl/wiki) are
Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes.
---
-2023-09-05
+2023-12-10
diff --git a/changelog.md b/changelog.md
index 72f3df6..e7bd105 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,5 +1,69 @@
## grblHAL changelog
+Build 20231210
+
+Core:
+
+* Spindle handling refactoring for improved management and configuration of multiple spindles.
+__NOTE:__ this is a relatively large change and may have introduced bugs and/or unintended side-effects. Please report any issues!
+
+* Added setting `$519` for binding spindle encoder to given spindle in multi spindle configurations.
+
+* Added machine readable spindle enumeration report, `$SPINDLESH`.
+
+* Increased _default_ value for setting `$398` \(number of planner blocs\) from 35 to 100 for faster laser engraving.
+Ref. [this discussion](https://github.com/grblHAL/core/discussions/402).
+__NOTE:__ the `$398` setting value will _not_ change on an upgrade!
+__NOTE:__ STM32F103 builds for the 128K flash variants does not have enough free RAM and will keep 35 as the default value.
+
+* Increased allowed number of decimal places from 3 to 5 for `$10x` stepper step/mm settings.
+Ref. [ioSender issue 346](https://github.com/terjeio/ioSender/issues/346).
+
+* Added setting `$650` for filing system options. Ref. [issue 397](https://github.com/grblHAL/core/issues/397).
+Currently the following bits are available \(depending on the configuration\):
+0 - Auto mount SD card on startup \(1\).
+1 - Do not add littlefs files when listing the root directory \(2\).
+
+* Added build option for [lathe UVW mode](https://www.cnctrainingcentre.com/haas-turn/u-and-w-on-a-cnc-lathe-incremental-programming/).
+Ref [this discussion](https://github.com/grblHAL/core/discussions/398).
+When enabled `UVW` words can be used to command relative moves for `XYZ` without switching to relative mode with `G91`. `U` -> `X`, `V` -> `Y`, `W` -> `Z`.
+__NOTE:__ This permanently sets lathe mode and disables the `$32` mode setting.
+
+For developers:
+
+* There are signature changes to some spindle, ioports enumeration and VFS filing system mount functions.
+
+* Added events to allow plugin code to handle tool table data, possibly stored on a SD card. Ref. [this discussion](https://github.com/grblHAL/core/discussions/392).
+
+Drivers:
+
+* Most: updated for refactored spindle handling and configuration.
+
+* ESP32: fix for spindle at speed failure. Ref. [ioSender issue 345](https://github.com/terjeio/ioSender/issues/345).
+Initial changes for ESP32-S3 support and some code refactoring.
+
+* STM32F7xx: fix to reduce stepper current surge on startup. Ref. [issue 400](https://github.com/grblHAL/core/issues/400).
+
+* iMRX1062: fix for hardfault when enabling aux input IRQ early in startup sequence. Ref. [issue 395](https://github.com/grblHAL/core/issues/395).
+
+Plugins:
+
+* Spindle: updated for core changes. Added several spindles:
+_Nowforever VFD._ \(untested\).
+_Stepper spindle._ This claims the stepper driver from the last configured axis.
+_PWM clone._ This clones the default driver implemented PWM spindle and changes it to use the direction signal for on/off control.
+Settings `$730`, `$731` and `$734` - `$736` will be used to configure the clone. These has the same function as the `$30` - `$36` counterparts.
+The driver spindle is suitable for controlling a laser when `$32` = `1` and the clone is suitable for controlling a spindle motor.
+Switching between the spindles is typically done with `M104Q` where `` is the spindle number.
+_Basic spindle._ Needs and claims 1 or 2 auxillary digital output ports depending on the configuration.
+_Additional PWM spindle._ Needs and claims 1 or 2 auxillary digital output ports and one analog PWM capable port.
+
+* Motors: fixed default Trinamic motor current - was incorrectly set to 0, changed to 500 mA RMS. Ref. [issue 400](https://github.com/grblHAL/core/issues/400).
+
+* Various: updated for core call signature changes.
+
+---
+
Build 20231005
Core:
diff --git a/config.h b/config.h
index d013d2c..8cb727f 100644
--- a/config.h
+++ b/config.h
@@ -493,7 +493,7 @@ by a driver or a plugin.
#if COMPATIBILITY_LEVEL == 0 || defined __DOXYGEN__
/*! \def N_TOOLS
\brief
-Number of tools in tool table, edit to enable (max. 16 allowed)
+Number of tools in tool table, edit to enable (max. 32 allowed)
*/
#if !defined N_TOOLS || defined __DOXYGEN__
#define N_TOOLS 0
@@ -518,6 +518,14 @@ Maximum number of parameters allowed in a block.
#define NGC_N_ASSIGN_PARAMETERS_PER_BLOCK 10
#endif
+/*! \def LATHE_UVW_OPTION
+\brief
+Allow use of UVW axis words for non-modal relative lathe motion.
+*/
+#if !defined LATHE_UVW_OPTION || defined __DOXYGEN__
+#define LATHE_UVW_OPTION Off
+#endif
+
// Max number of entries in log for PID data reporting, to be used for tuning
//#define PID_LOG 1000 // Default disabled. Uncomment to enable.
@@ -811,7 +819,7 @@ having trouble keeping up with planning new incoming motions as they are execute
*/
///@{
#if !defined DEFAULT_PLANNER_BUFFER_BLOCKS || defined __DOXYGEN__
-#define DEFAULT_PLANNER_BUFFER_BLOCKS 35
+#define DEFAULT_PLANNER_BUFFER_BLOCKS 100
#endif
///@}
@@ -1858,28 +1866,28 @@ __NOTE:__ Must be a positive values.
*/
///@{
#if !defined DEFAULT_X_CURRENT || defined __DOXYGEN__
-#define DEFAULT_X_CURRENT 0.0 // mA
+#define DEFAULT_X_CURRENT 500.0f // mA RMS
#endif
#if !defined DEFAULT_Y_CURRENT || defined __DOXYGEN__
-#define DEFAULT_Y_CURRENT 0.0 // mA
+#define DEFAULT_Y_CURRENT 500.0f // mA RMS
#endif
#if !defined DEFAULT_Z_CURRENT || defined __DOXYGEN__
-#define DEFAULT_Z_CURRENT 0.0 // mA
+#define DEFAULT_Z_CURRENT 500.0f // mA RMS
#endif
#if (defined A_AXIS && !defined DEFAULT_A_CURRENT) || defined __DOXYGEN__
-#define DEFAULT_A_CURRENT 0.0 // mA
+#define DEFAULT_A_CURRENT 500.0f // mA RMS
#endif
#if (defined B_AXIS && !defined DEFAULT_B_CURRENT) || defined __DOXYGEN__
-#define DEFAULT_B_CURRENT 0.0 // mA
+#define DEFAULT_B_CURRENT 500.0f // mA RMS
#endif
#if (defined C_AXIS && !defined DEFAULT_C_CURRENT) || defined __DOXYGEN__
-#define DEFAULT_C_CURRENT 0.0 // mA
+#define DEFAULT_C_CURRENT 500.0f // mA RMS
#endif
#if (defined U_AXIS && !defined DEFAULT_U_CURRENT) || defined __DOXYGEN__
-#define DEFAULT_U_CURRENT 0.0 // mA
+#define DEFAULT_U_CURRENT 500.0f // mA RMS
#endif
#if (defined V_AXIS && !defined DEFAULT_V_CURRENT) || defined __DOXYGEN__
-#define DEFAULT_V_CURRENT 0.0 // mA
+#define DEFAULT_V_CURRENT 500.0f // mA RMS
#endif
///@}
@@ -1890,9 +1898,9 @@ __NOTE:__ Must be a positive values.
#undef N_TOOLS
#endif
-#if defined(N_TOOLS) && N_TOOLS > 16
+#if defined(N_TOOLS) && N_TOOLS > 32
#undef N_TOOLS
-#define N_TOOLS 16
+#define N_TOOLS 32
#endif
#if N_SYS_SPINDLE > N_SPINDLE
@@ -1927,6 +1935,12 @@ __NOTE:__ Must be a positive values.
#error "Cannot enable laser and lathe mode at the same time!"
#endif
+#if LATHE_UVW_OPTION && (N_AXIS > 6 || AXIS_REMAP_ABC2UVW)
+#warning "Cannot enable lathe UVW option when N_AXIS > 6 or ABC words are remapped!"
+#undef LATHE_UVW_OPTION
+#define LATHE_UVW_OPTION Off
+#endif
+
#if DEFAULT_CONTROL_SIGNALS_INVERT_MASK < 0
#undef DEFAULT_CONTROL_SIGNALS_INVERT_MASK
#define DEFAULT_CONTROL_SIGNALS_INVERT_MASK SIGNALS_BITMASK
diff --git a/core_handlers.h b/core_handlers.h
index 20c2cff..31cf3c6 100644
--- a/core_handlers.h
+++ b/core_handlers.h
@@ -126,9 +126,23 @@ typedef sys_commands_t *(*on_get_commands_ptr)(void);
typedef status_code_t (*on_macro_execute_ptr)(macro_id_t macro); // macro implementations _must_ claim hal.stream.read to stream macros!
typedef void (*on_macro_return_ptr)(void);
+typedef bool (*write_tool_data_ptr)(tool_data_t *tool_data);
+typedef bool (*read_tool_data_ptr)(tool_id_t tool_id, tool_data_t *tool_data);
+typedef bool (*clear_tool_data_ptr)(void);
+
+typedef struct {
+ uint32_t n_tools;
+ tool_data_t *tool; //!< Array of tool data, size _must_ be n_tools + 1
+ read_tool_data_ptr read;
+ write_tool_data_ptr write;
+ clear_tool_data_ptr clear;
+} tool_table_t;
+
typedef struct {
// report entry points set by core at reset.
report_t report;
+ //
+ tool_table_t tool_table;
// grbl core events - may be subscribed to by drivers or by the core.
on_parser_init_ptr on_parser_init;
on_state_change_ptr on_state_change;
diff --git a/crossbar.h b/crossbar.h
index b610b84..19b323c 100644
--- a/crossbar.h
+++ b/crossbar.h
@@ -466,7 +466,7 @@ struct xbar;
typedef float (*xbar_get_value_ptr)(struct xbar *pin);
typedef void (*xbar_set_value_ptr)(struct xbar *pin, float value);
typedef void (*xbar_event_ptr)(bool on);
-typedef void (*xbar_config_ptr)(struct xbar *pin, void *cfg_data);
+typedef bool (*xbar_config_ptr)(struct xbar *pin, void *cfg_data);
typedef struct xbar {
pin_function_t function;
diff --git a/driver_opts.h b/driver_opts.h
index 0c6e89a..0217e1a 100644
--- a/driver_opts.h
+++ b/driver_opts.h
@@ -159,7 +159,15 @@
#endif
#ifndef SPINDLE_SYNC_ENABLE
-#define SPINDLE_SYNC_ENABLE 0
+#define SPINDLE_SYNC_ENABLE 0
+#endif
+
+#ifndef SPINDLE_ENCODER_ENABLE
+#if SPINDLE_SYNC_ENABLE
+#define SPINDLE_ENCODER_ENABLE 1
+#else
+#define SPINDLE_ENCODER_ENABLE 0
+#endif
#endif
#ifndef TRINAMIC_ENABLE
@@ -213,10 +221,6 @@
#define PPI_ENABLE 0
#endif
-#ifndef STEP_INJECT_ENABLE
-#define STEP_INJECT_ENABLE 0
-#endif
-
#if EMBROIDERY_ENABLE
#if defined(SDCARD_ENABLE) && SDCARD_ENABLE == 0
#undef SDCARD_ENABLE
@@ -226,6 +230,7 @@
#endif
#endif
+// TODO: remove?
#ifndef VFD_SPINDLE
#if VFD_ENABLE
#define VFD_SPINDLE 1
@@ -234,6 +239,93 @@
#endif
#endif
+#ifndef SPINDLE0_ENABLE
+ #if VFD_ENABLE
+ #define SPINDLE0_ENABLE VFD_ENABLE
+ #if N_SPINDLE > 1 && !defined(SPINDLE1_ENABLE)
+ #define SPINDLE1_ENABLE SPINDLE_PWM0
+ #endif
+ #else
+ #define SPINDLE0_ENABLE SPINDLE_PWM0
+ #endif
+#endif
+
+#ifndef SPINDLE1_ENABLE
+#define SPINDLE1_ENABLE 0
+#endif
+
+#ifndef SPINDLE2_ENABLE
+#define SPINDLE2_ENABLE 0
+#endif
+
+#ifndef SPINDLE3_ENABLE
+#define SPINDLE3_ENABLE 0
+#endif
+
+#if SPINDLE0_ENABLE == SPINDLE_ALL
+#define SPINDLE_ENABLE SPINDLE_ALL
+#else
+#define SPINDLE_ENABLE ((1< 1
-#define DRIVER_SPINDLE_ENABLE 1
-#else
-#define DRIVER_SPINDLE_ENABLE 0
+#ifndef STEP_INJECT_ENABLE
+ #if SPINDLE_ENABLE & (1<offset[idx] = 0.0f;
-#endif
+ if(grbl.tool_table.n_tools == 0)
+ gc_state.tool->offset[idx] = 0.0f;
} while(idx);
break;
@@ -228,9 +218,8 @@ void gc_set_tool_offset (tool_offset_mode_t mode, uint_fast8_t idx, int32_t offs
float new_offset = offset / settings.axis[idx].steps_per_mm;
tlo_changed |= gc_state.tool_length_offset[idx] != new_offset;
gc_state.tool_length_offset[idx] = new_offset;
-#ifndef N_TOOLS
- gc_state.tool->offset[idx] = new_offset;
-#endif
+ if(grbl.tool_table.n_tools == 0)
+ gc_state.tool->offset[idx] = new_offset;
}
break;
@@ -275,21 +264,15 @@ void gc_init (void)
{
#if COMPATIBILITY_LEVEL > 1
memset(&gc_state, 0, sizeof(parser_state_t));
- #if N_TOOLS
- gc_state.tool = &tool_table[0];
- #else
- memset(&tool_table, 0, sizeof(tool_table));
- gc_state.tool = &tool_table;
- #endif
+ gc_state.tool = &grbl.tool_table.tool[0];
+ if(grbl.tool_table.n_tools == 0)
+ memset(grbl.tool_table.tool, 0, sizeof(tool_data_t));
#else
if(sys.cold_start) {
memset(&gc_state, 0, sizeof(parser_state_t));
- #if N_TOOLS
- gc_state.tool = &tool_table[0];
- #else
- memset(&tool_table, 0, sizeof(tool_table));
- gc_state.tool = &tool_table;
- #endif
+ gc_state.tool = &grbl.tool_table.tool[0];
+ if(grbl.tool_table.n_tools == 0)
+ memset(grbl.tool_table.tool, 0, sizeof(tool_data_t));
} else {
memset(&gc_state, 0, offsetof(parser_state_t, g92_coord_offset));
gc_state.tool_pending = gc_state.tool->tool_id;
@@ -335,7 +318,6 @@ void gc_init (void)
grbl.on_parser_init(&gc_state);
}
-
// Set dynamic laser power mode to PPI (Pulses Per Inch)
// Returns true if driver uses hardware implementation.
// Driver support for pulsing the laser on signal is required for this to work.
@@ -369,25 +351,23 @@ spindle_ptrs_t *gc_spindle_get (void)
static tool_data_t *tool_get_pending (tool_id_t tool_id)
{
-#if N_TOOLS
- return &tool_table[tool_id];
-#else
static tool_data_t tool_data = {0};
+ if(grbl.tool_table.n_tools)
+ return &grbl.tool_table.tool[tool_id];
+
memcpy(&tool_data, gc_state.tool, sizeof(tool_data_t));
tool_data.tool_id = tool_id;
return &tool_data;
-#endif
}
static inline void tool_set (tool_data_t *tool)
{
-#if N_TOOLS
- gc_state.tool = tool;
-#else
- gc_state.tool->tool_id = tool->tool_id;
-#endif
+ if(grbl.tool_table.n_tools)
+ gc_state.tool = tool;
+ else
+ gc_state.tool->tool_id = tool->tool_id;
}
// Add output command to linked list
@@ -414,6 +394,9 @@ static bool add_output_command (output_command_t *command)
static status_code_t init_sync_motion (plan_line_data_t *pl_data, float pitch)
{
+ if(pl_data->spindle.hal->get_data == NULL)
+ FAIL(Status_GcodeUnsupportedCommand); // [Spindle not sync capable]
+
pl_data->condition.inverse_time = Off;
pl_data->feed_rate = gc_state.distance_per_rev = pitch;
pl_data->spindle.css = NULL; // Switch off CSS.
@@ -655,11 +638,17 @@ status_code_t gc_execute_block (char *block)
#ifdef C_AXIS
, .c = On
#endif
+#if LATHE_UVW_OPTION
+ , .u = On
+ , .v = On
+ , .w = On
+#else
#ifdef U_AXIS
, .u = On
#endif
#ifdef V_AXIS
, .v = On
+#endif
#endif
};
@@ -1067,16 +1056,14 @@ status_code_t gc_execute_block (char *block)
// 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.
// NOTE: cannot find the NIST statement referenced above, changed to match LinuxCNC behaviour in build 20210513.
- if (int_value == 49) // G49
+ if(int_value == 49) // G49
gc_block.modal.tool_offset_mode = ToolLengthOffset_Cancel;
-#if N_TOOLS
- else if (mantissa == 0) // G43
+ else if(mantissa == 0 && grbl.tool_table.n_tools) // G43
gc_block.modal.tool_offset_mode = ToolLengthOffset_Enable;
- else if (mantissa == 20) // G43.2
+ else if(mantissa == 20 && grbl.tool_table.n_tools) // G43.2
gc_block.modal.tool_offset_mode = ToolLengthOffset_ApplyAdditional;
-#endif
- else if (mantissa == 10) { // G43.1
- if (axis_command)
+ else if(mantissa == 10) { // G43.1
+ if(axis_command)
FAIL(Status_GcodeAxisCommandConflict); // [Axis word/command conflict] }
axis_command = AxisCommand_ToolLengthOffset;
gc_block.modal.tool_offset_mode = ToolLengthOffset_EnableDynamic;
@@ -1446,13 +1433,33 @@ status_code_t gc_execute_block (char *block)
break;
case 'T':
- if (mantissa > 0)
+ if(mantissa > 0)
FAIL(Status_GcodeCommandValueNotInteger);
- if (int_value > MAX_TOOL_NUMBER)
+ if(int_value > (grbl.tool_table.n_tools ? grbl.tool_table.n_tools : MAX_TOOL_NUMBER))
FAIL(Status_GcodeIllegalToolTableEntry);
word_bit.parameter.t = On;
gc_block.values.t = isnan(value) ? 0xFFFFFFFF : int_value;
break;
+#if LATHE_UVW_OPTION
+ case 'U':
+ axis_words.x = On;
+ word_bit.parameter.x = word_bit.parameter.u = On;
+ gc_block.values.uvw[X_AXIS] = value / 2.0f; // U is always a diameter
+ break;
+
+ case 'V':
+ axis_words.y = On;
+ word_bit.parameter.y = word_bit.parameter.v = On;
+ gc_block.values.uvw[Y_AXIS] = value;
+ break;
+
+ case 'W':
+ axis_words.z = On;
+ word_bit.parameter.z = word_bit.parameter.w = On;
+ gc_block.values.uvw[Z_AXIS] = value;
+ break;
+
+#else
#ifdef U_AXIS
case 'U':
@@ -1469,7 +1476,7 @@ status_code_t gc_execute_block (char *block)
gc_block.values.xyz[V_AXIS] = value;
break;
#endif
-
+#endif
case 'X':
axis_words.x = On;
word_bit.parameter.x = On;
@@ -1712,7 +1719,7 @@ status_code_t gc_execute_block (char *block)
FAIL(Status_GcodeValueWordMissing);
if (floorf(gc_block.values.q) - gc_block.values.q != 0.0f)
FAIL(Status_GcodeCommandValueNotInteger);
- if ((uint32_t)gc_block.values.q > MAX_TOOL_NUMBER)
+ if ((uint32_t)gc_block.values.q > (grbl.tool_table.n_tools ? grbl.tool_table.n_tools : MAX_TOOL_NUMBER))
FAIL(Status_GcodeIllegalToolTableEntry);
gc_block.values.t = (uint32_t)gc_block.values.q;
@@ -1720,13 +1727,13 @@ status_code_t gc_execute_block (char *block)
#if NGC_EXPRESSIONS_ENABLE
if(hal.stream.file) {
gc_state.tool_pending = 0; // force set tool
- #if N_TOOLS
- if(gc_state.g43_pending) {
- gc_block.values.h = gc_state.g43_pending;
- command_words.G8 = On;
+ if(grbl.tool_table.n_tools) {
+ if(gc_state.g43_pending) {
+ gc_block.values.h = gc_state.g43_pending;
+ command_words.G8 = On;
+ }
+ gc_state.g43_pending = 0;
}
- gc_state.g43_pending = 0;
- #endif
}
#endif
} else if (!gc_block.words.t)
@@ -1893,11 +1900,18 @@ status_code_t gc_execute_block (char *block)
if (gc_block.modal.units_imperial) do { // Axes indices are consistent, so loop may be used.
idx--;
#if N_AXIS > 3
- if (bit_istrue(axis_words.mask, bit(idx)) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx)))
+ if (bit_istrue(axis_words.mask, bit(idx)) && bit_isfalse(settings.steppers.is_rotational.mask, bit(idx))) {
#else
- if (bit_istrue(axis_words.mask, bit(idx)))
+ if (bit_istrue(axis_words.mask, bit(idx))) {
#endif
gc_block.values.xyz[idx] *= MM_PER_INCH;
+#if LATHE_UVW_OPTION
+ #if N_AXIS > 3
+ if(idx <= Z_AXIS)
+ #endif
+ gc_block.values.uvw[idx] *= MM_PER_INCH;
+#endif
+ }
} while(idx);
if (command_words.G15 && gc_state.modal.diameter_mode != gc_block.modal.diameter_mode) {
@@ -1987,6 +2001,12 @@ status_code_t gc_execute_block (char *block)
gc_block.values.xyz[idx] *= scale_factor.ijk[idx];
else
gc_block.values.xyz[idx] = (gc_block.values.xyz[idx] - scale_factor.xyz[idx]) * scale_factor.ijk[idx] + scale_factor.xyz[idx];
+#if LATHE_UVW_OPTION
+ #if N_AXIS > 3
+ if(idx <= Z_AXIS)
+ #endif
+ gc_block.values.uvw[idx] *= scale_factor.ijk[idx];
+#endif
}
} while(idx);
}
@@ -1996,7 +2016,7 @@ status_code_t gc_execute_block (char *block)
// NOTE: Since cutter radius compensation is never enabled, these G40 errors don't apply. Grbl supports G40
// only for the purpose to not error when G40 is sent with a g-code program header to setup the default modes.
- // [14. Tool length compensation ]: G43.1 and G49 are always supported, G43 and G43.2 if N_TOOLS defined.
+ // [14. Tool length compensation ]: G43.1 and G49 are always supported, G43 and G43.2 if grbl.tool_table.n_tools > 0
// [G43.1 Errors]: Motion command in same line.
// [G43.2 Errors]: Tool number not in the tool table,
if (command_words.G8) { // Indicates called in block.
@@ -2014,30 +2034,36 @@ status_code_t gc_execute_block (char *block)
switch(gc_block.modal.tool_offset_mode) {
case ToolLengthOffset_EnableDynamic:
- if (!axis_words.mask)
+ if(!axis_words.mask)
FAIL(Status_GcodeG43DynamicAxisError);
break;
-#if N_TOOLS
+
case ToolLengthOffset_Enable:
- if (gc_block.words.h) {
- if(gc_block.values.h > MAX_TOOL_NUMBER)
- FAIL(Status_GcodeIllegalToolTableEntry);
- gc_block.words.h = Off;
- if(gc_block.values.h == 0)
+ if(grbl.tool_table.n_tools) {
+ if(gc_block.words.h) {
+ if(gc_block.values.h > grbl.tool_table.n_tools)
+ FAIL(Status_GcodeIllegalToolTableEntry);
+ gc_block.words.h = Off;
+ if(gc_block.values.h == 0)
+ gc_block.values.h = gc_block.values.t;
+ } else
gc_block.values.h = gc_block.values.t;
} else
- gc_block.values.h = gc_block.values.t;
+ FAIL(Status_GcodeUnsupportedCommand);
break;
case ToolLengthOffset_ApplyAdditional:
- if (gc_block.words.h) {
- if(gc_block.values.h == 0 || gc_block.values.h > MAX_TOOL_NUMBER)
- FAIL(Status_GcodeIllegalToolTableEntry);
- gc_block.words.h = Off;
+ if(grbl.tool_table.n_tools) {
+ if(gc_block.words.h) {
+ if(gc_block.values.h == 0 || gc_block.values.h > grbl.tool_table.n_tools)
+ FAIL(Status_GcodeIllegalToolTableEntry);
+ gc_block.words.h = Off;
+ } else
+ FAIL(Status_GcodeValueWordMissing);
} else
- FAIL(Status_GcodeValueWordMissing);
+ FAIL(Status_GcodeUnsupportedCommand);
break;
-#endif
+
default:
break;
}
@@ -2078,7 +2104,7 @@ status_code_t gc_execute_block (char *block)
// [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 N_WorkCoordinateSystems (max 9). Axis words missing.
// [G10 L20 Errors]: P must be 0 to N_WorkCoordinateSystems (max 9). Axis words missing.
- // [G10 L1, L10, L11 Errors]: P must be 0 to MAX_TOOL_NUMBER (max 9). Axis words or R word missing.
+ // [G10 L1, L10, L11 Errors]: P must be 0 to grbl.tool_table.n_tools. Axis words or R word missing.
if (!(axis_words.mask || (gc_block.values.l != 20 && gc_block.words.r)))
FAIL(Status_GcodeNoAxisWords); // [No axis words (or R word for tool offsets)]
@@ -2112,8 +2138,8 @@ status_code_t gc_execute_block (char *block)
FAIL(Status_SettingReadFail); // [non-volatile storage read fail]
#if COMPATIBILITY_LEVEL <= 1
- if(settings.parking.flags.offset_lock && gc_block.values.coord_data.id >= CoordinateSystem_G59_1 && gc_block.values.coord_data.id <= CoordinateSystem_G59_3) {
- if(bit_istrue(settings.parking.flags.offset_lock, bit(gc_block.values.coord_data.id - CoordinateSystem_G59_1)))
+ if(settings.offset_lock.mask && gc_block.values.coord_data.id >= CoordinateSystem_G59_1 && gc_block.values.coord_data.id <= CoordinateSystem_G59_3) {
+ if(bit_istrue(settings.offset_lock.mask, bit(gc_block.values.coord_data.id - CoordinateSystem_G59_1)))
FAIL(Status_GCodeCoordSystemLocked);
}
#endif
@@ -2133,47 +2159,48 @@ status_code_t gc_execute_block (char *block)
} while(idx);
break;
-#if N_TOOLS
- case 1: case 10: case 11:;
- if(p_value == 0 || p_value > MAX_TOOL_NUMBER)
- FAIL(Status_GcodeIllegalToolTableEntry); // [Greater than MAX_TOOL_NUMBER]
+ case 1: case 10: case 11:
+ if(grbl.tool_table.n_tools) {
+ if(p_value == 0 || p_value > grbl.tool_table.n_tools)
+ FAIL(Status_GcodeIllegalToolTableEntry); // [Greater than max allowed tool number]
- tool_table[p_value].tool_id = (tool_id_t)p_value;
+ grbl.tool_table.tool[p_value].tool_id = (tool_id_t)p_value;
- if(gc_block.words.r) {
- tool_table[p_value].radius = gc_block.values.r;
- gc_block.words.r = Off;
- }
+ if(gc_block.words.r) {
+ grbl.tool_table.tool[p_value].radius = gc_block.values.r;
+ gc_block.words.r = Off;
+ }
- float g59_3_offset[N_AXIS];
- if(gc_block.values.l == 11 && !settings_read_coord_data(CoordinateSystem_G59_3, &g59_3_offset))
- FAIL(Status_SettingReadFail);
+ float g59_3_offset[N_AXIS];
+ if(gc_block.values.l == 11 && !settings_read_coord_data(CoordinateSystem_G59_3, &g59_3_offset))
+ FAIL(Status_SettingReadFail);
- if(gc_block.values.l == 1)
- settings_read_tool_data(p_value, &tool_table[p_value]);
+ if(gc_block.values.l == 1)
+ grbl.tool_table.read(p_value, &grbl.tool_table.tool[p_value]);
- idx = N_AXIS;
- do {
- if(bit_istrue(axis_words.mask, bit(--idx))) {
- if(gc_block.values.l == 1)
- tool_table[p_value].offset[idx] = gc_block.values.xyz[idx];
- else if(gc_block.values.l == 10)
- tool_table[p_value].offset[idx] = gc_state.position[idx] - gc_state.modal.coord_system.xyz[idx] - gc_state.g92_coord_offset[idx] - gc_block.values.xyz[idx];
- else if(gc_block.values.l == 11)
- tool_table[p_value].offset[idx] = g59_3_offset[idx] - gc_block.values.xyz[idx];
-// if(gc_block.values.l != 1)
-// tool_table[p_value].offset[idx] -= gc_state.tool_length_offset[idx];
- } else if(gc_block.values.l == 10 || gc_block.values.l == 11)
- tool_table[p_value].offset[idx] = gc_state.tool_length_offset[idx];
+ idx = N_AXIS;
+ do {
+ if(bit_istrue(axis_words.mask, bit(--idx))) {
+ if(gc_block.values.l == 1)
+ grbl.tool_table.tool[p_value].offset[idx] = gc_block.values.xyz[idx];
+ else if(gc_block.values.l == 10)
+ grbl.tool_table.tool[p_value].offset[idx] = gc_state.position[idx] - gc_state.modal.coord_system.xyz[idx] - gc_state.g92_coord_offset[idx] - gc_block.values.xyz[idx];
+ else if(gc_block.values.l == 11)
+ grbl.tool_table.tool[p_value].offset[idx] = g59_3_offset[idx] - gc_block.values.xyz[idx];
+ // if(gc_block.values.l != 1)
+ // tool_table[p_value].offset[idx] -= gc_state.tool_length_offset[idx];
+ } else if(gc_block.values.l == 10 || gc_block.values.l == 11)
+ grbl.tool_table.tool[p_value].offset[idx] = gc_state.tool_length_offset[idx];
- // else, keep current stored value.
- } while(idx);
-
- if(gc_block.values.l == 1)
- settings_write_tool_data(&tool_table[p_value]);
+ // else, keep current stored value.
+ } while(idx);
+ if(gc_block.values.l == 1)
+ grbl.tool_table.write(&grbl.tool_table.tool[p_value]);
+ } else
+ FAIL(Status_GcodeUnsupportedCommand);
break;
-#endif
+
default:
FAIL(Status_GcodeUnsupportedCommand); // [Unsupported L]
}
@@ -2207,13 +2234,22 @@ status_code_t gc_execute_block (char *block)
if (axis_words.mask && axis_command != AxisCommand_ToolLengthOffset) { // TLO block any axis command.
idx = N_AXIS;
do { // Axes indices are consistent, so loop may be used to save flash space.
- if (bit_isfalse(axis_words.mask, bit(--idx)))
+ if(bit_isfalse(axis_words.mask, bit(--idx)))
gc_block.values.xyz[idx] = gc_state.position[idx]; // No axis word in block. Keep same axis position.
- else if (gc_block.non_modal_command != NonModal_AbsoluteOverride) {
+ else if(gc_block.non_modal_command != NonModal_AbsoluteOverride) {
// 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.
// Apply coordinate offsets based on distance mode.
- if (gc_block.modal.distance_incremental)
+#if LATHE_UVW_OPTION
+ #if N_AXIS > 3
+ if(idx <= Z_AXIS && bit_istrue(axis_words.mask, bit(idx)) && gc_block.values.uvw[idx] != 0.0f)
+ #else
+ if(bit_istrue(axis_words.mask, bit(idx)) && gc_block.values.uvw[idx] != 0.0f)
+ #endif
+ gc_block.values.xyz[idx] = gc_state.position[idx] + gc_block.values.uvw[idx];
+ else
+#endif
+ if(gc_block.modal.distance_incremental)
gc_block.values.xyz[idx] += gc_state.position[idx];
else // Absolute mode
gc_block.values.xyz[idx] += gc_get_block_offset(&gc_block, idx);
@@ -3066,12 +3102,10 @@ status_code_t gc_execute_block (char *block)
#if NGC_EXPRESSIONS_ENABLE
if((status_code_t)int_value != Status_Unhandled)
tool_set(pending_tool);
- #if N_TOOLS
- else if(command_words.G8 && gc_block.modal.tool_offset_mode && ToolLengthOffset_Enable) {
+ else if(grbl.tool_table.n_tools && command_words.G8 && gc_block.modal.tool_offset_mode && ToolLengthOffset_Enable) {
gc_state.g43_pending = gc_block.values.h;
command_words.G8 = Off;
}
- #endif
#else
tool_set(pending_tool);
#endif
@@ -3164,7 +3198,7 @@ status_code_t gc_execute_block (char *block)
// [13. Cutter radius compensation ]: G41/42 NOT SUPPORTED
// gc_state.modal.cutter_comp = gc_block.modal.cutter_comp; // NOTE: Not needed since always disabled.
- // [14. Tool length compensation ]: G43, G43.1 and G49 supported. G43 supported when N_TOOLS defined.
+ // [14. Tool length compensation ]: G43, G43.1 and G49 supported. G43 supported when grbl.tool_table.n_tools > 0.
// NOTE: If G43 were supported, its operation wouldn't be any different from G43.1 in terms
// of execution. The error-checking step would simply load the offset value into the correct
// axis of the block XYZ value array.
@@ -3185,19 +3219,19 @@ status_code_t gc_execute_block (char *block)
tlo_changed |= gc_state.tool_length_offset[idx] != 0.0f;
gc_state.tool_length_offset[idx] = 0.0f;
break;
-#if N_TOOLS
+
case ToolLengthOffset_Enable: // G43
- if (gc_state.tool_length_offset[idx] != tool_table[gc_block.values.h].offset[idx]) {
+ if (gc_state.tool_length_offset[idx] != grbl.tool_table.tool[gc_block.values.h].offset[idx]) {
tlo_changed = true;
- gc_state.tool_length_offset[idx] = tool_table[gc_block.values.h].offset[idx];
+ gc_state.tool_length_offset[idx] = grbl.tool_table.tool[gc_block.values.h].offset[idx];
}
break;
case ToolLengthOffset_ApplyAdditional: // G43.2
- tlo_changed |= tool_table[gc_block.values.h].offset[idx] != 0.0f;
- gc_state.tool_length_offset[idx] += tool_table[gc_block.values.h].offset[idx];
+ tlo_changed |= grbl.tool_table.tool[gc_block.values.h].offset[idx] != 0.0f;
+ gc_state.tool_length_offset[idx] += grbl.tool_table.tool[gc_block.values.h].offset[idx];
break;
-#endif
+
case ToolLengthOffset_EnableDynamic: // G43.1
if (bit_istrue(axis_words.mask, bit(idx)) && gc_state.tool_length_offset[idx] != gc_block.values.xyz[idx]) {
tlo_changed = true;
@@ -3236,18 +3270,14 @@ status_code_t gc_execute_block (char *block)
switch(gc_block.non_modal_command) {
case NonModal_SetCoordinateData:
-#if N_TOOLS
if(gc_block.values.l == 2 || gc_block.values.l == 20) {
-#endif
settings_write_coord_data(gc_block.values.coord_data.id, &gc_block.values.coord_data.xyz);
// Update system coordinate system if currently active.
if (gc_state.modal.coord_system.id == gc_block.values.coord_data.id) {
memcpy(gc_state.modal.coord_system.xyz, gc_block.values.coord_data.xyz, sizeof(gc_state.modal.coord_system.xyz));
system_flag_wco_change();
}
-#if N_TOOLS
}
-#endif
break;
case NonModal_GoHome_0:
diff --git a/gcode.h b/gcode.h
index 054b7f2..856a9e5 100644
--- a/gcode.h
+++ b/gcode.h
@@ -385,6 +385,9 @@ typedef struct {
float r; //!< Arc radius or retract position
float s; //!< Spindle speed - single-meaning word
float xyz[N_AXIS]; //!< X,Y,Z (and A,B,C,U,V when enabled) translational axes
+#if LATHE_UVW_OPTION
+ float uvw[3]; //!< U,V,W lathe mode incremental mode motion
+#endif
coord_system_t coord_data; //!< Coordinate data
int32_t $; //!< Spindle id - single-meaning word
int32_t n; //!< Line number - single-meaning word
@@ -533,7 +536,7 @@ typedef struct {
// float blending_tolerance; //!< Motion blending tolerance
int32_t line_number; //!< Last line number sent
tool_id_t tool_pending; //!< Tool to be selected on next M6
-#if N_TOOLS && NGC_EXPRESSIONS_ENABLE
+#if NGC_EXPRESSIONS_ENABLE
uint32_t g43_pending; //!< Tool offset to be selected on next M6, for macro ATC
#endif
bool file_run; //!< Tracks % command
@@ -557,11 +560,6 @@ typedef struct {
} scale_factor_t;
extern parser_state_t gc_state;
-#if N_TOOLS
-extern tool_data_t tool_table[N_TOOLS + 1];
-#else
-extern tool_data_t tool_table;
-#endif
/*! \brief Parser block structure.
diff --git a/grbl.h b/grbl.h
index bdb9c0f..3c74805 100644
--- a/grbl.h
+++ b/grbl.h
@@ -42,7 +42,7 @@
#else
#define GRBL_VERSION "1.1f"
#endif
-#define GRBL_BUILD 20231005
+#define GRBL_BUILD 20231210
#define GRBL_URL "https://github.com/grblHAL"
diff --git a/grbllib.c b/grbllib.c
index f2c5f29..97dc70c 100644
--- a/grbllib.c
+++ b/grbllib.c
@@ -292,6 +292,13 @@ int grbl_enter (void)
grbl.on_execute_realtime = auto_realtime_report;
}
+ if(hal.driver_cap.sd_card || hal.driver_cap.littlefs) {
+ fs_options_t fs_options = {0};
+ fs_options.lfs_hidden = hal.driver_cap.littlefs;
+ fs_options.sd_mount_on_boot = hal.driver_cap.sd_card;
+ setting_remove_elements(Setting_FSOptions, fs_options.mask);
+ }
+
// Grbl initialization loop upon power-up or a system abort. For the latter, all processes
// will return to this loop to be cleanly re-initialized.
while(looping) {
diff --git a/hal.h b/hal.h
index 266986b..57e45ec 100644
--- a/hal.h
+++ b/hal.h
@@ -53,8 +53,10 @@ typedef union {
control_pull_up :1, //!< Pullup resistors for control inputs are supported.
probe_pull_up :1, //!< Pullup resistors for probe inputs are supported.
amass_level :2, // 0...3 Deprecated?
+ spindle_encoder :1, //!< Spindle encoder is supported.
spindle_sync :1, //!< Spindle synced motion is supported.
sd_card :1,
+ littlefs :1,
bluetooth :1,
ethernet :1,
wifi :1,
@@ -66,7 +68,7 @@ typedef union {
odometers :1,
pwm_spindle :1,
probe_latch :1,
- unassigned :11;
+ unassigned :9;
};
} driver_cap_t;
@@ -599,7 +601,7 @@ typedef struct {
enumerate_pins_ptr enumerate_pins; //!< Optional handler for enumerating pins used by the driver.
bool (*driver_release)(void); //!< Optional handler for releasing hardware resources before exiting.
uint32_t (*get_elapsed_ticks)(void); //!< Optional handler for getting number of elapsed 1 ms tics since startup. Rolls over every 49.71 days. Required by a number of plugins.
- uint32_t (*get_micros)(void); //!< Optional handler for getting number of elapsed 1 us tics since startup. Rolls over every 1.19 hours. Required by a number of plugins.
+ uint64_t (*get_micros)(void); //!< Optional handler for getting number of elapsed 1 us tics since startup. Rolls over every 1.19 hours. Required by a number of plugins.
pallet_shuttle_ptr pallet_shuttle; //!< Optional handler for performing a pallet shuttle on program end (M60).
void (*reboot)(void); //!< Optoional handler for rebooting the controller. This will be called when #ASCII_ESC followed by #CMD_REBOOT is received.
diff --git a/ioports.c b/ioports.c
index a06c60f..bb9bdc3 100644
--- a/ioports.c
+++ b/ioports.c
@@ -143,7 +143,7 @@ bool ioport_can_claim_explicit (void)
return !(hal.port.claim == NULL || hal.port.get_pin_info == NULL);
}
-bool ioports_enumerate (io_port_type_t type, io_port_direction_t dir, pin_mode_t filter, bool claimable, ioports_enumerate_callback_ptr callback)
+bool ioports_enumerate (io_port_type_t type, io_port_direction_t dir, pin_mode_t filter, bool claimable, ioports_enumerate_callback_ptr callback, void *data)
{
bool ok = false;
uint8_t n_ports = ioports_available(type, dir);
@@ -153,7 +153,7 @@ bool ioports_enumerate (io_port_type_t type, io_port_direction_t dir, pin_mode_t
portinfo = hal.port.get_pin_info(type, dir, --n_ports);
if(claimable && portinfo->mode.claimed)
continue;
- if((portinfo->mode.mask & filter.mask) == filter.mask && (ok = callback(portinfo, n_ports)))
+ if((portinfo->mode.mask & filter.mask) == filter.mask && (ok = callback(portinfo, n_ports, data)))
break;
} while(n_ports);
@@ -290,6 +290,8 @@ static inline uint_fast16_t invert_pwm (ioports_pwm_t *pwm_data, uint_fast16_t p
*/
bool ioports_precompute_pwm_values (pwm_config_t *config, ioports_pwm_t *pwm_data, uint32_t clock_hz)
{
+ pwm_data->f_clock = clock_hz;
+
if(config->max > config->min) {
pwm_data->min = config->min;
pwm_data->period = (uint_fast16_t)((float)clock_hz / config->freq_hz);
diff --git a/ioports.h b/ioports.h
index 03a63c9..0295db4 100644
--- a/ioports.h
+++ b/ioports.h
@@ -104,7 +104,7 @@ typedef void (*ioport_interrupt_callback_ptr)(uint8_t port, bool state);
*/
typedef bool (*ioport_register_interrupt_handler_ptr)(uint8_t port, pin_irq_mode_t irq_mode, ioport_interrupt_callback_ptr interrupt_callback);
-typedef bool (*ioports_enumerate_callback_ptr)(xbar_t *properties, uint8_t port);
+typedef bool (*ioports_enumerate_callback_ptr)(xbar_t *properties, uint8_t port, void *data);
//! Properties and handlers for auxiliary digital and analog I/O.
typedef struct {
@@ -125,7 +125,7 @@ typedef struct {
uint8_t ioports_available (io_port_type_t type, io_port_direction_t dir);
bool ioport_claim (io_port_type_t type, io_port_direction_t dir, uint8_t *port, const char *description);
bool ioport_can_claim_explicit (void);
-bool ioports_enumerate (io_port_type_t type, io_port_direction_t dir, pin_mode_t filter, bool claimable, ioports_enumerate_callback_ptr callback);
+bool ioports_enumerate (io_port_type_t type, io_port_direction_t dir, pin_mode_t filter, bool claimable, ioports_enumerate_callback_ptr callback, void *data);
//
@@ -146,6 +146,7 @@ typedef struct io_ports_data {
//!* \brief Precalculated values that may be set/used by HAL driver to speed up analog input to PWM conversions. */
typedef struct {
+ uint32_t f_clock;
uint_fast16_t period;
uint_fast16_t off_value; //!< NOTE: this value holds the inverted version if software PWM inversion is enabled by the driver.
uint_fast16_t min_value;
diff --git a/motion_control.c b/motion_control.c
index 4febedc..21fb0fd 100644
--- a/motion_control.c
+++ b/motion_control.c
@@ -176,7 +176,7 @@ bool mc_line (float *target, plan_line_data_t *pl_data)
// if there is a coincident position passed.
if(!plan_buffer_line(target, pl_data) && pl_data->spindle.hal->cap.laser && pl_data->spindle.state.on && !pl_data->spindle.state.ccw) {
protocol_buffer_synchronize();
- pl_data->spindle.hal->set_state(pl_data->spindle.state, pl_data->spindle.rpm);
+ pl_data->spindle.hal->set_state(pl_data->spindle.hal, pl_data->spindle.state, pl_data->spindle.rpm);
}
#ifdef KINEMATICS_API
@@ -287,9 +287,13 @@ void mc_arc (float *target, plan_line_data_t *pl_data, float *position, float *o
// (2x) settings.arc_tolerance. For 99% of users, this is just fine. If a different arc segment fit
// is desired, i.e. least-squares, midpoint on arc, just change the mm_per_arc_segment calculation.
// For the intended uses of Grbl, this value shouldn't exceed 2000 for the strictest of cases.
- uint_fast16_t segments = (uint_fast16_t)floorf(fabsf(0.5f * angular_travel * radius) / sqrtf(settings.arc_tolerance * (2.0f * radius - settings.arc_tolerance)));
- if (segments) {
+ uint_fast16_t segments = 0;
+
+ if(2.0f * radius > settings.arc_tolerance)
+ segments = (uint_fast16_t)floorf(fabsf(0.5f * angular_travel * radius) / sqrtf(settings.arc_tolerance * (2.0f * radius - settings.arc_tolerance)));
+
+ if(segments) {
// Multiply inverse feed_rate to compensate for the fact that this movement is approximated
// by a number of discrete segments. The inverse feed_rate should be correct for the sum of
@@ -602,7 +606,7 @@ void mc_canned_drill (motion_mode_t motion, float *target, plan_line_data_t *pl_
mc_dwell(canned->dwell);
if(canned->spindle_off)
- pl_data->spindle.hal->set_state((spindle_state_t){0}, 0.0f);
+ pl_data->spindle.hal->set_state(pl_data->spindle.hal, (spindle_state_t){0}, 0.0f);
// rapid retract
switch(motion) {
diff --git a/nvs_buffer.c b/nvs_buffer.c
index ac12805..5f2af4f 100644
--- a/nvs_buffer.c
+++ b/nvs_buffer.c
@@ -92,7 +92,7 @@ static const emap_t target[] = {
{TOOL_ADDR(6), NVS_GROUP_TOOLS, 6},
{TOOL_ADDR(7), NVS_GROUP_TOOLS, 7},
#if N_TOOLS > 8
- {TOOL_ADDR(8), NVS_GROUP_TOOLS, 8},
+ {TOOL_ADDR(8), NVS_GROUP_TOOLS, 8},
{TOOL_ADDR(9), NVS_GROUP_TOOLS, 9},
{TOOL_ADDR(10), NVS_GROUP_TOOLS, 10},
{TOOL_ADDR(11), NVS_GROUP_TOOLS, 11},
@@ -102,7 +102,22 @@ static const emap_t target[] = {
{TOOL_ADDR(15), NVS_GROUP_TOOLS, 15},
#endif
#if N_TOOLS > 16
-#error Increase number of tool entries!
+ {TOOL_ADDR(16), NVS_GROUP_TOOLS, 16},
+ {TOOL_ADDR(17), NVS_GROUP_TOOLS, 17},
+ {TOOL_ADDR(18), NVS_GROUP_TOOLS, 18},
+ {TOOL_ADDR(19), NVS_GROUP_TOOLS, 19},
+ {TOOL_ADDR(20), NVS_GROUP_TOOLS, 20},
+ {TOOL_ADDR(21), NVS_GROUP_TOOLS, 21},
+ {TOOL_ADDR(22), NVS_GROUP_TOOLS, 22},
+ {TOOL_ADDR(23), NVS_GROUP_TOOLS, 23},
+ {TOOL_ADDR(24), NVS_GROUP_TOOLS, 24},
+ {TOOL_ADDR(25), NVS_GROUP_TOOLS, 25},
+ {TOOL_ADDR(26), NVS_GROUP_TOOLS, 26},
+ {TOOL_ADDR(27), NVS_GROUP_TOOLS, 27},
+ {TOOL_ADDR(28), NVS_GROUP_TOOLS, 28},
+ {TOOL_ADDR(29), NVS_GROUP_TOOLS, 29},
+ {TOOL_ADDR(30), NVS_GROUP_TOOLS, 30},
+ {TOOL_ADDR(31), NVS_GROUP_TOOLS, 31},
#endif
#endif
{0, 0, 0} // list termination - do not remove
@@ -360,8 +375,13 @@ void nvs_buffer_sync_physical (void)
settings_dirty.build_info;
} else if(physical_nvs.memcpy_to_flash) {
- if(!physical_nvs.memcpy_to_flash(nvsbuffer))
- report_message("Settings write failed!", Message_Warning);
+ uint_fast8_t retries = 4;
+ do {
+ if(physical_nvs.memcpy_to_flash(nvsbuffer))
+ retries = 0;
+ else if(--retries == 0)
+ report_message("Settings write failed!", Message_Warning);
+ } while(retries);
memset(&settings_dirty, 0, sizeof(settings_dirty_t));
}
}
@@ -421,4 +441,3 @@ void nvs_memmap (void)
}
#endif
-
diff --git a/pid.h b/pid.h
index 34ea7c0..a3b27b0 100644
--- a/pid.h
+++ b/pid.h
@@ -7,7 +7,7 @@
Part of grblHAL
- Copyright (c) 2020-2021 Terje Io
+ Copyright (c) 2020-2023 Terje Io
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -28,7 +28,16 @@
#include
-#include "settings.h"
+typedef struct {
+ float p_gain;
+ float i_gain;
+ float d_gain;
+ float p_max_error;
+ float i_max_error;
+ float d_max_error;
+ float deadband;
+ float max_error;
+} pid_values_t;
typedef struct {
pid_values_t cfg;
diff --git a/pin_bits_masks.h b/pin_bits_masks.h
index 83eedeb..a1a9a76 100644
--- a/pin_bits_masks.h
+++ b/pin_bits_masks.h
@@ -226,15 +226,21 @@
#endif
#if SPINDLE_SYNC_ENABLE
-#ifndef SPINDLE_INDEX_BIT
-#define SPINDLE_INDEX_BIT (1<get_state();
+ spindle_0_state = spindle_0->get_state(spindle_0);
// Report realtime feed speed
if(settings.status_report.feed_speed) {
@@ -1201,7 +1199,7 @@ void report_realtime_status (void)
spindle_state_t spindle_n_state;
if((spindle_n = spindle_get(idx))) {
- spindle_n_state = spindle_n->get_state();
+ spindle_n_state = spindle_n->get_state(spindle_n);
hal.stream.write_all(appendbuf(3, "|SP", uitoa(idx), ":"));
hal.stream.write_all(appendbuf(3, uitoa(spindle_n_state.on ? lroundf(spindle_n->param->rpm_overridden) : 0), ",,", spindle_n_state.on ? (spindle_n_state.ccw ? "C" : "S") : ""));
if(settings.status_report.overrides)
@@ -2122,7 +2120,7 @@ status_code_t report_spindle_data (sys_state_t state, char *args)
float apos = spindle->get_data(SpindleData_AngularPosition)->angular_position;
spindle_data_t *data = spindle->get_data(SpindleData_Counters);
- hal.stream.write("[SPINDLE:");
+ hal.stream.write("[SPINDLEENCODER:");
hal.stream.write(uitoa(data->index_count));
hal.stream.write(",");
hal.stream.write(uitoa(data->pulse_count));
@@ -2203,23 +2201,69 @@ status_code_t report_time (void)
static void report_spindle (spindle_info_t *spindle, void *data)
{
- hal.stream.write(uitoa(spindle->id));
- hal.stream.write(" - ");
- hal.stream.write(spindle->name);
- if(spindle->enabled) {
-#if N_SPINDLE > 1
- hal.stream.write(", enabled as spindle ");
- hal.stream.write(uitoa(spindle->num));
-#else
- hal.stream.write(", active");
+ if(data) {
+ char *caps = buf;
+ hal.stream.write("[SPINDLE:");
+ hal.stream.write(uitoa(spindle->id));
+ hal.stream.write("|");
+ hal.stream.write(spindle->enabled ? uitoa(spindle->num) : "-");
+ hal.stream.write("|");
+ hal.stream.write(uitoa(spindle->hal->type));
+ *caps++ = '|';
+#if N_SYS_SPINDLE == 1
+ if(spindle->is_current)
+ *caps++ = '*';
#endif
+ if(spindle->hal->cap.at_speed)
+ *caps++ = 'S';
+ if(spindle->hal->cap.direction)
+ *caps++ = 'D';
+ if(spindle->hal->cap.laser)
+ *caps++ = 'L';
+ if(spindle->hal->cap.pid)
+ *caps++ = 'P';
+ if(spindle->hal->cap.pwm_invert)
+ *caps++ = 'I';
+ if(spindle->hal->cap.pwm_linearization)
+ *caps++ = 'N';
+ if(spindle->hal->cap.rpm_range_locked)
+ *caps++ = 'R';
+ if(spindle->hal->cap.variable)
+ *caps++ = 'V';
+ if(spindle->hal->get_data)
+ *caps++ = 'E';
+ *caps++ = '|';
+ *caps = '\0';
+ hal.stream.write(buf);
+ hal.stream.write(spindle->name);
+ if(spindle->hal->rpm_max > 0.0f) {
+ hal.stream.write("|");
+ hal.stream.write(ftoa(spindle->hal->rpm_min, 1));
+ hal.stream.write(",");
+ hal.stream.write(ftoa(spindle->hal->rpm_max, 1));
+ }
+ hal.stream.write("]" ASCII_EOL);
+ } else {
+ hal.stream.write(uitoa(spindle->id));
+ hal.stream.write(" - ");
+ hal.stream.write(spindle->name);
+ if(spindle->enabled) {
+#if N_SPINDLE > 1
+ hal.stream.write(", enabled as spindle ");
+ hal.stream.write(uitoa(spindle->num));
+ #if N_SYS_SPINDLE == 1
+ if(spindle->is_current)
+ hal.stream.write(", active");
+ #endif
+#endif
+ }
+ hal.stream.write(ASCII_EOL);
}
- hal.stream.write(ASCII_EOL);
}
-status_code_t report_spindles (void)
+status_code_t report_spindles (bool machine_readable)
{
- if(!spindle_enumerate_spindles(report_spindle, NULL))
+ if(!spindle_enumerate_spindles(report_spindle, (void *)machine_readable) && !machine_readable)
hal.stream.write("No spindles registered." ASCII_EOL);
return Status_OK;
diff --git a/report.h b/report.h
index 0df4729..e91c650 100644
--- a/report.h
+++ b/report.h
@@ -99,7 +99,7 @@ status_code_t report_spindle_data (sys_state_t state, char *args);
status_code_t report_pins (sys_state_t state, char *args);
// Prints registered spindles.
-status_code_t report_spindles (void);
+status_code_t report_spindles (bool machine_readable);
// Prints current RTC datetime in ISO8601 format (when available)
status_code_t report_time (void);
diff --git a/settings.c b/settings.c
index 6e3099f..a1bc939 100644
--- a/settings.c
+++ b/settings.c
@@ -367,6 +367,7 @@ static status_code_t set_spindle_invert (setting_id_t id, uint_fast16_t int_valu
static status_code_t set_pwm_mode (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_pwm_options (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_spindle_type (setting_id_t id, uint_fast16_t int_value);
+static status_code_t set_encoder_spindle (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_control_disable_pullup (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_probe_disable_pullup (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_soft_limits_enable (setting_id_t id, uint_fast16_t int_value);
@@ -375,7 +376,9 @@ static status_code_t set_jog_soft_limited (setting_id_t id, uint_fast16_t int_va
static status_code_t set_homing_enable (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_enable_legacy_rt_commands (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_homing_cycle (setting_id_t id, uint_fast16_t int_value);
+#if !LATHE_UVW_OPTION
static status_code_t set_mode (setting_id_t id, uint_fast16_t int_value);
+#endif
static status_code_t set_sleep_enable (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_hold_actions (setting_id_t id, uint_fast16_t int_value);
static status_code_t set_force_initialization_alarm (setting_id_t id, uint_fast16_t int_value);
@@ -422,6 +425,7 @@ static char control_signals[] = "Reset,Feed hold,Cycle start,Safety door,Block d
static char spindle_signals[] = "Spindle enable,Spindle direction,PWM";
static char coolant_signals[] = "Flood,Mist";
static char ganged_axes[] = "X-Axis,Y-Axis,Z-Axis";
+static char fs_options[] = "Auto mount SD card,Hide LittleFS";
static char spindle_types[100] = "";
static char axis_dist[4] = "mm";
static char axis_rate[8] = "mm/min";
@@ -462,7 +466,7 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_ReportInches, Group_General, "Report in inches", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsLegacyFn, set_report_inches, get_int, NULL },
{ Setting_ControlInvertMask, Group_ControlSignals, "Invert control pins", NULL, Format_Bitfield, control_signals, NULL, NULL, Setting_IsExpandedFn, set_control_invert, get_int, NULL },
{ Setting_CoolantInvertMask, Group_Coolant, "Invert coolant pins", NULL, Format_Bitfield, coolant_signals, NULL, NULL, Setting_IsExtended, &settings.coolant_invert.mask, NULL, NULL },
- { Setting_SpindleInvertMask, Group_Spindle, "Invert spindle signals", NULL, Format_Bitfield, spindle_signals, NULL, NULL, Setting_IsExtendedFn, set_spindle_invert, get_int, NULL, { .reboot_required = On } },
+ { Setting_SpindleInvertMask, Group_Spindle, "Invert spindle signals", NULL, Format_Bitfield, spindle_signals, NULL, NULL, Setting_IsExtendedFn, set_spindle_invert, get_int, is_setting_available, { .reboot_required = On } },
{ Setting_ControlPullUpDisableMask, Group_ControlSignals, "Pullup disable control pins", NULL, Format_Bitfield, control_signals, NULL, NULL, Setting_IsExtendedFn, set_control_disable_pullup, get_int, NULL },
{ Setting_LimitPullUpDisableMask, Group_Limits, "Pullup disable limit pins", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsExtended, &settings.limits.disable_pullup.mask, NULL, NULL },
{ Setting_ProbePullUpDisable, Group_Probing, "Pullup disable probe pin", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_probe_disable_pullup, get_int, is_setting_available },
@@ -490,7 +494,9 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_PulseDelayMicroseconds, Group_Stepper, "Pulse delay", "microseconds", Format_Decimal, "#0.0", NULL, "10", Setting_IsExtended, &settings.steppers.pulse_delay_microseconds, NULL, NULL },
{ Setting_RpmMax, Group_Spindle, "Maximum spindle speed", "RPM", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacy, &settings.spindle.rpm_max, NULL, is_setting_available },
{ Setting_RpmMin, Group_Spindle, "Minimum spindle speed", "RPM", Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacy, &settings.spindle.rpm_min, NULL, is_setting_available },
+#if !LATHE_UVW_OPTION
{ Setting_Mode, Group_General, "Mode of operation", NULL, Format_RadioButtons, "Normal,Laser mode,Lathe mode", NULL, NULL, Setting_IsLegacyFn, set_mode, get_int, NULL },
+#endif
{ Setting_PWMFreq, Group_Spindle, "Spindle PWM frequency", "Hz", Format_Decimal, "#####0", NULL, NULL, Setting_IsExtended, &settings.spindle.pwm_freq, NULL, is_setting_available },
{ Setting_PWMOffValue, Group_Spindle, "Spindle PWM off value", "percent", Format_Decimal, "##0.0", NULL, "100", Setting_IsExtended, &settings.spindle.pwm_off_value, NULL, is_setting_available },
{ Setting_PWMMinValue, Group_Spindle, "Spindle PWM min value", "percent", Format_Decimal, "##0.0", NULL, "100", Setting_IsExtended, &settings.spindle.pwm_min_value, NULL, is_setting_available },
@@ -549,7 +555,7 @@ PROGMEM static const setting_detail_t setting_detail[] = {
{ Setting_PositionIGain, Group_Spindle_Sync, "Spindle sync I-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.position.pid.i_gain, NULL, is_group_available },
{ Setting_PositionDGain, Group_Spindle_Sync, "Spindle sync D-gain", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.position.pid.d_gain, NULL, is_group_available },
{ Setting_PositionIMaxError, Group_Spindle_Sync, "Spindle sync PID max I error", NULL, Format_Decimal, "###0.000", NULL, NULL, Setting_IsExtended, &settings.position.pid.i_max_error, NULL, is_group_available },
- { Setting_AxisStepsPerMM, Group_Axis0, "-axis travel resolution", axis_steps, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
+ { Setting_AxisStepsPerMM, Group_Axis0, "-axis travel resolution", axis_steps, Format_Decimal, "#####0.000##", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
{ Setting_AxisMaxRate, Group_Axis0, "-axis maximum rate", axis_rate, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
{ Setting_AxisAcceleration, Group_Axis0, "-axis acceleration", axis_accel, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
{ Setting_AxisMaxTravel, Group_Axis0, "-axis maximum travel", axis_dist, Format_Decimal, "#####0.000", NULL, NULL, Setting_IsLegacyFn, set_axis_setting, get_float, NULL, AXIS_OPTS },
@@ -607,6 +613,8 @@ PROGMEM static const setting_detail_t setting_detail[] = {
#if COMPATIBILITY_LEVEL <= 1
{ Setting_OffsetLock, Group_General, "Lock coordinate systems", NULL, Format_Bitfield, "G59.1,G59.2,G59.3", NULL, NULL, Setting_IsExtendedFn, set_offset_lock, get_int, NULL },
#endif
+ { Setting_EncoderSpindle, Group_Spindle, "Encoder spindle", NULL, Format_RadioButtons, spindle_types, NULL, NULL, Setting_IsExtendedFn, set_encoder_spindle, get_int, is_setting_available },
+ { Setting_FSOptions, Group_General, "File systems options", NULL, Format_Bitfield, fs_options, NULL, NULL, Setting_IsExtended, &settings.fs_options.mask, NULL, is_setting_available }
};
#ifndef NO_SETTINGS_DESCRIPTIONS
@@ -668,9 +676,11 @@ PROGMEM static const setting_descr_t setting_descr[] = {
},
{ Setting_RpmMax, "Maximum spindle speed, can be overridden by spindle plugins." },
{ Setting_RpmMin, "Minimum spindle speed, can be overridden by spindle plugins." },
+#if !LATHE_UVW_OPTION
{ Setting_Mode, "Laser mode: consecutive G1/2/3 commands will not halt when spindle speed is changed.\\n"
"Lathe mode: allows use of G7, G8, G96 and G97."
},
+#endif
{ Setting_PWMFreq, "Spindle PWM frequency." },
{ Setting_PWMOffValue, "Spindle PWM off value in percent (duty cycle)." },
{ Setting_PWMMinValue, "Spindle PWM min value in percent (duty cycle)." },
@@ -779,6 +789,8 @@ PROGMEM static const setting_descr_t setting_descr[] = {
#if NGC_EXPRESSIONS_ENABLE
{ Setting_NGCDebugOut, "Example: (debug, metric mode: #<_metric>, coord system: #5220)" },
#endif
+ { Setting_EncoderSpindle, "Specifies which spindle has the encoder attached." },
+ { Setting_FSOptions, "Auto mount SD card on startup." }
};
#endif
@@ -997,6 +1009,18 @@ static status_code_t set_spindle_type (setting_id_t id, uint_fast16_t int_value)
return Status_OK;
}
+static status_code_t set_encoder_spindle (setting_id_t id, uint_fast16_t int_value)
+{
+ if(spindle_get_count() < 2)
+ return Status_SettingDisabled;
+ else if(int_value >= spindle_get_count())
+ return Status_SettingValueOutOfRange;
+
+ settings.offset_lock.encoder_spindle = int_value;
+
+ return Status_OK;
+}
+
static status_code_t set_spindle_invert (setting_id_t id, uint_fast16_t int_value)
{
settings.spindle.invert.mask = int_value;
@@ -1062,7 +1086,9 @@ static status_code_t set_estop_unlock (setting_id_t id, uint_fast16_t int_value)
static status_code_t set_offset_lock (setting_id_t id, uint_fast16_t int_value)
{
- settings.parking.flags.offset_lock = int_value & 0x07;
+ settings.parking.flags.offset_lock = int_value & 0b111; // TODO: remove
+ settings.offset_lock.mask &= ~0b111; // TODO: remove
+ settings.offset_lock.mask |= settings.parking.flags.offset_lock;
return Status_OK;
}
@@ -1147,6 +1173,8 @@ static status_code_t set_homing_cycle (setting_id_t id, uint_fast16_t int_value)
return Status_OK;
}
+#if !LATHE_UVW_OPTION
+
static status_code_t set_mode (setting_id_t id, uint_fast16_t int_value)
{
switch((machine_mode_t)int_value) {
@@ -1174,6 +1202,8 @@ static status_code_t set_mode (setting_id_t id, uint_fast16_t int_value)
return Status_OK;
}
+#endif // LATHE_UVW_OPTION
+
#ifndef NO_SAFETY_DOOR_SUPPORT
static status_code_t set_parking_enable (setting_id_t id, uint_fast16_t int_value)
@@ -1190,7 +1220,7 @@ static status_code_t set_restore_overrides (setting_id_t id, uint_fast16_t int_v
return Status_OK;
}
-#endif
+#endif // NO_SAFETY_DOOR_SUPPORT
static status_code_t set_sleep_enable (setting_id_t id, uint_fast16_t int_value)
{
@@ -1663,7 +1693,11 @@ static uint32_t get_int (setting_id_t id)
break;
case Setting_OffsetLock:
- value = settings.parking.flags.offset_lock;
+ value = settings.offset_lock.mask & 0b111;
+ break;
+
+ case Setting_EncoderSpindle:
+ value = settings.offset_lock.encoder_spindle;
break;
#if NGC_EXPRESSIONS_ENABLE
@@ -1878,6 +1912,10 @@ static bool is_setting_available (const setting_detail_t *setting)
available = hal.driver_cap.pwm_spindle && spindle_get_caps(false).laser;
break;
+ case Setting_SpindleInvertMask:
+ available = spindle_get_caps(false).gpio_controlled;
+ break;
+
case Setting_PWMFreq:
case Setting_PWMOffValue:
case Setting_PWMMinValue:
@@ -1890,7 +1928,7 @@ static bool is_setting_available (const setting_detail_t *setting)
break;
case Setting_SpindlePPR:
- available = hal.driver_cap.spindle_sync || hal.driver_cap.spindle_pid;
+ available = hal.driver_cap.spindle_encoder;
break;
case Setting_RpmMax:
@@ -1922,7 +1960,7 @@ static bool is_setting_available (const setting_detail_t *setting)
#endif
case Setting_SpindleAtSpeedTolerance:
- available = spindle_get_caps(true).at_speed || hal.driver_cap.spindle_sync;
+ available = spindle_get_caps(true).at_speed || hal.driver_cap.spindle_encoder;
break;
case Setting_SpindleOnDelay:
@@ -1941,6 +1979,14 @@ static bool is_setting_available (const setting_detail_t *setting)
available = hal.signals_cap.e_stop;
break;
+ case Setting_EncoderSpindle:
+ available = hal.driver_cap.spindle_encoder && spindle_get_count() > 1;
+ break;
+
+ case Setting_FSOptions:
+ available = hal.driver_cap.sd_card || hal.driver_cap.littlefs;
+ break;
+
default:
break;
}
@@ -2021,25 +2067,22 @@ bool settings_read_coord_data (coord_system_id_t id, float (*coord_data)[N_AXIS]
return true;
}
-// Write selected tool data to persistent storage.
-bool settings_write_tool_data (tool_data_t *tool_data)
-{
#if N_TOOLS
+
+// Write selected tool data to persistent storage.
+static bool settings_write_tool_data (tool_data_t *tool_data)
+{
assert(tool_data->tool_id > 0 && tool_data->tool_id <= N_TOOLS); // NOTE: idx 0 is a non-persistent entry for tools not in tool table
if(hal.nvs.type != NVS_None)
hal.nvs.memcpy_to_nvs(NVS_ADDR_TOOL_TABLE + (tool_data->tool_id - 1) * (sizeof(tool_data_t) + NVS_CRC_BYTES), (uint8_t *)tool_data, sizeof(tool_data_t), true);
return true;
-#else
- return false;
-#endif
}
// Read selected tool data from persistent storage.
-bool settings_read_tool_data (tool_id_t tool_id, tool_data_t *tool_data)
+static bool settings_read_tool_data (tool_id_t tool_id, tool_data_t *tool_data)
{
-#if N_TOOLS
assert(tool_id > 0 && tool_id <= N_TOOLS); // NOTE: idx 0 is a non-persistent entry for tools not in tool table
if (!(hal.nvs.type != NVS_None && hal.nvs.memcpy_from_nvs((uint8_t *)tool_data, NVS_ADDR_TOOL_TABLE + (tool_id - 1) * (sizeof(tool_data_t) + NVS_CRC_BYTES),
@@ -2049,11 +2092,25 @@ bool settings_read_tool_data (tool_id_t tool_id, tool_data_t *tool_data)
}
return tool_data->tool_id == tool_id;
-#else
- return false;
-#endif
}
+// Clear all tool data in persistent storage.
+static bool settings_clear_tool_data (void)
+{
+ uint_fast8_t idx;
+
+ tool_data_t tool_data;
+ memset(&tool_data, 0, sizeof(tool_data_t));
+ for (idx = 1; idx <= N_TOOLS; idx++) {
+ tool_data.tool_id = (tool_id_t)idx;
+ settings_write_tool_data(&tool_data);
+ }
+
+ return true;
+}
+
+#endif // N_TOOLS
+
// Read global settings from persistent storage.
// Checks version-byte of non-volatile storage and global settings copy.
bool read_global_settings ()
@@ -2061,16 +2118,17 @@ bool read_global_settings ()
bool ok = hal.nvs.type != NVS_None && SETTINGS_VERSION == hal.nvs.get_byte(0) && hal.nvs.memcpy_from_nvs((uint8_t *)&settings, NVS_ADDR_GLOBAL, sizeof(settings_t), true) == NVS_TransferResult_OK;
// Sanity check of settings, board map could have been changed...
+#if LATHE_UVW_OPTION
+ settings.mode = Mode_Lathe;
+#else
if(settings.mode == Mode_Laser && !spindle_get_caps(false).laser)
settings.mode = Mode_Standard;
-
- if(settings.spindle.flags.type >= spindle_get_count())
- settings.spindle.flags.type = 0;
+#endif
if(settings.planner_buffer_blocks < 30 || settings.planner_buffer_blocks > 1000)
settings.planner_buffer_blocks = 35;
- if(!(hal.driver_cap.spindle_sync || hal.driver_cap.spindle_pid))
+ if(!hal.driver_cap.spindle_encoder)
settings.spindle.ppr = 0;
#if COMPATIBILITY_LEVEL > 1 && DEFAULT_DISABLE_G92_PERSISTENCE
@@ -2134,12 +2192,7 @@ void settings_restore (settings_restore_t restore)
settings_write_coord_data(CoordinateSystem_G92, &coord_data); // Clear G92 offsets
#if N_TOOLS
- tool_data_t tool_data;
- memset(&tool_data, 0, sizeof(tool_data_t));
- for (idx = 1; idx <= N_TOOLS; idx++) {
- tool_data.tool_id = (tool_id_t)idx;
- settings_write_tool_data(&tool_data);
- }
+ settings_clear_tool_data();
#endif
}
@@ -2770,6 +2823,22 @@ void settings_init (void)
grbl.on_set_axis_setting_unit = set_axis_setting_unit;
#endif
+#if N_TOOLS
+ static tool_data_t tools[N_TOOLS + 1];
+
+ grbl.tool_table.n_tools = N_TOOLS;
+ grbl.tool_table.tool = tools;
+ grbl.tool_table.read = settings_read_tool_data;
+ grbl.tool_table.write = settings_write_tool_data;
+ grbl.tool_table.clear = settings_clear_tool_data;
+#else
+ static tool_data_t tools;
+ if(grbl.tool_table.tool == NULL) {
+ grbl.tool_table.n_tools = 0;
+ grbl.tool_table.tool = &tools;
+ }
+#endif
+
if(!read_global_settings()) {
settings_restore_t settings = settings_all;
@@ -2788,12 +2857,14 @@ void settings_init (void)
changed.spindle = settings_changed_spindle();
} else {
- memset(&tool_table, 0, sizeof(tool_data_t)); // First entry is for tools not in tool table
-#if N_TOOLS
- uint_fast8_t idx;
- for (idx = 1; idx <= N_TOOLS; idx++)
- settings_read_tool_data(idx, &tool_table[idx]);
-#endif
+ memset(grbl.tool_table.tool, 0, sizeof(tool_data_t)); // First entry is for tools not in tool table
+
+ if(grbl.tool_table.n_tools) {
+ uint_fast8_t idx;
+ for(idx = 1; idx <= grbl.tool_table.n_tools; idx++)
+ grbl.tool_table.read(idx, &grbl.tool_table.tool[idx]);
+ }
+
report_init();
changed.spindle = settings_changed_spindle();
@@ -2804,6 +2875,9 @@ void settings_init (void)
hal.probe.configure(false, false);
}
+ settings.offset_lock.mask &= ~0b111; // TODO: remove
+ settings.offset_lock.mask |= settings.parking.flags.offset_lock; // TODO: remove
+
xbar_set_homing_source();
tmp_set_soft_limits();
@@ -2812,14 +2886,16 @@ void settings_init (void)
if(spindle_get_count() == 0)
spindle_add_null();
- spindle_state_t spindle_cap = {
- .on = On,
- };
+ spindle_cap_t spindle_cap = spindle_get_caps(false);
+ if(spindle_cap.gpio_controlled) {
- spindle_cap.ccw = spindle_get_caps(false).direction;
- spindle_cap.pwm = spindle_get_caps(false).pwm_invert;
+ spindle_state_t spindle_state = { .on = On };
+ spindle_state.ccw = spindle_cap.direction;
+ spindle_state.pwm = spindle_cap.pwm_invert;
+
+ setting_remove_elements(Setting_SpindleInvertMask, spindle_state.mask);
+ }
- setting_remove_elements(Setting_SpindleInvertMask, spindle_cap.mask);
setting_remove_elements(Setting_ControlInvertMask, hal.signals_cap.mask);
if(hal.stepper.get_ganged)
@@ -2838,4 +2914,10 @@ void settings_init (void)
} while((details = details->next));
setting_details.on_changed = hal.settings_changed;
+
+ // Sanity checks for spindle configuration
+ if(settings.spindle.flags.type >= spindle_get_count())
+ settings.spindle.flags.type = 0;
+ if(settings.offset_lock.encoder_spindle >= spindle_get_count())
+ settings.offset_lock.encoder_spindle = 0;
}
diff --git a/settings.h b/settings.h
index b30591d..fda553b 100644
--- a/settings.h
+++ b/settings.h
@@ -314,6 +314,9 @@ typedef enum {
Setting_UnlockAfterEStop = 484,
Setting_EnableToolPersistence = 485,
Setting_OffsetLock = 486,
+ Setting_Spindle_OnPort = 487,
+ Setting_Spindle_DirPort = 488,
+ Setting_Spindle_PWMPort = 489,
Setting_Macro0 = 490,
Setting_Macro1 = 491,
@@ -345,6 +348,7 @@ typedef enum {
Setting_SpindleEnable5 = 515,
Setting_SpindleEnable6 = 516,
Setting_SpindleEnable7 = 517,
+ Setting_EncoderSpindle = 519,
Setting_SpindleToolStart0 = 520,
Setting_SpindleToolStart1 = 521,
@@ -401,6 +405,23 @@ typedef enum {
Setting_Kinematics8 = 648,
Setting_Kinematics9 = 649,
+ Setting_FSOptions = 650,
+
+ Setting_RpmMax1 = 730,
+ Setting_RpmMin1 = 731,
+ Setting_Mode1 = 732,
+ Setting_PWMFreq1 = 733,
+ Setting_PWMOffValue1 = 734,
+ Setting_PWMMinValue1 = 735,
+ Setting_PWMMaxValue1 = 736,
+// Optional driver implemented settings for piecewise linear spindle PWM algorithm
+ Setting_LinearSpindle1Piece1 = 737,
+ Setting_LinearSpindle1Piece2 = 738,
+ Setting_LinearSpindle1Piece3 = 739,
+ Setting_LinearSpindle1Piece4 = 740,
+//
+// 900-999 - reserved for automatic tool changers (ATC)
+//
Setting_SettingsMax,
Setting_SettingsAll = Setting_SettingsMax,
@@ -538,44 +559,6 @@ typedef struct {
float pullout_increment; // Spindle pull-out and plunge distance in mm. Incremental distance.
} parking_settings_t;
-typedef struct {
- float p_gain;
- float i_gain;
- float d_gain;
- float p_max_error;
- float i_max_error;
- float d_max_error;
- float deadband;
- float max_error;
-} pid_values_t;
-
-typedef union {
- uint8_t value;
- uint8_t mask;
- struct {
- uint8_t enable_rpm_controlled :1, // PWM spindle only
- unused :1,
- type :5,
- pwm_disable :1; // PWM spindle only
- };
-} spindle_settings_flags_t;
-
-typedef struct {
- float rpm_max;
- float rpm_min;
- float pwm_freq;
- float pwm_period;
- float pwm_off_value;
- float pwm_min_value;
- float pwm_max_value;
- float at_speed_tolerance;
- pwm_piece_t pwm_piece[SPINDLE_NPWM_PIECES];
- pid_values_t pid;
- uint16_t ppr; // Spindle encoder pulses per revolution
- spindle_state_t invert;
- spindle_settings_flags_t flags;
-} spindle_settings_t;
-
typedef struct {
pid_values_t pid;
} position_pid_t; // Used for synchronized motion
@@ -657,6 +640,16 @@ typedef struct {
// axes_signals_t soft_enabled; // TODO: add per axis soft limits, replace soft_enabled flag
} limit_settings_t;
+typedef union {
+ uint8_t value;
+ uint8_t mask;
+ struct {
+ uint8_t sd_mount_on_boot :1,
+ lfs_hidden :1,
+ unused :6;
+ };
+} fs_options_t;
+
typedef union {
uint8_t value;
uint8_t mask;
@@ -664,7 +657,7 @@ typedef union {
uint8_t g59_1 :1,
g59_2 :1,
g59_3 :1,
- unused :5;
+ encoder_spindle :5; // TODO: move to spindle settings
};
} offset_lock_t;
@@ -728,13 +721,14 @@ typedef struct {
reportmask_t status_report; // Mask to indicate desired report data.
settingflags_t flags; // Contains default boolean settings
probeflags_t probe;
+ offset_lock_t offset_lock;
+ fs_options_t fs_options;
homing_settings_t homing;
limit_settings_t limits;
parking_settings_t parking;
safety_door_settings_t safety_door;
position_pid_t position; // Used for synchronized motion
ioport_signals_t ioport;
- // offset_lock_t offset_lock; // TODO: add in next settings version.
} settings_t;
typedef enum {
@@ -961,12 +955,6 @@ void settings_write_coord_data(coord_system_id_t id, float (*coord_data)[N_AXIS]
// Reads selected coordinate data from persistent storage
bool settings_read_coord_data(coord_system_id_t id, float (*coord_data)[N_AXIS]);
-// Writes selected tool data to persistent storage
-bool settings_write_tool_data (tool_data_t *tool_data);
-
-// Read selected tool data from persistent storage
-bool settings_read_tool_data (uint32_t tool, tool_data_t *tool_data);
-
// Temporarily override acceleration, if 0 restore to configured setting value
bool settings_override_acceleration (uint8_t axis, float acceleration);
diff --git a/spindle_control.c b/spindle_control.c
index 9c136b3..44d0a8f 100644
--- a/spindle_control.c
+++ b/spindle_control.c
@@ -71,7 +71,7 @@ static bool spindle_activate (spindle_id_t spindle_id, spindle_num_t spindle_num
}
if((pwm_spindle->init_ok = pwm_spindle->hal.config == NULL || pwm_spindle->hal.config(&pwm_spindle->hal)))
- pwm_spindle->hal.set_state((spindle_state_t){0}, 0.0f);
+ pwm_spindle->hal.set_state(&pwm_spindle->hal, (spindle_state_t){0}, 0.0f);
}
pwm_spindle = NULL;
@@ -97,10 +97,17 @@ static bool spindle_activate (spindle_id_t spindle_id, spindle_num_t spindle_num
memcpy(&spindle_hal, &spindle->hal, sizeof(spindle_ptrs_t));
if(spindle->cfg->get_data == NULL) {
- spindle_hal.get_data = hal.spindle_data.get;
- spindle_hal.reset_data = hal.spindle_data.reset;
- if(!spindle->cfg->cap.at_speed)
- spindle_hal.cap.at_speed = !!spindle_hal.get_data;
+ if(settings.offset_lock.encoder_spindle == spindle_id) {
+ spindle_hal.get_data = hal.spindle_data.get;
+ spindle_hal.reset_data = hal.spindle_data.reset;
+ if(!spindle->cfg->cap.at_speed)
+ spindle_hal.cap.at_speed = !!spindle_hal.get_data;
+ } else {
+ spindle_hal.get_data = NULL;
+ spindle_hal.reset_data = NULL;
+ if(!spindle->cfg->cap.at_speed)
+ spindle_hal.cap.at_speed = Off;
+ }
}
spindle_hal.cap.laser &= settings.mode == Mode_Laser;
@@ -134,6 +141,9 @@ __NOTE:__ up to \ref N_SPINDLE spindles can be registered at a time.
*/
spindle_id_t spindle_register (const spindle_ptrs_t *spindle, const char *name)
{
+ if(n_spindle == 1 && spindles[0].cfg->type == SpindleType_Null)
+ n_spindle = 0;
+
if(n_spindle < N_SPINDLE && settings_add_spindle_type(name)) {
spindles[n_spindle].cfg = spindle;
@@ -323,9 +333,9 @@ bool spindle_enumerate_spindles (spindle_enumerate_callback_ptr callback, void *
spindle.id = idx;
spindle.name = spindles[idx].name;
- spindle.hal = &spindles[idx].hal;
spindle.num = spindle_get_num(idx);
spindle.enabled = spindle.num != -1;
+ spindle.hal = spindle.enabled && sys_spindle[spindle.num].hal.id == spindle.id ? &sys_spindle[spindle.num].hal : &spindles[idx].hal;
spindle.is_current = spindle.enabled && sys_spindle[0].hal.id == idx;
callback(&spindle, data);
@@ -366,32 +376,38 @@ spindle_ptrs_t *spindle_get (spindle_num_t spindle_num)
// Null (dummy) spindle, automatically installed if no spindles are registered.
//
-static void null_set_state (spindle_state_t state, float rpm)
+static void null_set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
{
+ UNUSED(spindle);
UNUSED(state);
UNUSED(rpm);
}
-static spindle_state_t null_get_state (void)
+static spindle_state_t null_get_state (spindle_ptrs_t *spindle)
{
+ UNUSED(spindle);
+
return (spindle_state_t){0};
}
// Sets spindle speed
-static void null_update_pwm (uint_fast16_t pwm_value)
+static void null_update_pwm (spindle_ptrs_t *spindle, uint_fast16_t pwm_value)
{
+ UNUSED(spindle);
UNUSED(pwm_value);
}
-static uint_fast16_t null_get_pwm (float rpm)
+static uint_fast16_t null_get_pwm (spindle_ptrs_t *spindle, float rpm)
{
+ UNUSED(spindle);
UNUSED(rpm);
return 0;
}
-static void null_update_rpm (float rpm)
+static void null_update_rpm (spindle_ptrs_t *spindle, float rpm)
{
+ UNUSED(spindle);
UNUSED(rpm);
}
@@ -478,14 +494,14 @@ static bool set_state (spindle_ptrs_t *spindle, spindle_state_t state, float rpm
if (!state.on) { // Halt or set spindle direction and rpm.
spindle->param->rpm = rpm = 0.0f;
- spindle->set_state((spindle_state_t){0}, 0.0f);
+ spindle->set_state(spindle, (spindle_state_t){0}, 0.0f);
} else {
// NOTE: Assumes all calls to this function is when Grbl is not moving or must remain off.
// TODO: alarm/interlock if going from CW to CCW directly in non-laser mode?
if (spindle->cap.laser && state.ccw)
rpm = 0.0f; // TODO: May need to be rpm_min*(100/MAX_SPINDLE_RPM_OVERRIDE);
- spindle->set_state(state, spindle_set_rpm(spindle, rpm, spindle->param->override_pct));
+ spindle->set_state(spindle, state, spindle_set_rpm(spindle, rpm, spindle->param->override_pct));
}
system_add_rt_report(Report_Spindle); // Set to report change immediately
@@ -529,7 +545,7 @@ bool spindle_sync (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
// Empty planner buffer to ensure spindle is set when programmed.
if((ok = protocol_buffer_synchronize()) && set_state(spindle, state, rpm) && !at_speed) {
float on_delay = 0.0f;
- while(!(at_speed = spindle->get_state().at_speed)) {
+ while(!(at_speed = spindle->get_state(spindle).at_speed)) {
delay_sec(0.2f, DelayMode_Dwell);
on_delay += 0.2f;
if(ABORTED)
@@ -567,7 +583,7 @@ bool spindle_restore (spindle_ptrs_t *spindle, spindle_state_t state, float rpm)
delay_sec(settings.safety_door.spindle_on_delay, DelayMode_SysSuspend);
else if((ok == (settings.spindle.at_speed_tolerance <= 0.0f))) {
float delay = 0.0f;
- while(!(ok = spindle->get_state().at_speed)) {
+ while(!(ok = spindle->get_state(spindle).at_speed)) {
delay_sec(0.1f, DelayMode_SysSuspend);
delay += 0.1f;
if(ABORTED)
@@ -620,9 +636,9 @@ void spindle_all_off (void)
spindle->param->rpm = spindle->param->rpm_overridden = 0.0f;
spindle->param->state.value = 0;
#ifdef GRBL_ESP32
- spindle->esp32_off();
+ spindle->esp32_off(spindle);
#else
- spindle->set_state((spindle_state_t){0}, 0.0f);
+ spindle->set_state(spindle, (spindle_state_t){0}, 0.0f);
#endif
}
} while(spindle_num);
@@ -639,7 +655,7 @@ bool spindle_is_on (void)
uint_fast8_t spindle_num = N_SYS_SPINDLE;
do {
if((spindle = spindle_get(--spindle_num)))
- on = spindle->get_state().on;
+ on = spindle->get_state(spindle).on;
} while(spindle_num && !on);
return on;
@@ -659,43 +675,6 @@ static inline uint_fast16_t invert_pwm (spindle_pwm_t *pwm_data, uint_fast16_t p
return pwm_data->invert_pwm ? pwm_data->period - pwm_value - 1 : pwm_value;
}
-/*! \brief Precompute PWM values for faster conversion.
-\param spindle pointer to a \ref spindle_ptrs_t structure.
-\param pwm_data pointer to a \a spindle_pwm_t structure, to hold the precomputed values.
-\param clock_hz timer clock frequency used for PWM generation.
-\returns \a true if successful, \a false if no PWM range possible - driver should then revert to simple on/off spindle control.
-*/
-bool spindle_precompute_pwm_values (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_data, uint32_t clock_hz)
-{
- if(spindle->rpm_max > spindle->rpm_min) {
- pwm_data->rpm_min = spindle->rpm_min;
- pwm_data->period = (uint_fast16_t)((float)clock_hz / settings.spindle.pwm_freq);
- if(settings.spindle.pwm_off_value == 0.0f)
- pwm_data->off_value = pwm_data->invert_pwm ? pwm_data->period : 0;
- else
- pwm_data->off_value = invert_pwm(pwm_data, (uint_fast16_t)(pwm_data->period * settings.spindle.pwm_off_value / 100.0f));
- pwm_data->min_value = (uint_fast16_t)(pwm_data->period * settings.spindle.pwm_min_value / 100.0f);
- pwm_data->max_value = (uint_fast16_t)(pwm_data->period * settings.spindle.pwm_max_value / 100.0f) + pwm_data->offset;
- pwm_data->pwm_gradient = (float)(pwm_data->max_value - pwm_data->min_value) / (spindle->rpm_max - spindle->rpm_min);
- pwm_data->always_on = settings.spindle.pwm_off_value != 0.0f;
- }
-
-#if ENABLE_SPINDLE_LINEARIZATION
- uint_fast8_t idx;
-
- pwm_data->n_pieces = 0;
-
- for(idx = 0; idx < SPINDLE_NPWM_PIECES; idx++) {
- if(!isnan(settings.spindle.pwm_piece[idx].rpm) && settings.spindle.pwm_piece[idx].start != 0.0f)
- memcpy(&pwm_data->piece[pwm_data->n_pieces++], &settings.spindle.pwm_piece[idx], sizeof(pwm_piece_t));
- }
-
- spindle->cap.pwm_linearization = pwm_data->n_pieces > 0;
-#endif
-
- return spindle->rpm_max > spindle->rpm_min;
-}
-
/*! \brief Spindle RPM to PWM conversion.
\param pwm_data pointer t a \a spindle_pwm_t structure.
\param rpm spindle RPM.
@@ -705,7 +684,7 @@ bool spindle_precompute_pwm_values (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_
__NOTE:__ \a spindle_precompute_pwm_values() must be called to precompute values before this function is called.
Typically this is done by the spindle initialization code.
*/
-uint_fast16_t spindle_compute_pwm_value (spindle_pwm_t *pwm_data, float rpm, bool pid_limit)
+static uint_fast16_t spindle_compute_pwm_value (spindle_pwm_t *pwm_data, float rpm, bool pid_limit)
{
uint_fast16_t pwm_value;
@@ -738,3 +717,63 @@ uint_fast16_t spindle_compute_pwm_value (spindle_pwm_t *pwm_data, float rpm, boo
return pwm_value;
}
+
+/*! \internal \brief Dummy spindle RPM to PWM conversion, used if precompute fails.
+\param pwm_data pointer t a \a spindle_pwm_t structure.
+\param rpm spindle RPM.
+\param pid_limit boolean, \a true if PID based spindle sync is used, \a false otherwise.
+\returns the PWM value to use.
+*/
+static uint_fast16_t compute_dummy_pwm_value (spindle_pwm_t *pwm_data, float rpm, bool pid_limit)
+{
+ return pwm_data->off_value;
+}
+
+/*! \brief Precompute PWM values for faster conversion.
+\param spindle pointer to a \ref spindle_ptrs_t structure.
+\param pwm_data pointer to a \a spindle_pwm_t structure, to hold the precomputed values.
+\param clock_hz timer clock frequency used for PWM generation.
+\returns \a true if successful, \a false if no PWM range possible - driver should then revert to simple on/off spindle control.
+*/
+bool spindle_precompute_pwm_values (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_data, spindle_settings_t *settings, uint32_t clock_hz)
+{
+ pwm_data->settings = settings;
+ spindle->rpm_min = pwm_data->rpm_min = settings->rpm_min;
+ spindle->rpm_max = settings->rpm_max;
+ spindle->cap.rpm_range_locked = On;
+
+ if((spindle->cap.variable = !settings->flags.pwm_disable && spindle->rpm_max > spindle->rpm_min)) {
+ pwm_data->f_clock = clock_hz;
+ pwm_data->period = (uint_fast16_t)((float)clock_hz / settings->pwm_freq);
+ if(settings->pwm_off_value == 0.0f)
+ pwm_data->off_value = pwm_data->invert_pwm ? pwm_data->period : 0;
+ else
+ pwm_data->off_value = invert_pwm(pwm_data, (uint_fast16_t)(pwm_data->period * settings->pwm_off_value / 100.0f));
+ pwm_data->min_value = (uint_fast16_t)(pwm_data->period * settings->pwm_min_value / 100.0f);
+ pwm_data->max_value = (uint_fast16_t)(pwm_data->period * settings->pwm_max_value / 100.0f) + pwm_data->offset;
+ pwm_data->pwm_gradient = (float)(pwm_data->max_value - pwm_data->min_value) / (spindle->rpm_max - spindle->rpm_min);
+ pwm_data->always_on = settings->pwm_off_value != 0.0f;
+ pwm_data->compute_value = spindle_compute_pwm_value;
+ } else {
+ pwm_data->off_value = 0;
+ pwm_data->always_on = false;
+ pwm_data->compute_value = compute_dummy_pwm_value;
+ }
+
+ spindle->context = pwm_data;
+
+#if ENABLE_SPINDLE_LINEARIZATION
+ uint_fast8_t idx;
+
+ pwm_data->n_pieces = 0;
+
+ for(idx = 0; idx < SPINDLE_NPWM_PIECES; idx++) {
+ if(!isnan(settings->pwm_piece[idx].rpm) && settings->pwm_piece[idx].start != 0.0f)
+ memcpy(&pwm_data->piece[pwm_data->n_pieces++], &settings->pwm_piece[idx], sizeof(pwm_piece_t));
+ }
+
+ spindle->cap.pwm_linearization = pwm_data->n_pieces > 0;
+#endif
+
+ return spindle->cap.variable;
+}
diff --git a/spindle_control.h b/spindle_control.h
index 634c95b..d0ea78c 100644
--- a/spindle_control.h
+++ b/spindle_control.h
@@ -24,6 +24,32 @@
#ifndef _SPINDLE_CONTROL_H_
#define _SPINDLE_CONTROL_H_
+#include "pid.h"
+
+#define SPINDLE_ALL -1
+#define SPINDLE_NONE 0
+#define SPINDLE_HUANYANG1 1
+#define SPINDLE_HUANYANG2 2
+#define SPINDLE_GS20 3
+#define SPINDLE_YL620A 4
+#define SPINDLE_MODVFD 5
+#define SPINDLE_H100 6
+#define SPINDLE_ONOFF0 7 // typically implemented by driver.c
+#define SPINDLE_ONOFF0_DIR 8 // typically implemented by driver.c
+#define SPINDLE_ONOFF1 9
+#define SPINDLE_ONOFF1_DIR 10
+#define SPINDLE_PWM0 11 // typically implemented by driver.c
+#define SPINDLE_PWM0_NODIR 12 // typically implemented by driver.c
+#define SPINDLE_PWM1 13 // typically implemented by driver.c
+#define SPINDLE_PWM1_NODIR 14
+#define SPINDLE_PWM2 15
+#define SPINDLE_PWM2_NODIR 16
+#define SPINDLE_PWM0_CLONE 17
+#define SPINDLE_SOLENOID 18
+#define SPINDLE_STEPPER 19
+#define SPINDLE_NOWFOREVER 20
+#define SPINDLE_MY_SPINDLE 30
+
typedef int8_t spindle_id_t;
typedef int8_t spindle_num_t;
@@ -45,16 +71,19 @@ typedef union {
/*! \brief Bitmap flags for spindle capabilities. */
typedef union {
- uint8_t value; //!< All bitmap flags.
+ uint16_t value; //!< All bitmap flags.
struct {
- uint8_t variable :1, //!< Variable spindle speed is supported.
- direction :1, //!< Spindle direction (M4) is supported.
- at_speed :1, //!< Spindle at speed feedback is supported.
- laser :1, //!< Spindle can control a laser.
- pwm_invert :1, //!< Spindle PWM output can be inverted.
- pid :1,
- pwm_linearization :1,
- rpm_range_locked :1; //!< Spindle RPM range (min, max) not inherited from settings.
+ uint16_t variable :1, //!< Variable spindle speed is supported.
+ direction :1, //!< Spindle direction (M4) is supported.
+ at_speed :1, //!< Spindle at speed feedback is supported.
+ laser :1, //!< Spindle can control a laser.
+ pwm_invert :1, //!< Spindle PWM output can be inverted.
+ pid :1,
+ pwm_linearization :1,
+ rpm_range_locked :1, //!< Spindle RPM range (min, max) not inherited from settings.
+ gpio_controlled :1, //!< On/off and direction is controlled by GPIO.
+ cmd_controlled :1, //!< Command controlled, e.g. over ModBus.
+ unassigned :6;
};
} spindle_cap_t;
@@ -92,16 +121,26 @@ typedef enum {
SpindleHAL_Active, //!< 2
} spindle_hal_t;
+struct spindle_ptrs; // members defined below
+struct spindle_pwm; // members defined below
+struct spindle_param; // members defined below
+
+/*! \brief Pointer to function for configuring a spindle.
+\param spindle a pointer to a \ref spindle_struct.
+\returns \a true if successful, \false if not.
+*/
+typedef bool (*spindle_config_ptr)(struct spindle_ptrs *spindle);
+
/*! \brief Pointer to function for setting the spindle state.
\param state a \a spindle_state_t union variable.
\param rpm spindle RPM.
*/
-typedef void (*spindle_set_state_ptr)(spindle_state_t state, float rpm);
+typedef void (*spindle_set_state_ptr)(struct spindle_ptrs *spindle, spindle_state_t state, float rpm);
/*! \brief Pointer to function for getting the spindle state.
\returns state in a \a spindle_state_t union variable.
*/
-typedef spindle_state_t (*spindle_get_state_ptr)(void);
+typedef spindle_state_t (*spindle_get_state_ptr)(struct spindle_ptrs *spindle);
/*! \brief Pointer to function for converting a RPM value to a PWM value.
@@ -110,7 +149,7 @@ Typically this is a wrapper for the spindle_compute_pwm_value() function provide
\param rpm spindle RPM.
\returns the corresponding PWM value.
*/
-typedef uint_fast16_t (*spindle_get_pwm_ptr)(float rpm);
+typedef uint_fast16_t (*spindle_get_pwm_ptr)(struct spindle_ptrs *spindle, float rpm);
/*! \brief Pointer to function for updating spindle speed on the fly.
\param pwm new spindle PWM value.
@@ -118,13 +157,12 @@ typedef uint_fast16_t (*spindle_get_pwm_ptr)(float rpm);
__NOTE:__ this function will be called from an interrupt context.
*/
-typedef void (*spindle_update_pwm_ptr)(uint_fast16_t pwm);
-
+typedef void (*spindle_update_pwm_ptr)(struct spindle_ptrs *spindle, uint_fast16_t pwm);
/*! \brief Pointer to function for updating spindle RPM.
\param rpm spindle RPM.
*/
-typedef void (*spindle_update_rpm_ptr)(float rpm);
+typedef void (*spindle_update_rpm_ptr)(struct spindle_ptrs *spindle, float rpm);
/*! \brief Pointer to function for getting spindle data.
\param request request type as a \a #spindle_data_request_t enum.
@@ -143,25 +181,25 @@ Used for Pulses Per Inch (PPI) laser mode.
*/
typedef void (*spindle_pulse_on_ptr)(uint_fast16_t pulse_length);
-struct spindle_param; // members defined below
-
/*! \brief Handlers and data for spindle support. */
struct spindle_ptrs {
- spindle_id_t id; //!< Spindle id, assingned on spindle registration .
+ spindle_id_t id; //!< Spindle id, assingned on spindle registration.
struct spindle_param *param; //!< Pointer to current spindle parameters, assigned when spindle is enabled.
spindle_type_t type; //!< Spindle type.
spindle_cap_t cap; //!< Spindle capabilities.
+ void *context; //!< Optional pointer to spindle specific context data.
uint_fast16_t pwm_off_value; //!< Value for switching PWM signal off.
+// struct spindle_pwm *pwm_data; //!< Optional pointer to PWM configuration.
float rpm_min; //!< Minimum spindle RPM.
float rpm_max; //!< Maximum spindle RPM.
- bool (*config)(struct spindle_ptrs *spindle); //!< Optional handler for configuring the spindle.
+ spindle_config_ptr config; //!< Optional handler for configuring the spindle.
spindle_set_state_ptr set_state; //!< Handler for setting spindle state.
spindle_get_state_ptr get_state; //!< Handler for getting spindle state.
spindle_get_pwm_ptr get_pwm; //!< Handler for calculating spindle PWM value from RPM.
spindle_update_pwm_ptr update_pwm; //!< Handler for updating spindle PWM output.
spindle_update_rpm_ptr update_rpm; //!< Handler for updating spindle RPM.
#ifdef GRBL_ESP32
- void (*esp32_off)(void); //!< Workaround handler for snowflake ESP32 Guru awaken by floating point data in ISR context.
+ void (*esp32_off)(struct spindle_ptrs *spindle); //!< Workaround handler for snowflake ESP32 Guru awaken by floating point data in ISR context.
#endif
// Optional entry points.
spindle_pulse_on_ptr pulse_on; //!< Optional handler for Pulses Per Inch (PPI) mode. Required for the laser PPI plugin.
@@ -196,11 +234,6 @@ typedef struct {
spindle_reset_data_ptr reset; //!< Optional handler for resetting spindle data. Required for spindle sync.
} spindle_data_ptrs_t;
-/*! \brief Pointer to function for configuring the spindle.
-\returns state in a \a spindle_state_t union variable.
-*/
-typedef bool (*spindle_config_ptr)(spindle_ptrs_t *spindle);
-
/*! \brief Structure holding data passed to the callback function called by spindle_enumerate_spindles(). */
typedef struct {
spindle_id_t id;
@@ -217,8 +250,37 @@ typedef struct {
float end;
} pwm_piece_t;
-//!* \brief Precalculated values that may be set/used by HAL driver to speed up RPM to PWM conversions if variable spindle is supported. */
+typedef union {
+ uint8_t value;
+ uint8_t mask;
+ struct {
+ uint8_t enable_rpm_controlled :1, // PWM spindle only
+ unused :1,
+ type :5,
+ pwm_disable :1; // PWM spindle only
+ };
+} spindle_settings_flags_t;
+
typedef struct {
+ float rpm_max;
+ float rpm_min;
+ float pwm_freq;
+ float pwm_period; // currently unused
+ float pwm_off_value;
+ float pwm_min_value;
+ float pwm_max_value;
+ float at_speed_tolerance;
+ pwm_piece_t pwm_piece[SPINDLE_NPWM_PIECES];
+ pid_values_t pid;
+ uint16_t ppr; // Spindle encoder pulses per revolution
+ spindle_state_t invert;
+ spindle_settings_flags_t flags;
+} spindle_settings_t;
+
+//!* \brief Precalculated values that may be set/used by HAL driver to speed up RPM to PWM conversions if variable spindle is supported. */
+typedef struct spindle_pwm {
+ uint32_t f_clock;
+ spindle_settings_t *settings;
uint_fast16_t period;
uint_fast16_t off_value; //!< NOTE: this value holds the inverted version if software PWM inversion is enabled by the driver.
uint_fast16_t min_value;
@@ -227,9 +289,11 @@ typedef struct {
float pwm_gradient;
bool invert_pwm; //!< NOTE: set (by driver) when inversion is done in code
bool always_on;
+ bool cloned;
int_fast16_t offset;
uint_fast16_t n_pieces;
pwm_piece_t piece[SPINDLE_NPWM_PIECES];
+ uint_fast16_t (*compute_value)(struct spindle_pwm *pwm_data, float rpm, bool pid_limit);
} spindle_pwm_t;
/*! \brief Pointer to callback function called by spindle_enumerate_spindles().
@@ -257,9 +321,7 @@ void spindle_all_off (void);
// The following functions are not called by the core, may be called by driver code.
//
-bool spindle_precompute_pwm_values (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_data, uint32_t clock_hz);
-
-uint_fast16_t spindle_compute_pwm_value (spindle_pwm_t *pwm_data, float rpm, bool pid_limit);
+bool spindle_precompute_pwm_values (spindle_ptrs_t *spindle, spindle_pwm_t *pwm_data, spindle_settings_t *settings, uint32_t clock_hz);
spindle_id_t spindle_register (const spindle_ptrs_t *spindle, const char *name);
diff --git a/state_machine.c b/state_machine.c
index a6b4dd5..bf24b16 100644
--- a/state_machine.c
+++ b/state_machine.c
@@ -378,7 +378,7 @@ void state_suspend_manager (void)
grbl.on_override_changed(OverrideChanged_SpindleState);
}
- } else if (sys.step_control.update_spindle_rpm && restore_condition.spindle[0].hal->get_state().on) {
+ } else if (sys.step_control.update_spindle_rpm && restore_condition.spindle[0].hal->get_state(restore_condition.spindle[0].hal).on) {
// Handles spindle state during hold. NOTE: Spindle speed overrides may be altered during hold state.
state_spindle_set_state(&restore_condition.spindle[restore_condition.spindle_num]);
sys.step_control.update_spindle_rpm = Off;
@@ -654,12 +654,12 @@ static void state_await_resume (uint_fast16_t rt_exec)
default:
if (!settings.flags.restore_after_feed_hold) {
- if (!restore_condition.spindle[restore_condition.spindle_num].hal->get_state().on)
+ if (!restore_condition.spindle[restore_condition.spindle_num].hal->get_state(restore_condition.spindle[restore_condition.spindle_num].hal).on)
gc_spindle_off();
sys.override.spindle_stop.value = 0; // Clear spindle stop override states
} else {
- if (restore_condition.spindle[restore_condition.spindle_num].state.on != restore_condition.spindle[restore_condition.spindle_num].hal->get_state().on) {
+ if (restore_condition.spindle[restore_condition.spindle_num].state.on != restore_condition.spindle[restore_condition.spindle_num].hal->get_state(restore_condition.spindle[restore_condition.spindle_num].hal).on) {
grbl.report.feedback_message(Message_SpindleRestore);
state_spindle_restore(&restore_condition.spindle[restore_condition.spindle_num]);
}
@@ -747,7 +747,7 @@ static void state_await_waypoint_retract (uint_fast16_t rt_exec)
// NOTE: Clear accessory state after retract and after an aborted restore motion.
park.plan_data.spindle.state.value = 0;
park.plan_data.spindle.rpm = 0.0f;
- park.plan_data.spindle.hal->set_state(park.plan_data.spindle.state, 0.0f); // De-energize
+ park.plan_data.spindle.hal->set_state(park.plan_data.spindle.hal, park.plan_data.spindle.state, 0.0f); // De-energize
if (!settings.safety_door.flags.keep_coolant_on) {
park.plan_data.condition.coolant.value = 0;
diff --git a/stepper.c b/stepper.c
index 2a553f9..8adcfef 100644
--- a/stepper.c
+++ b/stepper.c
@@ -407,16 +407,16 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void)
#endif
if(st.exec_segment->update_pwm)
- st.exec_segment->update_pwm(st.exec_segment->spindle_pwm);
+ st.exec_segment->update_pwm(st.exec_block->spindle, st.exec_segment->spindle_pwm);
else if(st.exec_segment->update_rpm)
- st.exec_segment->update_rpm(st.exec_segment->spindle_rpm);
+ st.exec_segment->update_rpm(st.exec_block->spindle, st.exec_segment->spindle_rpm);
} else {
// Segment buffer empty. Shutdown.
st_go_idle();
// Ensure pwm is set properly upon completion of rate-controlled motion.
if (st.exec_block->dynamic_rpm && st.exec_block->spindle->cap.laser)
- st.exec_block->spindle->update_pwm(st.exec_block->spindle->pwm_off_value);
+ st.exec_block->spindle->update_pwm(st.exec_block->spindle, st.exec_block->spindle->pwm_off_value);
st.exec_block = NULL;
system_set_exec_state_flag(EXEC_CYCLE_COMPLETE); // Flag main program for cycle complete
@@ -732,6 +732,7 @@ void st_prep_buffer (void)
// st_prep_block->r = pl_block->programmed_rate;
st_prep_block->millimeters = pl_block->millimeters;
st_prep_block->steps_per_mm = (float)pl_block->step_event_count / pl_block->millimeters;
+ st_prep_block->spindle = pl_block->spindle.hal;
st_prep_block->output_commands = pl_block->output_commands;
st_prep_block->overrides = pl_block->overrides;
st_prep_block->backlash_motion = pl_block->condition.backlash_motion;
@@ -987,8 +988,6 @@ void st_prep_buffer (void)
float rpm;
- st_prep_block->spindle = pl_block->spindle.hal;
-
if (pl_block->spindle.state.on) {
if(pl_block->spindle.css) {
float npos = (float)(pl_block->step_event_count - prep.steps_remaining) / (float)pl_block->step_event_count;
@@ -1011,7 +1010,7 @@ void st_prep_buffer (void)
if(pl_block->spindle.hal->get_pwm != NULL) {
prep.current_spindle_rpm = rpm;
prep_segment->update_pwm = pl_block->spindle.hal->update_pwm;
- prep_segment->spindle_pwm = pl_block->spindle.hal->get_pwm(rpm);
+ prep_segment->spindle_pwm = pl_block->spindle.hal->get_pwm(pl_block->spindle.hal, rpm);
} else {
prep_segment->update_rpm = pl_block->spindle.hal->update_rpm;
prep.current_spindle_rpm = prep_segment->spindle_rpm = rpm;
diff --git a/stepper.h b/stepper.h
index 013cb34..2baaf6d 100644
--- a/stepper.h
+++ b/stepper.h
@@ -68,7 +68,7 @@ typedef struct st_segment {
bool cruising; //!< True when in cruising part of profile, only set for spindle synced moves
uint_fast8_t amass_level; //!< Indicates AMASS level for the ISR to execute this segment
spindle_update_pwm_ptr update_pwm; //!< Valid pointer to spindle.update_pwm() if set spindle speed at the start of the segment execution
- spindle_update_rpm_ptr update_rpm; //!< Valid pointer to spindle.update_rmp() if set spindle speed at the start of the segment execution
+ spindle_update_rpm_ptr update_rpm; //!< Valid pointer to spindle.update_rpm() if set spindle speed at the start of the segment execution
} segment_t;
//! Stepper ISR data struct. Contains the running data for the main stepper ISR.
diff --git a/stepper2.c b/stepper2.c
index 440c4be..f169b56 100644
--- a/stepper2.c
+++ b/stepper2.c
@@ -30,17 +30,23 @@
#include "stepper2.h"
typedef enum {
- State_Idle = 0,
- State_Accel,
- State_Run,
- State_RunInfinite,
- State_DecelTo,
- State_Decel
+ State_Idle = 0, //!< 0
+ State_Accel, //!< 1
+ State_Run, //!< 2
+ State_RunInfinite, //!< 3
+ State_DecelTo, //!< 4
+ State_Decel //!< 5
} st2_state_t;
+/*! \brief Internal structure for holding motor configuration and keeping track of its status.
+
+__NOTE:__ The contents of this structure should _not_ be accessed directly by user code.
+*/
struct st2_motor {
uint_fast8_t idx;
axes_signals_t axis;
+ bool is_spindle;
+ bool position_lost;
volatile int64_t position; // absolute step number
position_t ptype; //
st2_state_t state; // state machine state
@@ -49,7 +55,7 @@ struct st2_motor {
uint32_t step_run; //
uint32_t step_down; // start of down-ramp
uint64_t c64; // 24.16 fixed point delay count
- uint32_t delay; // integer delay count
+ uint64_t delay; // integer delay count
uint32_t first_delay; // integer delay count
uint16_t min_delay; // integer delay count
int32_t denom; // 4.n+1 in ramp algo
@@ -58,30 +64,49 @@ struct st2_motor {
float prev_speed; // speed steps/s
float acceleration; // acceleration steps/s^2
axes_signals_t dir; // current direction
- uint32_t next_step;
+ uint64_t next_step;
st2_motor_t *next;
};
static st2_motor_t *motors = NULL;
+static uint8_t spindle_motors = 0;
static settings_changed_ptr settings_changed;
+static on_set_axis_setting_unit_ptr on_set_axis_setting_unit;
+static on_setting_get_description_ptr on_setting_get_description;
static on_reset_ptr on_reset;
+/*! \brief Calculate basic motor configuration.
+
+\param motor pointer to a \a st2_motor structure.
+*/
static void st_motor_config (st2_motor_t *motor)
{
motor->acceleration = settings.axis[motor->idx].acceleration * settings.axis[motor->idx].steps_per_mm / 3600.0f;
motor->first_delay = (uint32_t)(0.676f * sqrtf(2.0f / motor->acceleration) * 1000000.0f);
}
+/*! \brief Stop all motors.
+ *
+This will be called on a soft reset and stops all running motors abruptly.
+
+__NOTE:__ position will likely be lost for running motors.
+*/
static void st2_reset (void)
{
st2_motor_t *motor = motors;
while(motor) {
+ motor->position_lost = motor->state != State_Idle;
motor->state = State_Idle;
motor = motor->next;
}
}
+/*! \brief Update basic motor configuration on settings changes.
+
+\param settings pointer to a \a settings_t structure.
+\param changed a \a settings_changed_flags_t structure.
+*/
static void st2_settings_changed (settings_t *settings, settings_changed_flags_t changed)
{
st2_motor_t *motor = motors;
@@ -94,18 +119,118 @@ static void st2_settings_changed (settings_t *settings, settings_changed_flags_t
}
}
-st2_motor_t *st2_motor_init (uint_fast8_t axis_idx)
+/*! \brief Override default axis settings units for stepper spindle motors.
+
+\param setting_id id of setting.
+\param axis_idx axis index, X = 0, Y = 1, Z = 2, ...
+\returns pointer to new unit string or NULL if no change.
+*/
+static const char *st2_set_axis_setting_unit (setting_id_t setting_id, uint_fast8_t axis_idx)
+{
+ const char *unit = NULL;
+
+ if(bit_istrue(spindle_motors, bit(axis_idx))) switch(setting_id) {
+
+ case Setting_AxisStepsPerMM:
+ unit = "step/rev";
+ break;
+
+ case Setting_AxisMaxRate:
+ unit = "rev/min";
+ break;
+
+ case Setting_AxisAcceleration:
+ unit = "rev/sec^2";
+ break;
+
+ case Setting_AxisMaxTravel:
+ case Setting_AxisBacklash:
+ unit = "--";
+ break;
+
+ default:
+ break;
+ }
+
+ return unit == NULL && on_set_axis_setting_unit != NULL
+ ? on_set_axis_setting_unit(setting_id, axis_idx)
+ : unit;
+}
+
+/*! \brief Override default axis settings descriptions for stepper spindle motors.
+
+\param setting_id id of setting.
+\returns pointer to new description string or original string if no change.
+*/
+static const char *st2_setting_get_description (setting_id_t id)
+{
+ uint_fast8_t axis_idx;
+ const char *descr = NULL;
+
+ switch(settings_get_axis_base(id, &axis_idx)) {
+
+ case Setting_AxisStepsPerMM:
+ if(bit_istrue(spindle_motors, bit(axis_idx)))
+ descr = "Stepper resolution in steps per revolution.";
+ break;
+
+ case Setting_AxisMaxRate:
+ if(bit_istrue(spindle_motors, bit(axis_idx)))
+ descr = "Max RPM for stepper spindle.";
+ break;
+
+ case Setting_AxisAcceleration:
+ if(bit_istrue(spindle_motors, bit(axis_idx)))
+ descr = "Acceleration in revolutions/sec^2.";
+ break;
+
+ case Setting_AxisBacklash:
+ case Setting_AxisMaxTravel:
+ if(bit_istrue(spindle_motors, bit(axis_idx)))
+ descr = "This setting is ignored for stepper spindles.";
+ break;
+
+ default:
+ break;
+ }
+
+ return descr ? descr
+ : (on_setting_get_description ? on_setting_get_description(id) : NULL);
+}
+
+/*! \brief Bind and initialize a motor.
+
+Allocates and initializes motor configuration/data structure.
+If \a is_spindle is set \a true then axis settings will be changed to step/rev etc.
+
__NOTE:__ X, Y or Z motor cannot be bound as a spindle.
+
__NOTE:__ currently any axis bound as a spindle should not be instructed to move via gcode commands.
+\param axis_idx axis index of motor to bind to. 0 = X, 1 = Y, 2 = Z, ...
+\param is_spindle set to \a true if axis is to be used as a spindle (infinite motion).
+\returns pointer to a \a st2_motor structure if successful, \a NULL if not.
+*/
+st2_motor_t *st2_motor_init (uint_fast8_t axis_idx, bool is_spindle)
{
st2_motor_t *motor, *new = motors;
- if((motor = malloc(sizeof(st2_motor_t)))) {
+ if((motor = calloc(sizeof(st2_motor_t), 1))) {
- memset(motor, 0, sizeof(st2_motor_t));
motor->idx = axis_idx;
motor->axis.mask = 1 << axis_idx;
+ motor->is_spindle = is_spindle;
st_motor_config(motor);
+ if(motor->is_spindle) {
+
+ spindle_motors |= motor->axis.mask;
+
+ on_set_axis_setting_unit = grbl.on_set_axis_setting_unit;
+ grbl.on_set_axis_setting_unit = st2_set_axis_setting_unit;
+
+ on_setting_get_description = grbl.on_setting_get_description;
+ grbl.on_setting_get_description = st2_setting_get_description;
+ }
+
if(new == NULL) {
motors = motor;
@@ -125,6 +250,14 @@ st2_motor_t *st2_motor_init (uint_fast8_t axis_idx)
return motor;
}
+/*! \brief Set speed.
+
+Change speed of a running motor. Typically used for motors bound as a spindle.
+Motor will be accelerated or decelerated to the new speed.
+\param motor pointer to a \a st2_motor structure.
+\param speed new speed.
+\returns new speed in steps/s.
+*/
float st2_motor_set_speed (st2_motor_t *motor, float speed)
{
motor->speed = speed > settings.axis[motor->idx].max_rate ? settings.axis[motor->idx].max_rate : speed;
@@ -134,7 +267,7 @@ float st2_motor_set_speed (st2_motor_t *motor, float speed)
return motor->speed;
motor->min_delay = (uint32_t)(1000000.0f / motor->speed);
- motor->n = (uint32_t)(motor->speed * motor->speed) / (2.0f * motor->acceleration);
+ motor->n = (uint32_t)((motor->speed * motor->speed) / (2.0f * motor->acceleration));
if(motor->n == 0)
motor->n = 1;
@@ -183,6 +316,18 @@ float st2_motor_set_speed (st2_motor_t *motor, float speed)
return motor->prev_speed;
}
+/*! \brief Command a motor to move.
+
+__NOTE:__ For all motions except single steps st2_motor_run() has to be called from
+the foreground process at a high frequency in order for steps to be generated.
+Typically this is done by registering a function with the hal.on_execute_realtime event
+that calls st2_motor_run().
+\param motor pointer to a \a st2_motor structure.
+\param move relative distance to move.
+\param speed speed
+\param type a #position_t enum.
+\returns \a true if command is accepted, \a false if not.
+*/
bool st2_motor_move (st2_motor_t *motor, const float move, const float speed, position_t type)
{
bool dir = move < 0.0f;
@@ -198,55 +343,48 @@ bool st2_motor_move (st2_motor_t *motor, const float move, const float speed, po
switch(type) {
case Stepper2_Steps:
- motor->move = (uint32_t)fabs((int32_t)move);
- break;
-
case Stepper2_InfiniteSteps:
- motor->move = (uint32_t)fabs((int32_t)move);
+ motor->move = (uint32_t)fabsf(move);
break;
case Stepper2_mm:
- motor->move = (uint32_t)fabs(move * settings.axis[motor->idx].steps_per_mm);
-
+ motor->move = (uint32_t)lroundf(fabsf(move * settings.axis[motor->idx].steps_per_mm));
break;
}
st2_motor_set_speed(motor, speed);
- motor->step_no = 0; // step counter
+ if(motor->move == 1 && type == Stepper2_Steps) {
+ if(motor->state == State_Idle) {
+
+ if(motor->dir.mask)
+ motor->position--;
+ else
+ motor->position++;
+
+ hal.stepper.output_step(motor->axis, motor->dir);
+ }
+
+ return motor->state == State_Idle;
+ }
if(type == Stepper2_InfiniteSteps) {
-
- motor->state = State_Accel;
motor->step_run = motor->n;
motor->step_down = motor->n + 1;
- motor->delay = motor->first_delay;
- motor->c64 = ((uint32_t)motor->delay) << 16; // keep delay in 24.16 fixed-point format for ramp calcs
- motor->denom = 1; // 4.n + 1, n = 0
- motor->next_step = hal.get_micros();
- } else if(motor->move == 1) {
-
- motor->step_run = 1;
- motor->step_down = 1;
- motor->delay = motor->first_delay;
- motor->c64 = ((uint32_t)motor->delay) << 8; // keep delay in 24.16 fixed-point format for ramp calcs
- motor->denom = 1; // 4.n + 1, n = 0
-
- hal.stepper.output_step(motor->axis, motor->dir);
-
} else if(motor->move != 0) {
-
- motor->state = State_Accel;
motor->step_run = (motor->move - ((motor->move & 0x0001) ? 1 : 0)) >> 1;
if(motor->step_run > motor->n)
motor->step_run = motor->n;
motor->step_down = motor->move - motor->step_run;
- motor->delay = motor->first_delay;
- motor->c64 = ((uint32_t)motor->delay) << 8; // keep delay in 24.16 fixed-point format for ramp calcs
- motor->denom = 1; // 4.n + 1, n = 0
- motor->next_step = hal.get_micros();
- }
+ } else
+ return false;
+ motor->state = State_Accel;
+ motor->delay = motor->first_delay;
+ motor->c64 = motor->delay << 16; // keep delay in 24.16 fixed-point format for ramp calcs
+ motor->denom = 1; // 4.n + 1, n = 0
+ motor->step_no = 0; // step counter
+ motor->next_step = hal.get_micros();
#ifdef DEBUGOUT
uint32_t nn = motor->n;
@@ -268,55 +406,62 @@ bool st2_motor_move (st2_motor_t *motor, const float move, const float speed, po
return true;
}
+/*! \brief Get current position in steps.
+\param motor pointer to a \a st2_motor structure.
+\returns current position as number of steps.
+*/
int64_t st2_get_position (st2_motor_t *motor)
{
return motor->position;
}
+/*! \brief Set current position in steps.
+
+__NOTE:__ position will _not_ be set if motor is moving.
+\param motor pointer to a \a st2_motor structure.
+\param position position to set.
+\returns \a true if new position was accepted, \a false if not.
+*/
bool st2_set_position (st2_motor_t *motor, int64_t position)
{
- if(motor->state == State_Idle)
+ if(motor->state == State_Idle) {
motor->position = position;
+ motor->position_lost = false;
+ }
return motor->state == State_Idle;
}
+/*! \brief Execute a move commanded by st2_motor_move().
+
+This should be called from the foreground process as often as possible.
+\param motor pointer to a \a st2_motor structure.
+\returns \a true if motor is moving (steps are output), \a false if not (motion is completed).
+*/
bool st2_motor_run (st2_motor_t *motor)
{
- uint32_t t = hal.get_micros();
+ uint64_t t = hal.get_micros();
if(motor->state == State_Idle || t - motor->next_step < motor->delay)
return motor->state != State_Idle;
- // output step;
-
- hal.stepper.output_step(motor->axis, motor->dir);
-
- if(motor->dir.mask)
- motor->position--;
- else
- motor->position++;
-
- motor->step_no++;
-
switch(motor->state) {
case State_Accel:
- if(motor->step_no == motor->step_run) {
- motor->state = motor->step_run == motor->step_down ? State_Decel : (motor->ptype == Stepper2_InfiniteSteps ? State_RunInfinite : State_Run);
- motor->denom -= 2;
- if(motor->state == State_Run || motor->state == State_RunInfinite)
- motor->delay = motor->min_delay;
- } else {
+ if(motor->step_no != motor->step_run) {
motor->denom += 4;
motor->c64 -= (motor->c64 << 1) / motor->denom; // ramp algorithm
motor->delay = (motor->c64 + 32768) >> 16; // round 24.16 format -> int16
if (motor->delay < motor->min_delay) { // go to constant speed?
- motor->denom -= 6; // causes issues with speed override for infinite moves
+ // motor->denom -= 6; // causes issues with speed override for infinite moves
motor->state = motor->ptype == Stepper2_InfiniteSteps ? State_RunInfinite : State_Run;
motor->step_down = motor->move - motor->step_no;
motor->delay = motor->min_delay;
}
+ } else {
+ motor->state = motor->step_run == motor->step_down ? State_Decel : (motor->ptype == Stepper2_InfiniteSteps ? State_RunInfinite : State_Run);
+ if(motor->state != State_Decel)
+ motor->delay = motor->min_delay;
}
break;
@@ -342,21 +487,38 @@ bool st2_motor_run (st2_motor_t *motor)
case State_DecelTo:
if(motor->step_no != motor->step_run) {
- motor->c64 += (motor->c64 << 1) / motor->denom; // ramp algorithm
- motor->delay = (motor->c64 - 32768) >> 16; // round 24.16 format -> int16
motor->denom -= 4;
- } else
+ motor->c64 += (motor->c64 << 1) / motor->denom; // ramp algorithm
+ motor->delay = (motor->c64 + 32768) >> 16; // round 24.16 format -> int16
+ } else {
+ motor->delay = motor->min_delay;
motor->state = motor->ptype == Stepper2_InfiniteSteps ? State_RunInfinite : State_Run;
+ }
break;
default:
break;
}
+
+ // output step;
+ hal.stepper.output_step(motor->axis, motor->dir);
+
+ if(motor->dir.mask)
+ motor->position--;
+ else
+ motor->position++;
+
+ motor->step_no++;
motor->next_step = t;
return motor->state != State_Idle;
}
+/*! \brief Stop a move.
+This will initiate deceleration to stop the motor if it is running.
+\param motor pointer to a \a st2_motor structure.
+\returns \a true if motor was running, \a false if not.
+*/
bool st2_motor_stop (st2_motor_t *motor)
{
switch(motor->state) {
@@ -382,11 +544,19 @@ bool st2_motor_stop (st2_motor_t *motor)
return motor->state != State_Idle;
}
+/*! \brief Check if motor is running.
+\param motor pointer to a \a st2_motor structure.
+\returns \a true if motor is running, \a false if not.
+*/
bool st2_motor_running (st2_motor_t *motor)
{
return motor->state != State_Idle;
}
+/*! \brief Check if motor is running in cruising phase.
+\param motor pointer to a \a st2_motor structure.
+\returns \a true if motor is cruising (not acceleration or decelerating), \a false if not.
+*/
bool st2_motor_cruising (st2_motor_t *motor)
{
return motor->state == State_Run || motor->state == State_RunInfinite;
diff --git a/stepper2.h b/stepper2.h
index 45d1184..dc5afb6 100644
--- a/stepper2.h
+++ b/stepper2.h
@@ -23,15 +23,15 @@
#include
typedef enum {
- Stepper2_Steps = 0,
- Stepper2_InfiniteSteps,
- Stepper2_mm
+ Stepper2_Steps = 0, //!< 0
+ Stepper2_InfiniteSteps, //!< 1
+ Stepper2_mm //!< 2
} position_t;
struct st2_motor; // members defined in stepper2.c
typedef struct st2_motor st2_motor_t;
-st2_motor_t *st2_motor_init (uint_fast8_t axis_idx);
+st2_motor_t *st2_motor_init (uint_fast8_t axis_idx, bool is_spindle);
float st2_motor_set_speed (st2_motor_t *motor, float speed);
bool st2_motor_move (st2_motor_t *motor, const float move, const float speed, position_t type);
bool st2_motor_run (st2_motor_t *motor);
diff --git a/system.c b/system.c
index a660d9f..f5df9d8 100644
--- a/system.c
+++ b/system.c
@@ -4,7 +4,7 @@
Part of grblHAL
Copyright (c) 2017-2023 Terje Io
- Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC
+ Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea mResearch LLC
Grbl is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -33,74 +33,21 @@
#include "kinematics.h"
#endif
-static status_code_t jog (sys_state_t state, char *args);
-static status_code_t enumerate_alarms (sys_state_t state, char *args);
-static status_code_t enumerate_alarms_grblformatted (sys_state_t state, char *args);
-static status_code_t enumerate_errors (sys_state_t state, char *args);
-static status_code_t enumerate_errors_grblformatted (sys_state_t state, char *args);
-static status_code_t enumerate_groups (sys_state_t state, char *args);
-static status_code_t enumerate_settings (sys_state_t state, char *args);
-static status_code_t enumerate_all (sys_state_t state, char *args);
-static status_code_t enumerate_settings_grblformatted (sys_state_t state, char *args);
-static status_code_t enumerate_settings_halformatted (sys_state_t state, char *args);
-static status_code_t enumerate_pins (sys_state_t state, char *args);
-static status_code_t output_settings (sys_state_t state, char *args);
-static status_code_t output_all_settings (sys_state_t state, char *args);
-#ifndef NO_SETTINGS_DESCRIPTIONS
-static status_code_t output_setting_description (sys_state_t state, char *args);
-#endif
-static status_code_t output_parser_state (sys_state_t state, char *args);
-static status_code_t toggle_block_delete (sys_state_t state, char *args);
-static status_code_t toggle_single_block (sys_state_t state, char *args);
-static status_code_t toggle_optional_stop (sys_state_t state, char *args);
-static status_code_t check_mode (sys_state_t state, char *args);
-static status_code_t disable_lock (sys_state_t state, char *args);
-static status_code_t output_help (sys_state_t state, char *args);
-static status_code_t output_spindles (sys_state_t state, char *args);
-static status_code_t home (sys_state_t state, char *args);
-static status_code_t home_x (sys_state_t state, char *args);
-static status_code_t home_y (sys_state_t state, char *args);
-static status_code_t home_z (sys_state_t state, char *args);
-#ifdef A_AXIS
-static status_code_t home_a (sys_state_t state, char *args);
-#endif
-#ifdef B_AXIS
-static status_code_t home_b (sys_state_t state, char *args);
-#endif
-#ifdef C_AXIS
-static status_code_t home_c (sys_state_t state, char *args);
-#endif
-#ifdef U_AXIS
-static status_code_t home_u (sys_state_t state, char *args);
-#endif
-#ifdef V_AXIS
-static status_code_t home_v (sys_state_t state, char *args);
-#endif
-static status_code_t enter_sleep (sys_state_t state, char *args);
-static status_code_t set_tool_reference (sys_state_t state, char *args);
-static status_code_t tool_probe_workpiece (sys_state_t state, char *args);
-static status_code_t output_ngc_parameters (sys_state_t state, char *args);
-static status_code_t build_info (sys_state_t state, char *args);
-static status_code_t output_all_build_info (sys_state_t state, char *args);
-static status_code_t settings_reset (sys_state_t state, char *args);
-static status_code_t output_startup_lines (sys_state_t state, char *args);
-static status_code_t set_startup_line0 (sys_state_t state, char *args);
-static status_code_t set_startup_line1 (sys_state_t state, char *args);
-static status_code_t rtc_action (sys_state_t state, char *args);
-#ifdef DEBUGOUT
-static status_code_t output_memmap (sys_state_t state, char *args);
-#endif
-
-// Simple hypotenuse computation function.
+/*! \internal \brief Simple hypotenuse computation function.
+\param x length
+\param y height
+\returns length of hypotenuse
+ */
inline static float hypot_f (float x, float y)
{
- return sqrtf(x*x + y*y);
+ return sqrtf(x * x + y * y);
}
-
-// 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 data stream.
+/*! \brief Pin change interrupt handler for pin-out commands, i.e. cycle start, feed hold, reset etc.
+Mainly sets 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 data stream.
+\param signals a \a control_signals_t union holding status of the signals.
+*/
ISR_CODE void ISR_FUNC(control_interrupt_handler)(control_signals_t signals)
{
if(signals.deasserted)
@@ -154,7 +101,8 @@ ISR_CODE void ISR_FUNC(control_interrupt_handler)(control_signals_t signals)
}
-// Executes user startup script, if stored.
+/*! \brief Executes user startup scripts, if stored.
+*/
void system_execute_startup (void)
{
if(hal.nvs.type != NVS_None) {
@@ -171,18 +119,7 @@ void system_execute_startup (void)
}
}
-// Reset spindle encoder data
-status_code_t spindle_reset_data (sys_state_t state, char *args)
-{
- spindle_ptrs_t *spindle = gc_spindle_get();
-
- if(spindle->reset_data)
- spindle->reset_data();
-
- return spindle->reset_data ? Status_OK : Status_InvalidStatement;
-}
-
-status_code_t read_int (char *s, int32_t *value)
+static status_code_t read_int (char *s, int32_t *value)
{
uint_fast8_t counter = 0;
float parameter;
@@ -197,254 +134,20 @@ status_code_t read_int (char *s, int32_t *value)
return Status_OK;
}
-PROGMEM static const sys_command_t sys_commands[] = {
- { "G", output_parser_state, { .noargs = On, .allow_blocking = On } },
- { "J", jog },
- { "#", output_ngc_parameters, { .allow_blocking = On } },
- { "$", output_settings, { .allow_blocking = On } },
- { "+", output_all_settings, { .allow_blocking = On } },
-#ifndef NO_SETTINGS_DESCRIPTIONS
- { "SED", output_setting_description, { .allow_blocking = On } },
-#endif
- { "B", toggle_block_delete, { .noargs = On } },
- { "S", toggle_single_block, { .noargs = On } },
- { "O", toggle_optional_stop, { .noargs = On } },
- { "C", check_mode, { .noargs = On } },
- { "X", disable_lock },
- { "H", home },
- { "HX", home_x },
- { "HY", home_y },
- { "HZ", home_z },
-#if AXIS_REMAP_ABC2UVW
- #ifdef A_AXIS
- { "HU", home_a },
- #endif
- #ifdef B_AXIS
- { "HV", home_b },
- #endif
- #ifdef C_AXIS
- { "HW", home_c },
- #endif
-#else
- #ifdef A_AXIS
- { "HA", home_a },
- #endif
- #ifdef B_AXIS
- { "HB", home_b },
- #endif
- #ifdef C_AXIS
- { "HC", home_c },
- #endif
-#endif
-#ifdef U_AXIS
- { "HU", home_u },
-#endif
-#ifdef V_AXIS
- { "HV", home_v },
-#endif
- { "HSS", report_current_home_signal_state, { .noargs = On, .allow_blocking = On } },
- { "HELP", output_help, { .allow_blocking = On } },
- { "SPINDLES", output_spindles },
- { "SLP", enter_sleep, { .noargs = On } },
- { "TLR", set_tool_reference, { .noargs = On } },
- { "TPW", tool_probe_workpiece, { .noargs = On } },
- { "I", build_info, { .allow_blocking = On } },
- { "I+", output_all_build_info, { .noargs = On, .allow_blocking = On } },
- { "RST", settings_reset, { .allow_blocking = On } },
- { "N", output_startup_lines, { .noargs = On, .allow_blocking = On } },
- { "N0", set_startup_line0 },
- { "N1", set_startup_line1 },
- { "EA", enumerate_alarms, { .noargs = On, .allow_blocking = On } },
- { "EAG", enumerate_alarms_grblformatted, { .noargs = On, .allow_blocking = On } },
- { "EE", enumerate_errors, { .noargs = On, .allow_blocking = On } },
- { "EEG", enumerate_errors_grblformatted, { .noargs = On, .allow_blocking = On } },
- { "EG", enumerate_groups, { .noargs = On, .allow_blocking = On } },
- { "ES", enumerate_settings, { .noargs = On, .allow_blocking = On } },
- { "ESG", enumerate_settings_grblformatted, { .noargs = On, .allow_blocking = On } },
- { "ESH", enumerate_settings_halformatted, { .noargs = On, .allow_blocking = On } },
- { "E*", enumerate_all, { .noargs = On, .allow_blocking = On } },
- { "PINS", enumerate_pins, { .noargs = On, .allow_blocking = On } },
- { "RST", settings_reset, { .allow_blocking = On } },
- { "LEV", report_last_signals_event, { .noargs = On, .allow_blocking = On } },
- { "LIM", report_current_limit_state, { .noargs = On, .allow_blocking = On } },
- { "SD", report_spindle_data },
- { "SR", spindle_reset_data },
- { "RTC", rtc_action, { .allow_blocking = On } },
-#ifdef DEBUGOUT
- { "Q", output_memmap, { .noargs = On } },
-#endif
-};
-
-void system_command_help (void)
-{
- hal.stream.write("$I - output system information" ASCII_EOL);
- hal.stream.write("$I+ - output extended system information" ASCII_EOL);
-#if !DISABLE_BUILD_INFO_WRITE_COMMAND
- hal.stream.write("$I= set build info string" ASCII_EOL);
-#endif
- hal.stream.write("$ - output setting value" ASCII_EOL);
- hal.stream.write("$= - assign to settings " ASCII_EOL);
- hal.stream.write("$$ - output all setting values" ASCII_EOL);
- hal.stream.write("$+ - output all setting values" ASCII_EOL);
- hal.stream.write("$$= - output setting details for setting " ASCII_EOL);
- hal.stream.write("$# - output offsets, tool table, probing and home position" ASCII_EOL);
- hal.stream.write("$#= - output value for parameter " ASCII_EOL);
- hal.stream.write("$G - output parser state" ASCII_EOL);
- hal.stream.write("$N - output startup lines" ASCII_EOL);
- if(settings.homing.flags.enabled)
- hal.stream.write("$H - home configured axes" ASCII_EOL);
- if(settings.homing.flags.single_axis_commands)
- hal.stream.write("$H - home single axis" ASCII_EOL);
- hal.stream.write("$HSS - report homing switches status" ASCII_EOL);
- hal.stream.write("$X - unlock machine" ASCII_EOL);
- hal.stream.write("$SLP - enter sleep mode" ASCII_EOL);
- hal.stream.write("$HELP - output help topics" ASCII_EOL);
- hal.stream.write("$HELP - output help for " ASCII_EOL);
- hal.stream.write("$SPINDLES - output spindle list" ASCII_EOL);
-#if ENABLE_RESTORE_NVS_WIPE_ALL
- hal.stream.write("$RST=* - restore/reset all settings" ASCII_EOL);
-#endif
-#if ENABLE_RESTORE_NVS_DEFAULT_SETTINGS
- hal.stream.write("$RST=$ - restore default settings" ASCII_EOL);
-#endif
-#if ENABLE_RESTORE_NVS_DRIVER_PARAMETERS
- if(settings_get_details()->next)
- hal.stream.write("$RST=& - restore driver and plugin default settings" ASCII_EOL);
-#endif
-#if ENABLE_RESTORE_NVS_CLEAR_PARAMETERS
- #if N_TOOLS
- hal.stream.write("$RST=# - reset offsets and tool data" ASCII_EOL);
- #else
- hal.stream.write("$RST=# - reset offsets" ASCII_EOL);
- #endif
-#endif
- spindle_ptrs_t *spindle = gc_spindle_get();
- if(spindle->reset_data)
- hal.stream.write("$SR - reset spindle encoder data" ASCII_EOL);
- if(spindle->get_data)
- hal.stream.write("$SD - output spindle encoder data" ASCII_EOL);
-
- hal.stream.write("$TLR - set tool offset reference" ASCII_EOL);
- hal.stream.write("$TPW - probe tool plate" ASCII_EOL);
- hal.stream.write("$EA - enumerate alarms" ASCII_EOL);
- hal.stream.write("$EAG - enumerate alarms, Grbl formatted" ASCII_EOL);
- hal.stream.write("$EE - enumerate status codes" ASCII_EOL);
- hal.stream.write("$EEG - enumerate status codes, Grbl formatted" ASCII_EOL);
- hal.stream.write("$ES - enumerate settings" ASCII_EOL);
- hal.stream.write("$ESG - enumerate settings, Grbl formatted" ASCII_EOL);
- hal.stream.write("$ESH- enumerate settings, grblHAL formatted" ASCII_EOL);
- hal.stream.write("$E* - enumerate alarms, status codes and settings" ASCII_EOL);
- if(hal.enumerate_pins)
- hal.stream.write("$PINS - enumerate pin bindings" ASCII_EOL);
- hal.stream.write("$LEV - output last control signal events" ASCII_EOL);
- hal.stream.write("$LIM - output current limit pins state" ASCII_EOL);
- if(hal.rtc.get_datetime) {
- hal.stream.write("$RTC - output current time" ASCII_EOL);
- hal.stream.write("$RTC= - set current time" ASCII_EOL);
- }
-#ifndef NO_SETTINGS_DESCRIPTIONS
- hal.stream.write("$SED= - output settings description for setting " ASCII_EOL);
-#endif
-}
-
-// 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
-// the realtime command module by being susceptible to when Grbl is ready to execute the
-// next line during a cycle, so for switches like block delete, the switch only effects
-// 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.
-
-// NOTE: Code calling system_execute_line() needs to provide a line buffer of at least LINE_BUFFER_SIZE
-status_code_t system_execute_line (char *line)
-{
- if(line[1] == '\0') {
- grbl.report.help_message();
- return Status_OK;
- }
-
- sys_commands_t base = {
- .n_commands = sizeof(sys_commands) / sizeof(sys_command_t),
- .commands = sys_commands,
- .on_get_commands = grbl.on_get_commands
- };
-
- status_code_t retval = Status_Unhandled;
-
- char c, *s1, *s2;
-
- s1 = s2 = ++line;
-
- c = *s1;
- while(c && c != '=') {
- if(c != ' ')
- *s2++ = CAPS(c);
- c = *++s1;
- }
-
- while((c = *s1++))
- *s2++ = c;
-
- *s2 = '\0';
-
- if(!strncmp(line, "HELP", 4))
- return report_help(&line[4]);
-
- char *args = strchr(line, '=');
-
- if(args)
- *args++ = '\0';
-
- uint_fast8_t idx;
- sys_commands_t *cmd = &base;
- do {
- for(idx = 0; idx < cmd->n_commands; idx++) {
- if(!strcmp(line, cmd->commands[idx].command)) {
- if(sys.blocking_event && !cmd->commands[idx].flags.allow_blocking) {
- retval = Status_NotAllowedCriticalEvent;
- break;
- } else if(!cmd->commands[idx].flags.noargs || args == NULL) {
- if((retval = cmd->commands[idx].execute(state_get(), args)) != Status_Unhandled)
- break;
- }
- }
- }
- cmd = retval == Status_Unhandled && cmd->on_get_commands ? cmd->on_get_commands() : NULL;
- } while(cmd);
-
- // Let user code have a peek at system commands before check for global setting
- if(retval == Status_Unhandled && grbl.on_unknown_sys_command) {
- if(args)
- *(--args) = '=';
-
- retval = grbl.on_unknown_sys_command(state_get(), line);
-
- if(args)
- *args++ = '\0';
- }
-
- if (retval == Status_Unhandled) {
- // Check for global setting, store if so
- if(state_get() == STATE_IDLE || (state_get() & (STATE_ALARM|STATE_ESTOP|STATE_CHECK_MODE))) {
- uint_fast8_t counter = 0;
- float parameter;
- if(!read_float(line, &counter, ¶meter))
- retval = Status_BadNumberFormat;
- else if(!isintf(parameter))
- retval = Status_InvalidStatement;
- else if(args)
- retval = settings_store_setting((setting_id_t)parameter, args);
- else
- retval = report_grbl_setting((setting_id_t)parameter, NULL);
- } else
- retval = Status_IdleError;
- }
-
- return retval;
-}
-
+//
// System commands
+//
+
+// Reset spindle encoder data
+static status_code_t spindle_reset_data (sys_state_t state, char *args)
+{
+ spindle_ptrs_t *spindle = gc_spindle_get();
+
+ if(spindle->reset_data)
+ spindle->reset_data();
+
+ return spindle->reset_data ? Status_OK : Status_InvalidStatement;
+}
static status_code_t jog (sys_state_t state, char *args)
{
@@ -656,9 +359,14 @@ static status_code_t output_help (sys_state_t state, char *args)
return report_help(args);
}
-static status_code_t output_spindles (sys_state_t state, char *args)
+static status_code_t enumerate_spindles (sys_state_t state, char *args)
{
- return report_spindles();
+ return report_spindles(false);
+}
+
+static status_code_t enumerate_spindles_mr (sys_state_t state, char *args)
+{
+ return report_spindles(true);
}
static status_code_t go_home (sys_state_t state, axes_signals_t axes)
@@ -980,8 +688,273 @@ static status_code_t output_memmap (sys_state_t state, char *args)
}
#endif
+/*! \brief Command dispatch table
+ */
+PROGMEM static const sys_command_t sys_commands[] = {
+ { "G", output_parser_state, { .noargs = On, .allow_blocking = On } },
+ { "J", jog },
+ { "#", output_ngc_parameters, { .allow_blocking = On } },
+ { "$", output_settings, { .allow_blocking = On } },
+ { "+", output_all_settings, { .allow_blocking = On } },
+#ifndef NO_SETTINGS_DESCRIPTIONS
+ { "SED", output_setting_description, { .allow_blocking = On } },
+#endif
+ { "B", toggle_block_delete, { .noargs = On } },
+ { "S", toggle_single_block, { .noargs = On } },
+ { "O", toggle_optional_stop, { .noargs = On } },
+ { "C", check_mode, { .noargs = On } },
+ { "X", disable_lock },
+ { "H", home },
+ { "HX", home_x },
+ { "HY", home_y },
+ { "HZ", home_z },
+#if AXIS_REMAP_ABC2UVW
+ #ifdef A_AXIS
+ { "HU", home_a },
+ #endif
+ #ifdef B_AXIS
+ { "HV", home_b },
+ #endif
+ #ifdef C_AXIS
+ { "HW", home_c },
+ #endif
+#else
+ #ifdef A_AXIS
+ { "HA", home_a },
+ #endif
+ #ifdef B_AXIS
+ { "HB", home_b },
+ #endif
+ #ifdef C_AXIS
+ { "HC", home_c },
+ #endif
+#endif
+#ifdef U_AXIS
+ { "HU", home_u },
+#endif
+#ifdef V_AXIS
+ { "HV", home_v },
+#endif
+ { "HSS", report_current_home_signal_state, { .noargs = On, .allow_blocking = On } },
+ { "HELP", output_help, { .allow_blocking = On } },
+ { "SPINDLES", enumerate_spindles, { .noargs = On, .allow_blocking = On } },
+ { "SPINDLESH", enumerate_spindles_mr, { .noargs = On, .allow_blocking = On } },
+ { "SLP", enter_sleep, { .noargs = On } },
+ { "TLR", set_tool_reference, { .noargs = On } },
+ { "TPW", tool_probe_workpiece, { .noargs = On } },
+ { "I", build_info, { .allow_blocking = On } },
+ { "I+", output_all_build_info, { .noargs = On, .allow_blocking = On } },
+ { "RST", settings_reset, { .allow_blocking = On } },
+ { "N", output_startup_lines, { .noargs = On, .allow_blocking = On } },
+ { "N0", set_startup_line0 },
+ { "N1", set_startup_line1 },
+ { "EA", enumerate_alarms, { .noargs = On, .allow_blocking = On } },
+ { "EAG", enumerate_alarms_grblformatted, { .noargs = On, .allow_blocking = On } },
+ { "EE", enumerate_errors, { .noargs = On, .allow_blocking = On } },
+ { "EEG", enumerate_errors_grblformatted, { .noargs = On, .allow_blocking = On } },
+ { "EG", enumerate_groups, { .noargs = On, .allow_blocking = On } },
+ { "ES", enumerate_settings, { .noargs = On, .allow_blocking = On } },
+ { "ESG", enumerate_settings_grblformatted, { .noargs = On, .allow_blocking = On } },
+ { "ESH", enumerate_settings_halformatted, { .noargs = On, .allow_blocking = On } },
+ { "E*", enumerate_all, { .noargs = On, .allow_blocking = On } },
+ { "PINS", enumerate_pins, { .noargs = On, .allow_blocking = On } },
+ { "RST", settings_reset, { .allow_blocking = On } },
+ { "LEV", report_last_signals_event, { .noargs = On, .allow_blocking = On } },
+ { "LIM", report_current_limit_state, { .noargs = On, .allow_blocking = On } },
+ { "SD", report_spindle_data },
+ { "SR", spindle_reset_data },
+ { "RTC", rtc_action, { .allow_blocking = On } },
+#ifdef DEBUGOUT
+ { "Q", output_memmap, { .noargs = On } },
+#endif
+};
+
+void system_command_help (void)
+{
+ hal.stream.write("$I - output system information" ASCII_EOL);
+ hal.stream.write("$I+ - output extended system information" ASCII_EOL);
+#if !DISABLE_BUILD_INFO_WRITE_COMMAND
+ hal.stream.write("$I= set build info string" ASCII_EOL);
+#endif
+ hal.stream.write("$ - output setting value" ASCII_EOL);
+ hal.stream.write("$= - assign to settings " ASCII_EOL);
+ hal.stream.write("$$ - output all setting values" ASCII_EOL);
+ hal.stream.write("$+ - output all setting values" ASCII_EOL);
+ hal.stream.write("$$= - output setting details for setting " ASCII_EOL);
+ hal.stream.write("$# - output offsets, tool table, probing and home position" ASCII_EOL);
+ hal.stream.write("$#= - output value for parameter " ASCII_EOL);
+ hal.stream.write("$G - output parser state" ASCII_EOL);
+ hal.stream.write("$N - output startup lines" ASCII_EOL);
+ if(settings.homing.flags.enabled)
+ hal.stream.write("$H - home configured axes" ASCII_EOL);
+ if(settings.homing.flags.single_axis_commands)
+ hal.stream.write("$H - home single axis" ASCII_EOL);
+ hal.stream.write("$HSS - report homing switches status" ASCII_EOL);
+ hal.stream.write("$X - unlock machine" ASCII_EOL);
+ hal.stream.write("$SLP - enter sleep mode" ASCII_EOL);
+ hal.stream.write("$HELP - output help topics" ASCII_EOL);
+ hal.stream.write("$HELP - output help for " ASCII_EOL);
+ hal.stream.write("$SPINDLES - enumerate spindles, human readable" ASCII_EOL);
+ hal.stream.write("$SPINDLESH - enumerate spindles, machine readable" ASCII_EOL);
+#if ENABLE_RESTORE_NVS_WIPE_ALL
+ hal.stream.write("$RST=* - restore/reset all settings" ASCII_EOL);
+#endif
+#if ENABLE_RESTORE_NVS_DEFAULT_SETTINGS
+ hal.stream.write("$RST=$ - restore default settings" ASCII_EOL);
+#endif
+#if ENABLE_RESTORE_NVS_DRIVER_PARAMETERS
+ if(settings_get_details()->next)
+ hal.stream.write("$RST=& - restore driver and plugin default settings" ASCII_EOL);
+#endif
+#if ENABLE_RESTORE_NVS_CLEAR_PARAMETERS
+ if(grbl.tool_table.n_tools)
+ hal.stream.write("$RST=# - reset offsets and tool data" ASCII_EOL);
+ else
+ hal.stream.write("$RST=# - reset offsets" ASCII_EOL);
+#endif
+ spindle_ptrs_t *spindle = gc_spindle_get();
+ if(spindle->reset_data)
+ hal.stream.write("$SR - reset spindle encoder data" ASCII_EOL);
+ if(spindle->get_data)
+ hal.stream.write("$SD - output spindle encoder data" ASCII_EOL);
+
+ hal.stream.write("$TLR - set tool offset reference" ASCII_EOL);
+ hal.stream.write("$TPW - probe tool plate" ASCII_EOL);
+ hal.stream.write("$EA - enumerate alarms" ASCII_EOL);
+ hal.stream.write("$EAG - enumerate alarms, Grbl formatted" ASCII_EOL);
+ hal.stream.write("$EE - enumerate status codes" ASCII_EOL);
+ hal.stream.write("$EEG - enumerate status codes, Grbl formatted" ASCII_EOL);
+ hal.stream.write("$ES - enumerate settings" ASCII_EOL);
+ hal.stream.write("$ESG - enumerate settings, Grbl formatted" ASCII_EOL);
+ hal.stream.write("$ESH- enumerate settings, grblHAL formatted" ASCII_EOL);
+ hal.stream.write("$E* - enumerate alarms, status codes and settings" ASCII_EOL);
+ if(hal.enumerate_pins)
+ hal.stream.write("$PINS - enumerate pin bindings" ASCII_EOL);
+ hal.stream.write("$LEV - output last control signal events" ASCII_EOL);
+ hal.stream.write("$LIM - output current limit pins state" ASCII_EOL);
+ if(hal.rtc.get_datetime) {
+ hal.stream.write("$RTC - output current time" ASCII_EOL);
+ hal.stream.write("$RTC= - set current time" ASCII_EOL);
+ }
+#ifndef NO_SETTINGS_DESCRIPTIONS
+ hal.stream.write("$SED= - output settings description for setting " ASCII_EOL);
+#endif
+}
+
+/*! \brief Directs and executes one line of 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
+the realtime command module by being susceptible to when Grbl is ready to execute the
+next line during a cycle, so for switches like block delete, the switch only effects
+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.
+If the command is not known to the core a grbl.on_unknown_sys_command event is raised
+so that plugin code can check if it is a command it supports.
+
+__NOTE:__ Code calling this function needs to provide the command in a writable buffer since
+ the first part of the command (up to the first = character) is changed to uppercase and having
+ spaces removed.
+
+\param line pointer to the command string.
+\returns \a status_code_t enum value; #Status_OK if successfully handled, another relevant status code if not.
+*/
+status_code_t system_execute_line (char *line)
+{
+ if(line[1] == '\0') {
+ grbl.report.help_message();
+ return Status_OK;
+ }
+
+ sys_commands_t base = {
+ .n_commands = sizeof(sys_commands) / sizeof(sys_command_t),
+ .commands = sys_commands,
+ .on_get_commands = grbl.on_get_commands
+ };
+
+ status_code_t retval = Status_Unhandled;
+
+ char c, *s1, *s2;
+
+ s1 = s2 = ++line;
+
+ c = *s1;
+ while(c && c != '=') {
+ if(c != ' ')
+ *s2++ = CAPS(c);
+ c = *++s1;
+ }
+
+ while((c = *s1++))
+ *s2++ = c;
+
+ *s2 = '\0';
+
+ if(!strncmp(line, "HELP", 4))
+ return report_help(&line[4]);
+
+ char *args = strchr(line, '=');
+
+ if(args)
+ *args++ = '\0';
+
+ uint_fast8_t idx;
+ sys_commands_t *cmd = &base;
+ do {
+ for(idx = 0; idx < cmd->n_commands; idx++) {
+ if(!strcmp(line, cmd->commands[idx].command)) {
+ if(sys.blocking_event && !cmd->commands[idx].flags.allow_blocking) {
+ retval = Status_NotAllowedCriticalEvent;
+ break;
+ } else if(!cmd->commands[idx].flags.noargs || args == NULL) {
+ if((retval = cmd->commands[idx].execute(state_get(), args)) != Status_Unhandled)
+ break;
+ }
+ }
+ }
+ cmd = retval == Status_Unhandled && cmd->on_get_commands ? cmd->on_get_commands() : NULL;
+ } while(cmd);
+
+ // Let user code have a peek at system commands before check for global setting
+ if(retval == Status_Unhandled && grbl.on_unknown_sys_command) {
+ if(args)
+ *(--args) = '=';
+
+ retval = grbl.on_unknown_sys_command(state_get(), line);
+
+ if(args)
+ *args++ = '\0';
+ }
+
+ if (retval == Status_Unhandled) {
+ // Check for global setting, store if so
+ if(state_get() == STATE_IDLE || (state_get() & (STATE_ALARM|STATE_ESTOP|STATE_CHECK_MODE))) {
+ uint_fast8_t counter = 0;
+ float parameter;
+ if(!read_float(line, &counter, ¶meter))
+ retval = Status_BadNumberFormat;
+ else if(!isintf(parameter))
+ retval = Status_InvalidStatement;
+ else if(args)
+ retval = settings_store_setting((setting_id_t)parameter, args);
+ else
+ retval = report_grbl_setting((setting_id_t)parameter, NULL);
+ } else
+ retval = Status_IdleError;
+ }
+
+ return retval;
+}
+
// End system commands
+/*! \brief Called on a work coordinate (WCO) changes.
+
+If configured waits for the planner buffer to empty then fires the
+grbl.on_wco_changed event and sets the #Report_WCO flag to add
+the WCO report element to the next status report.
+ */
void system_flag_wco_change (void)
{
if(!settings.status_report.sync_on_wco_change)
@@ -993,9 +966,13 @@ void system_flag_wco_change (void)
system_add_rt_report(Report_WCO);
}
-// Sets machine position. 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.
+/*! \brief Sets machine position. 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.
+\param position pointer to the target float array for the machine position.
+\param steps pointer to the source step count array to transform.
+ */
void system_convert_array_steps_to_mpos (float *position, int32_t *steps)
{
#ifdef KINEMATICS_API
@@ -1009,8 +986,11 @@ void system_convert_array_steps_to_mpos (float *position, int32_t *steps)
#endif
}
-// Checks if XY position is within coordinate system XY with given tolerance.
-// If tolerance is 0 false is returned.
+/*! \brief Checks if XY position is within coordinate system XY with given tolerance.
+\param id a \a coord_system_id_t, typically #CoordinateSystem_G59_3.
+\param tolerance as the allowed radius the current position has to be within.
+\returns \a false if tolerance is 0 or position is outside the allowed radius, otherwise \a true.
+*/
bool system_xy_at_fixture (coord_system_id_t id, float tolerance)
{
bool ok = false;
@@ -1025,6 +1005,9 @@ bool system_xy_at_fixture (coord_system_id_t id, float tolerance)
return ok;
}
+/*! \brief Raise and report a system alarm.
+\param a #alarm_code_t enum representing the alarm code.
+ */
void system_raise_alarm (alarm_code_t alarm)
{
if(state_get() == STATE_HOMING && !(sys.rt_exec_state & EXEC_RESET))
@@ -1043,11 +1026,19 @@ void system_raise_alarm (alarm_code_t alarm)
// TODO: encapsulate sys.report
+/*! \brief Get the active realtime report addon flags for the next report.
+\return a #report_tracking_flags_t union containing the flags.
+ */
report_tracking_flags_t system_get_rt_report_flags (void)
{
return sys.report;
}
+/*! \brief Set(s) or clear all active realtime report addon flag(s) for the next report.
+
+Fires the \ref grbl.on_rt_reports_added event.
+\param report a #report_tracking_t enum containing the flag(s) to set or clear.
+ */
void system_add_rt_report (report_tracking_t report)
{
if(report == Report_ClearAll)
diff --git a/system.h b/system.h
index 0ffced1..155467f 100644
--- a/system.h
+++ b/system.h
@@ -338,33 +338,18 @@ typedef struct sys_commands_str {
extern system_t sys;
-//! Executes an internal system command, defined as a string starting with a '$'
status_code_t system_execute_line (char *line);
-
-//! Execute the startup script lines stored in non-volatile storage upon initialization
void system_execute_startup (void);
-
void system_flag_wco_change (void);
-
-// Returns machine position of axis 'idx'. Must be sent a 'step' array.
-//float system_convert_axis_steps_to_mpos(int32_t *steps, uint_fast8_t idx);
-
-//! Updates a machine 'position' array based on the 'step' array sent.
void system_convert_array_steps_to_mpos (float *position, int32_t *steps);
-
-//! Checks if XY position is within coordinate system XY with given tolerance.
bool system_xy_at_fixture (coord_system_id_t id, float tolerance);
-
-//! Raise and report alarm state
void system_raise_alarm (alarm_code_t alarm);
-
-//! Provide system command help
void system_command_help (void);
void system_add_rt_report (report_tracking_t report);
report_tracking_flags_t system_get_rt_report_flags (void);
-// Special handlers for setting and clearing Grbl's real-time execution flags.
+// Special handlers for setting and clearing grblHAL's real-time execution flags.
#define system_set_exec_state_flag(mask) hal.set_bits_atomic(&sys.rt_exec_state, (mask))
#define system_clear_exec_state_flag(mask) hal.clear_bits_atomic(&sys.rt_exec_state, (mask))
#define system_clear_exec_states() hal.set_value_atomic(&sys.rt_exec_state, 0)
diff --git a/tool_change.c b/tool_change.c
index 300d522..8732824 100644
--- a/tool_change.c
+++ b/tool_change.c
@@ -92,17 +92,13 @@ static void reset (void)
{
if(next_tool) { //TODO: move to gc_xxx() function?
// Restore previous tool if reset is during change
-#if N_TOOLS
if(current_tool.tool_id != next_tool->tool_id) {
- memcpy(gc_state.tool, ¤t_tool, sizeof(tool_data_t));
+ if(grbl.tool_table.n_tools)
+ memcpy(gc_state.tool, ¤t_tool, sizeof(tool_data_t));
+ else
+ memcpy(next_tool, ¤t_tool, sizeof(tool_data_t));
system_add_rt_report(Report_Tool);
}
-#else
- if(current_tool.tool_id != next_tool->tool_id) {
- memcpy(next_tool, ¤t_tool, sizeof(tool_data_t));
- system_add_rt_report(Report_Tool);
- }
-#endif
gc_state.tool_pending = gc_state.tool->tool_id;
next_tool = NULL;
}
diff --git a/vfs.c b/vfs.c
index af19b0a..7534555 100644
--- a/vfs.c
+++ b/vfs.c
@@ -101,9 +101,6 @@ static char *fs_getcwd (char *buf, size_t size)
}
static const vfs_t fs_null = {
- .mode.directory = true,
- .mode.hidden = true,
- .mode.read_only = true,
.fopen = fs_open,
.fclose = fs_close,
.fread = fs_read,
@@ -126,6 +123,7 @@ static const vfs_t fs_null = {
static vfs_mount_t root = {
.path = "/",
.vfs = &fs_null,
+ .mode = { .directory = true, .read_only = true, .hidden = true },
.next = NULL
};
static vfs_mount_t *cwdmount = &root;
@@ -342,7 +340,7 @@ vfs_dir_t *vfs_opendir (const char *path)
if((add_mount = root.next)) do {
if(add_mount != mount && !strncmp(add_mount->path, path, strlen(path))) {
- if(!add_mount->vfs->mode.hidden && (mln = malloc(sizeof(vfs_mount_ll_entry_t)))) {
+ if(!add_mount->mode.hidden && (mln = malloc(sizeof(vfs_mount_ll_entry_t)))) {
mln->mount = add_mount;
mln->next = NULL;
if(dir->mounts == NULL)
@@ -376,7 +374,7 @@ vfs_dirent_t *vfs_readdir (vfs_dir_t *dir)
if((s = strrchr(dirent.name, '/')))
*s = '\0';
- dirent.st_mode = ml->mount->vfs->mode;
+ dirent.st_mode = ml->mount->mode;
dirent.st_mode.directory = true;
dir->mounts = dir->mounts->next;
free(ml);
@@ -460,7 +458,7 @@ vfs_free_t *vfs_fgetfree (const char *path)
return NULL;
}
-bool vfs_mount (const char *path, const vfs_t *fs)
+bool vfs_mount (const char *path, const vfs_t *fs, vfs_st_mode_t mode)
{
vfs_mount_t *vfs;
@@ -474,6 +472,7 @@ bool vfs_mount (const char *path, const vfs_t *fs)
strcat(vfs->path, "/");
vfs->vfs = fs;
+ vfs->mode = mode;
vfs->next = NULL;
vfs_mount_t *mount = &root;
@@ -527,7 +526,7 @@ vfs_drive_t *vfs_get_drive (const char *path)
vfs_mount_t *mount = get_mount(path);
drive.name = mount->vfs->fs_name;
drive.path = (const char *)mount->path;
- drive.mode = mount->vfs->mode;
+ drive.mode = mount->mode;
drive.removable = mount->vfs->removable;
drive.fs = mount->vfs;
@@ -543,7 +542,7 @@ vfs_drives_t *vfs_drives_open (void)
handle->mount = NULL;
do {
- if(mount->vfs->mode.hidden)
+ if(mount->mode.hidden)
mount = mount->next;
else
handle->mount = mount;
@@ -568,14 +567,14 @@ vfs_drive_t *vfs_drives_read (vfs_drives_t *handle)
drive.name = handle->mount->vfs->fs_name;
drive.path = (const char *)handle->mount->path;
- drive.mode = handle->mount->vfs->mode;
+ drive.mode = handle->mount->mode;
drive.removable = handle->mount->vfs->removable;
drive.fs = handle->mount->vfs;
handle->mount = handle->mount->next;
if(handle->mount) do {
- if(!handle->mount->vfs->mode.hidden && handle->mount->vfs->fs_name)
+ if(!handle->mount->mode.hidden && handle->mount->vfs->fs_name)
break;
} while((handle->mount = handle->mount->next));
}
diff --git a/vfs.h b/vfs.h
index eb2ab1d..5ab8304 100644
--- a/vfs.h
+++ b/vfs.h
@@ -130,7 +130,6 @@ typedef struct
{
const char *fs_name;
bool removable;
- vfs_st_mode_t mode;
vfs_open_ptr fopen;
vfs_close_ptr fclose;
vfs_read_ptr fread;
@@ -157,6 +156,7 @@ typedef struct vfs_mount
{
char path[64];
const vfs_t *vfs;
+ vfs_st_mode_t mode;
struct vfs_mount *next;
} vfs_mount_t;
@@ -188,7 +188,7 @@ extern int vfs_errno;
char *vfs_fixpath (char *path);
-bool vfs_mount (const char *path, const vfs_t *fs);
+bool vfs_mount (const char *path, const vfs_t *fs, vfs_st_mode_t mode);
bool vfs_unmount (const char *path);
vfs_file_t *vfs_open (const char *filename, const char *mode);
void vfs_close (vfs_file_t *file);