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