From 32b70e75364a1e0b68d931d24ea2142c5c200849 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Wed, 30 Jul 2025 03:56:47 +0200 Subject: [PATCH 1/2] tmc: merge the current configuration Signed-off-by: Timofey Titovets --- klippy/extras/tmc.py | 23 ++++++++++++++++++----- klippy/extras/tmc2130.py | 12 +----------- klippy/extras/tmc2240.py | 20 ++++++-------------- klippy/extras/tmc2660.py | 9 ++------- klippy/extras/tmc5160.py | 12 +----------- 5 files changed, 28 insertions(+), 48 deletions(-) diff --git a/klippy/extras/tmc.py b/klippy/extras/tmc.py index 23b05305e..fe6217a21 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -322,7 +322,17 @@ class TMCCommandHelper: self.stepper_name = ' '.join(config.get_name().split()[1:]) self.name = config.get_name().split()[-1] self.mcu_tmc = mcu_tmc + # Handle current settings self.current_helper = current_helper + _, hold_support, max_cur = self.current_helper.get_current() + self.run_current = config.getfloat('run_current', + above=0., maxval=max_cur) + self.hold_current = None + run_cur = self.run_current + if hold_support: + hold = config.getfloat('hold_current', run_cur, + above=0., maxval=run_cur) + self.hold_current = hold self.fields = mcu_tmc.get_fields() self.stepper = None # Stepper phase tracking @@ -389,22 +399,23 @@ class TMCCommandHelper: cmd_SET_TMC_CURRENT_help = "Set the current of a TMC driver" def cmd_SET_TMC_CURRENT(self, gcmd): ch = self.current_helper - prev_cur, prev_hold_cur, req_hold_cur, max_cur = ch.get_current() - run_current = gcmd.get_float('CURRENT', None, minval=0., maxval=max_cur) + 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) if run_current is not None or hold_current is not None: if run_current is None: run_current = prev_cur if hold_current is None: - hold_current = req_hold_cur + hold_current = prev_hold_cur toolhead = self.printer.lookup_object('toolhead') print_time = toolhead.get_last_move_time() ch.set_current(run_current, hold_current, print_time) - prev_cur, prev_hold_cur, req_hold_cur, max_cur = ch.get_current() + prev_cur, prev_hold_cur, max_cur = ch.get_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" % (prev_cur)) else: gcmd.respond_info("Run Current: %0.2fA Hold Current: %0.2fA" % (prev_cur, prev_hold_cur)) @@ -500,6 +511,8 @@ class TMCCommandHelper: self.stepper_name) # Send init try: + ch = self.current_helper + ch.set_current(self.run_current, self.hold_current, None) self._init_registers() except self.printer.command_error as e: logging.info("TMC %s failed to init: %s", self.name, str(e)) diff --git a/klippy/extras/tmc2130.py b/klippy/extras/tmc2130.py index 181fe07ae..a3c36c36f 100644 --- a/klippy/extras/tmc2130.py +++ b/klippy/extras/tmc2130.py @@ -124,16 +124,7 @@ class TMCCurrentHelper: self.name = config.get_name().split()[-1] self.mcu_tmc = mcu_tmc self.fields = mcu_tmc.get_fields() - run_current = config.getfloat('run_current', - above=0., maxval=MAX_CURRENT) - hold_current = config.getfloat('hold_current', MAX_CURRENT, - above=0., maxval=MAX_CURRENT) - self.req_hold_current = hold_current self.sense_resistor = config.getfloat('sense_resistor', 0.110, above=0.) - vsense, irun, ihold = self._calc_current(run_current, hold_current) - self.fields.set_field("vsense", vsense) - self.fields.set_field("ihold", ihold) - self.fields.set_field("irun", irun) def _calc_current_bits(self, current, vsense): sense_resistor = self.sense_resistor + 0.020 vref = 0.32 @@ -166,9 +157,8 @@ class TMCCurrentHelper: vsense = self.fields.get_field("vsense") run_current = self._calc_current_from_bits(irun, vsense) hold_current = self._calc_current_from_bits(ihold, vsense) - return run_current, hold_current, self.req_hold_current, MAX_CURRENT + return (run_current, hold_current, MAX_CURRENT) def set_current(self, run_current, hold_current, print_time): - self.req_hold_current = hold_current vsense, irun, ihold = self._calc_current(run_current, hold_current) if vsense != self.fields.get_field("vsense"): val = self.fields.set_field("vsense", vsense) diff --git a/klippy/extras/tmc2240.py b/klippy/extras/tmc2240.py index d57a93b83..f078b95f1 100644 --- a/klippy/extras/tmc2240.py +++ b/klippy/extras/tmc2240.py @@ -280,17 +280,6 @@ class TMC2240CurrentHelper: self.fields = mcu_tmc.get_fields() self.Rref = config.getfloat('rref', 12000., minval=12000., maxval=60000.) - max_cur = self._get_ifs_rms(3) - run_current = config.getfloat('run_current', above=0., maxval=max_cur) - hold_current = config.getfloat('hold_current', max_cur, - above=0., maxval=max_cur) - self.req_hold_current = hold_current - current_range = self._calc_current_range(run_current) - self.fields.set_field("current_range", current_range) - gscaler, irun, ihold = self._calc_current(run_current, hold_current) - self.fields.set_field("globalscaler", gscaler) - self.fields.set_field("ihold", ihold) - self.fields.set_field("irun", irun) def _get_ifs_rms(self, current_range=None): if current_range is None: current_range = self.fields.get_field("current_range") @@ -327,12 +316,15 @@ class TMC2240CurrentHelper: bits = self.fields.get_field(field_name) return globalscaler * (bits + 1) * ifs_rms / (256. * 32.) def get_current(self): - ifs_rms = self._get_ifs_rms() + ifs_rms = self._get_ifs_rms(3) run_current = self._calc_current_from_field("irun") hold_current = self._calc_current_from_field("ihold") - return (run_current, hold_current, self.req_hold_current, ifs_rms) + return (run_current, hold_current, ifs_rms) def set_current(self, run_current, hold_current, print_time): - self.req_hold_current = hold_current + current_range = self._calc_current_range(run_current) + if current_range != self.fields.get_field("current_range"): + val = self.fields.set_field("current_range", current_range) + self.mcu_tmc.set_register("DRV_CONF", val, print_time) gscaler, irun, ihold = self._calc_current(run_current, hold_current) val = self.fields.set_field("globalscaler", gscaler) self.mcu_tmc.set_register("GLOBALSCALER", val, print_time) diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index c97f1effc..39ca37b94 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -118,13 +118,8 @@ class TMC2660CurrentHelper: self.name = config.get_name().split()[-1] self.mcu_tmc = mcu_tmc self.fields = mcu_tmc.get_fields() - self.current = config.getfloat('run_current', minval=0.1, - maxval=MAX_CURRENT) self.sense_resistor = config.getfloat('sense_resistor') - vsense, cs = self._calc_current(self.current) - self.fields.set_field("cs", cs) - self.fields.set_field("vsense", vsense) - + self.current = .0 # Register ready/printing handlers self.idle_current_percentage = config.getint( 'idle_current_percent', default=100, minval=0, maxval=100) @@ -177,7 +172,7 @@ class TMC2660CurrentHelper: self.mcu_tmc.set_register("DRVCONF", val, print_time) def get_current(self): - return self.current, None, None, MAX_CURRENT + return (self.current, None, MAX_CURRENT) def set_current(self, run_current, hold_current, print_time): self.current = run_current diff --git a/klippy/extras/tmc5160.py b/klippy/extras/tmc5160.py index b773135c2..bd4d7d069 100644 --- a/klippy/extras/tmc5160.py +++ b/klippy/extras/tmc5160.py @@ -268,16 +268,7 @@ class TMC5160CurrentHelper: self.name = config.get_name().split()[-1] self.mcu_tmc = mcu_tmc self.fields = mcu_tmc.get_fields() - run_current = config.getfloat('run_current', - above=0., maxval=MAX_CURRENT) - hold_current = config.getfloat('hold_current', MAX_CURRENT, - above=0., maxval=MAX_CURRENT) - self.req_hold_current = hold_current self.sense_resistor = config.getfloat('sense_resistor', 0.075, above=0.) - gscaler, irun, ihold = self._calc_current(run_current, hold_current) - self.fields.set_field("globalscaler", gscaler) - self.fields.set_field("ihold", ihold) - self.fields.set_field("irun", irun) def _calc_globalscaler(self, current): globalscaler = int((current * 256. * math.sqrt(2.) * self.sense_resistor / VREF) + .5) @@ -307,9 +298,8 @@ class TMC5160CurrentHelper: def get_current(self): run_current = self._calc_current_from_field("irun") hold_current = self._calc_current_from_field("ihold") - return run_current, hold_current, self.req_hold_current, MAX_CURRENT + return (run_current, hold_current, MAX_CURRENT) def set_current(self, run_current, hold_current, print_time): - self.req_hold_current = hold_current gscaler, irun, ihold = self._calc_current(run_current, hold_current) val = self.fields.set_field("globalscaler", gscaler) self.mcu_tmc.set_register("GLOBALSCALER", val, print_time) From 7260cca7c890337669606f6a1538ad8010479466 Mon Sep 17 00:00:00 2001 From: Timofey Titovets Date: Thu, 24 Jul 2025 00:24:49 +0200 Subject: [PATCH 2/2] 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)