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 8e7ed287a..824720cd2 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -3755,6 +3755,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. @@ -3870,6 +3873,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. @@ -3915,6 +3921,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. @@ -3999,10 +4006,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 @@ -4076,6 +4083,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. @@ -4211,6 +4221,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 23b05305e..a7e499823 100644 --- a/klippy/extras/tmc.py +++ b/klippy/extras/tmc.py @@ -322,7 +322,21 @@ 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.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 @@ -346,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, @@ -389,25 +405,68 @@ 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) - hold_current = gcmd.get_float('HOLDCURRENT', None, - above=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 = 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 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() + 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 @@ -500,6 +559,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)) @@ -512,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/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..59df3d9fb 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -118,21 +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) - - # 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) + self.current = .0 def _calc_current_bits(self, current, vsense): vref = 0.165 if vsense else 0.310 @@ -157,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) @@ -177,7 +154,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)