From 106f5fd573fe36e1069558a2915650e311a124de Mon Sep 17 00:00:00 2001 From: Terje Io Date: Thu, 16 Oct 2025 17:22:32 +0200 Subject: [PATCH] Refactored assignment of of auxiliary I/O for "standard" inputs and outputs to make it more robust and easier to follow for developers. Moved probe signal handling from drivers to the core, improved handling of probe disconnected signal. NOTE: The changes above are quite large, please verify probe operation after installation. --- CMakeLists.txt | 1 + README.md | 2 +- changelog.md | 25 +++- config.h | 11 +- crossbar.h | 37 +++-- grbl.h | 2 +- grbllib.c | 3 + hal.h | 18 ++- ioports.c | 6 +- motion_control.c | 4 +- ngc_params.c | 33 +---- nuts_bolts.h | 25 ++++ pin_bits_masks.h | 353 +++++++++++++++++++++++------------------------ probe.c | 227 ++++++++++++++++++++++++++++++ probe.h | 40 +++--- protocol.c | 10 +- report.c | 5 +- settings.c | 31 ++++- stream.c | 8 +- system.c | 10 +- system.h | 31 +---- 21 files changed, 583 insertions(+), 299 deletions(-) create mode 100644 probe.c diff --git a/CMakeLists.txt b/CMakeLists.txt index cfb1bf6..f0340c4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ target_sources(grbl INTERFACE ${CMAKE_CURRENT_LIST_DIR}/nuts_bolts.c ${CMAKE_CURRENT_LIST_DIR}/override.c ${CMAKE_CURRENT_LIST_DIR}/planner.c + ${CMAKE_CURRENT_LIST_DIR}/probe.c ${CMAKE_CURRENT_LIST_DIR}/protocol.c ${CMAKE_CURRENT_LIST_DIR}/report.c ${CMAKE_CURRENT_LIST_DIR}/settings.c diff --git a/README.md b/README.md index 7e52e18..cd15e98 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## grblHAL ## -Latest build date is 20251011, see the [changelog](changelog.md) for details. +Latest build date is 20251016, see the [changelog](changelog.md) for details. > [!NOTE] > A settings reset will be performed on an update of builds prior to 20241208. Backup and restore of settings is recommended. diff --git a/changelog.md b/changelog.md index f1d8d6f..ac0dfd7 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,28 @@ ## grblHAL changelog +Build 20251016 + +Core: + +* Refactored assignment of of auxiliary I/O for "standard" inputs and outputs to make it more robust and easier to follow for developers. + +* Moved probe signal handling from drivers to the core, improved handling of probe disconnected signal. + +> [!NOTE] +> The changes above are quite large, please verify probe operation after installation. + +Drivers: + +* All: updated for move of probe signal handling to the core. + +* RP2040: fixed "leak" of stepper signals between axes when plasma plugin is enabled, affects the PicoCNC board. + +* ESP32: fix for compilation failure for boards using the SDIO interface for SD card interface. + +* STM32F4xx: SuperLongBoards, now returns unambigous values for `_probe_state` and `_toolsetter_state` system parameters. + +--- + 20251015 Drivers: @@ -8,7 +31,7 @@ Drivers: Plugins: -* Keypad, macros: "hardened" code to avoid hardfault when macros settings data is bad. +* Keypad, macros: "hardened" code to avoid hardfault when macros settings data are bad. --- diff --git a/config.h b/config.h index ef63f9b..0026e53 100644 --- a/config.h +++ b/config.h @@ -736,7 +736,7 @@ If set to \ref Off or 0 the `|DTG:` distance-to-go element is not included in th \internal Bit 13 in settings.status_report. */ #if !defined DEFAULT_REPORT_DISTANCE_TO_GO || defined __DOXYGEN__ -#define DEFAULT_REPORT_DISTANCE_TO_GO On // Default on. Set to \ref Off or 0 to disable. +#define DEFAULT_REPORT_DISTANCE_TO_GO Off // Default off. Set to \ref On or 1 to enable. #endif ///@} @@ -933,14 +933,13 @@ Enable this setting to feedrate override over a soft reset. ///@} - // Control signals settings (Group_ControlSignals) #ifndef __DOXYGEN__ // For now do not include in documentation /*! @name Control signals bit definitions and mask. -__NOTE:__ these definitions are only referenced in this file. Do __NOT__ change! +__NOTE:__ Do __NOT__ change! Definitions MUST match #control_signals_t struct bit order. */ ///@{ #define SIGNALS_RESET_BIT (1<<0) @@ -952,6 +951,12 @@ __NOTE:__ these definitions are only referenced in this file. Do __NOT__ change! #define SIGNALS_ESTOP_BIT (1<<6) #define SIGNALS_PROBE_CONNECTED_BIT (1<<7) #define SIGNALS_MOTOR_FAULT_BIT (1<<8) +#define SIGNALS_MOTOR_WARNING_BIT (1<<9) +#define SIGNALS_LIMITS_OVERRIDE_BIT (1<<10) +#define SIGNALS_SINGLE_BLOCK_BIT (1<<11) +#define SIGNALS_TLS_OVERTRAVEL_BIT (1<<12) +#define SIGNALS_PROBE_OVERTRAVEL (1<<13) +#define SIGNALS_PROBE_TRIGGERED_BIT (1<<14) #define SIGNALS_BITMASK (SIGNALS_RESET_BIT|SIGNALS_FEEDHOLD_BIT|SIGNALS_CYCLESTART_BIT|SIGNALS_SAFETYDOOR_BIT|SIGNALS_BLOCKDELETE_BIT|SIGNALS_STOPDISABLE_BIT|SIGNALS_ESTOP_BIT|SIGNALS_PROBE_CONNECTED_BIT|SIGNALS_MOTOR_FAULT_BIT) ///@} diff --git a/crossbar.h b/crossbar.h index 87a5777..9e3fd66 100644 --- a/crossbar.h +++ b/crossbar.h @@ -38,7 +38,7 @@ typedef enum { Input_MotorWarning, Input_LimitsOverride, Input_SingleBlock, - Input_Unassigned, + Input_ToolsetterOvertravel, Input_ProbeOvertravel, Input_Probe, // end control_signals_t sequence @@ -56,7 +56,6 @@ typedef enum { Input_Probe2, Input_Probe2Overtravel, Input_Toolsetter, - Input_ToolsetterOvertravel, Input_MPGSelect, Input_ModeSelect = Input_MPGSelect, // Deprecated Input_LimitX, @@ -726,22 +725,27 @@ typedef bool (*xbar_set_function_ptr)(struct xbar *pin, pin_function_t function) typedef void (*xbar_event_ptr)(bool on); typedef bool (*xbar_config_ptr)(struct xbar *pin, xbar_cfg_ptr_t cfg_data, bool persistent); +// MCU port base address and pin number typedef struct { - pin_function_t function; - uint8_t aux_port; - pin_irq_mode_t irq_mode; - control_signals_t cap; - uint8_t pin; - void *port; - void *input; + void *port; //!< MCU port address (may be NULL). + uint8_t pin; //!< MCU pin number. +} aux_gpio_t; + +typedef struct { + pin_function_t function; //!< Pin function. + uint8_t port; //!< Auxiliary port number, post claimed. + pin_irq_mode_t irq_mode; //!< Required IRQ mode for the input. + control_signals_t signal; //!< Set to the pin the input maps to, 0 if none. + aux_gpio_t gpio; //!< MCU port base address (may be NULL) and pin number. + void *input; //!< Pointer to the driver input array entry for the pin. + bool scan; //!< true if the pin is to be scanned when control state is requested. } aux_ctrl_t; typedef struct { - pin_function_t function; - uint8_t aux_port; - uint8_t pin; - void *port; - void *output; + pin_function_t function; //!< Pin function. + uint8_t port; //!< Auxiliary port number, post claimed. + aux_gpio_t gpio; //!< MCU port base address (may be NULL) and pin number. + void *output; //!< Pointer to the driver input array entry for the pin. } aux_ctrl_out_t; typedef struct xbar { @@ -806,6 +810,11 @@ static inline bool xbar_stepper_state_get (stepper_state_t state, uint8_t axis, return bit_istrue(b ? state.details.b.bits : state.details.a.bits, bit(axis)); } +static inline bool xbar_is_probe_in (pin_function_t fn) +{ + return fn == Input_Probe || fn == Input_Probe2 || fn == Input_Toolsetter; +} + void xbar_set_homing_source (void); limit_signals_t xbar_get_homing_source (void); limit_signals_t xbar_get_homing_source_from_cycle (axes_signals_t homing_cycle); diff --git a/grbl.h b/grbl.h index 4b8c4c3..ec5bb58 100644 --- a/grbl.h +++ b/grbl.h @@ -42,7 +42,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20251011 +#define GRBL_BUILD 20251016 #define GRBL_URL "https://github.com/grblHAL" diff --git a/grbllib.c b/grbllib.c index a35f3db..9f81fab 100644 --- a/grbllib.c +++ b/grbllib.c @@ -411,6 +411,9 @@ int grbl_enter (void) if(grbl.on_probe_toolsetter == NULL && hal.driver_cap.toolsetter && hal.probe.select) grbl.on_probe_toolsetter = onProbeToolsetter; + if(hal.driver_cap.probe && hal.signals_cap.probe_disconnected) + task_run_on_startup(probe_connected_event, hal.control.get_state().probe_disconnected ? NULL : (void *)1); + // 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 b632b42..69ba0a0 100644 --- a/hal.h +++ b/hal.h @@ -373,6 +373,12 @@ __NOTE:__ this function will be called from an interrupt context. */ typedef probe_state_t (*probe_get_state_ptr)(void); +/*! \brief Pointer to function for getting triggered status for a specific probe. +\param probe_id probe number. 0 - default probe, 1 - toolsetter, ... +\returns \a true if triggered, \a false otherwise. +*/ +typedef bool (*probe_is_triggered_ptr)(probe_id_t probe_id); + /*! \brief Pointer to function for setting probe operation mode. \param is_probe_away true if probing away from the workpiece, false otherwise. When probing away the signal must be inverted in the probe_get_state_ptr() implementation. \param probing true if probe cycle is active, false otherwise. @@ -381,13 +387,17 @@ typedef void (*probe_configure_ptr)(bool is_probe_away, bool probing); /*! \brief Pointer to function for selecting probe input. \param probe_id probe number. 0 - default probe, 1 - toolsetter, ... -\returns true if selectetion succeded, false otherwise. +\returns true if selection succeded, false otherwise. */ typedef bool (*probe_select_ptr)(probe_id_t probe_id); +/*! \brief Pointer to function for getting probe capabilities. +\param probe_id probe number. 0 - default probe, 1 - toolsetter, ... +\returns a \a probe_flags_t struct. +*/ +typedef probe_flags_t (*probe_get_caps_ptr)(probe_id_t probe_id); + /*! \brief Pointer to function for toggling probe connected status. - - If the driver does not support a probe connected input signal this can be used to implement toggling of probe connected status via a #CMD_PROBE_CONNECTED_TOGGLE real time command. */ @@ -397,7 +407,9 @@ typedef void (*probe_connected_toggle_ptr)(void); typedef struct { probe_configure_ptr configure; //!< Optional handler for setting probe operation mode. probe_get_state_ptr get_state; //!< Optional handler for getting probe status. Called from interrupt context. + probe_is_triggered_ptr is_triggered; //!< Optional handler for getting probe triggered status. probe_select_ptr select; //!< Optional handler for selecting probe to use. + probe_get_caps_ptr get_caps; //!< Optional handler for getting probe capabilities. probe_connected_toggle_ptr connected_toggle; //!< Optional handler for toggling probe connected status. } probe_ptrs_t; diff --git a/ioports.c b/ioports.c index bde94f9..9617c05 100644 --- a/ioports.c +++ b/ioports.c @@ -352,12 +352,12 @@ void ioport_assign_function (aux_ctrl_t *aux_ctrl, pin_function_t *function) { xbar_t *input; - if((input = hal.port.get_pin_info(Port_Digital, Port_Input, aux_ctrl->aux_port))) { + if((input = hal.port.get_pin_info(Port_Digital, Port_Input, aux_ctrl->port))) { *function = aux_ctrl->function; ports_cfg[Port_DigitalIn].bus.mask &= ~(1 << input->id); ports_cfg[Port_DigitalIn].count = ports_cfg[Port_DigitalIn].free = -1; - hal.signals_cap.mask |= aux_ctrl->cap.mask; + hal.signals_cap.mask |= aux_ctrl->signal.mask; if(aux_ctrl->function == Input_Probe || xbar_fn_to_signals_mask(aux_ctrl->function).mask) setting_remove_elements(Settings_IoPort_InvertIn, ports_cfg[Port_DigitalIn].bus.mask); @@ -369,7 +369,7 @@ void ioport_assign_out_function (aux_ctrl_out_t *aux_ctrl, pin_function_t *funct { xbar_t *output; - if((output = hal.port.get_pin_info(Port_Digital, Port_Output, aux_ctrl->aux_port))) { + if((output = hal.port.get_pin_info(Port_Digital, Port_Output, aux_ctrl->port))) { *function = aux_ctrl->function; ports_cfg[Port_DigitalOut].bus.mask &= ~(1UL << output->id); diff --git a/motion_control.c b/motion_control.c index 9be0cf3..4bf62ef 100644 --- a/motion_control.c +++ b/motion_control.c @@ -409,10 +409,10 @@ static inline float interp (const float a, const float b, const float t) } /** - * Compute a B�zier curve using the De Casteljau's algorithm (see + * Compute a Bezier curve using the De Casteljau's algorithm (see * https://en.wikipedia.org/wiki/De_Casteljau's_algorithm), which is * easy to code and has good numerical stability (very important, - * since Arudino works with limited precision real numbers). + * since Arduino works with limited precision real numbers). */ static inline float eval_bezier (const float a, const float b, const float c, const float d, const float t) { diff --git a/ngc_params.c b/ngc_params.c index 3b29094..36cb98d 100644 --- a/ngc_params.c +++ b/ngc_params.c @@ -612,40 +612,11 @@ float ngc_named_param_get_by_id (ncg_name_param_id_t id) // grblHAL extensions case NGCParam_probe_state: - value = -1.0f; - if(hal.probe.get_state /*&& hal.driver_cap.probe*/) { - - probe_state_t probe_state = hal.probe.get_state(); - - if((probe_id_t)probe_state.probe_id == Probe_Default) - value = (float)probe_state.triggered; - else if(hal.probe.select) { - - if(hal.probe.select(Probe_Default)) - value = (float)hal.probe.get_state().triggered; - - hal.probe.select((probe_id_t)probe_state.probe_id); - } - } + value = hal.driver_cap.probe && hal.probe.is_triggered ? (float)hal.probe.is_triggered(Probe_Default) : -1.0f; break; case NGCParam_toolsetter_state: - value = -1.0f; - if(hal.probe.get_state && hal.driver_cap.toolsetter) { - - probe_state_t probe_state = hal.probe.get_state(); - - if((probe_id_t)probe_state.probe_id == Probe_Toolsetter) - value = (float)probe_state.triggered; - else if(hal.probe.select) { - - if(hal.probe.select(Probe_Toolsetter)) - value = (float)hal.probe.get_state().triggered; - - hal.probe.select((probe_id_t)probe_state.probe_id); - } else - value = (float)probe_state.tls_triggered; - } + value = hal.driver_cap.toolsetter && hal.probe.is_triggered ? (float)hal.probe.is_triggered(Probe_Toolsetter) : -1.0f; break; case NGCParam_homed_state: diff --git a/nuts_bolts.h b/nuts_bolts.h index 150eec3..aa62747 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -246,6 +246,31 @@ typedef struct { #pragma pack(pop) +// NOTE: the pin_function_t enum must be kept in sync with any changes! +typedef union { + uint16_t bits; + uint16_t mask; + uint16_t value; + struct { + uint16_t reset :1, + feed_hold :1, + cycle_start :1, + safety_door_ajar :1, + block_delete :1, + stop_disable :1, //! M1 + e_stop :1, + probe_disconnected :1, + motor_fault :1, + motor_warning :1, + limits_override :1, + single_block :1, + tls_overtravel :1, //! used for probe (toolsetter) protection + probe_overtravel :1, //! used for probe protection + probe_triggered :1, //! used for probe protection + deasserted :1; //! this flag is set if signals are deasserted. + }; +} control_signals_t; + typedef enum { DelayMode_Dwell = 0, DelayMode_SysSuspend diff --git a/pin_bits_masks.h b/pin_bits_masks.h index 1266e04..74ef1ab 100644 --- a/pin_bits_masks.h +++ b/pin_bits_masks.h @@ -111,110 +111,90 @@ #define CONTROL_ENABLE 0 #endif +#define a_cap(pin) .cap.pin + +#if defined(ESP_PLATFORM) || defined(RP2040) || defined(__IMXRT1062__) +#define add_aux_input(fn, aux, irq, signal_bit) { .function = fn, .irq_mode = irq, .signal.value = signal_bit, .port = IOPORT_UNASSIGNED, .gpio.pin = aux##_PIN }, +#else +#define add_aux_input(fn, aux, irq, signal_bit) { .function = fn, .irq_mode = irq, .signal.value = signal_bit, .port = IOPORT_UNASSIGNED, .gpio.port = (void *)aux##_PORT, .gpio.pin = aux##_PIN }, +#endif +#if defined(__IMXRT1062__) || defined(ESP_PLATFORM) +#define add_aux_output(fn, aux) { .function = fn, .port = IOPORT_UNASSIGNED, .gpio.pin = aux##_PIN }, +#else +#define add_aux_output(fn, aux) { .function = fn, .port = IOPORT_UNASSIGNED, .gpio.port = (void *)aux##_PORT, .gpio.pin = aux##_PIN }, +#endif +#define add_aux_input_scan(fn, irq, signal_bit) { .function = fn, .irq_mode = irq, .signal.value = signal_bit, .port = IOPORT_UNASSIGNED, .gpio.pin = 0xFF, .scan = On }, +#define add_aux_input_no_signal(fn, irq) { .function = fn, .irq_mode = irq, .port = IOPORT_UNASSIGNED, .gpio.pin = 0xFE }, +#define add_aux_output_exp(fn, aux) { .function = fn, .port = IOPORT_UNASSIGNED, .gpio.port = (void *)aux##_PORT, .gpio.pin = aux##_PIN }, + static aux_ctrl_t aux_ctrl[] = { // The following pins are bound explicitly to aux input pins. -#if (CONTROL_ENABLE & CONTROL_ESTOP) && defined(RESET_PIN) - #ifndef RESET_PORT - #define RESET_PORT 0 - #endif - { .function = Input_EStop, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .e_stop = On }, .pin = RESET_PIN, .port = (void *)RESET_PORT }, -#elif (CONTROL_ENABLE & CONTROL_RESET) && defined(RESET_PIN) - #ifndef RESET_PORT - #define RESET_PORT 0 - #endif - { .function = Input_Reset, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .reset = On }, .pin = RESET_PIN, .port = (void *)RESET_PORT }, +#ifdef RESET_PIN + #if (CONTROL_ENABLE & CONTROL_ESTOP) + add_aux_input(Input_EStop, RESET, IRQ_Mode_RisingFalling, SIGNALS_ESTOP_BIT) + #elif (CONTROL_ENABLE & CONTROL_RESET) + add_aux_input(Input_Reset, RESET, IRQ_Mode_RisingFalling, SIGNALS_RESET_BIT) + #endif #endif #if (CONTROL_ENABLE & CONTROL_FEED_HOLD) && defined(FEED_HOLD_PIN) - #ifndef FEED_HOLD_PORT - #define FEED_HOLD_PORT 0 - #endif - { .function = Input_FeedHold, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .feed_hold = On }, .pin = FEED_HOLD_PIN, .port = (void *)FEED_HOLD_PORT }, + add_aux_input(Input_FeedHold, FEED_HOLD, IRQ_Mode_RisingFalling, SIGNALS_FEEDHOLD_BIT) #endif #if (CONTROL_ENABLE & CONTROL_CYCLE_START) && defined(CYCLE_START_PIN) - #ifndef CYCLE_START_PORT - #define CYCLE_START_PORT 0 - #endif - { .function = Input_CycleStart, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .cycle_start = On }, .pin = CYCLE_START_PIN, .port = (void *)CYCLE_START_PORT }, + add_aux_input(Input_CycleStart, CYCLE_START, IRQ_Mode_RisingFalling, SIGNALS_CYCLESTART_BIT) #endif #if SAFETY_DOOR_ENABLE && defined(SAFETY_DOOR_PIN) - #ifndef SAFETY_DOOR_PORT - #define SAFETY_DOOR_PORT 0 - #endif - { .function = Input_SafetyDoor, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .safety_door_ajar = On }, .pin = SAFETY_DOOR_PIN, .port = (void *)SAFETY_DOOR_PORT }, + add_aux_input(Input_SafetyDoor, SAFETY_DOOR, IRQ_Mode_RisingFalling, SIGNALS_SAFETYDOOR_BIT) #endif #if MOTOR_FAULT_ENABLE && defined(MOTOR_FAULT_PIN) - #ifndef MOTOR_FAULT_PORT - #define MOTOR_FAULT_PORT 0 - #endif - { .function = Input_MotorFault, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .motor_fault = On }, .pin = MOTOR_FAULT_PIN, .port = (void *)MOTOR_FAULT_PORT }, + add_aux_input(Input_MotorFault, MOTOR_FAULT, IRQ_Mode_RisingFalling, SIGNALS_MOTOR_FAULT_BIT) #endif #if MOTOR_WARNING_ENABLE && defined(MOTOR_WARNING_PIN) - #ifndef MOTOR_WARNING_PORT - #define MOTOR_WARNING_PORT 0 - #endif - { .function = Input_MotorWarning, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .motor_fault = On }, .pin = MOTOR_WARNING_PIN, .port = (void *)MOTOR_WARNING_PORT }, + add_aux_input(Input_MotorWarning, MOTOR_WARNING, IRQ_Mode_RisingFalling, SIGNALS_MOTOR_WARNING_BIT) #endif - #if I2C_STROBE_ENABLE && defined(I2C_STROBE_PIN) - #ifndef I2C_STROBE_PORT - #define I2C_STROBE_PORT 0 - #endif - { .function = Input_I2CStrobe, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_Change, .cap = { .value = 0 }, .pin = I2C_STROBE_PIN, .port = (void *)I2C_STROBE_PORT }, + add_aux_input(Input_I2CStrobe, I2C_STROBE, IRQ_Mode_Change, 0) #endif #if MPG_ENABLE == 1 && defined(MPG_MODE_PIN) - #ifndef MPG_MODE_PORT - #define MPG_MODE_PORT 0 - #endif - { .function = Input_MPGSelect, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_Change, .cap = { .value = 0 }, .pin = MPG_MODE_PIN, .port = (void *)MPG_MODE_PORT }, + add_aux_input(Input_MPGSelect, MPG_MODE, IRQ_Mode_RisingFalling, 0) #endif #if QEI_SELECT_ENABLE && defined(QEI_SELECT_PIN) - #ifndef QEI_SELECT_PORT - #define QEI_SELECT_PORT 0 - #endif - { .function = Input_QEI_Select, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .value = 0 }, .pin = QEI_SELECT_PIN, .port = (void *)QEI_SELECT_PORT }, + add_aux_input(Input_QEI_Select, QEI_SELECT, IRQ_Mode_RisingFalling, 0) #endif - // Probe pins can be bound explicitly and can be "degraded" to not interrupt capable. #if PROBE_ENABLE && defined(PROBE_PIN) - #ifndef PROBE_PORT - #define PROBE_PORT 0 - #endif - { .function = Input_Probe, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .value = 0 }, .pin = PROBE_PIN, .port = (void *)PROBE_PORT }, + add_aux_input(Input_Probe, PROBE, IRQ_Mode_RisingFalling, 0) #endif #if PROBE2_ENABLE && defined(PROBE2_PIN) - #ifndef PROBE2_PORT - #define PROBE2_PORT 0 - #endif - { .function = Input_Probe2, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .value = 0 }, .pin = PROBE2_PIN, .port = (void *)PROBE2_PORT }, + add_aux_input(Input_Probe2, PROBE2, IRQ_Mode_RisingFalling, 0) #endif #if TOOLSETTER_ENABLE && defined(TOOLSETTER_PIN) - #ifndef TOOLSETTER_PORT - #define TOOLSETTER_PORT 0 - #endif - { .function = Input_Toolsetter, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .value = 0 }, .pin = TOOLSETTER_PIN, .port = (void *)TOOLSETTER_PORT }, + add_aux_input(Input_Toolsetter, TOOLSETTER, IRQ_Mode_RisingFalling, 0) #endif // The following pins are allocated from remaining aux inputs pool #if TOOLSETTER_ENABLE && !defined(TOOLSETTER_PIN) - { .function = Input_Toolsetter, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .value = 0 }, .pin = 0xFE, .port = NULL }, + add_aux_input_no_signal(Input_Toolsetter, IRQ_Mode_RisingFalling) #endif #if PROBE2_ENABLE && !defined(PROBE2_PIN) - { .function = Input_Probe2, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .value = 0 }, .pin = 0xFE, .port = NULL }, + add_aux_input_no_signal(Input_Probe2, IRQ_Mode_RisingFalling) +#endif +#if TLS_OVERTRAVEL_ENABLE + add_aux_input_scan(Input_ToolsetterOvertravel, IRQ_Mode_Change, SIGNALS_TLS_OVERTRAVEL_BIT) #endif #if LIMITS_OVERRIDE_ENABLE - { .function = Input_LimitsOverride, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_None, .cap = { .limits_override = On }, .pin = 0xFF, .port = NULL }, + add_aux_input_scan(Input_LimitsOverride, IRQ_Mode_Change, SIGNALS_LIMITS_OVERRIDE_BIT) #endif #if STOP_DISABLE_ENABLE - { .function = Input_StopDisable, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_Change, .cap = { .stop_disable = On }, .pin = 0xFF, .port = NULL }, + add_aux_input_scan(Input_StopDisable, IRQ_Mode_Change, SIGNALS_STOPDISABLE_BIT) #endif #if BLOCK_DELETE_ENABLE - { .function = Input_BlockDelete, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_Change, .cap = { .block_delete = On }, .pin = 0xFF, .port = NULL }, + add_aux_input_scan(Input_BlockDelete, IRQ_Mode_Change, SIGNALS_BLOCKDELETE_BIT) #endif #if SINGLE_BLOCK_ENABLE - { .function = Input_SingleBlock, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_Change, .cap = { .single_block = On }, .pin = 0xFF, .port = NULL }, + add_aux_input_scan(Input_SingleBlock, IRQ_Mode_Change, SIGNALS_SINGLE_BLOCK_BIT) #endif #if PROBE_DISCONNECT_ENABLE - { .function = Input_ProbeDisconnect, .aux_port = IOPORT_UNASSIGNED, .irq_mode = IRQ_Mode_RisingFalling, .cap = { .probe_disconnected = On }, .pin = 0xFF, .port = NULL }, + add_aux_input_scan(Input_ProbeDisconnect, IRQ_Mode_Change, SIGNALS_PROBE_CONNECTED_BIT) #endif }; @@ -225,14 +205,14 @@ static inline bool aux_ctrl_is_probe (pin_function_t function) #ifdef STM32_PLATFORM -static inline aux_ctrl_t *aux_ctrl_get_fn (void *port, uint8_t pin) +static inline aux_ctrl_t *aux_ctrl_get_fn (aux_gpio_t gpio) { aux_ctrl_t *ctrl_pin = NULL; if(sizeof(aux_ctrl) / sizeof(aux_ctrl_t)) { uint_fast8_t idx; - for(idx = 0; ctrl_pin == NULL && aux_ctrl[idx].pin != 0xFF && idx < sizeof(aux_ctrl) / sizeof(aux_ctrl_t); idx++) { - if(aux_ctrl[idx].pin == pin && aux_ctrl[idx].port == port) + for(idx = 0; ctrl_pin == NULL && aux_ctrl[idx].gpio.pin != 0xFF && idx < sizeof(aux_ctrl) / sizeof(aux_ctrl_t); idx++) { + if(aux_ctrl[idx].gpio.pin == gpio.pin && aux_ctrl[idx].gpio.port == gpio.port) ctrl_pin = &aux_ctrl[idx]; } } @@ -240,9 +220,27 @@ static inline aux_ctrl_t *aux_ctrl_get_fn (void *port, uint8_t pin) return ctrl_pin; } -#endif +#endif // STM32_PLATFORM -static inline aux_ctrl_t *aux_ctrl_remap_explicit (void *port, uint8_t pin, uint8_t aux_port, void *input) +static inline xbar_t *aux_ctrl_claim_port (aux_ctrl_t *aux_ctrl) +{ + xbar_t *pin = NULL; + + if(aux_ctrl) { + if(aux_ctrl->port != IOPORT_UNASSIGNED && (pin = ioport_claim(Port_Digital, Port_Input, &aux_ctrl->port, NULL))) { + + aux_ctrl->gpio.port = pin->port; + aux_ctrl->gpio.pin = pin->pin; + + ioport_set_function(pin, aux_ctrl->function, &aux_ctrl->signal); + } else + aux_ctrl->port = IOPORT_UNASSIGNED; + } + + return pin; +} + +static inline aux_ctrl_t *aux_ctrl_remap_explicit (aux_gpio_t gpio, uint8_t port, void *input) { aux_ctrl_t *ctrl_pin = NULL; @@ -250,10 +248,10 @@ static inline aux_ctrl_t *aux_ctrl_remap_explicit (void *port, uint8_t pin, uint uint_fast8_t idx; - for(idx = 0; ctrl_pin == NULL && idx < sizeof(aux_ctrl) / sizeof(aux_ctrl_t) && aux_ctrl[idx].pin != 0xFF; idx++) { - if(aux_ctrl[idx].pin == pin && aux_ctrl[idx].port == port) { + for(idx = 0; ctrl_pin == NULL && idx < sizeof(aux_ctrl) / sizeof(aux_ctrl_t) && aux_ctrl[idx].gpio.pin != 0xFF; idx++) { + if(aux_ctrl[idx].gpio.pin == gpio.pin && aux_ctrl[idx].gpio.port == gpio.port) { ctrl_pin = &aux_ctrl[idx]; - ctrl_pin->aux_port = aux_port; + ctrl_pin->port = port; ctrl_pin->input = input; break; } @@ -263,14 +261,14 @@ static inline aux_ctrl_t *aux_ctrl_remap_explicit (void *port, uint8_t pin, uint return ctrl_pin; } -static inline aux_ctrl_t *aux_ctrl_get_pin (uint8_t aux_port) +static inline aux_ctrl_t *aux_ctrl_in_get (uint8_t port) { aux_ctrl_t *ctrl_pin = NULL; uint_fast8_t idx = sizeof(aux_ctrl) / sizeof(aux_ctrl_t); if(idx) do { - if(aux_ctrl[--idx].aux_port == aux_port) + if(aux_ctrl[--idx].port == port) ctrl_pin = &aux_ctrl[idx]; } while(idx && ctrl_pin == NULL); @@ -282,12 +280,12 @@ static inline void aux_ctrl_irq_enable (settings_t *settings, ioport_interrupt_c uint_fast8_t idx = sizeof(aux_ctrl) / sizeof(aux_ctrl_t); if(idx) do { - if(aux_ctrl[--idx].aux_port != 0xFF && aux_ctrl[idx].irq_mode != IRQ_Mode_None) { + if(aux_ctrl[--idx].port != 0xFF && aux_ctrl[idx].irq_mode != IRQ_Mode_None) { if(!aux_ctrl_is_probe(aux_ctrl[idx].function)) { pin_irq_mode_t irq_mode; if((irq_mode = aux_ctrl[idx].irq_mode) & IRQ_Mode_RisingFalling) - irq_mode = (settings->control_invert.mask & aux_ctrl[idx].cap.mask) ? IRQ_Mode_Falling : IRQ_Mode_Rising; - hal.port.register_interrupt_handler(aux_ctrl[idx].aux_port, irq_mode, aux_irq_handler); + irq_mode = (settings->control_invert.mask & aux_ctrl[idx].signal.mask) ? IRQ_Mode_Falling : IRQ_Mode_Rising; + hal.port.register_interrupt_handler(aux_ctrl[idx].port, irq_mode, aux_irq_handler); } } } while(idx); @@ -295,45 +293,56 @@ static inline void aux_ctrl_irq_enable (settings_t *settings, ioport_interrupt_c typedef bool (*aux_claim_explicit_ptr)(aux_ctrl_t *aux_ctrl); -static bool aux_ctrl_claim_port (xbar_t *properties, uint8_t port, void *data) +// Default/internal functions for aux_ctrl_claim_ports() + +static bool __claim_in_port (xbar_t *properties, uint8_t port, void *data) { if(ioport_claim(Port_Digital, Port_Input, &port, NULL)) { - ((aux_ctrl_t *)data)->aux_port = port; - ioport_set_function(properties, ((aux_ctrl_t *)data)->function, &((aux_ctrl_t *)data)->cap); + ((aux_ctrl_t *)data)->port = port; + ((aux_ctrl_t *)data)->gpio.port = properties->port; + ((aux_ctrl_t *)data)->gpio.pin = properties->pin; + ioport_set_function(properties, ((aux_ctrl_t *)data)->function, &((aux_ctrl_t *)data)->signal); } - return ((aux_ctrl_t *)data)->aux_port != IOPORT_UNASSIGNED; + return ((aux_ctrl_t *)data)->port != IOPORT_UNASSIGNED; } -static bool aux_ctrl_find_port (xbar_t *properties, uint8_t port, void *data) +static bool __find_in_port (xbar_t *properties, uint8_t port, void *data) { - ((aux_ctrl_t *)data)->aux_port = port; + ((aux_ctrl_t *)data)->port = port; return true; } +// -- + static inline void aux_ctrl_claim_ports (aux_claim_explicit_ptr aux_claim_explicit, ioports_enumerate_callback_ptr aux_claim) { uint_fast8_t idx; if(aux_claim == NULL) - aux_claim = aux_ctrl_claim_port; + aux_claim = __claim_in_port; for(idx = 0; idx < sizeof(aux_ctrl) / sizeof(aux_ctrl_t); idx++) { - pin_cap_t cap = { .irq_mode = aux_ctrl[idx].irq_mode, .claimable = On }; - - if(aux_ctrl[idx].pin == 0xFE) // Toolsetter and Probe2 - ioports_enumerate(Port_Digital, Port_Input, cap, aux_ctrl_find_port, (void *)&aux_ctrl[idx]); -#ifdef STM32_PLATFORM - if(aux_ctrl[idx].irq_mode == IRQ_Mode_None && !(aux_ctrl[idx].function == Input_Probe || aux_ctrl[idx].function == Input_LimitsOverride)) - continue; -#endif - if(aux_ctrl[idx].pin == 0xFF) { - if(ioports_enumerate(Port_Digital, Port_Input, cap, aux_claim, (void *)&aux_ctrl[idx])) - hal.signals_cap.mask |= aux_ctrl[idx].cap.mask; - } else if(aux_ctrl[idx].aux_port != IOPORT_UNASSIGNED) + if(aux_ctrl[idx].port != IOPORT_UNASSIGNED) aux_claim_explicit(&aux_ctrl[idx]); + + else { + + pin_cap_t cap = { .irq_mode = aux_ctrl[idx].irq_mode, .claimable = On }; + + if(aux_ctrl[idx].gpio.pin == 0xFE) // Toolsetter and Probe2 + ioports_enumerate(Port_Digital, Port_Input, cap, __find_in_port, (void *)&aux_ctrl[idx]); +#ifdef STM32_PLATFORM + if(aux_ctrl[idx].irq_mode == IRQ_Mode_None && !(aux_ctrl[idx].function == Input_Probe || aux_ctrl[idx].function == Input_LimitsOverride)) + continue; +#endif + if(aux_ctrl[idx].gpio.pin == 0xFF) { + if(ioports_enumerate(Port_Digital, Port_Input, cap, aux_claim, (void *)&aux_ctrl[idx])) + hal.signals_cap.mask |= aux_ctrl[idx].signal.mask; + } + } } } @@ -344,16 +353,16 @@ static inline control_signals_t aux_ctrl_scan_status (control_signals_t signals) uint_fast8_t idx = sizeof(aux_ctrl) / sizeof(aux_ctrl_t); if(idx) do { - if(aux_ctrl[--idx].pin != 0xFF) + if(!aux_ctrl[--idx].scan) break; - if(aux_ctrl[idx].aux_port != IOPORT_UNASSIGNED) { - signals.mask &= ~aux_ctrl[idx].cap.mask; + signals.mask &= ~aux_ctrl[idx].signal.mask; + if(aux_ctrl[idx].port != IOPORT_UNASSIGNED) { #ifdef GRBL_ESP32 // Snowflake guru workaround - if(hal.port.wait_on_input(Port_Digital, aux_ctrl[idx].aux_port, WaitMode_Immediate, FZERO) == 1) + if(hal.port.wait_on_input(Port_Digital, aux_ctrl[idx].port, WaitMode_Immediate, FZERO) == 1) signals.mask |= aux_ctrl[idx].cap.mask; #else - if(hal.port.wait_on_input(Port_Digital, aux_ctrl[idx].aux_port, WaitMode_Immediate, 0.0f) == 1) - signals.mask |= aux_ctrl[idx].cap.mask; + if(hal.port.wait_on_input(Port_Digital, aux_ctrl[idx].port, WaitMode_Immediate, 0.0f) == 1) + signals.mask |= aux_ctrl[idx].signal.mask; #endif } } while(idx); @@ -367,116 +376,85 @@ static inline control_signals_t aux_ctrl_scan_status (control_signals_t signals) static aux_ctrl_out_t aux_ctrl_out[] = { #if defined(ESP_PLATFORM) || defined(RP2040) // for now #if defined(STEPPERS_ENABLE_PIN) && STEPPERS_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnable, .aux_port = IOPORT_UNASSIGNED, .pin = STEPPERS_ENABLE_PIN, .port = (void *)STEPPERS_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnable, STEPPERS_ENABLE) #endif #if defined(X_ENABLE_PIN) && X_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableX, .aux_port = IOPORT_UNASSIGNED, .pin = X_ENABLE_PIN, .port = (void *)X_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableX, X_ENABLE) #endif #if defined(X2_ENABLE_PIN) && X2_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableX2, .aux_port = IOPORT_UNASSIGNED, .pin = X2_ENABLE_PIN, .port = (void *)X2_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableX2, X2_ENABLE) #endif #if defined(Y_ENABLE_PIN) && Y_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableY, .aux_port = IOPORT_UNASSIGNED, .pin = Y_ENABLE_PIN, .port = (void *)Y_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableY, Y_ENABLE) #endif #if defined(Y2_ENABLE_PIN) && Y2_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableY2, .aux_port = IOPORT_UNASSIGNED, .pin = Y2_ENABLE_PIN, .port = (void *)Y2_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableY2, Y2_ENABLE) #endif #if defined(XY_ENABLE_PIN) && XY_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableXY, .aux_port = IOPORT_UNASSIGNED, .pin = XY_ENABLE_PIN, .port = (void *)XY_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableXY, XY_ENABLE) #endif #if defined(Z_ENABLE_PIN) && Z_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableZ, .aux_port = IOPORT_UNASSIGNED, .pin = Z_ENABLE_PIN, .port = (void *)Z_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableZ, Z_ENABLE) #endif #if defined(Z2_ENABLE_PIN) && Z2_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableZ2, .aux_port = IOPORT_UNASSIGNED, .pin = Z2_ENABLE_PIN, .port = (void *)Z2_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableZ2, Z2_ENABLE) #endif #if defined(A_ENABLE_PIN) && A_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableA, .aux_port = IOPORT_UNASSIGNED, .pin = A_ENABLE_PIN, .port = (void *)A_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableA, A_ENABLE) #endif #if defined(B_ENABLE_PIN) && B_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableB, .aux_port = IOPORT_UNASSIGNED, .pin = B_ENABLE_PIN, .port = (void *)B_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableB, B_ENABLE) #endif #if defined(C_ENABLE_PIN) && C_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableC, .aux_port = IOPORT_UNASSIGNED, .pin = C_ENABLE_PIN, .port = (void *)C_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableC, C_ENABLE) #endif #if defined(U_ENABLE_PIN) && U_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableU, .aux_port = IOPORT_UNASSIGNED, .pin = U_ENABLE_PIN, .port = (void *)U_ENABLE_PORT }, + add_aux_output_exp(Output_StepperEnableU, U_ENABLE) #endif -#if defined(V_ENABLE_PIN) && AV_ENABLE_PORT == EXPANDER_PORT - { .function = Output_StepperEnableV, .aux_port = IOPORT_UNASSIGNED, .pin = V_ENABLE_PIN, .port = (void *)V_ENABLE_PORT }, +#if defined(V_ENABLE_PIN) && V_ENABLE_PORT == EXPANDER_PORT + add_aux_output_exp(Output_StepperEnableV, V_ENABLE) #endif #endif // + #ifdef SPINDLE_ENABLE_PIN - #ifndef SPINDLE_ENABLE_PORT - #define SPINDLE_ENABLE_PORT 0 - #endif - { .function = Output_SpindleOn, .aux_port = IOPORT_UNASSIGNED, .pin = SPINDLE_ENABLE_PIN, .port = (void *)SPINDLE_ENABLE_PORT }, + add_aux_output(Output_SpindleOn, SPINDLE_ENABLE) #endif #ifdef SPINDLE_PWM_PIN - #ifndef SPINDLE_PWM_PORT - #define SPINDLE_PWM_PORT 0 - #endif - { .function = Output_SpindlePWM, .aux_port = IOPORT_UNASSIGNED, .pin = SPINDLE_PWM_PIN, .port = (void *)SPINDLE_PWM_PORT }, + add_aux_output(Output_SpindlePWM, SPINDLE_PWM) #endif #ifdef SPINDLE_DIRECTION_PIN - #ifndef SPINDLE_DIRECTION_PORT - #define SPINDLE_DIRECTION_PORT 0 - #endif - { .function = Output_SpindleDir, .aux_port = IOPORT_UNASSIGNED, .pin = SPINDLE_DIRECTION_PIN, .port = (void *)SPINDLE_DIRECTION_PORT }, + add_aux_output(Output_SpindleDir, SPINDLE_DIRECTION) #endif - #ifdef SPINDLE1_ENABLE_PIN - #ifndef SPINDLE1_ENABLE_PORT - #define SPINDLE1_ENABLE_PORT 0 - #endif - { .function = Output_Spindle1On, .aux_port = IOPORT_UNASSIGNED, .pin = SPINDLE1_ENABLE_PIN, .port = (void *)SPINDLE1_ENABLE_PORT }, -#endif -#ifdef SPINDLE1_PWM_PIN - #ifndef SPINDLE1_PWM_PORT - #define SPINDLE1_PWM_PORT 0 - #endif - { .function = Output_Spindle1PWM, .aux_port = IOPORT_UNASSIGNED, .pin = SPINDLE1_PWM_PIN, .port = (void *)SPINDLE1_PWM_PORT }, + add_aux_output(Output_Spindle1On, SPINDLE1_ENABLE) #endif #ifdef SPINDLE1_DIRECTION_PIN - #ifndef SPINDLE1_DIRECTION_PORT - #define SPINDLE1_DIRECTION_PORT 0 - #endif - { .function = Output_Spindle1Dir, .aux_port = IOPORT_UNASSIGNED, .pin = SPINDLE1_DIRECTION_PIN, .port = (void *)SPINDLE1_DIRECTION_PORT }, + add_aux_output(Output_Spindle1Dir, SPINDLE1_DIRECTION) +#endif +#ifdef SPINDLE1_PWM_PIN + add_aux_output(Output_Spindle1PWM, SPINDLE1_PWM) #endif - #ifdef COOLANT_FLOOD_PIN - #ifndef COOLANT_FLOOD_PORT - #define COOLANT_FLOOD_PORT 0 - #endif - { .function = Output_CoolantFlood, .aux_port = IOPORT_UNASSIGNED, .pin = COOLANT_FLOOD_PIN, .port = (void *)COOLANT_FLOOD_PORT }, + add_aux_output(Output_CoolantFlood, COOLANT_FLOOD) #endif #ifdef COOLANT_MIST_PIN - #ifndef COOLANT_MIST_PORT - #define COOLANT_MIST_PORT 0 - #endif - { .function = Output_CoolantMist, .aux_port = IOPORT_UNASSIGNED, .pin = COOLANT_MIST_PIN, .port = (void *)COOLANT_MIST_PORT }, + add_aux_output(Output_CoolantMist, COOLANT_MIST) #endif - #ifdef COPROC_RESET_PIN - #ifndef COPROC_RESET_PORT - #define COPROC_RESET_PORT 0 - #endif - { .function = Output_CoProc_Reset, .aux_port = IOPORT_UNASSIGNED, .pin = COPROC_RESET_PIN, .port = (void *)COPROC_RESET_PORT }, + add_aux_output(Output_CoProc_Reset, COPROC_RESET) #endif #ifdef COPROC_BOOT0_PIN - #ifndef COPROC_BOOT0_PORT - #define COPROC_BOOT0_PORT 0 - #endif - { .function = Output_CoProc_Boot0, .aux_port = IOPORT_UNASSIGNED, .pin = COPROC_BOOT0_PIN, .port = (void *)COPROC_BOOT0_PORT }, + add_aux_output(Output_CoProc_Boot0, COPROC_BOOT0) #endif #if defined(SPI_RST_PIN) && defined(RP2040) - #if SPI_RST_PORT == EXPANDER_PORT - { .function = Output_SPIRST, .aux_port = IOPORT_UNASSIGNED, .pin = SPI_RST_PIN, .port = (void *)SPI_RST_PORT }, + #ifndef SPI_RST_PORT + #define SPI_RST_PORT 0 #endif + add_aux_output(Output_SPIRST, SPI_RST) #endif }; -static inline aux_ctrl_out_t *aux_out_remap_explicit (void *port, uint8_t pin, uint8_t aux_port, void *output) +static inline aux_ctrl_out_t *aux_out_remap_explicit (aux_gpio_t gpio, uint8_t port, void *output) { aux_ctrl_out_t *ctrl_pin = NULL; @@ -484,9 +462,9 @@ static inline aux_ctrl_out_t *aux_out_remap_explicit (void *port, uint8_t pin, u if(idx) do { idx--; - if(aux_ctrl_out[idx].port == port && aux_ctrl_out[idx].pin == pin) { + if(aux_ctrl_out[idx].gpio.port == gpio.port && aux_ctrl_out[idx].gpio.pin == gpio.pin) { ctrl_pin = &aux_ctrl_out[idx]; - ctrl_pin->aux_port = aux_port; + ctrl_pin->port = port; ctrl_pin->output = output; } } while(idx && ctrl_pin == NULL); @@ -496,37 +474,56 @@ static inline aux_ctrl_out_t *aux_out_remap_explicit (void *port, uint8_t pin, u typedef bool (*aux_claim_explicit_out_ptr)(aux_ctrl_out_t *aux_ctrl); -static bool aux_ctrl_claim_out_port (xbar_t *properties, uint8_t port, void *data) -{ - if(((aux_ctrl_out_t *)data)->port == (void *)EXPANDER_PORT) { - if(((aux_ctrl_out_t *)data)->pin == properties->pin && properties->set_value) - ((aux_ctrl_out_t *)data)->aux_port = port; - } else if(ioport_claim(Port_Digital, Port_Output, &port, xbar_fn_to_pinname(((aux_ctrl_out_t *)data)->function))) - ((aux_ctrl_out_t *)data)->aux_port = port; +// Default functions for aux_ctrl_claim_out_ports() - return ((aux_ctrl_out_t *)data)->aux_port != 0xFF; +static bool __claim_out_port (xbar_t *properties, uint8_t port, void *data) +{ + if(((aux_ctrl_out_t *)data)->gpio.port == (void *)EXPANDER_PORT) { + if(((aux_ctrl_out_t *)data)->gpio.pin == properties->pin && properties->set_value) + ((aux_ctrl_out_t *)data)->port = port; + } else if(ioport_claim(Port_Digital, Port_Output, &port, xbar_fn_to_pinname(((aux_ctrl_out_t *)data)->function))) + ((aux_ctrl_out_t *)data)->port = port; + + return ((aux_ctrl_out_t *)data)->port != IOPORT_UNASSIGNED; } +static bool ___claim_out_port_explicit (aux_ctrl_out_t *aux_ctrl) +{ + xbar_t *pin; + + if((pin = ioport_claim(Port_Digital, Port_Output, &aux_ctrl->port, NULL))) + ioport_set_function(pin, aux_ctrl->function, NULL); + else + aux_ctrl->port = IOPORT_UNASSIGNED; + + return aux_ctrl->port != IOPORT_UNASSIGNED; +} + +// + static inline void aux_ctrl_claim_out_ports (aux_claim_explicit_out_ptr aux_claim_explicit, ioports_enumerate_callback_ptr aux_claim) { uint_fast8_t idx; if(aux_claim == NULL) - aux_claim = aux_ctrl_claim_out_port; + aux_claim = __claim_out_port; + + if(aux_claim_explicit == NULL) + aux_claim_explicit = ___claim_out_port_explicit; for(idx = 0; idx < sizeof(aux_ctrl_out) / sizeof(aux_ctrl_out_t); idx++) { - if(aux_ctrl_out[idx].port == (void *)EXPANDER_PORT) { + if(aux_ctrl_out[idx].gpio.port == (void *)EXPANDER_PORT) { if(ioports_enumerate(Port_Digital, Port_Output, (pin_cap_t){ .external = On, .claimable = On }, aux_claim, &aux_ctrl_out[idx])) { - if((aux_ctrl_out[idx].output = ioport_claim(Port_Digital, Port_Output, &aux_ctrl_out[idx].aux_port, NULL /*xbar_fn_to_pinname(aux_ctrl_out[idx].function)*/))) { + if((aux_ctrl_out[idx].output = ioport_claim(Port_Digital, Port_Output, &aux_ctrl_out[idx].port, NULL /*xbar_fn_to_pinname(aux_ctrl_out[idx].function)*/))) { ioport_set_function((xbar_t *)aux_ctrl_out[idx].output, aux_ctrl_out[idx].function, NULL); // TODO: else set description? aux_claim_explicit(&aux_ctrl_out[idx]); } } - } else if(aux_ctrl_out[idx].pin == 0xFF) { + } else if(aux_ctrl_out[idx].gpio.pin == 0xFF) { if(ioports_enumerate(Port_Digital, Port_Output, (pin_cap_t){ .claimable = On }, aux_claim, &aux_ctrl_out[idx])) aux_claim_explicit(&aux_ctrl_out[idx]); - } else if(aux_ctrl_out[idx].aux_port != 0xFF) + } else if(aux_ctrl_out[idx].port != IOPORT_UNASSIGNED) aux_claim_explicit(&aux_ctrl_out[idx]); } } diff --git a/probe.c b/probe.c new file mode 100644 index 0000000..5c8a9a1 --- /dev/null +++ b/probe.c @@ -0,0 +1,227 @@ +/* + probe.c - An embedded CNC Controller with rs274/ngc (g-code) support + + Part of grblHAL + + Copyright (c) 2025 Terje Io + + grblHAL is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + grblHAL is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with grblHAL. If not, see . +*/ + +#include "hal.h" + +typedef struct { + probe_id_t probe_id; + probe_flags_t flags; + probeflags_t inverted_bit; + uint8_t port; + void *input; + get_probe_input_ptr get_input; +} probe_t; + +static probe_state_t probe_state = { .connected = On }; +static probe_t probes[N_PROBE_MAX], *probe = NULL; + +static void probe_irq_handler (uint8_t port, bool state) +{ + if(probe_state.is_probing) { + probe_state.triggered = On; + if(!probe_state.was_probing) { + probe_state.was_probing = true; + } + } else { + control_signals_t signals = { .mask = hal.control.get_state().mask }; + signals.probe_triggered = On; + hal.control.interrupt_callback(signals); + } +} + +// Toggle probe connected status. +static void probe_connected_toggle (void) +{ + if(!probe_state.is_probing) { + if((probe->flags.connected = probe_state.connected = !probe_state.connected)) { + if(probe->flags.watchable && settings.probe.enable_protection) + probe->flags.guarded = probe_state.irq_enabled = ioport_enable_irq(probe->port, IRQ_Mode_Change, probe_irq_handler); + } else if(probe->flags.guarded && ioport_enable_irq(probe->port, IRQ_Mode_None, probe_irq_handler)) + probe->flags.guarded = probe_state.irq_enabled = Off; + + if(settings.probe.enable_protection) + system_add_rt_report(Report_ProbeProtect); + } +} + +// Sets up the probe pin invert mask to +// appropriately set the pin logic according to setting for normal-high/normal-low operation +// and the probing cycle modes for toward-workpiece/away-from-workpiece. +static void probe_configure (bool is_probe_away, bool probing) +{ + bool invert = !!(settings.probe.value & probe->inverted_bit.value); + + probe_state.inverted = is_probe_away ? !invert : invert; + + if(probing) { + if(probe->flags.latchable) { + probe_state.is_probing = probe_state.was_probing = Off; + probe_state.triggered = hal.probe.get_state().triggered; + probe_state.irq_mode = probe_state.triggered ? IRQ_Mode_None : (probe_state.inverted ? IRQ_Mode_Falling : IRQ_Mode_Rising); + } + } else + probe_state.irq_mode = probe->flags.connected && probe->flags.watchable && settings.probe.enable_protection ? IRQ_Mode_Change : IRQ_Mode_None; + + if(ioport_enable_irq(probe->port, probe_state.irq_mode, probe_irq_handler)) { + probe_state.irq_enabled = probe_state.irq_mode != IRQ_Mode_None; + probe->flags.guarded = !probing && probe_state.irq_enabled; + } + + if(settings.probe.enable_protection) + system_add_rt_report(Report_ProbeProtect); + + if(!probe_state.irq_enabled) + probe_state.triggered = Off; + + probe_state.is_probing = probing; +} + +static bool probe_select (probe_id_t probe_id) +{ + uint_fast8_t idx = 0; + probe_t *selected_probe = NULL; + + do { + if(probes[idx].probe_id == probe_id && probes[idx].get_input) + selected_probe = &probes[idx]; + } while(selected_probe == NULL && ++idx < N_PROBE_MAX); + + if(!probe_state.is_probing && selected_probe && selected_probe != probe) { + + if(probe_state.irq_mode != IRQ_Mode_None) + ioport_enable_irq(probe->port, IRQ_Mode_None, probe_irq_handler); + + probe = selected_probe; + hal.probe.configure(false, false); + } + + return probe == selected_probe; +} + +static probe_flags_t probe_get_caps (probe_id_t probe_id) +{ + uint_fast8_t idx = 0; + probe_t *probe = NULL; + + do { + if(probes[idx].probe_id == probe_id && probes[idx].get_input) + probe = &probes[idx]; + } while(probe == NULL && ++idx < N_PROBE_MAX); + + return probe ? probe->flags : (probe_flags_t){0}; +} + +static bool is_triggered (probe_id_t probe_id) +{ + uint_fast8_t idx = 0; + probe_t *probe = NULL; + + do { + if(probes[idx].probe_id == probe_id && probes[idx].get_input) + probe = &probes[idx]; + } while(probe == NULL && ++idx < N_PROBE_MAX); + + return !!probe && probe->get_input(probe->input) ^ !!(settings.probe.value & probe->inverted_bit.value); +} + +// Returns the probe connected and triggered pin states. +static probe_state_t get_state (void) +{ + probe_state_t state = { .value = probe_state.value }; + + state.probe_id = probe->probe_id; + state.connected = probe->flags.connected; + + if(probe_state.is_probing && probe_state.irq_enabled) + state.triggered = probe_state.triggered; + else + state.triggered = probe->get_input(probe->input) ^ probe_state.inverted; + + return state; +} + +bool probe_add (probe_id_t probe_id, uint8_t port, pin_irq_mode_t irq_mode, void *input, get_probe_input_ptr get_input) +{ + static uint_fast8_t n_probes = 0; + + if(get_input == NULL || n_probes >= N_PROBE_MAX) + return false; + + bool can_latch; + + if(!(can_latch = (irq_mode & IRQ_Mode_RisingFalling) == IRQ_Mode_RisingFalling)) + hal.signals_cap.probe_triggered = Off; + else if(n_probes == 0) + hal.signals_cap.probe_triggered = On; + + probes[n_probes].probe_id = probe_id; + probes[n_probes].port = port; + probes[n_probes].flags.available = On; + probes[n_probes].flags.connected = probe_state.connected; + probes[n_probes].flags.latchable = can_latch; + probes[n_probes].flags.watchable = !!(irq_mode & IRQ_Mode_Change); + probes[n_probes].input = input; + probes[n_probes].get_input = get_input; + + switch(probe_id) { + + case Probe_Toolsetter: + probes[n_probes].inverted_bit.invert_toolsetter_input = On; + break; + + case Probe_2: + probes[n_probes].inverted_bit.invert_probe2_input = On; + break; + + default: // Probe_Default + probes[n_probes].inverted_bit.invert_probe_pin = On; + break; + } + + hal.driver_cap.probe_pull_up = On; + hal.probe.configure = probe_configure; + hal.probe.connected_toggle = probe_connected_toggle; + hal.probe.get_caps = probe_get_caps; + hal.probe.get_state = get_state; + hal.probe.is_triggered = is_triggered; + + if(probe == NULL || probe_id == Probe_Default) + probe = &probes[n_probes]; + + if(++n_probes == 2) + hal.probe.select = probe_select; + + return true; +} + +void probe_connected_event (void *data) +{ + if(hal.probe.connected_toggle) { + if((uint32_t)data == 2) + hal.probe.connected_toggle(); + else { + probe_state_t state = hal.probe.get_state(); + + if(state.probe_id == Probe_Default && hal.driver_cap.probe && state.connected != !!data) + hal.probe.connected_toggle(); + } + } +} diff --git a/probe.h b/probe.h index 7358944..27040f2 100644 --- a/probe.h +++ b/probe.h @@ -22,8 +22,14 @@ #ifndef _PROBE_H_ #define _PROBE_H_ +#include "crossbar.h" + +#define N_PROBE_MAX 3 + // Values that define the probing state machine. +typedef bool (*get_probe_input_ptr)(void *input); + typedef enum { Probing_Off = 0, //!< 0 Probing_Active //!< 1 @@ -36,33 +42,33 @@ typedef enum { } probe_id_t; typedef union { - uint8_t value; + uint16_t value; struct { - uint8_t triggered :1, //!< Set to true when probe or toolsetter is triggered. - connected :1, //!< Set to true when probe is connected. Always set to true if the driver does not have a probe connected input. - inverted :1, //!< For driver use - is_probing :1, //!< For driver use - irq_enabled :1, //!< For driver use - tls_triggered :1, //!< Set to true when toolsetter is triggered. - probe_id :2; //!< Id of active probe (for now). + uint16_t triggered :1, //!< Set to true when probe or toolsetter is triggered. + connected :1, //!< Set to true when probe is connected. Always set to true if the driver does not have a probe connected input. + inverted :1, //!< For driver use + is_probing :1, //!< For driver use + was_probing :1, //!< For driver use + irq_enabled :1, //!< For driver use + irq_mode :5, //!< pin_irq_mode_t - for driver use + unused :3, + probe_id :2; //!< Id of active probe (for now). }; } probe_state_t; typedef union { uint8_t value; struct { - uint8_t connected :1, //!< Set to true when probe is connected. Always set to true if the driver does not have a probe connected input. + uint8_t available :1, //!< Set to true when probe input is available. + connected :1, //!< Set to true when probe is connected. Always set to true if the driver does not have a probe connected input. latchable :1, //!< Set to true when probe input supports change rising/falling. watchable :1, //!< Set to true when probe input supports change interrupt. - unassigned :5; + guarded :1, //!< Set to true when probe protection is enabled. + unassigned :3; }; } probe_flags_t; -typedef struct { - probe_id_t probe_id; - probe_flags_t flags; - uint8_t port; - void *input; -} probe_t; +void probe_connected_event (void *data); +bool probe_add (probe_id_t probe_id, uint8_t port, pin_irq_mode_t irq_mode, void *input, get_probe_input_ptr get_input); -#endif +#endif // _PROBE_H_ diff --git a/protocol.c b/protocol.c index 2992a64..1dc4327 100644 --- a/protocol.c +++ b/protocol.c @@ -183,7 +183,7 @@ bool protocol_main_loop (void) spindle_all_off(); hal.coolant.set_state((coolant_state_t){0}); sys.cold_start = false; - system_set_exec_state_flag(EXEC_RT_COMMAND); // execute any statup up tasks + system_set_exec_state_flag(EXEC_RT_COMMAND); // execute any startup up tasks } // --------------------------------------------------------------------------------- @@ -918,17 +918,17 @@ ISR_CODE bool ISR_FUNC(protocol_enqueue_realtime_command)(char c) break; case CMD_PROBE_CONNECTED_TOGGLE: - if(hal.probe.connected_toggle) - hal.probe.connected_toggle(); + if((drop = !!hal.probe.connected_toggle)) + task_add_immediate(probe_connected_event, (void *)2); break; case CMD_OPTIONAL_STOP_TOGGLE: - if((signals.stop_disable = !hal.signals_cap.stop_disable)) // Not available as realtime command if HAL supports physical switch + if((drop = (signals.stop_disable = !hal.signals_cap.stop_disable))) // Not available as realtime command if HAL supports physical switch sys.flags.optional_stop_disable = !sys.flags.optional_stop_disable; break; case CMD_SINGLE_BLOCK_TOGGLE: - if((signals.single_block = !hal.signals_cap.single_block)) // Not available as realtime command if HAL supports physical switch + if((drop = (signals.single_block = !hal.signals_cap.single_block))) // Not available as realtime command if HAL supports physical switch sys.flags.single_block = !sys.flags.single_block; break; diff --git a/report.c b/report.c index 7760798..7ff6390 100644 --- a/report.c +++ b/report.c @@ -1473,8 +1473,11 @@ void report_realtime_status (stream_write_ptr stream_write) if(report.tool) stream_write(appendbuf(2, "|T:", uitoa((uint32_t)gc_state.tool->tool_id))); - if(report.probe_id) + if(report.probe_id || report.probe_protect) { stream_write(appendbuf(2, "|P:", uitoa((uint32_t)probe_state.probe_id))); + if(report.probe_protect && !probe_state.is_probing && probe_state.irq_enabled) + stream_write(",P"); + } if(report.tlo_reference) stream_write(appendbuf(2, "|TLR:", uitoa(sys.tlo_reference_set.mask != 0))); diff --git a/settings.c b/settings.c index ce95de3..3525ffa 100644 --- a/settings.c +++ b/settings.c @@ -426,8 +426,8 @@ static char homing_options[] = "Enable,Enable single axis commands,Homing on sta #else static char probe_signals[] = "Probe"; #endif -static char probing_options[] = "Allow feed override,Apply soft limits,N/A,Auto select toolsetter,Auto select probe 2"; -static char control_signals[] = "Reset,Feed hold,Cycle start,Safety door,Block delete,Optional stop,EStop,Probe connected,Motor fault,Motor warning,Limits override,Single step blocks"; +static char probing_options[] = "Allow feed override,Apply soft limits,N/A,Auto select toolsetter,Auto select probe 2,Probe protection"; +static char control_signals[] = "Reset,Feed hold,Cycle start,Safety door,Block delete,Optional stop,EStop,Probe disconnected,Motor fault,Motor warning,Limits override,Single step blocks,Toolsetter overtravel"; 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"; @@ -702,11 +702,18 @@ static status_code_t set_ngc_debug_out (setting_id_t id, uint_fast16_t int_value static status_code_t set_control_invert (setting_id_t id, uint_fast16_t int_value) { - settings.control_invert.mask = (int_value & hal.signals_cap.mask) | limits_override.mask; + int_value = (int_value & hal.signals_cap.mask) | limits_override.mask; + + bool pd_changed = hal.probe.connected_toggle && settings.control_invert.probe_disconnected != (control_signals_t){ .value = int_value }.probe_disconnected; + + settings.control_invert.mask = int_value; ioport_setting_changed(id); system_init_switches(); + if(pd_changed) + hal.probe.connected_toggle(); + return Status_OK; } @@ -1077,9 +1084,12 @@ static status_code_t set_probe_flags (setting_id_t id, uint_fast16_t int_value) { settings.probe.allow_feed_override = bit_istrue(int_value, bit(0)); settings.probe.soft_limited = bit_istrue(int_value, bit(1)); - settings.probe.enable_protection = bit_istrue(int_value, bit(2)); +// settings.probe.? = bit_istrue(int_value, bit(2)); settings.probe.toolsetter_auto_select = bit_istrue(int_value, bit(3)) && hal.driver_cap.toolsetter && hal.probe.select; settings.probe.probe2_auto_select = bit_istrue(int_value, bit(4)) && hal.driver_cap.probe2 && hal.probe.select; + settings.probe.enable_protection = bit_istrue(int_value, bit(5)); + + hal.probe.configure(false, false); return Status_OK; } @@ -1601,9 +1611,10 @@ static uint32_t get_int (setting_id_t id) case Setting_ProbingFlags: value = settings.probe.allow_feed_override | (settings.probe.soft_limited << 1) | - (settings.probe.enable_protection << 2) | + // (settings.probe.enable_protection << 2) | (settings.probe.toolsetter_auto_select << 3) | - (settings.probe.probe2_auto_select << 4); + (settings.probe.probe2_auto_select << 4) | + (settings.probe.enable_protection << 5); break; case Setting_ToolChangeMode: @@ -3509,6 +3520,14 @@ void settings_init (void) setting_remove_elements(Setting_ProbePullUpDisable, mask); mask = 0b00011 | (hal.probe.select ? ((hal.driver_cap.toolsetter << 3) | (hal.driver_cap.probe2 << 4)) : 0); +#if 0 + if(hal.probe.get_caps) { + if(hal.probe.get_caps(Probe_Default).watchable) + mask |= (1 << 5); + } +#else + settings.probe.enable_protection = Off; +#endif setting_remove_elements(Setting_ProbingFlags, mask); if(!settings.flags.settings_downgrade && settings.version.build != (GRBL_BUILD - 20000000UL)) { diff --git a/stream.c b/stream.c index eb4dcb0..274330f 100644 --- a/stream.c +++ b/stream.c @@ -724,8 +724,8 @@ void debug_write (const char *s) { if(dbg_write) { dbg_write(s); -// while(hal.debug.get_tx_buffer_count()) // Wait until message is delivered -// grbl.on_execute_realtime(state_get()); + while(hal.debug.get_tx_buffer_count()) // Wait until message is delivered + grbl.on_execute_realtime(state_get()); } } @@ -740,8 +740,8 @@ void debug_writeln (const char *s) dbg_write(s); dbg_write(ASCII_EOL); -// while(hal.debug.get_tx_buffer_count()) // Wait until message is delivered -// grbl.on_execute_realtime(state_get()); + while(hal.debug.get_tx_buffer_count()) // Wait until message is delivered + grbl.on_execute_realtime(state_get()); lock = false; } diff --git a/system.c b/system.c index 44b98aa..6944651 100644 --- a/system.c +++ b/system.c @@ -72,8 +72,13 @@ ISR_CODE void ISR_FUNC(control_interrupt_handler)(control_signals_t signals) .motor_fault = On }; - if(signals.deasserted) + if(signals.deasserted) { + if(signals.probe_disconnected) { + signals.probe_disconnected = Off; + task_add_immediate(probe_connected_event, (void *)1); + } signals.bits &= onoff_signals.bits; + } if(signals.bits) { @@ -116,7 +121,8 @@ ISR_CODE void ISR_FUNC(control_interrupt_handler)(control_signals_t signals) if(sys.probing_state == Probing_Active && state_get() == STATE_CYCLE) { system_set_exec_state_flag(EXEC_FEED_HOLD); sys.alarm_pending = Alarm_ProbeProtect; - } + } else + task_add_immediate(probe_connected_event, NULL); } else if(signals.feed_hold) system_set_exec_state_flag(EXEC_FEED_HOLD); else if(signals.cycle_start) diff --git a/system.h b/system.h index 3e1ebd8..e46083c 100644 --- a/system.h +++ b/system.h @@ -158,31 +158,6 @@ typedef union { }; } step_control_t; -// NOTE: the pin_function_t enum must be kept in sync with any changes! -typedef union { - uint16_t bits; - uint16_t mask; - uint16_t value; - struct { - uint16_t reset :1, - feed_hold :1, - cycle_start :1, - safety_door_ajar :1, - block_delete :1, - stop_disable :1, //! M1 - e_stop :1, - probe_disconnected :1, - motor_fault :1, - motor_warning :1, - limits_override :1, - single_block :1, - tls_overtravel :1, //! used for probe (toolsetter) protection - probe_overtravel :1, //! used for probe protection - probe_triggered :1, //! used for probe protection - deasserted :1; //! this flag is set if signals are deasserted. - }; -} control_signals_t; - // Define spindle stop override control states. typedef union { uint8_t value; @@ -229,9 +204,10 @@ typedef enum { Report_SpindleId = (1 << 17), Report_ProbeId = (1 << 18), Report_DistanceToGo = (1 << 19), + Report_ProbeProtect = (1 << 20), Report_ForceWCO = (1 << 29), Report_CycleStart = (1 << 30), - Report_All = 0x8003FFFF + Report_All = 0x801FFFFF } report_tracking_t; typedef union { @@ -257,7 +233,8 @@ typedef union { spindle_id :1, //!< Spindle changed. probe_id :1, //!< Probe changed. distance_to_go :1, //!< Distance to go. - unassigned :9, // + probe_protect :1, //!< Probe protection state changed. + unassigned :8, // force_wco :1, //!< Add work coordinates (due to WCO changed during motion). cycle_start :1, //!< Cycle start signal triggered. __NOTE:__ do __NOT__ add to Report_All enum above! all :1; //!< Set when CMD_STATUS_REPORT_ALL is requested, may be used by user code.