From 7260cca7c890337669606f6a1538ad8010479466 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Thu, 24 Jul 2025 00:24:49 +0200 Subject: [PATCH] tmc: implement ready state current reduction Signed-off-by: Timofey Titovets --- config/generic-duet2-duex.cfg | 4 +-- docs/Config_Reference.md | 21 +++++++++--- klippy/extras/tmc.py | 61 +++++++++++++++++++++++++++++++---- klippy/extras/tmc2660.py | 18 ----------- 4 files changed, 74 insertions(+), 30 deletions(-) diff --git a/config/generic-duet2-duex.cfg b/config/generic-duet2-duex.cfg index e63577135..df6ee4c7e 100644 --- a/config/generic-duet2-duex.cfg +++ b/config/generic-duet2-duex.cfg @@ -98,7 +98,7 @@ cs_pin: PD14 # X_SPI_EN Required for communication spi_bus: usart1 # All TMC2660 drivers are connected to USART1 run_current: 1.000 sense_resistor: 0.051 -idle_current_percent: 20 +ready_current: 0.5 [stepper_y] step_pin: PD7 @@ -115,7 +115,7 @@ cs_pin: PC9 spi_bus: usart1 run_current: 1.000 sense_resistor: 0.051 -idle_current_percent: 20 +ready_current: 0.5 [stepper_z] step_pin: PD8 diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index b01360adf..47804dcf3 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -3752,6 +3752,9 @@ run_current: # when the stepper is not moving. Setting a hold_current is not # recommended (see TMC_Drivers.md for details). The default is to # not reduce the current. +#ready_current: +# The amount of current (in amps RMS) to configure the driver to use +# when the printer is in ready state - between last move and [idle_timeout]. #sense_resistor: 0.110 # The resistance (in ohms) of the motor sense resistor. The default # is 0.110 ohms. @@ -3867,6 +3870,9 @@ run_current: # when the stepper is not moving. Setting a hold_current is not # recommended (see TMC_Drivers.md for details). The default is to # not reduce the current. +#ready_current: +# The amount of current (in amps RMS) to configure the driver to use +# when the printer is in ready state - between last move and [idle_timeout]. #sense_resistor: 0.110 # The resistance (in ohms) of the motor sense resistor. The default # is 0.110 ohms. @@ -3912,6 +3918,7 @@ uart_pin: #interpolate: True run_current: #hold_current: +#ready_current: #sense_resistor: 0.110 #stealthchop_threshold: 0 # See the "tmc2208" section for the definition of these parameters. @@ -3996,10 +4003,10 @@ run_current: #sense_resistor: # The resistance (in ohms) of the motor sense resistor. This # parameter must be provided. -#idle_current_percent: 100 -# The percentage of the run_current the stepper driver will be -# lowered to when the idle timeout expires (you need to set up the -# timeout using a [idle_timeout] config section). The current will +#ready_current: +# The amount of current (in amps RMS) to configure the driver to use +# when the printer is in ready state - between last move and [idle_timeout]. +# Upon move, the current will # be raised again once the stepper has to move again. Make sure to # set this to a high enough value such that the steppers do not lose # their position. There is also small delay until the current is @@ -4073,6 +4080,9 @@ run_current: # when the stepper is not moving. Setting a hold_current is not # recommended (see TMC_Drivers.md for details). The default is to # not reduce the current. +#ready_current: +# The amount of current (in amps RMS) to configure the driver to use +# when the printer is in ready state - between last move and [idle_timeout]. #rref: 12000 # The resistance (in ohms) of the resistor between IREF and GND. The # default is 12000. @@ -4208,6 +4218,9 @@ run_current: # when the stepper is not moving. Setting a hold_current is not # recommended (see TMC_Drivers.md for details). The default is to # not reduce the current. +#ready_current: +# The amount of current (in amps RMS) to configure the driver to use +# when the printer is in ready state - between last move and [idle_timeout]. #sense_resistor: 0.075 # The resistance (in ohms) of the motor sense resistor. The default # is 0.075 ohms. diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index fe6217a21..a7e499823 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -333,6 +333,10 @@ class TMCCommandHelper: hold = config.getfloat('hold_current', run_cur, above=0., maxval=run_cur) self.hold_current = hold + self.ready_current = config.getfloat('ready_current', run_cur, + above=0., maxval=run_cur) + self._restore_current = [self.run_current, self.hold_current] + self._in_ready = False self.fields = mcu_tmc.get_fields() self.stepper = None # Stepper phase tracking @@ -356,6 +360,8 @@ class TMCCommandHelper: self._handle_mcu_identify) self.printer.register_event_handler("klippy:connect", self._handle_connect) + self.printer.register_event_handler("idle_timeout:ready", + self._handle_ready) # Register commands gcode = self.printer.lookup_object("gcode") gcode.register_mux_command("SET_TMC_FIELD", "STEPPER", self.name, @@ -402,8 +408,12 @@ class TMCCommandHelper: prev_cur, prev_hold_cur, max_cur = ch.get_current() run_current = gcmd.get_float('CURRENT', None, minval=0., maxval=max_cur) - hold_current = gcmd.get_float('HOLDCURRENT', None, - above=0., maxval=max_cur) + hold_current = None + if prev_hold_cur is not None: + hold_current = gcmd.get_float('HOLDCURRENT', None, + above=0., maxval=max_cur) + ready_current = gcmd.get_float('READYCURRENT', None, + above=0., maxval=max_cur) if run_current is not None or hold_current is not None: if run_current is None: run_current = prev_cur @@ -413,12 +423,50 @@ class TMCCommandHelper: print_time = toolhead.get_last_move_time() ch.set_current(run_current, hold_current, print_time) prev_cur, prev_hold_cur, max_cur = ch.get_current() + if ready_current is not None: + self.ready_current = ready_current + # Don't silently miss user input to change ihold upon restore + if self._in_ready and hold_current is not None: + self._restore_current[1] = hold_current # Report values if prev_hold_cur is None: - gcmd.respond_info("Run Current: %0.2fA" % (prev_cur)) + gcmd.respond_info("Run Current: %0.2fA " + "Ready Current: %0.2fA" + % (prev_cur, self.ready_current)) else: - gcmd.respond_info("Run Current: %0.2fA Hold Current: %0.2fA" - % (prev_cur, prev_hold_cur)) + gcmd.respond_info("Run Current: %0.2fA " + "Hold Current: %0.2fA " + "Ready Current: %0.2fA" + % (prev_cur, prev_hold_cur, self.ready_current)) + # Handle printer ready state current reduction + def _handle_ready(self, print_time): + if self.ready_current >= self.run_current: + return + if self._in_ready: + return + ch = self.current_helper + run, hold, max_cur = ch.get_current() + self._restore_current = [run, hold] + def callback(eventtime): + if hold is not None: + ch.set_current(run, self.ready_current, print_time) + else: + #TMC2660 + ch.set_current(self.ready_current, None, print_time) + self.printer.get_reactor().register_callback(callback) + self._in_ready = True + force_move = self.printer.lookup_object("force_move") + stepper = force_move.lookup_stepper(self.stepper_name) + stepper.add_active_callback(self._restore) + def _restore(self, flush_time): + if not self._in_ready: + return + ch = self.current_helper + run, hold = self._restore_current + def callback(eventtime): + ch.set_current(run, hold, flush_time) + self.printer.get_reactor().register_callback(callback) + self._in_ready = False # Stepper phase tracking def _get_phases(self): return (256 >> self.fields.get_field("mres")) * 4 @@ -525,7 +573,8 @@ class TMCCommandHelper: res = {'mcu_phase_offset': self.mcu_phase_offset, 'phase_offset_position': cpos, 'run_current': current[0], - 'hold_current': current[1]} + 'hold_current': current[1], + 'ready_current': self.ready_current} res.update(self.echeck_helper.get_status(eventtime)) return res # DUMP_TMC support diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index 39ca37b94..59df3d9fb 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -120,14 +120,6 @@ class TMC2660CurrentHelper: self.fields = mcu_tmc.get_fields() self.sense_resistor = config.getfloat('sense_resistor') self.current = .0 - # Register ready/printing handlers - self.idle_current_percentage = config.getint( - 'idle_current_percent', default=100, minval=0, maxval=100) - if self.idle_current_percentage < 100: - self.printer.register_event_handler("idle_timeout:printing", - self._handle_printing) - self.printer.register_event_handler("idle_timeout:ready", - self._handle_ready) def _calc_current_bits(self, current, vsense): vref = 0.165 if vsense else 0.310 @@ -152,16 +144,6 @@ class TMC2660CurrentHelper: irun = irun2 return vsense, irun - def _handle_printing(self, print_time): - print_time -= 0.100 # Schedule slightly before deadline - self.printer.get_reactor().register_callback( - (lambda ev: self._update_current(self.current, print_time))) - - def _handle_ready(self, print_time): - current = self.current * float(self.idle_current_percentage) / 100. - self.printer.get_reactor().register_callback( - (lambda ev: self._update_current(current, print_time))) - def _update_current(self, current, print_time): vsense, cs = self._calc_current(current) val = self.fields.set_field("cs", cs)