diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index 906cfb8f..a3d9842a 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -119,7 +119,9 @@ different names for the stepper (eg, `stepper_x` vs `stepper_a`). Below are common stepper definitions. See the [rotation distance document](Rotation_Distance.md) for -information on calculating the `rotation_distance` parameter. +information on calculating the `rotation_distance` parameter. See the +[Multi-MCU homing](Multi_MCU_Homing.md) document for information on +homing using multiple micro-controllers. ``` [stepper_x] @@ -152,8 +154,10 @@ microsteps: # distance the axis travels for one full rotation of the final gear. # The default is to not use a gear ratio. endstop_pin: -# Endstop switch detection pin. This parameter must be provided for -# the X, Y, and Z steppers on cartesian style printers. +# Endstop switch detection pin. If this endstop pin is on a +# different mcu than the stepper motor then it enables "multi-mcu +# homing". This parameter must be provided for the X, Y, and Z +# steppers on cartesian style printers. #position_min: 0 # Minimum valid distance (in mm) the user may command the stepper to # move to. The default is 0mm. @@ -1611,7 +1615,9 @@ stepper_z config section. ``` [probe] pin: -# Probe detection pin. This parameter must be provided. +# Probe detection pin. If the pin is on a different microcontroller +# than the Z steppers then it enables "multi-mcu homing". This +# parameter must be provided. #deactivate_on_each_sample: True # This determines if Klipper should execute deactivation gcode # between each probe attempt when performing a multiple probe diff --git a/docs/Multi_MCU_Homing.md b/docs/Multi_MCU_Homing.md new file mode 100644 index 00000000..22d2508e --- /dev/null +++ b/docs/Multi_MCU_Homing.md @@ -0,0 +1,42 @@ +# Multiple Micro-controller Homing and Probing + +Klipper supports a mechanism for homing with an endstop attached to +one micro-controller while its stepper motors are on a different +micro-controller. This support is referred to as "multi-mcu +homing". This feature is also used when a Z probe is on a different +micro-controller than the Z stepper motors. + +This feature can be useful to simplify wiring, as it may be more +convenient to attach an endstop or probe to a closer micro-controller. +However, using this feature may result in "overshoot" of the stepper +motors during homing and probing operations. + +The overshoot occurs due to possible message transmission delays +between the micro-controller monitoring the endstop and the +micro-controllers moving the stepper motors. The Klipper code is +designed to limit this delay to no more than 25ms. (When multi-mcu +homing is activated, the micro-controllers send periodic status +messages and check that corresponding status messages are received +within 25ms.) + +So, for example, if homing at 10mm/s then it is possible for an +overshoot of up to 0.250mm (10mm/s * .025s == 0.250mm). Care should be +taken when configuring multi-mcu homing to account for this type of +overshoot. Using slower homing or probing speeds can reduce the +overshoot. + +Stepper motor overshoot should not adversely impact the precision of +the homing and probing procedure. The Klipper code will detect the +overshoot and account for it in its calculations. However, it is +important that the hardware design is capable of handling overshoot +without causing damage to the machine. + +Should Klipper detect a communication issue between micro-controllers +during multi-mcu homing then it will raise a "Communication timeout +during homing" error. + +Note that an axis with multiple steppers (eg, `stepper_z` and +`stepper_z1`) need to be on the same micro-controller in order to use +multi-mcu homing. For example, if an endstop is on a separate +micro-controller from `stepper_z` then `stepper_z1` must be on the +same micro-controller as `stepper_z`. diff --git a/docs/Overview.md b/docs/Overview.md index 632f584e..a758a991 100644 --- a/docs/Overview.md +++ b/docs/Overview.md @@ -41,18 +41,19 @@ communication with the Klipper developers. using adxl345 accelerometer hardware to measure resonance. - [Pressure advance](Pressure_Advance.md): Calibrate extruder pressure. -- [Slicers](Slicers.md): Configure "slicer" software for Klipper. +- [G-Codes](G-Codes.md): Information on commands supported by Klipper. - [Command Templates](Command_Templates.md): G-Code macros and conditional evaluation. - [Status Reference](Status_Reference.md): Information available to macros (and similar). - [TMC Drivers](TMC_Drivers.md): Using Trinamic stepper motor drivers with Klipper. +- [Multi-MCU Homing](Multi_MCU_Homing.md): Homing and probing using multiple micro-controllers. +- [Slicers](Slicers.md): Configure "slicer" software for Klipper. - [Skew correction](skew_correction.md): Adjustments for axes not perfectly square. - [PWM tools](Using_PWM_Tools.md): Guide on how to use PWM controlled tools such as lasers or spindles. -- [G-Codes](G-Codes.md): Information on commands supported by Klipper. ## Developer Documentation diff --git a/docs/_klipper3d/mkdocs.yml b/docs/_klipper3d/mkdocs.yml index bf8f1908..c7724dc0 100644 --- a/docs/_klipper3d/mkdocs.yml +++ b/docs/_klipper3d/mkdocs.yml @@ -94,14 +94,15 @@ nav: - Resonance_Compensation.md - Measuring_Resonances.md - Pressure_Advance.md - - Slicers.md + - G-Codes.md - Command templates: - Command_Templates.md - Status_Reference.md - TMC_Drivers.md + - Multi_MCU_Homing.md + - Slicers.md - skew_correction.md - Using_PWM_Tools.md - - G-Codes.md - Developer Documentation: - Code_Overview.md - Kinematics.md diff --git a/klippy/mcu.py b/klippy/mcu.py index eb1f319f..d568d194 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -125,6 +125,9 @@ class MCU_trsync: s.note_homing_end() return params['trigger_reason'] +TRSYNC_TIMEOUT = 0.025 +TRSYNC_SINGLE_MCU_TIMEOUT = 0.250 + class MCU_endstop: RETRY_QUERY = 1.000 def __init__(self, mcu, pin_params): @@ -139,15 +142,27 @@ class MCU_endstop: self._rest_ticks = 0 ffi_main, ffi_lib = chelper.get_ffi() self._trdispatch = ffi_main.gc(ffi_lib.trdispatch_alloc(), ffi_lib.free) - self._trsync = MCU_trsync(mcu, self._trdispatch) + self._trsyncs = [MCU_trsync(mcu, self._trdispatch)] def get_mcu(self): return self._mcu def add_stepper(self, stepper): - if stepper.get_mcu() is not self._mcu: - raise pins.error("Endstop and stepper must be on the same mcu") - self._trsync.add_stepper(stepper) + trsyncs = {trsync.get_mcu(): trsync for trsync in self._trsyncs} + trsync = trsyncs.get(stepper.get_mcu()) + if trsync is None: + trsync = MCU_trsync(stepper.get_mcu(), self._trdispatch) + self._trsyncs.append(trsync) + trsync.add_stepper(stepper) + # Check for unsupported multi-mcu shared stepper rails + sname = stepper.get_name() + if sname.startswith('stepper_'): + for ot in self._trsyncs: + for s in ot.get_steppers(): + if ot is not trsync and s.get_name().startswith(sname[:9]): + cerror = self._mcu.get_printer().config_error + raise cerror("Multi-mcu homing not supported on" + " multi-mcu shared axis") def get_steppers(self): - return self._trsync.get_steppers() + return [s for trsync in self._trsyncs for s in trsync.get_steppers()] def _build_config(self): # Setup config self._mcu.add_config_cmd("config_endstop oid=%d pin=%s pull_up=%d" @@ -157,7 +172,7 @@ class MCU_endstop: " rest_ticks=0 pin_value=0 trsync_oid=0 trigger_reason=0" % (self._oid,), on_restart=True) # Lookup commands - cmd_queue = self._trsync.get_command_queue() + cmd_queue = self._trsyncs[0].get_command_queue() self._home_cmd = self._mcu.lookup_command( "endstop_home oid=%c clock=%u sample_ticks=%u sample_count=%c" " rest_ticks=%u pin_value=%c trsync_oid=%c trigger_reason=%c", @@ -173,8 +188,12 @@ class MCU_endstop: self._rest_ticks = rest_ticks reactor = self._mcu.get_printer().get_reactor() self._trigger_completion = reactor.completion() - etrsync = self._trsync - etrsync.start(print_time, self._trigger_completion, .250) + expire_timeout = TRSYNC_TIMEOUT + if len(self._trsyncs) == 1: + expire_timeout = TRSYNC_SINGLE_MCU_TIMEOUT + for trsync in self._trsyncs: + trsync.start(print_time, self._trigger_completion, expire_timeout) + etrsync = self._trsyncs[0] ffi_main, ffi_lib = chelper.get_ffi() ffi_lib.trdispatch_start(self._trdispatch, etrsync.REASON_HOST_REQUEST) self._home_cmd.send( @@ -183,7 +202,7 @@ class MCU_endstop: etrsync.get_oid(), etrsync.REASON_ENDSTOP_HIT], reqclock=clock) return self._trigger_completion def home_wait(self, home_end_time): - etrsync = self._trsync + etrsync = self._trsyncs[0] etrsync.set_home_end_time(home_end_time) if self._mcu.is_fileoutput(): self._trigger_completion.complete(True) @@ -191,10 +210,10 @@ class MCU_endstop: self._home_cmd.send([self._oid, 0, 0, 0, 0, 0, 0, 0]) ffi_main, ffi_lib = chelper.get_ffi() ffi_lib.trdispatch_stop(self._trdispatch) - res = etrsync.stop() - if res == etrsync.REASON_COMMS_TIMEOUT: + res = [trsync.stop() for trsync in self._trsyncs] + if any([r == etrsync.REASON_COMMS_TIMEOUT for r in res]): return -1. - if res != etrsync.REASON_ENDSTOP_HIT: + if res[0] != etrsync.REASON_ENDSTOP_HIT: return 0. if self._mcu.is_fileoutput(): return home_end_time