From 472012e137471a5dd95495a98f92c989bb61b8a7 Mon Sep 17 00:00:00 2001 From: Terje Io Date: Thu, 20 Feb 2025 10:46:04 +0100 Subject: [PATCH] Changed $65 setting to flags for allowing feed rate overrides during probing and limiting probe motion to be within machine workspace. Improved handling of stop realtime command (0x19) for faster motion halt without losing position. Optimized RAM usage. Fixed regression related to losing current coordinate system and tool offset over a stop. --- README.md | 4 +-- changelog.md | 29 +++++++++++++++++- config.h | 5 +++- core_handlers.h | 4 +-- gcode.h | 2 +- grbl.h | 2 +- ioports.c | 10 ++++--- ioports.h | 2 ++ kinematics/delta.c | 14 ++++----- machine_limits.c | 7 +++-- motion_control.c | 15 +++++----- planner.c | 6 ++-- plugins.h | 3 +- protocol.c | 36 +++++++++++++++------- settings.c | 21 +++++++------ settings.h | 5 ++-- state_machine.c | 17 +++++++---- stepper.c | 75 ++++++++++++++++++++++++++-------------------- stepper.h | 31 ++++++++++++------- system.h | 33 ++++++++++---------- 20 files changed, 203 insertions(+), 118 deletions(-) diff --git a/README.md b/README.md index 0df5071..178c209 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## grblHAL ## -Latest build date is 20250216, see the [changelog](changelog.md) for details. +Latest build date is 20250220, 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. @@ -89,4 +89,4 @@ G/M-codes not supported by [legacy Grbl](https://github.com/gnea/grbl/wiki) are Some [plugins](https://github.com/grblHAL/plugins) implements additional M-codes. --- -20250216 +20250220 diff --git a/changelog.md b/changelog.md index 97e46f2..318c527 100644 --- a/changelog.md +++ b/changelog.md @@ -1,10 +1,37 @@ ## grblHAL changelog +Build 20250220 + +Core: + +* Changed `$65` setting to flags for allowing feed rate overrides during probing and limiting probe motion to be within machine workspace. +Ref. ioSender issue [#402](https://github.com/terjeio/ioSender/issues/402). + +* Fixed regression, ref. discussion [#610](https://github.com/grblHAL/core/issues/610#issuecomment-2670224036). + +* Improved handling of stop realtime command \(`0x19`\) for faster motion halt without losing position. + +* Optimized RAM usage. + +Drivers: + +* Networking capable: updated for networking plugin changes. + +Plugins: + +* Keypad: now sends `$H` and `$X` commands directly to the system command parser bypassing status checks. Ref. issue [#12](https://github.com/grblHAL/Plugin_keypad/issues/12). + +* Networking: added network status changed event, refactored top level code to allow multiple interfaces. Added `$NETIF` command for listing interfaces with associated IP and MAC addresses. + +* WebUI: updated for networking plugin changes. + +--- + 20250216 Core: -* Fixed issue with restoring spindle status for spindles making use optional spindle status flags. Ref. PR [#673](https://github.com/grblHAL/core/pull/673); +* Fixed issue with restoring spindle status for spindles making use optional spindle status flags. Ref. PR [#680](https://github.com/grblHAL/core/pull/680). Drivers: diff --git a/config.h b/config.h index b7683a6..6d3fb7b 100644 --- a/config.h +++ b/config.h @@ -1642,7 +1642,7 @@ are used the logic of the input signals should be be inverted with the \ref axis #endif ///@} -/*! @name $65 - Setting_ProbingFeedOverride +/*! @name $65 - Setting_ProbingFlags // By default, grblHAL disables feed rate overrides for all G38.x probe cycle commands. Although this // may be different than some pro-class machine control, it's arguable that it should be this way. // Most probe sensors produce different levels of error that is dependent on rate of speed. By @@ -1653,6 +1653,9 @@ are used the logic of the input signals should be be inverted with the \ref axis #if !defined DEFAULT_ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES || defined __DOXYGEN__ #define DEFAULT_ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES Off #endif +#if !defined DEFAULT_SOFT_LIMIT_PROBE_CYCLES || defined __DOXYGEN__ +#define DEFAULT_SOFT_LIMIT_PROBE_CYCLES Off +#endif ///@} // Safety door/parking settings (Group_SafetyDoor) diff --git a/core_handlers.h b/core_handlers.h index e13bf18..62620c4 100644 --- a/core_handlers.h +++ b/core_handlers.h @@ -81,7 +81,7 @@ typedef bool (*protocol_enqueue_realtime_command_ptr)(char c); typedef bool (*travel_limits_ptr)(float *target, axes_signals_t axes, bool is_cartesian); typedef bool (*arc_limits_ptr)(coord_data_t *target, coord_data_t *position, point_2d_t center, float radius, plane_t plane, int32_t turns); -typedef void (*jog_limits_ptr)(float *target, float *position); +typedef void (*apply_travel_limits_ptr)(float *target, float *position); typedef bool (*home_machine_ptr)(axes_signals_t cycle, axes_signals_t auto_square); typedef void (*on_parser_init_ptr)(parser_state_t *gc_state); @@ -263,7 +263,7 @@ typedef struct { home_machine_ptr home_machine; travel_limits_ptr check_travel_limits; arc_limits_ptr check_arc_travel_limits; - jog_limits_ptr apply_jog_limits; + apply_travel_limits_ptr apply_travel_limits; enqueue_gcode_ptr enqueue_gcode; enqueue_realtime_command_ptr enqueue_realtime_command; on_macro_execute_ptr on_macro_execute; diff --git a/gcode.h b/gcode.h index 1c229f8..021a593 100644 --- a/gcode.h +++ b/gcode.h @@ -516,8 +516,8 @@ typedef union { typedef struct { spindle_state_t state; //!< {M3,M4,M5} spindle_rpm_mode_t rpm_mode; //!< {G96,G97} - spindle_css_data_t *css; //!< Data used for Constant Surface Speed Mode calculations spindle_cond_t condition; //!< TODO: move data from planner_cond_t here + spindle_css_data_t *css; //!< Data used for Constant Surface Speed Mode calculations float rpm; //!< Spindle speed. Must be second last! spindle_ptrs_t *hal; //!< Spindle function pointers etc. Must be last! } spindle_t; diff --git a/grbl.h b/grbl.h index 90fca64..767f2fa 100644 --- a/grbl.h +++ b/grbl.h @@ -42,7 +42,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20250213 +#define GRBL_BUILD 20250220 #define GRBL_URL "https://github.com/grblHAL" diff --git a/ioports.c b/ioports.c index b13ca7b..cf64fce 100644 --- a/ioports.c +++ b/ioports.c @@ -32,9 +32,11 @@ #include "hal.h" #include "settings.h" +#define MAX_PORTS 16 + typedef struct { io_ports_detail_t *ports; - char port_names[50]; + char port_names[8 * 6 + (MAX_PORTS - 8) * 7]; ioport_bus_t enabled; } io_ports_private_t; @@ -137,7 +139,7 @@ static bool match_port (xbar_t *properties, uint8_t port, void *data) */ uint8_t ioport_find_free (io_port_type_t type, io_port_direction_t dir, pin_cap_t filter, const char *description) { - ff_data.port = 0xFF; + ff_data.port = IOPORT_UNASSIGNED; ff_data.description = (description && *description) ? description : NULL; // TODO: pass modified filter with .claimable off when looking for description match? @@ -382,7 +384,7 @@ bool ioports_add (io_ports_data_t *ports, io_port_type_t type, uint8_t n_in, uin ports->in.map[i] = i; if(hal.port.set_pin_description) hal.port.set_pin_description(type, Port_Input, i, get_pnum(ports, i)); - if(i < 8) { + if(i < MAX_PORTS) { cfg->inx.mask = (cfg->inx.mask << 1) + 1; strcat(cfg->in.port_names, i == 0 ? "Aux " : ",Aux "); strcat(cfg->in.port_names, uitoa(i)); @@ -394,7 +396,7 @@ bool ioports_add (io_ports_data_t *ports, io_port_type_t type, uint8_t n_in, uin ports->out.map[i] = i; if(hal.port.set_pin_description) hal.port.set_pin_description(type, Port_Output, i, get_pnum(ports, i)); - if(i < 8) { + if(i < MAX_PORTS) { cfg->outx.mask = (cfg->outx.mask << 1) + 1; strcat(cfg->out.port_names, i == 0 ? "Aux " : ",Aux "); strcat(cfg->out.port_names, uitoa(i)); diff --git a/ioports.h b/ioports.h index bcb56aa..1c3a661 100644 --- a/ioports.h +++ b/ioports.h @@ -21,6 +21,8 @@ #pragma once +#define IOPORT_UNASSIGNED 255 + typedef enum { Port_Analog = 0, //!< 0 Port_Digital = 1 //!< 1 diff --git a/kinematics/delta.c b/kinematics/delta.c index a59ebbb..4df1f72 100644 --- a/kinematics/delta.c +++ b/kinematics/delta.c @@ -94,7 +94,7 @@ static on_homing_completed_ptr on_homing_completed; static on_set_axis_setting_unit_ptr on_set_axis_setting_unit; static on_setting_get_description_ptr on_setting_get_description; static on_realtime_report_ptr on_realtime_report; -static jog_limits_ptr apply_jog_limits; +static apply_travel_limits_ptr apply_travel_limits; #if N_AXIS > 3 static travel_limits_ptr check_travel_limits; #endif @@ -616,15 +616,15 @@ static bool delta_check_travel_limits (float *target, axes_signals_t axes, bool return !failed; } -static void delta_apply_jog_limits (float *target, float *position) +static void delta_apply_travel_limits (float *target, float *position) { if(sys.homed.mask == 0) return; if(machine.cfg.flags.limit_to_cuboid) - apply_jog_limits(target, position); + apply_travel_limits(target, position); - else if(!is_target_inside_cuboid(target, true)) { + else if(position && !is_target_inside_cuboid(target, true)) { coord_data_t pos; @@ -828,7 +828,7 @@ static void report_options (bool newopt) on_report_options(newopt); if(!newopt) - hal.stream.write("[KINEMATICS:Delta v0.04]" ASCII_EOL); + hal.stream.write("[KINEMATICS:Delta v0.05]" ASCII_EOL); } static status_code_t delta_info (sys_state_t state, char *args) @@ -919,8 +919,8 @@ void delta_robot_init (void) grbl.on_jog_cancel = cancel_jog; - apply_jog_limits = grbl.apply_jog_limits; - grbl.apply_jog_limits = delta_apply_jog_limits; + apply_travel_limits = grbl.apply_travel_limits; + grbl.apply_travel_limits = delta_apply_travel_limits; #if N_AXIS > 3 check_travel_limits = grbl.check_travel_limits; diff --git a/machine_limits.c b/machine_limits.c index 4594bad..3ebcce7 100644 --- a/machine_limits.c +++ b/machine_limits.c @@ -814,14 +814,15 @@ static void clip_3d_target (coord_data_t *position, coord_data_t *target, work_e } // Limits jog commands to be within machine limits, homed axes only. -static void apply_jog_limits (float *target, float *position) +// If position is non-null clip XYZ motion. +static void apply_travel_limits (float *target, float *position) { if(sys.homed.mask == 0) return; uint_fast8_t idx; - if((sys.homed.mask & 0b111) == 0b111) { + if(position && (sys.homed.mask & 0b111) == 0b111) { uint_fast8_t n_axes = 0; @@ -849,6 +850,6 @@ void limits_init (void) hal.homing.get_feedrate = get_homing_rate; grbl.check_travel_limits = check_travel_limits; grbl.check_arc_travel_limits = check_arc_travel_limits; - grbl.apply_jog_limits = apply_jog_limits; + grbl.apply_travel_limits = apply_travel_limits; grbl.home_machine = homing_cycle; } diff --git a/motion_control.c b/motion_control.c index 9f79203..afac85c 100644 --- a/motion_control.c +++ b/motion_control.c @@ -787,7 +787,7 @@ status_code_t mc_jog_execute (plan_line_data_t *pl_data, parser_block_t *gc_bloc pl_data->line_number = gc_block->values.n; if(settings.limits.flags.jog_soft_limited) - grbl.apply_jog_limits(gc_block->values.xyz, position); + grbl.apply_travel_limits(gc_block->values.xyz, position); else if(sys.soft_limits.mask && !grbl.check_travel_limits(gc_block->values.xyz, sys.soft_limits, true)) return Status_TravelExceeded; @@ -978,6 +978,9 @@ gc_probe_t mc_probe_cycle (float *target, plan_line_data_t *pl_data, gc_parser_f if (state_get() == STATE_CHECK_MODE) return GCProbe_CheckMode; + if(settings.probe.soft_limited) + grbl.apply_travel_limits(target, NULL); + do { idx--; sys.probe_position[idx] = lroundf(target[idx] * settings.axis[idx].steps_per_mm); @@ -1116,7 +1119,7 @@ void mc_override_ctrl_update (gc_override_flags_t override_state) ISR_CODE void ISR_FUNC(mc_reset)(void) { // Only this function can set the system reset. Helps prevent multiple kill calls. - if (bit_isfalse(sys.rt_exec_state, EXEC_RESET)) { + if(bit_isfalse(sys.rt_exec_state, EXEC_RESET)) { system_set_exec_state_flag(EXEC_RESET); @@ -1127,13 +1130,11 @@ ISR_CODE void ISR_FUNC(mc_reset)(void) // NOTE: If steppers are kept enabled via the step idle delay setting, this also keeps // the steppers enabled by avoiding the go_idle call altogether, unless the motion state is // violated, by which, all bets are off. - if ((state_get() & (STATE_CYCLE|STATE_HOMING|STATE_JOG)) || sys.step_control.execute_hold || sys.step_control.execute_sys_motion) { + if((sys.position_lost = (state_get() & (STATE_CYCLE|STATE_HOMING|STATE_JOG)) || sys.step_control.execute_hold || sys.step_control.execute_sys_motion)) { - sys.position_lost = true; - - if (state_get() != STATE_HOMING) + if(state_get() != STATE_HOMING) system_set_exec_alarm(Alarm_AbortCycle); - else if (!sys.rt_exec_alarm) + else if(!sys.rt_exec_alarm) system_set_exec_alarm(Alarm_HomingFailReset); st_go_idle(); // Force kill steppers. Position has likely been lost. diff --git a/planner.c b/planner.c index 110f299..663cf1a 100644 --- a/planner.c +++ b/planner.c @@ -136,7 +136,7 @@ static void planner_recalculate (void) if (block == block_buffer_planned) { // Only two plannable blocks in buffer. Reverse pass complete. // Check if the first block is the tail. If so, notify stepper to update its current parameters. if (block == block_buffer_tail) - st_update_plan_block_parameters(); + st_update_plan_block_parameters(false); } else while (block != block_buffer_planned) { // Three or more plan-able blocks next = current; @@ -145,7 +145,7 @@ static void planner_recalculate (void) // Check if next block is the tail block(=planned block). If so, update current stepper parameters. if (block == block_buffer_tail) - st_update_plan_block_parameters(); + st_update_plan_block_parameters(false); // Compute maximum entry speed decelerating over the current block from its exit speed. if (current->entry_speed_sqr != current->max_entry_speed_sqr) { @@ -679,7 +679,7 @@ uint_fast16_t plan_get_block_buffer_available (void) void plan_cycle_reinitialize (void) { // Re-plan from a complete stop. Reset planner entry speeds and buffer planned pointer. - st_update_plan_block_parameters(); + st_update_plan_block_parameters(false); if((block_buffer_planned = block_buffer_tail) != block_buffer_head) planner_recalculate(); } diff --git a/plugins.h b/plugins.h index a3f870f..a4ef0ff 100644 --- a/plugins.h +++ b/plugins.h @@ -7,7 +7,7 @@ Part of grblHAL - Copyright (c) 2020-2024 Terje Io + Copyright (c) 2020-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 @@ -105,6 +105,7 @@ typedef enum { } grbl_wifi_mode_t; typedef struct { + const char *interface; bool is_ethernet; bool link_up; uint16_t mbps; diff --git a/protocol.c b/protocol.c index 6c27f48..4af4ebd 100644 --- a/protocol.c +++ b/protocol.c @@ -75,7 +75,7 @@ bool protocol_enqueue_gcode (char *gcode) { bool ok = xcommand[0] == '\0' && (state_get() == STATE_IDLE || (state_get() & (STATE_ALARM|STATE_JOG|STATE_TOOL_CHANGE))) && - bit_isfalse(sys.rt_exec_state, EXEC_MOTION_CANCEL); + !((sys.rt_exec_state & (EXEC_MOTION_CANCEL|EXEC_MOTION_CANCEL_FAST))); if(ok && gc_state.file_run) ok = gc_state.modal.program_flow != ProgramFlow_Running || strncmp((char *)gcode, "$J=", 3); @@ -362,7 +362,7 @@ bool protocol_main_loop (void) // completed. In either case, auto-cycle start, if enabled, any queued moves. protocol_auto_cycle_start(); - if(sys.abort || !protocol_execute_realtime()) // Runtime command check point. + if(!protocol_execute_realtime() && sys.abort) // Runtime command check point. return !sys.flags.exit; // Bail to main() program loop to reset system. sys.cancel = false; @@ -399,7 +399,7 @@ bool protocol_buffer_synchronize (void) // execute calls a buffer sync, or the planner buffer is full and ready to go. void protocol_auto_cycle_start (void) { - if (plan_get_current_block() != NULL) // Check if there are any blocks in the buffer. + if(!ABORTED && plan_get_current_block()) // Check if there are any blocks in the buffer. system_set_exec_state_flag(EXEC_CYCLE_START); // If so, execute them! } @@ -562,6 +562,8 @@ bool protocol_exec_rt_system (void) if(rt_exec & EXEC_STOP) { // Experimental for now, must be verified. Do NOT move to interrupt context! + // Note: homing cannot be cancelled with EXEC_STOP + sys.cancel = true; sys.step_control.flags = 0; sys.flags.feed_hold_pending = Off; @@ -579,14 +581,27 @@ bool protocol_exec_rt_system (void) sys.flags.keep_input = Off; + if(sys.alarm_pending != Alarm_None) { + + sys.position_lost = st_is_stepping(); + system_raise_alarm(sys.alarm_pending); + sys.alarm_pending = Alarm_None; + + } else if(st_is_stepping()) { + + state_update(EXEC_MOTION_CANCEL_FAST); + + do { + st_prep_buffer(); // Check and prep segment buffer. + grbl.on_execute_realtime(state_get()); + } while(st_is_stepping()); + + rt_exec |= system_clear_exec_states(); + } + gc_init(true); plan_reset(); - if(sys.alarm_pending == Alarm_ProbeProtect) { - st_go_idle(); - system_set_exec_alarm(sys.alarm_pending); - sys.alarm_pending = Alarm_None; - } else - st_reset(); + st_reset(); sync_position(); // Kill spindle and coolant. TODO: Check Mach3 behaviour? @@ -594,7 +609,8 @@ bool protocol_exec_rt_system (void) gc_coolant((coolant_state_t){0}); flush_override_buffers(); - if(!((state_get() == STATE_ALARM) && (sys.alarm == Alarm_LimitsEngaged || sys.alarm == Alarm_HomingRequired))) { + + if(!((state_get() == STATE_ALARM) && (sys.alarm == Alarm_LimitsEngaged || sys.alarm == Alarm_HomingRequired || sys.alarm == Alarm_ProbeProtect))) { state_set(hal.control.get_state().safety_door_ajar ? STATE_SAFETY_DOOR : STATE_IDLE); grbl.report.feedback_message(Message_Stop); } diff --git a/settings.c b/settings.c index 5e4f006..7152e71 100644 --- a/settings.c +++ b/settings.c @@ -88,6 +88,7 @@ PROGMEM const settings_t defaults = { .probe.disable_probe_pullup = DEFAULT_PROBE_SIGNAL_DISABLE_PULLUP, .probe.allow_feed_override = DEFAULT_ALLOW_FEED_OVERRIDE_DURING_PROBE_CYCLES, + .probe.soft_limited = DEFAULT_SOFT_LIMIT_PROBE_CYCLES, .probe.invert_probe_pin = DEFAULT_PROBE_SIGNAL_INVERT, .probe.invert_toolsetter_input = DEFAULT_TOOLSETTER_SIGNAL_INVERT, .probe.disable_toolsetter_pullup = DEFAULT_TOOLSETTER_SIGNAL_DISABLE_PULLUP, @@ -996,9 +997,11 @@ static status_code_t set_force_initialization_alarm (setting_id_t id, uint_fast1 return Status_OK; } -static status_code_t set_probe_allow_feed_override (setting_id_t id, uint_fast16_t int_value) +static status_code_t set_probe_flags (setting_id_t id, uint_fast16_t int_value) { - settings.probe.allow_feed_override = int_value != 0; + 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)); return Status_OK; } @@ -1513,15 +1516,15 @@ static uint32_t get_int (setting_id_t id) break; case Setting_HoldActions: - value = (settings.flags.disable_laser_during_hold ? bit(0) : 0) | (settings.flags.restore_after_feed_hold ? bit(1) : 0); + value = settings.flags.disable_laser_during_hold | (settings.flags.restore_after_feed_hold << 1); break; case Setting_ForceInitAlarm: value = settings.flags.force_initialization_alarm; break; - case Setting_ProbingFeedOverride: - value = settings.probe.allow_feed_override; + case Setting_ProbingFlags: + value = settings.probe.allow_feed_override | (settings.probe.soft_limited << 1) | (settings.probe.enable_protection << 2); break; case Setting_ToolChangeMode: @@ -1770,7 +1773,7 @@ static bool is_setting_available (const setting_detail_t *setting, uint_fast16_t available = hal.stepper.get_ganged && hal.stepper.get_ganged(false).mask != 0; break; - case Setting_ProbingFeedOverride: + case Setting_ProbingFlags: // case Setting_ToolChangeProbingDistance: // case Setting_ToolChangeFeedRate: // case Setting_ToolChangeSeekRate: @@ -2063,7 +2066,7 @@ PROGMEM static const setting_detail_t setting_detail[] = { { Setting_SleepEnable, Group_General, "Sleep enable", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_sleep_enable, get_int, is_setting_available }, { Setting_HoldActions, Group_General, "Feed hold actions", NULL, Format_Bitfield, "Disable laser during hold,Restore spindle and coolant state on resume", NULL, NULL, Setting_IsExtendedFn, set_hold_actions, get_int, NULL }, { Setting_ForceInitAlarm, Group_General, "Force init alarm", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_force_initialization_alarm, get_int, NULL }, - { Setting_ProbingFeedOverride, Group_Probing, "Probing feed override", NULL, Format_Bool, NULL, NULL, NULL, Setting_IsExtendedFn, set_probe_allow_feed_override, get_int, is_setting_available }, + { Setting_ProbingFlags, Group_Probing, "Probing flags", NULL, Format_Bitfield, "Allow feed override,Apply soft limits", NULL, NULL, Setting_IsExtendedFn, set_probe_flags, get_int, is_setting_available }, #if ENABLE_SPINDLE_LINEARIZATION { Setting_LinearSpindlePiece1, Group_Spindle, "Spindle linearisation, 1st point", NULL, Format_String, "x(39)", NULL, "39", Setting_IsExtendedFn, set_linear_piece, get_linear_piece, NULL }, #if SPINDLE_NPWM_PIECES > 1 @@ -2211,7 +2214,7 @@ PROGMEM static const setting_descr_t setting_descr[] = { { Setting_SteppersEnergize, "Specifies which steppers not to disable when stopped." }, { Setting_SpindlePPR, "Spindle encoder pulses per revolution." }, { Setting_EnableLegacyRTCommands, "Enables \"normal\" processing of ?, ! and ~ characters when part of $-setting or comment. If disabled then they are added to the input string instead." }, - { Setting_JogSoftLimited, "Limit jog commands to machine limits for homed axes." }, + { Setting_JogSoftLimited, "Limit jog commands to machine workspace for homed axes." }, { Setting_ParkingEnable, "Enables parking cycle, requires parking axis homed." }, { Setting_ParkingAxis, "Define which axis that performs the parking motion." }, { Setting_HomingLocateCycles, "Number of homing passes. Minimum 1, maximum 128." }, @@ -2244,7 +2247,7 @@ PROGMEM static const setting_descr_t setting_descr[] = { { Setting_SleepEnable, "Enable sleep mode." }, { Setting_HoldActions, "Actions taken during feed hold and on resume from feed hold." }, { Setting_ForceInitAlarm, "Start in alarm mode after a cold reset." }, - { Setting_ProbingFeedOverride, "Allow feed override during probing." }, + { Setting_ProbingFlags, "Allow feed override during probing and/or limit probing commands to machine workspace for homed axes." }, #if ENABLE_SPINDLE_LINEARIZATION { Setting_LinearSpindlePiece1, "Comma separated list of values: RPM_MIN, RPM_LINE_A1, RPM_LINE_B1, set to blank to disable." }, #if SPINDLE_NPWM_PIECES > 1 diff --git a/settings.h b/settings.h index e65e1ad..552f55c 100644 --- a/settings.h +++ b/settings.h @@ -113,7 +113,7 @@ typedef enum { Setting_SleepEnable = 62, Setting_HoldActions = 63, Setting_ForceInitAlarm = 64, - Setting_ProbingFeedOverride = 65, + Setting_ProbingFlags = 65, // Optional driver implemented settings for piecewise linear spindle PWM algorithm Setting_LinearSpindlePiece1 = 66, Setting_LinearSpindlePiece2 = 67, @@ -602,7 +602,8 @@ typedef union { enable_protection :1, invert_toolsetter_input :1, disable_toolsetter_pullup :1, - unassigned :8; + soft_limited :1, + unassigned :7; }; } probeflags_t; diff --git a/state_machine.c b/state_machine.c index f160cf7..02fc7e2 100644 --- a/state_machine.c +++ b/state_machine.c @@ -163,8 +163,8 @@ static bool initiate_hold (uint_fast16_t new_state) enqueue_spindle_override(CMD_OVERRIDE_SPINDLE_STOP); if (sys_state & (STATE_CYCLE|STATE_JOG)) { - st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration. - sys.step_control.execute_hold = On; // Initiate suspend state with active flag. + st_update_plan_block_parameters(false); // Notify stepper module to recompute for hold deceleration. + sys.step_control.execute_hold = On; // Initiate suspend state with active flag. stateHandler = state_await_hold; } @@ -424,8 +424,8 @@ static void state_cycle (uint_fast16_t rt_exec) if (rt_exec & EXEC_CYCLE_COMPLETE) state_set(gc_state.tool_change ? STATE_TOOL_CHANGE : STATE_IDLE); - if (rt_exec & EXEC_MOTION_CANCEL) { - st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration. + if (rt_exec & (EXEC_MOTION_CANCEL|EXEC_MOTION_CANCEL_FAST)) { + st_update_plan_block_parameters(!!(rt_exec & EXEC_MOTION_CANCEL_FAST)); // Notify stepper module to recompute for hold deceleration. sys.suspend = true; sys.step_control.execute_hold = On; // Initiate suspend state with active flag. stateHandler = state_await_motion_cancel; @@ -470,8 +470,13 @@ static void state_await_motion_cancel (uint_fast16_t rt_exec) sync_position(); sys.suspend = false; } + state_set(pending_state); - if (gc_state.tool_change) + + if(sys.alarm_pending) { + system_set_exec_alarm(sys.alarm_pending); + sys.alarm_pending = Alarm_None; + } else if(gc_state.tool_change) state_set(STATE_TOOL_CHANGE); } } @@ -719,7 +724,7 @@ static void restart_retract (void) sys.parking_state = Parking_Retracting; if (sys.step_control.execute_sys_motion) { - st_update_plan_block_parameters(); // Notify stepper module to recompute for hold deceleration. + st_update_plan_block_parameters(false); // Notify stepper module to recompute for hold deceleration. sys.step_control.execute_hold = On; sys.step_control.execute_sys_motion = On; } else // else NO_MOTION is active. diff --git a/stepper.c b/stepper.c index 91c6b6e..4293a19 100644 --- a/stepper.c +++ b/stepper.c @@ -39,13 +39,6 @@ #define DT_SEGMENT (1.0f / (ACCELERATION_TICKS_PER_SECOND * 60.0f)) // min/segment #define REQ_MM_INCREMENT_SCALAR 1.25f -typedef enum { - Ramp_Accel, - Ramp_Cruise, - Ramp_Decel, - Ramp_DecelOverride -} ramp_type_t; - typedef union { uint8_t flags; struct { @@ -57,6 +50,8 @@ typedef union { }; } prep_flags_t; +static bool stepping = false; + // Holds the planner block Bresenham algorithm execution data for the segments in the segment // buffer. Normally, this buffer is partially in-use, but, for the worst case scenario, it will // never exceed the number of accessible stepper buffer segments (SEGMENT_BUFFER_SIZE-1). @@ -85,15 +80,14 @@ static amass_t amass; #endif // Used for blocking new segments being added to the seqment buffer until deceleration starts -// after probe signal has been asserted. -static volatile bool probe_asserted = false; +// when fast stop is called for. TODO: it is likely that this flag can be removed - more testing required. +static volatile bool exec_fast_hold = false; // Stepper timer ticks per minute static float cycles_per_min; // Step segment ring buffer pointers -static volatile segment_t *segment_buffer_tail; -static segment_t *segment_buffer_head, *segment_next_head; +static volatile segment_t *segment_buffer_tail, *segment_buffer_head; #if ENABLE_JERK_ACCELERATION // Static storage for acceleration value of last computed segment. @@ -200,6 +194,7 @@ void st_wake_up (void) // Initialize stepper data to ensure first ISR call does not step and // cancel any pending steppers deenergize //st.exec_block = NULL; + stepping = true; sys.steppers_deenergize = false; hal.stepper.wake_up(); } @@ -211,6 +206,7 @@ ISR_CODE void ISR_FUNC(st_go_idle)(void) sys_state_t state = state_get(); + stepping = false; hal.stepper.go_idle(false); // Set stepper driver idle state, disabled or enabled, depending on settings and circumstances. @@ -227,6 +223,10 @@ ISR_CODE void ISR_FUNC(st_go_idle)(void) hal.stepper.enable(settings.steppers.idle_lock_time == 255 ? (axes_signals_t){AXES_BITMASK} : settings.steppers.energize, true); } +bool st_is_stepping (void) +{ + return stepping && st.exec_block; +} /* "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of grblHAL. grblHAL employs the venerable Bresenham line algorithm to manage and exactly synchronize multi-axis moves. @@ -411,19 +411,12 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void) // Monitors probe pin state and records the system position when detected. // NOTE: This function must be extremely efficient as to not bog down the stepper ISR. if (sys.probing_state == Probing_Active && hal.probe.get_state().triggered) { - sys.probing_state = Probing_Off; memcpy(sys.probe_position, sys.position, sizeof(sys.position)); - bit_true(sys.rt_exec_state, EXEC_MOTION_CANCEL); - #ifdef MINIMIZE_PROBE_OVERSHOOT - // "Flush" segment buffer if full in order to start deceleration early. - if((probe_asserted = segment_buffer_head->next == segment_buffer_tail)) { - segment_buffer_head = segment_buffer_tail->next; - if(st.step_count < 3 || st.step_count < (st.exec_segment->n_step >> 3)) - segment_buffer_head = segment_buffer_head->next; - segment_next_head = segment_next_head->next; - } + bit_true(sys.rt_exec_state, EXEC_MOTION_CANCEL_FAST); +#else + bit_true(sys.rt_exec_state, EXEC_MOTION_CANCEL); #endif } @@ -567,7 +560,6 @@ void st_reset (void) // Initialize stepper algorithm variables. pl_block = NULL; // Planner block pointer used by segment buffer segment_buffer_tail = segment_buffer_head = &segment_buffer[0]; // empty = tail - segment_next_head = segment_buffer_head->next; memset(&prep, 0, sizeof(st_prep_t)); memset(&st, 0, sizeof(stepper_t)); @@ -592,9 +584,28 @@ void st_rpm_changed (float rpm) } // Called by planner_recalculate() when the executing block is updated by the new plan. -void st_update_plan_block_parameters (void) +void st_update_plan_block_parameters (bool fast_hold) { - if (pl_block != NULL) { // Ignore if at start of a new block. + if(fast_hold) { // NOTE: experimental code! + + hal.irq_disable(); + + segment_t *head = (segment_t *)segment_buffer_head; + + if((exec_fast_hold = segment_buffer_head->next == segment_buffer_tail)) { + segment_buffer_head = segment_buffer_tail->next; + if(st.step_count < 3 || st.step_count < (st.exec_segment->n_step >> 3)) + segment_buffer_head = segment_buffer_head->next; + while(segment_buffer_head->next != head && segment_buffer_head->ramp_type == Ramp_Decel) + segment_buffer_head = segment_buffer_head->next; + prep.current_speed = segment_buffer_head->current_rate; + segment_buffer_head = segment_buffer_head->next; + } + + hal.irq_enable(); + } + + if(pl_block) { // Ignore if at start of a new block. prep.recalculate.velocity_profile = On; pl_block->entry_speed_sqr = prep.current_speed * prep.current_speed; // Update entry speed. pl_block = NULL; // Flag st_prep_segment() to load and check active velocity profile. @@ -657,7 +668,7 @@ void st_prep_buffer (void) if (sys.step_control.end_motion) return; - while (segment_buffer_tail != segment_next_head) { // Check if we need to fill the buffer. + while (segment_buffer_head->next != segment_buffer_tail) { // Check if we need to fill the buffer. // Determine if we need to load a new planner block or if the block needs to be recomputed. if (pl_block == NULL) { @@ -838,15 +849,15 @@ void st_prep_buffer (void) if(state_get() != STATE_HOMING) sys.step_control.update_spindle_rpm |= pl_block->spindle.hal->cap.laser; // Force update whenever updating block in laser mode. - probe_asserted = false; + exec_fast_hold = false; } - // Block adding new segments after probe is asserted until deceleration is started. - if(probe_asserted) + // Block adding new segments until deceleration is started. + if(exec_fast_hold) return; // Initialize new segment - segment_t *prep_segment = segment_buffer_head; + segment_t *prep_segment = (segment_t *)segment_buffer_head; // Set new segment to point to the current segment data block. prep_segment->exec_block = st_prep_block; @@ -1096,10 +1107,10 @@ void st_prep_buffer (void) prep_segment->cycles_per_tick = cycles; prep_segment->current_rate = prep.current_speed; + prep_segment->ramp_type = prep.ramp_type; - // Segment complete! Increment segment pointers, so stepper ISR can immediately execute it. - segment_buffer_head = segment_next_head; - segment_next_head = segment_next_head->next; + // Segment complete! Increment segment pointer, so stepper ISR can immediately execute it. + segment_buffer_head = segment_buffer_head->next; // Update the appropriate planner and segment data. pl_block->millimeters = mm_remaining; diff --git a/stepper.h b/stepper.h index 946524c..a6d7533 100644 --- a/stepper.h +++ b/stepper.h @@ -3,22 +3,22 @@ Part of grblHAL - Copyright (c) 2019-2023 Terje Io + Copyright (c) 2019-2025 Terje Io Copyright (c) 2011-2016 Sungeun K. Jeon for Gnea Research LLC Copyright (c) 2009-2011 Simen Svale Skogsrud - Grbl is free software: you can redistribute it and/or modify + 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. - Grbl is distributed in the hope that it will be useful, + 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 + 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 Grbl. If not, see . + along with grblHAL. If not, see . */ #include "planner.h" @@ -32,6 +32,13 @@ typedef enum { SquaringMode_B, //!< 2 } squaring_mode_t; +typedef enum { + Ramp_Accel, + Ramp_Cruise, + Ramp_Decel, + Ramp_DecelOverride +} ramp_type_t; + /*! \brief Holds the planner block Bresenham algorithm execution data for the segments in the segment buffer. __NOTE:__ This data is copied from the prepped planner blocks so that the planner blocks may be @@ -42,13 +49,13 @@ typedef struct st_block { struct st_block *next; //!< Pointer to next element in cirular list of blocks uint32_t steps[N_AXIS]; uint32_t step_event_count; - axes_signals_t direction_bits; - gc_override_flags_t overrides; //!< Block bitfield variable for overrides float steps_per_mm; float millimeters; float programmed_rate; char *message; //!< Message to be displayed when block is executed output_command_t *output_commands; //!< Output commands (linked list) to be performed when block is executed + axes_signals_t direction_bits; + gc_override_flags_t overrides; //!< Block bitfield variable for overrides bool backlash_motion; bool dynamic_rpm; //!< Tracks motions that require dynamic RPM adjustment offset_id_t offset_id; @@ -65,6 +72,7 @@ typedef struct st_segment { uint_fast16_t n_step; //!< Number of step events to be executed for this segment uint_fast16_t spindle_pwm; //!< Spindle PWM to be set at the start of segment execution float spindle_rpm; //!< Spindle RPM to be set at the start of the segment execution + ramp_type_t ramp_type; //!< Segment ramp type bool spindle_sync; //!< True if block is spindle synchronized bool cruising; //!< True when in cruising part of profile, only set for spindle synced moves uint_fast8_t amass_level; //!< Indicates AMASS level for the ISR to execute this segment @@ -115,14 +123,17 @@ void st_wake_up (void); // Immediately disables steppers void st_go_idle (void); +// Returns true if motion is ongoing +bool st_is_stepping (void); + // Reset the stepper subsystem variables void st_reset (void); // Called by spindle_set_state() to inform about RPM changes. -void st_rpm_changed(float rpm); +void st_rpm_changed (float rpm); // Changes the run state of the step segment buffer to execute the special parking motion. -void st_parking_setup_buffer(); +void st_parking_setup_buffer (void); // Restores the step segment buffer to the normal run state after a parking motion. void st_parking_restore_buffer (void); @@ -131,7 +142,7 @@ void st_parking_restore_buffer (void); void st_prep_buffer (void); // Called by planner_recalculate() when the executing block is updated by the new plan. -void st_update_plan_block_parameters (void); +void st_update_plan_block_parameters (bool fast_hold); // Called by realtime status reporting if realtime rate reporting is enabled in config.h. float st_get_realtime_rate (void); diff --git a/system.h b/system.h index 8e780f9..b071b73 100644 --- a/system.h +++ b/system.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2017-2024 Terje Io + Copyright (c) 2017-2025 Terje Io Copyright (c) 2014-2016 Sungeun K. Jeon for Gnea Research LLC grblHAL is free software: you can redistribute it and/or modify @@ -42,21 +42,22 @@ flags are always false, so the realtime protocol only needs to check for a non-z know when there is a realtime command to execute. */ ///@{ -#define EXEC_STATUS_REPORT bit(0) -#define EXEC_CYCLE_START bit(1) -#define EXEC_CYCLE_COMPLETE bit(2) -#define EXEC_FEED_HOLD bit(3) -#define EXEC_STOP bit(4) -#define EXEC_RESET bit(5) -#define EXEC_SAFETY_DOOR bit(6) -#define EXEC_MOTION_CANCEL bit(7) -#define EXEC_SLEEP bit(8) -#define EXEC_TOOL_CHANGE bit(9) -#define EXEC_PID_REPORT bit(10) -#define EXEC_GCODE_REPORT bit(11) -#define EXEC_TLO_REPORT bit(12) -#define EXEC_RT_COMMAND bit(13) -#define EXEC_DOOR_CLOSED bit(14) +#define EXEC_STATUS_REPORT bit(0) +#define EXEC_CYCLE_START bit(1) +#define EXEC_CYCLE_COMPLETE bit(2) +#define EXEC_FEED_HOLD bit(3) +#define EXEC_STOP bit(4) +#define EXEC_RESET bit(5) +#define EXEC_SAFETY_DOOR bit(6) +#define EXEC_MOTION_CANCEL bit(7) +#define EXEC_MOTION_CANCEL_FAST bit(7) +#define EXEC_SLEEP bit(8) +#define EXEC_TOOL_CHANGE bit(9) +#define EXEC_PID_REPORT bit(10) +#define EXEC_GCODE_REPORT bit(11) +#define EXEC_TLO_REPORT bit(12) +#define EXEC_RT_COMMAND bit(13) +#define EXEC_DOOR_CLOSED bit(14) ///@} //! \def sys_state