From b7376877439df1ff8b16d6259438ce6b8b84dca0 Mon Sep 17 00:00:00 2001 From: Terje Io Date: Fri, 28 Mar 2025 18:02:56 +0100 Subject: [PATCH] Reduced default step pulse length to 5 microseconds. Added HAL parameter for minimum step pulse length set by driver, used for validation of $0 setting. Changed HAL API signature for outputting step pulses, optimized to allow drivers to only change direction outputs when there is an actual direction change. Improved handling of overrides at end of program when all motion is buffered. Possible fix for issue #714. Some optimizations to allow higher step rates. --- README.md | 4 +- changelog.md | 29 ++++++++ config.h | 6 +- crossbar.c | 2 +- driver_opts2.h | 4 +- gcode.c | 9 +-- grbl.h | 4 +- grbllib.c | 3 +- hal.h | 1 + nuts_bolts.h | 24 +++++++ planner.c | 20 +++--- planner.h | 30 +++++++- protocol.c | 15 ++-- report.c | 8 ++- settings.c | 104 ++++++++++++++++++++++------ state_machine.c | 2 + stepper.c | 179 ++++++++++++++++++++++++------------------------ stepper.h | 34 +++------ system.c | 2 +- system.h | 27 +++++++- 20 files changed, 332 insertions(+), 175 deletions(-) diff --git a/README.md b/README.md index a5bf374..907526d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## grblHAL ## -Latest build date is 20250320, see the [changelog](changelog.md) for details. +Latest build date is 20250328, 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. --- -20250320 +20250328 diff --git a/changelog.md b/changelog.md index c1de3cb..0cf6dd9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,34 @@ ## grblHAL changelog +Build 20250328 + +Core: + +* Reduced default step pulse length to 5 microseconds. Added HAL parameter for minimum step pulse length set by driver, used for validation of $0 setting. + +* Changed HAL API signature for outputting step pulses, optimized to allow drivers to only change direction outputs when there is an actual direction change. + +* Improved handling of overrides at end of program when all motion is buffered. Possible fix for issue [#714](https://github.com/grblHAL/core/discussions/714). + +* Some optimizations to allow higher step rates. + +Drivers: + +* All: updated for core HAL signature change. + +* Most: added hardcoded (compile time overridable) minimum step pulse off time, defaults to 2 microseconds. This will limit max. possible step rate with a given step pulse length \(from $0 setting\). + +* iMRXT1062, RP2040: reduced minimum step pulse length to 1 microsecond. + +* STM32F1xx, STM32F3xx: increased minimum step pulse length to 3.5 microseconds. + +* STM32F7xx: reduced minimum step pulse length to 1.5 microsecond, moved critical code run in IRQ context to ITC RAM. + +* STM32F1xx, STM32F3xx, STM32F4xx and STM32F7xx: changed to use single timer for step generation, eliminates \(reduces?\) risk for lost steps at very high step rates and reduces jitter. +Added new compile time tuning parameters for interrupt latency used for step pulse timings, board developers may want to check and possibly override these in their board maps. + +--- + Build 20250320 Core: diff --git a/config.h b/config.h index cc8f113..6691089 100644 --- a/config.h +++ b/config.h @@ -530,7 +530,7 @@ Set to \ref On or 1 to enable experimental support for expressions. Some LinuxCNC extensions are supported, conditionals and subroutines are not. */ #if !defined NGC_EXPRESSIONS_ENABLE || defined __DOXYGEN__ -#define NGC_EXPRESSIONS_ENABLE Off +#define NGC_EXPRESSIONS_ENABLE On #endif /*! \def NGC_PARAMETERS_ENABLE @@ -1818,7 +1818,7 @@ machine limits this setting, when enabled, keeps them with the limits.
__NOTE:__ The different MCUs supported have different interrupt latencies and some drivers may enable features that are not available on others. This may lead to this setting not beeing respected exactly over the supported range. -Typically drivers are calibrated to be correct for 10 microsecond pulse lengths, +Typically drivers are calibrated to be correct for 5 microsecond pulse lengths, however if a precise pulse length is required it should be measured and adjusted either by changing this value or by changing the `STEP_PULSE_LATENCY` symbol value that many drivers supports. Note that `STEP_PULSE_LATENCY` symbol is driver @@ -1826,7 +1826,7 @@ specific - it is _not_ defined in the core. */ ///@{ #if !defined DEFAULT_STEP_PULSE_MICROSECONDS || defined __DOXYGEN__ -#define DEFAULT_STEP_PULSE_MICROSECONDS 10.0f +#define DEFAULT_STEP_PULSE_MICROSECONDS 5.0f #endif ///@} diff --git a/crossbar.c b/crossbar.c index 5f9862e..978b419 100644 --- a/crossbar.c +++ b/crossbar.c @@ -117,7 +117,7 @@ void xbar_set_homing_source (void) } // Returns limit signals used by homing when home signals are not available. -limit_signals_t xbar_get_homing_source (void) +ISR_CODE limit_signals_t xbar_get_homing_source (void) { return home_source; } diff --git a/driver_opts2.h b/driver_opts2.h index dbb4d27..1d6076a 100644 --- a/driver_opts2.h +++ b/driver_opts2.h @@ -49,9 +49,9 @@ #error "MPG_MODE_PIN must be defined!" #endif -#if KEYPAD_ENABLE == 1 && !defined(I2C_STROBE_PORT) +#if KEYPAD_ENABLE == 1 && !defined(I2C_STROBE_PIN) #error Keypad plugin not supported! -#elif I2C_STROBE_ENABLE && !defined(I2C_STROBE_PORT) +#elif I2C_STROBE_ENABLE && !defined(I2C_STROBE_PIN) #error "I2C strobe not supported!" #endif diff --git a/gcode.c b/gcode.c index 169f00f..86aeb0c 100644 --- a/gcode.c +++ b/gcode.c @@ -3821,8 +3821,7 @@ status_code_t gc_execute_block (char *block) mc_line(gc_block.values.xyz, &plan_data); - protocol_buffer_synchronize(); // Wait until synchronized move is finished, - sys.override.control = overrides; // then restore previous override disable status. + mc_override_ctrl_update(overrides); // Wait until synchronized move is finished, then restore previous override disable status. } break; @@ -3838,7 +3837,7 @@ status_code_t gc_execute_block (char *block) mc_thread(&plan_data, gc_state.position, &thread, overrides.feed_hold_disable); - sys.override.control = overrides; // then restore previous override disable status. + mc_override_ctrl_update(overrides); // Wait until synchronized move is finished, then restore previous override disable status. } break; @@ -3889,9 +3888,7 @@ status_code_t gc_execute_block (char *block) // [21. Program flow ]: // M0,M1,M2,M30,M60: Perform non-running program flow actions. During a program pause, the buffer may // refill and can only be resumed by the cycle start run-time command. - gc_state.modal.program_flow = gc_block.modal.program_flow; - - if(gc_state.modal.program_flow || sys.flags.single_block) { + if((gc_state.modal.program_flow = gc_block.modal.program_flow) || sys.flags.single_block) { protocol_buffer_synchronize(); // Sync and finish all remaining buffered motions before moving on. diff --git a/grbl.h b/grbl.h index f8fe7c2..89fde8c 100644 --- a/grbl.h +++ b/grbl.h @@ -42,7 +42,7 @@ #else #define GRBL_VERSION "1.1f" #endif -#define GRBL_BUILD 20250317 +#define GRBL_BUILD 20250328 #define GRBL_URL "https://github.com/grblHAL" @@ -52,7 +52,7 @@ #ifdef GRBL_ESP32 #include "esp_attr.h" #define ISR_CODE IRAM_ATTR -#else +#elif !defined(ISR_CODE) // #define ISR_CODE __attribute__((long_call, section(".data"))) // Used to decorate code run in interrupt context. // Do not remove or change unless you know what you are doing. diff --git a/grbllib.c b/grbllib.c index 04906c6..c2ab24f 100644 --- a/grbllib.c +++ b/grbllib.c @@ -212,6 +212,7 @@ int grbl_enter (void) hal.irq_disable = dummy_handler; hal.irq_claim = dummy_irq_claim; hal.nvs.size = GRBL_NVS_SIZE; + hal.step_us_min = 2.0f; hal.coolant_cap.flood = On; hal.limits.interrupt_callback = limit_interrupt_handler; hal.control.interrupt_callback = control_interrupt_handler; @@ -536,7 +537,7 @@ ISR_CODE bool ISR_FUNC(task_add_delayed)(foreground_task_ptr fn, void *data, uin return task != NULL; } -void task_delete (foreground_task_ptr fn, void *data) +ISR_CODE void task_delete (foreground_task_ptr fn, void *data) { core_task_t *task, *prev = NULL; diff --git a/hal.h b/hal.h index 046d7ba..0448d29 100644 --- a/hal.h +++ b/hal.h @@ -562,6 +562,7 @@ typedef struct { char *driver_url; //!< Pointer to optional URL for the driver. char *board; //!< Pointer to optional board name string. char *board_url; //!< Pointer to optional URL for the board. + float step_us_min; //!< Minimum step pulse width (microseconds). uint32_t f_step_timer; //!< Frequency of main stepper timer in Hz. uint32_t f_mcu; //!< Frequency of MCU in MHz. uint32_t rx_buffer_size; //!< Input stream buffer size in bytes. diff --git a/nuts_bolts.h b/nuts_bolts.h index 22eab88..d125d59 100644 --- a/nuts_bolts.h +++ b/nuts_bolts.h @@ -138,6 +138,30 @@ typedef union { }; } axes_signals_t; +typedef union { + int32_t value[N_AXIS]; + struct { + int32_t x; + int32_t y; + int32_t z; +#ifdef A_AXIS + int32_t a; +#endif +#ifdef B_AXIS + int32_t b; +#endif +#ifdef C_AXIS + int32_t c; +#endif +#ifdef U_AXIS + int32_t u; +#endif +#ifdef V_AXIS + int32_t v; +#endif + }; +} mpos_t; + typedef union { float values[2]; struct { diff --git a/planner.c b/planner.c index 663cf1a..d229390 100644 --- a/planner.c +++ b/planner.c @@ -394,6 +394,8 @@ static inline float limit_max_rate_by_axis_maximum (float *unit_vec) to execute the special system motion. */ bool plan_buffer_line (float *target, plan_line_data_t *pl_data) { + static axes_signals_t direction = {}; + // Prepare and initialize new block. Copy relevant pl_data for block execution. plan_block_t *block = block_buffer_head; int32_t target_steps[N_AXIS], position_steps[N_AXIS], delta_steps; @@ -427,23 +429,25 @@ bool plan_buffer_line (float *target, plan_line_data_t *pl_data) target_steps[idx] = lroundf(target[idx] * settings.axis[idx].steps_per_mm); if((delta_steps = target_steps[idx] - position_steps[idx])) { - block->steps[idx] = labs(delta_steps); - block->step_event_count = max(block->step_event_count, block->steps[idx]); + block->steps.value[idx] = labs(delta_steps); + block->step_event_count = max(block->step_event_count, block->steps.value[idx]); unit_vec[idx] = (float)delta_steps / settings.axis[idx].steps_per_mm; // Store unit vector numerator + // Set direction bits. Bit enabled always means direction is negative. + if(delta_steps < 0) + direction.bits |= bit(idx); + else + direction.bits &= ~bit(idx); #if N_AXIS > 3 && ROTARY_FIX motion.mask |= bit(idx); #endif } else { - block->steps[idx] = 0; + block->steps.value[idx] = 0; unit_vec[idx] = 0.0f; // Store unit vector numerator } - - // Set direction bits. Bit enabled always means direction is negative. - if (delta_steps < 0) - block->direction_bits.mask |= bit(idx); - } while(idx); + block->direction = direction; + // Calculate RPMs to be used for Constant Surface Speed (CSS) calculations. if(block->spindle.css) { diff --git a/planner.h b/planner.h index 6b872f7..bbe9f2b 100644 --- a/planner.h +++ b/planner.h @@ -3,7 +3,7 @@ Part of grblHAL - Copyright (c) 2019-2024 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 @@ -43,14 +43,38 @@ typedef union { }; } planner_cond_t; +typedef union { + uint32_t value[N_AXIS]; + struct { + uint32_t x; + uint32_t y; + uint32_t z; +#ifdef A_AXIS + uint32_t a; +#endif +#ifdef B_AXIS + uint32_t b; +#endif +#ifdef C_AXIS + uint32_t c; +#endif +#ifdef U_AXIS + uint32_t u; +#endif +#ifdef V_AXIS + uint32_t v; +#endif + }; +} steps_t; + // This struct stores a linear movement of a g-code block motion with its critical "nominal" values // are as specified in the source g-code. typedef struct plan_block { // Fields used by the bresenham algorithm for tracing the line // NOTE: Used by stepper algorithm to execute the block correctly. Do not alter these values. - uint32_t steps[N_AXIS]; // Step count along each axis + steps_t steps; // Step count along each axis uint32_t step_event_count; // The maximum step axis count and number of steps required to complete this block. - axes_signals_t direction_bits; // The direction bit set for this block (refers to *_DIRECTION_PIN in config.h) + axes_signals_t direction; // The direction bit set for this block (refers to *_DIRECTION_PIN in config.h) offset_id_t offset_id; // Block condition data to ensure correct execution depending on states and overrides. diff --git a/protocol.c b/protocol.c index 721fe2e..0748e87 100644 --- a/protocol.c +++ b/protocol.c @@ -138,7 +138,7 @@ static bool recheck_line (char *line, line_flags_t *flags) } /* - GRBL PRIMARY LOOP: + grblHAL PRIMARY LOOP: */ bool protocol_main_loop (void) { @@ -274,8 +274,15 @@ bool protocol_main_loop (void) } } else if(*line == '[' && grbl.on_user_command) gc_state.last_error = grbl.on_user_command(line); - else if (state_get() & (STATE_ALARM|STATE_ESTOP|STATE_JOG)) // Everything else is gcode. Block if in alarm, eStop or jog mode. - gc_state.last_error = Status_SystemGClock; + else if(state_get() & (STATE_ALARM|STATE_ESTOP|STATE_JOG)) { // Everything else is gcode. Block if in alarm, eStop or jog mode. + if(*line == CMD_PROGRAM_DEMARCATION && line[1] == '\0' && (state_get() & (STATE_ALARM|STATE_ESTOP))) { + gc_state.file_run = !gc_state.file_run; + gc_state.last_error = Status_OK; + if(grbl.on_file_demarcate) + grbl.on_file_demarcate(gc_state.file_run); + } else + gc_state.last_error = Status_SystemGClock; + } #if COMPATIBILITY_LEVEL == 0 else if(gc_state.last_error == Status_OK || gc_state.last_error == Status_GcodeToolChangePending) { // Parse and execute g-code block. #else @@ -383,7 +390,7 @@ bool protocol_buffer_synchronize (void) // If system is queued, ensure cycle resumes if the auto start flag is present. protocol_auto_cycle_start(); - sys.flags.synchronizing = On; + sys.flags.synchronizing = gc_state.modal.program_flow == ProgramFlow_Running; while ((ok = protocol_execute_realtime()) && (plan_get_current_block() || state_get() == STATE_CYCLE)); sys.flags.synchronizing = Off; diff --git a/report.c b/report.c index 8875291..db60daf 100644 --- a/report.c +++ b/report.c @@ -1338,9 +1338,11 @@ void report_realtime_status (void) if (override_counter > 0 && !report.overrides) override_counter--; - else if((report.overrides = !report.wco)) { - report.spindle = report.spindle || spindle_0_state.on; - report.coolant = report.coolant || hal.coolant.get_state().value != 0; + else { + if((report.overrides = !report.wco)) { + report.spindle = report.spindle || spindle_0_state.on; + report.coolant = report.coolant || hal.coolant.get_state().value != 0; + } override_counter = state & (STATE_HOMING|STATE_CYCLE|STATE_HOLD|STATE_JOG|STATE_SAFETY_DOOR) ? (REPORT_OVERRIDE_REFRESH_BUSY_COUNT - 1) // Reset counter for slow refresh : (REPORT_OVERRIDE_REFRESH_IDLE_COUNT - 1); diff --git a/settings.c b/settings.c index 2047fef..7806596 100644 --- a/settings.c +++ b/settings.c @@ -436,6 +436,7 @@ static char ganged_axes[] = "X-Axis,Y-Axis,Z-Axis"; #endif static on_file_demarcate_ptr on_file_demarcate; +static char step_us_min[4]; static char fs_options[] = "Auto mount SD card,Hide LittleFS"; static char spindle_types[100] = ""; static char axis_dist[4] = "mm"; @@ -571,6 +572,35 @@ static status_code_t set_limits_invert_mask (setting_id_t id, uint_fast16_t int_ #endif +static status_code_t validate_pulse_width (float max_rate, float steps_per_mm, float pulse_width) +{ +/* + float f_step = (max_rate * steps_per_mm) / 60.0f; + + return !gc_state.file_run && ((hal.max_step_rate && f_step > (float)hal.max_step_rate) || (pulse_width + 2.0f) > 1000000.0 / f_step) ? Status_MaxStepRateExceeded : Status_OK; +*/ + return Status_OK; +} + +static status_code_t set_pulse_width (setting_id_t id, float value) +{ + uint_fast8_t idx = N_AXIS; + status_code_t status = Status_OK; + + do { + idx--; +#if N_AXIS > 3 + if(bit_isfalse(settings.steppers.is_rotary.mask, bit(idx))) +#endif + status = validate_pulse_width(settings.axis[idx].max_rate, settings.axis[idx].steps_per_mm, value); + } while(idx && status == Status_OK); + + if(status == Status_OK) + settings.steppers.pulse_microseconds = value; + + return status; +} + static status_code_t set_probe_invert (setting_id_t id, uint_fast16_t int_value) { if(!hal.probe.configure) @@ -1223,9 +1253,7 @@ static status_code_t set_axis_setting (setting_id_t setting, float value) switch(settings_get_axis_base(setting, &idx)) { case Setting_AxisStepsPerMM: - if (hal.max_step_rate && value * settings.axis[idx].max_rate > (float)hal.max_step_rate * 60.0f) - status = Status_MaxStepRateExceeded; - else { + if((status = validate_pulse_width(settings.axis[idx].max_rate, value, settings.steppers.pulse_microseconds)) == Status_OK) { if(settings.axis[idx].steps_per_mm > 0.0f && settings.axis[idx].steps_per_mm != value) { float comp = value / settings.axis[idx].steps_per_mm; sys.position[idx] *= comp; @@ -1239,9 +1267,7 @@ static status_code_t set_axis_setting (setting_id_t setting, float value) break; case Setting_AxisMaxRate: - if (hal.max_step_rate && value * settings.axis[idx].steps_per_mm > (float)hal.max_step_rate * 60.0f) - status = Status_MaxStepRateExceeded; - else + if((status = validate_pulse_width(value, settings.axis[idx].steps_per_mm, settings.steppers.pulse_microseconds)) == Status_OK) settings.axis[idx].max_rate = value; break; @@ -1362,6 +1388,10 @@ static float get_float (setting_id_t setting) } } else switch(setting) { + case Setting_PulseMicroseconds: + value = settings.steppers.pulse_microseconds; + break; + case Setting_HomingFeedRate: value = settings.axis[0].homing_feed_rate; break; @@ -1981,7 +2011,7 @@ static bool no_toolsetter_available (const setting_detail_t *setting, uint_fast1 } PROGMEM static const setting_detail_t setting_detail[] = { - { Setting_PulseMicroseconds, Group_Stepper, "Step pulse time", "microseconds", Format_Decimal, "#0.0", "2.0", NULL, Setting_IsLegacy, &settings.steppers.pulse_microseconds, NULL, NULL }, + { Setting_PulseMicroseconds, Group_Stepper, "Step pulse time", "microseconds", Format_Decimal, "#0.0", step_us_min, NULL, Setting_IsLegacyFn, set_pulse_width, get_float, NULL }, { Setting_StepperIdleLockTime, Group_Stepper, "Step idle delay", "milliseconds", Format_Int16, "####0", NULL, "65535", Setting_IsLegacy, &settings.steppers.idle_lock_time, NULL, NULL }, { Setting_StepInvertMask, Group_Stepper, "Step pulse invert", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsLegacy, &settings.steppers.step_invert.mask, NULL, NULL }, { Setting_DirInvertMask, Group_Stepper, "Step direction invert", NULL, Format_AxisMask, NULL, NULL, NULL, Setting_IsLegacy, &settings.steppers.dir_invert.mask, NULL, NULL }, @@ -2158,8 +2188,9 @@ PROGMEM static const setting_detail_t setting_detail[] = { #ifndef NO_SETTINGS_DESCRIPTIONS PROGMEM static const setting_descr_t setting_descr[] = { - { Setting_PulseMicroseconds, "Sets time length per step. Minimum 2 microseconds.\\n\\n" - "This needs to be reduced from the default value of 10 when max. step rates exceed approximately 80 kHz." + { Setting_PulseMicroseconds, "Step pulse length in microseconds.\\n" + "Minimum depends on the processor and is typically in the range of 1 - 2.5.\\n\\n" + "The length has to be reduced from the default value of 5 when max. step rate exceed approximately 140 kHz." }, { Setting_StepperIdleLockTime, "Sets a short hold delay when stopping to let dynamics settle before disabling steppers. Value 255 keeps motors enabled." }, { Setting_StepInvertMask, "Inverts the step signals (active low)." }, @@ -2210,7 +2241,7 @@ PROGMEM static const setting_descr_t setting_descr[] = { { Setting_HomingPulloff, "Retract distance after triggering switch to disengage it. Homing will fail if switch isn't cleared." }, { Setting_G73Retract, "G73 retract distance (for chip breaking drilling)." }, { Setting_PulseDelayMicroseconds, "Step pulse delay.\\n\\n" - "Normally leave this at 0 as there is an implicit delay on direction changes when AMASS is active." + "When set > 0 and less than 2 the value is rounded up to 2 microseconds." }, { Setting_RpmMax, "Maximum spindle speed, can be overridden by spindle plugins." }, { Setting_RpmMin, "Minimum spindle speed, can be overridden by spindle plugins.\\n\\n" @@ -2506,14 +2537,9 @@ static bool settings_clear_tool_data (void) #endif // N_TOOLS -// Read global settings from persistent storage. -// Checks version-byte of non-volatile storage and global settings copy. -bool read_global_settings () +// Sanity check of settings, board map could have been changed... +static void sanity_check (void) { - bool ok = hal.nvs.type != NVS_None && SETTINGS_VERSION == hal.nvs.get_byte(0) && hal.nvs.memcpy_from_nvs((uint8_t *)&settings, NVS_ADDR_GLOBAL, sizeof(settings_t), true) == NVS_TransferResult_OK; - - // Sanity check of settings, board map could have been changed... - #if LATHE_UVW_OPTION settings.mode = Mode_Lathe; #else @@ -2527,6 +2553,25 @@ bool read_global_settings () if(!hal.driver_cap.spindle_encoder) settings.spindle.ppr = 0; + if(settings.steppers.pulse_microseconds < hal.step_us_min) + settings.steppers.pulse_microseconds = hal.step_us_min; + + if(hal.max_step_rate) { + + uint_fast8_t idx = N_AXIS; + do { + idx--; +#if N_AXIS > 3 + if(bit_isfalse(settings.steppers.is_rotary.mask, bit(idx)) && +#else + if( +#endif + (settings.axis[idx].max_rate * settings.axis[idx].steps_per_mm) / 60.0f > (float)hal.max_step_rate) + settings.axis[idx].max_rate = (float)hal.max_step_rate * 60.0f / settings.axis[idx].steps_per_mm; + // TODO: warn if changed? + } while(idx); + } + if(SLEEP_DURATION <= 0.0f) settings.flags.sleep_enable = Off; @@ -2545,11 +2590,19 @@ bool read_global_settings () settings.control_invert.mask |= limits_override.mask; settings.control_disable_pullup.mask &= ~limits_override.mask; +} + +// Read global settings from persistent storage. +// Checks version-byte of non-volatile storage and global settings copy. +bool read_global_settings (void) +{ + bool ok = hal.nvs.type != NVS_None && SETTINGS_VERSION == hal.nvs.get_byte(0) && hal.nvs.memcpy_from_nvs((uint8_t *)&settings, NVS_ADDR_GLOBAL, sizeof(settings_t), true) == NVS_TransferResult_OK; + + sanity_check(); return ok && settings.version.id == SETTINGS_VERSION; } - // Write global settings to persistent storage void settings_write_global (void) { @@ -3247,10 +3300,15 @@ bool settings_add_spindle_type (const char *type) void onFileDemarcate (bool start) { - if(!start && sys.flags.travel_changed && limits_homing_required()) { - sys.flags.travel_changed = Off; - system_raise_alarm(Alarm_HomingRequired); - grbl.report.feedback_message(Message_HomingCycleRequired); + if(!start) { + + sanity_check(); + + if(sys.flags.travel_changed && limits_homing_required()) { + sys.flags.travel_changed = Off; + system_raise_alarm(Alarm_HomingRequired); + grbl.report.feedback_message(Message_HomingCycleRequired); + } } if(on_file_demarcate) @@ -3290,6 +3348,8 @@ void settings_init (void) } #endif + strcpy(step_us_min, ftoa(hal.step_us_min, 1)); + if(!read_global_settings()) { settings_restore_t settings = settings_all; diff --git a/state_machine.c b/state_machine.c index 02fc7e2..dcf465b 100644 --- a/state_machine.c +++ b/state_machine.c @@ -338,6 +338,8 @@ void state_set (sys_state_t new_state) break; } + sys.flags.is_homing = sys_state == STATE_HOMING; + if(!(sys_state & (STATE_ALARM|STATE_ESTOP))) sys.alarm = Alarm_None; diff --git a/stepper.c b/stepper.c index 4293a19..93caa7f 100644 --- a/stepper.c +++ b/stepper.c @@ -67,7 +67,7 @@ static st_block_t st_block_buffer[SEGMENT_BUFFER_SIZE - 1]; static segment_t segment_buffer[SEGMENT_BUFFER_SIZE]; // Stepper ISR data struct. Contains the running data for the main stepper ISR. -static stepper_t st; +static stepper_t st = {}; #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING typedef struct { @@ -90,7 +90,7 @@ static float cycles_per_min; static volatile segment_t *segment_buffer_tail, *segment_buffer_head; #if ENABLE_JERK_ACCELERATION -// Static storage for acceleration value of last computed segment. +// Static storage for acceleration value of last computed segment. static float last_segment_accel = 0.0f; #endif @@ -276,6 +276,7 @@ bool st_is_stepping (void) ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void) { + static uint32_t cycles_per_tick = 0; #if ENABLE_BACKLASH_COMPENSATION static bool backlash_motion; #endif @@ -285,29 +286,32 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void) hal.stepper.pulse_start(&st); - st.new_block = st.dir_change = false; + st.new_block = false; - if (st.step_count == 0) // Segment is complete. Discard current segment. + if(st.step_count == 0) // Segment is complete. Discard current segment. st.exec_segment = NULL; } // If there is no step segment, attempt to pop one from the stepper buffer - if (st.exec_segment == NULL) { + if(st.exec_segment == NULL) { // Anything in the buffer? If so, load and initialize next step segment. - if (segment_buffer_tail != segment_buffer_head) { + if(segment_buffer_tail != segment_buffer_head) { - // Initialize new step segment and load number of steps to execute + // Initialize new step segment. st.exec_segment = (segment_t *)segment_buffer_tail; - // Initialize step segment timing per step and load number of steps to execute. - hal.stepper.cycles_per_tick(st.exec_segment->cycles_per_tick); + // Initialize step segment timing per step. + if(st.exec_segment->cycles_per_tick != cycles_per_tick) + hal.stepper.cycles_per_tick((cycles_per_tick = st.exec_segment->cycles_per_tick)); + + // Load number of steps to execute. st.step_count = st.exec_segment->n_step; // NOTE: Can sometimes be zero when moving slow. // If the new segment starts a new planner block, initialize stepper variables and counters. - if (st.exec_block != st.exec_segment->exec_block) { + if(st.exec_block != st.exec_segment->exec_block) { - if((st.dir_change = st.exec_block == NULL || st.dir_outbits.value != st.exec_segment->exec_block->direction_bits.value)) - st.dir_outbits = st.exec_segment->exec_block->direction_bits; + if((st.dir_changed.bits = st.dir_out.bits ^ st.exec_segment->exec_block->direction.bits)) + st.dir_out = st.exec_segment->exec_block->direction; if(st.exec_block != NULL && st.exec_block->offset_id != st.exec_segment->exec_block->offset_id) sys.report.wco = sys.report.force_wco = On; // Do not generate grbl.on_rt_reports_added event! @@ -340,21 +344,21 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void) } // Initialize Bresenham line and distance counters - st.counter_x = st.counter_y = st.counter_z + st.counter.x = st.counter.y = st.counter.z #ifdef A_AXIS - = st.counter_a + = st.counter.a #endif #ifdef B_AXIS - = st.counter_b + = st.counter.b #endif #ifdef C_AXIS - = st.counter_c + = st.counter.c #endif #ifdef U_AXIS - = st.counter_u + = st.counter.u #endif #ifdef V_AXIS - = st.counter_v + = st.counter.v #endif = st.step_event_count >> 1; @@ -363,28 +367,18 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void) #endif } - #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING +#ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING + // With AMASS enabled, adjust Bresenham axis increment counters according to AMASS level. st.amass_level = st.exec_segment->amass_level; - st.steps[X_AXIS] = st.exec_block->steps[X_AXIS] >> st.amass_level; - st.steps[Y_AXIS] = st.exec_block->steps[Y_AXIS] >> st.amass_level; - st.steps[Z_AXIS] = st.exec_block->steps[Z_AXIS] >> st.amass_level; - #ifdef A_AXIS - st.steps[A_AXIS] = st.exec_block->steps[A_AXIS] >> st.amass_level; - #endif - #ifdef B_AXIS - st.steps[B_AXIS] = st.exec_block->steps[B_AXIS] >> st.amass_level; - #endif - #ifdef C_AXIS - st.steps[C_AXIS] = st.exec_block->steps[C_AXIS] >> st.amass_level; - #endif - #ifdef U_AXIS - st.steps[U_AXIS] = st.exec_block->steps[U_AXIS] >> st.amass_level; - #endif - #ifdef V_AXIS - st.steps[V_AXIS] = st.exec_block->steps[V_AXIS] >> st.amass_level; - #endif - #endif + + uint_fast8_t idx = N_AXIS; + do { + idx--; + st.steps.value[idx] = st.exec_block->steps.value[idx] >> st.amass_level; + } while(idx); + +#endif if(st.exec_segment->update_pwm) st.exec_segment->update_pwm(st.exec_block->spindle, st.exec_segment->spindle_pwm); @@ -400,6 +394,7 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void) st.exec_block->spindle->update_pwm(st.exec_block->spindle, st.exec_block->spindle->pwm_off_value); } + cycles_per_tick = 0; st.exec_block = NULL; system_set_exec_state_flag(EXEC_CYCLE_COMPLETE); // Flag main program for cycle complete @@ -420,107 +415,107 @@ ISR_CODE void ISR_FUNC(stepper_driver_interrupt_handler)(void) #endif } - register axes_signals_t step_outbits = (axes_signals_t){0}; + register axes_signals_t step_out = (axes_signals_t){0}; // Execute step displacement profile by Bresenham line algorithm - st.counter_x += st.steps[X_AXIS]; - if (st.counter_x > st.step_event_count) { - step_outbits.x = On; - st.counter_x -= st.step_event_count; + st.counter.x += st.steps.value[X_AXIS]; + if (st.counter.x > st.step_event_count) { + step_out.x = On; + st.counter.x -= st.step_event_count; #if ENABLE_BACKLASH_COMPENSATION if(!backlash_motion) #endif - sys.position[X_AXIS] = sys.position[X_AXIS] + (st.dir_outbits.x ? -1 : 1); + sys.position[X_AXIS] = sys.position[X_AXIS] + (st.dir_out.x ? -1 : 1); } - st.counter_y += st.steps[Y_AXIS]; - if (st.counter_y > st.step_event_count) { - step_outbits.y = On; - st.counter_y -= st.step_event_count; + st.counter.y += st.steps.value[Y_AXIS]; + if (st.counter.y > st.step_event_count) { + step_out.y = On; + st.counter.y -= st.step_event_count; #if ENABLE_BACKLASH_COMPENSATION if(!backlash_motion) #endif - sys.position[Y_AXIS] = sys.position[Y_AXIS] + (st.dir_outbits.y ? -1 : 1); + sys.position[Y_AXIS] = sys.position[Y_AXIS] + (st.dir_out.y ? -1 : 1); } - st.counter_z += st.steps[Z_AXIS]; - if (st.counter_z > st.step_event_count) { - step_outbits.z = On; - st.counter_z -= st.step_event_count; + st.counter.z += st.steps.value[Z_AXIS]; + if (st.counter.z > st.step_event_count) { + step_out.z = On; + st.counter.z -= st.step_event_count; #if ENABLE_BACKLASH_COMPENSATION if(!backlash_motion) #endif - sys.position[Z_AXIS] = sys.position[Z_AXIS] + (st.dir_outbits.z ? -1 : 1); + sys.position[Z_AXIS] = sys.position[Z_AXIS] + (st.dir_out.z ? -1 : 1); } #ifdef A_AXIS - st.counter_a += st.steps[A_AXIS]; - if (st.counter_a > st.step_event_count) { - step_outbits.a = On; - st.counter_a -= st.step_event_count; + st.counter.a += st.steps.value[A_AXIS]; + if (st.counter.a > st.step_event_count) { + step_out.a = On; + st.counter.a -= st.step_event_count; #if ENABLE_BACKLASH_COMPENSATION if(!backlash_motion) #endif - sys.position[A_AXIS] = sys.position[A_AXIS] + (st.dir_outbits.a ? -1 : 1); + sys.position[A_AXIS] = sys.position[A_AXIS] + (st.dir_out.a ? -1 : 1); } #endif #ifdef B_AXIS - st.counter_b += st.steps[B_AXIS]; - if (st.counter_b > st.step_event_count) { - step_outbits.b = On; - st.counter_b -= st.step_event_count; + st.counter.b += st.steps.value[B_AXIS]; + if (st.counter.b > st.step_event_count) { + step_out.b = On; + st.counter.b -= st.step_event_count; #if ENABLE_BACKLASH_COMPENSATION if(!backlash_motion) #endif - sys.position[B_AXIS] = sys.position[B_AXIS] + (st.dir_outbits.b ? -1 : 1); + sys.position[B_AXIS] = sys.position[B_AXIS] + (st.dir_out.b ? -1 : 1); } #endif #ifdef C_AXIS - st.counter_c += st.steps[C_AXIS]; - if (st.counter_c > st.step_event_count) { - step_outbits.c = On; - st.counter_c -= st.step_event_count; + st.counter.c += st.steps.value[C_AXIS]; + if (st.counter.c > st.step_event_count) { + step_out.c = On; + st.counter.c -= st.step_event_count; #if ENABLE_BACKLASH_COMPENSATION if(!backlash_motion) #endif - sys.position[C_AXIS] = sys.position[C_AXIS] + (st.dir_outbits.c ? -1 : 1); + sys.position[C_AXIS] = sys.position[C_AXIS] + (st.dir_out.c ? -1 : 1); } #endif #ifdef U_AXIS - st.counter_u += st.steps[U_AXIS]; - if (st.counter_u > st.step_event_count) { - step_outbits.u = On; - st.counter_u -= st.step_event_count; + st.counter.u += st.steps.value[U_AXIS]; + if (st.counter.u > st.step_event_count) { + step_out.u = On; + st.counter.u -= st.step_event_count; #if ENABLE_BACKLASH_COMPENSATION if(!backlash_motion) #endif - sys.position[U_AXIS] = sys.position[U_AXIS] + (st.dir_outbits.u ? -1 : 1); + sys.position[U_AXIS] = sys.position[U_AXIS] + (st.dir_out.u ? -1 : 1); } #endif #ifdef V_AXIS - st.counter_v += st.steps[V_AXIS]; - if (st.counter_v > st.step_event_count) { - step_outbits.v = On; - st.counter_v -= st.step_event_count; + st.counter.v += st.steps.value[V_AXIS]; + if (st.counter.v > st.step_event_count) { + step_out.v = On; + st.counter.v -= st.step_event_count; #if ENABLE_BACKLASH_COMPENSATION if(!backlash_motion) #endif - sys.position[V_AXIS] = sys.position[V_AXIS] + (st.dir_outbits.v ? -1 : 1); + sys.position[V_AXIS] = sys.position[V_AXIS] + (st.dir_out.v ? -1 : 1); } #endif - st.step_outbits.value = step_outbits.value; + st.step_out.bits = step_out.bits; // During a homing cycle, lock out and prevent desired axes from moving. - if (state_get() == STATE_HOMING) - st.step_outbits.value &= sys.homing_axis_lock.mask; + if(sys.flags.is_homing) + st.step_out.bits &= sys.homing_axis_lock.bits; - if (st.step_count == 0 || --st.step_count == 0) { + if(st.step_count == 0 || --st.step_count == 0) { // Segment is complete. Advance segment tail pointer. segment_buffer_tail = segment_buffer_tail->next; } @@ -561,9 +556,13 @@ void st_reset (void) pl_block = NULL; // Planner block pointer used by segment buffer segment_buffer_tail = segment_buffer_head = &segment_buffer[0]; // empty = tail + axes_signals_t dir_out = st.dir_out; + memset(&prep, 0, sizeof(st_prep_t)); memset(&st, 0, sizeof(stepper_t)); + st.dir_out = dir_out; + #ifdef ADAPTIVE_MULTI_AXIS_STEP_SMOOTHING // TODO: move to driver? // AMASS_LEVEL0: Normal operation. No AMASS. No upper cutoff frequency. Starts at LEVEL1 cutoff frequency. @@ -710,12 +709,12 @@ void st_prep_buffer (void) // If the original data is divided, we can lose a step from integer roundoff. do { idx--; - st_prep_block->steps[idx] = pl_block->steps[idx] << MAX_AMASS_LEVEL; + st_prep_block->steps.value[idx] = pl_block->steps.value[idx] << MAX_AMASS_LEVEL; } while(idx); st_prep_block->step_event_count = pl_block->step_event_count << MAX_AMASS_LEVEL; #endif - st_prep_block->direction_bits = pl_block->direction_bits; + st_prep_block->direction = pl_block->direction; st_prep_block->programmed_rate = pl_block->programmed_rate; // st_prep_block->r = pl_block->programmed_rate; st_prep_block->millimeters = pl_block->millimeters; @@ -921,9 +920,9 @@ void st_prep_buffer (void) // Check if we are on ramp up or ramp down. Ramp down if distance to end of acceleration is less than distance needed to reach 0 acceleration. // Then limit acceleration change by jerk up to max acceleration and update for next segment. // Minimum acceleration jerk per time_var to ensure acceleration completes. Acceleration change at end of ramp is in acceptable jerk range. - last_segment_accel = min(last_segment_accel + pl_block->jerk * time_var, pl_block->max_acceleration); + last_segment_accel = min(last_segment_accel + pl_block->jerk * time_var, pl_block->max_acceleration); } else { - last_segment_accel = max(last_segment_accel - pl_block->jerk * time_var, pl_block->jerk * time_var); + last_segment_accel = max(last_segment_accel - pl_block->jerk * time_var, pl_block->jerk * time_var); } speed_var = last_segment_accel * time_var; #else @@ -959,16 +958,16 @@ void st_prep_buffer (void) default: // case Ramp_Decel: // NOTE: mm_var used as a misc worker variable to prevent errors when near zero speed. -#if ENABLE_JERK_ACCELERATION +#if ENABLE_JERK_ACCELERATION time_to_jerk = last_segment_accel / pl_block->jerk; jerk_rampdown = prep.exit_speed + time_to_jerk * (last_segment_accel - (0.5f * pl_block->jerk * time_to_jerk)); // Speedpoint to start ramping down deceleration. (V = a * t - 1/2 j * t^2) if (prep.current_speed > jerk_rampdown) { // Check if we are on ramp up or ramp down. Ramp down if speed is less than speed needed for reaching 0 acceleration. // Then limit acceleration change by jerk up to max acceleration and update for next segment. // Minimum acceleration of jerk per time_var to ensure deceleration completes. Acceleration change at end of ramp is in acceptable jerk range. - last_segment_accel = min(last_segment_accel + pl_block->jerk * time_var, pl_block->max_acceleration); + last_segment_accel = min(last_segment_accel + pl_block->jerk * time_var, pl_block->max_acceleration); } else { - last_segment_accel = max(last_segment_accel - pl_block->jerk * time_var, pl_block->jerk * time_var); + last_segment_accel = max(last_segment_accel - pl_block->jerk * time_var, pl_block->jerk * time_var); } speed_var = last_segment_accel * time_var; // Used as delta speed (mm/min) #else diff --git a/stepper.h b/stepper.h index a6d7533..3ca4488 100644 --- a/stepper.h +++ b/stepper.h @@ -47,14 +47,14 @@ __NOTE:__ This data is copied from the prepped planner blocks so that the planne typedef struct st_block { uint_fast8_t id; //!< Id may be used by driver to track changes struct st_block *next; //!< Pointer to next element in cirular list of blocks - uint32_t steps[N_AXIS]; + steps_t steps; uint32_t step_event_count; 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; + axes_signals_t direction; gc_override_flags_t overrides; //!< Block bitfield variable for overrides bool backlash_motion; bool dynamic_rpm; //!< Tracks motions that require dynamic RPM adjustment @@ -82,34 +82,16 @@ typedef struct st_segment { //! Stepper ISR data struct. Contains the running data for the main stepper ISR. typedef struct stepper { - uint32_t counter_x, //!< Counter variable for the Bresenham line tracer, X-axis - counter_y, //!< Counter variable for the Bresenham line tracer, Y-axis - counter_z //!< Counter variable for the Bresenham line tracer, Z-axis - #ifdef A_AXIS - , counter_a //!< Counter variable for the Bresenham line tracer, A-axis - #endif - #ifdef B_AXIS - , counter_b //!< Counter variable for the Bresenham line tracer, B-axis - #endif - #ifdef C_AXIS - , counter_c //!< Counter variable for the Bresenham line tracer, C-axis - #endif - #ifdef U_AXIS - , counter_u //!< Counter variable for the Bresenham line tracer, U-axis - #endif - #ifdef V_AXIS - , counter_v //!< Counter variable for the Bresenham line tracer, V-axis - #endif -; bool new_block; //!< Set to true when a new block is started, might be referenced by driver code for advanced functionality. - bool dir_change; //!< Set to true on direction changes, might be referenced by driver for advanced functionality. - axes_signals_t step_outbits; //!< The stepping signals to be output. - axes_signals_t dir_outbits; //!< The direction signals to be output. The direction signals may be output only when \ref stepper.dir_change is true to reduce overhead. - uint32_t steps[N_AXIS]; //!< Number of step pulse event events per axis step pulse generated. + axes_signals_t dir_changed; //!< Per axis bits set to true on direction changes, might be referenced by driver for advanced functionality. + axes_signals_t step_out; //!< The stepping signals to be output. + axes_signals_t dir_out; //!< The direction signals to be output. The direction signals may be output only when \ref stepper.dir_change is true to reduce overhead. uint_fast8_t amass_level; //!< AMASS level for this segment. -// uint_fast16_t spindle_pwm; uint_fast16_t step_count; //!< Steps remaining in line segment motion. uint32_t step_event_count; //!< Number of step pulse events to be output by this segment. + steps_t steps; //!< Number of step pulse event events per axis step pulse generated. + steps_t counter; //!< Counter variables for the Bresenham line tracer. +// uint_fast16_t spindle_pwm; st_block_t *exec_block; //!< Pointer to the block data for the segment being executed. segment_t *exec_segment; //!< Pointer to the segment being executed. } stepper_t; diff --git a/system.c b/system.c index 83034a6..2d0e097 100644 --- a/system.c +++ b/system.c @@ -1254,7 +1254,7 @@ report_tracking_flags_t system_get_rt_report_flags (void) Fires the \ref grbl.on_rt_reports_added event. \param report a #report_tracking_t enum containing the flag(s) to set or clear. */ -void system_add_rt_report (report_tracking_t report) +ISR_CODE void system_add_rt_report (report_tracking_t report) { switch(report) { diff --git a/system.h b/system.h index c7d6f82..c1933b6 100644 --- a/system.h +++ b/system.h @@ -85,6 +85,30 @@ __NOTE:__ flags are mutually exclusive, bit map allows testing for multiple stat #define STATE_TOOL_CHANGE bit(9) //!< Manual tool change, similar to #STATE_HOLD - but stops spindle and allows jogging. ///@} +//! \def system_state_t +/*! @name System state enum values. + +This enum contains definitions for the bit position of \ref sys_state flags, bit 0 is 1. ffs() can be used to get it from a \ref sys_state_t value. + +__NOTE:__ these enum values are not referenced in the core. +*/ +///@{ +typedef enum { + SystemState_Idle = 0, + SystemState_Alarm = 1, + SystemState_CheckMode = 2, + SystemState_Homing = 3, + SystemState_Cycle = 4, + SystemState_Hold = 5, + SystemState_Jog = 6, + SystemState_DoorOpen = 7, + SystemState_Sleep = 8, + SystemState_EStop = 9, + SystemState_ToolChange = 10, + SystemState_Undefined = 255 +} __attribute__ ((__packed__)) system_state_t; +///@} + #ifdef ARDUINO typedef enum { @@ -269,7 +293,8 @@ typedef union { auto_reporting :1, //!< Set to true when auto real time reporting is enabled. synchronizing :1, //!< Set to true when protocol_buffer_synchronize() is running. travel_changed :1, //!< Set to true when maximum travel settings has changed. - unused :4; + is_homing :1, + unused :3; }; } system_flags_t;