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