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.