diff --git a/config/example-extras.cfg b/config/example-extras.cfg index e81e05d1..a686e80f 100644 --- a/config/example-extras.cfg +++ b/config/example-extras.cfg @@ -1050,9 +1050,9 @@ #run_current: # The amount of current (in ampere) used by the driver during stepper # movement. This parameter must be provided. -#sense_resistor: 0.051 -# The resistance (in ohms) of the motor sense resistor. The default -# is 0.051 ohms. +#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 @@ -1062,42 +1062,35 @@ # their position. There is also small delay until the current is # raised again, so take this into account when commanding fast moves # while the stepper is idling. The default is 100 (no reduction). -#driver_DEDGE: False -#driver_TBL: 36 -# Valid values are 16, 24, 36, 54. -#driver_CHM: spreadcycle -# Valid values are 'spreadcycle' and 'constant_toff' -#driver_RNDTF: False -#driver_HDEC: False -#driver_HEND: 7 if spreadcycle is used, 3 otherwise -#driver_HSTRT: 5 if spreadcycle is used, 4 otherwise -#driver_TOFF: 7 if spreadcycle is used, 4 otherwise -#driver_SEIMIN: half -# Valid values are 'quarter' and 'half'. -#driver_SEDN: 32 -# Valid values are 1, 2, 8, 32. +#driver_DEDGE: 0 +#driver_TBL: 2 +#driver_RNDTF: 0 +#driver_HDEC: 0 +#driver_CHM: 0 +#driver_HEND: 6 +#driver_HSTRT: 3 +#driver_TOFF: 4 +#driver_SEIMIN: 0 +#driver_SEDN: 0 #driver_SEMAX: 0 -#driver_SEUP: 1 -# Valid values are 1, 2, 4, 8. +#driver_SEUP: 0 #driver_SEMIN: 0 -#driver_SFILT: True -#driver_SGT: -64 -#driver_SLPH: min -# Valid values are 'min', 'min_tc', 'med_tc', 'max' -#driver_SLPL: min -# Valid values are 'min', 'med', 'max' -#driver_DISS2G: False -#driver_TS2G: 0.8 -# Valid values are 0.8, 1.2, 1.6, 3.2. -#driver_VSENSE: high -# Valid values are 'high' and 'low' +#driver_SFILT: 1 +#driver_SGT: 0 +#driver_SLPH: 0 +#driver_SLPL: 0 +#driver_DISS2G: 0 +#driver_TS2G: 3 +#driver_VSENSE: 1 # # Set the given parameter during the configuration of the TMC2660 # chip. This may be used to set custom driver parameters. The # defaults for each parameter are next to the parameter name in the # list above. See the TMC2660 datasheet about what each parameter # does and what the restrictions on parameter combinations are. - +# Be especially aware of the CHOPCONF register, where setting CHM to +# either 0 or one will lead to layout changes (the first bit of HDEC) +# is interpreted as the MSB of HSTRT in this case). # Homing override. One may use this mechanism to run a series of # g-code commands in place of a G28 found in the normal g-code input. diff --git a/config/generic-duet2.cfg b/config/generic-duet2.cfg index 9a290e5f..163575eb 100644 --- a/config/generic-duet2.cfg +++ b/config/generic-duet2.cfg @@ -98,6 +98,7 @@ spi_bus: 1 # All TMC2660 drivers are connected to USART1, which is bus 1 on the microsteps: 16 interpolate: True # 1/16 micro-steps interpolated to 1/256 run_current: 1.000 +sense_resistor: 0.051 idle_current_percent: 20 [stepper_y] @@ -115,6 +116,7 @@ spi_bus: 1 microsteps: 16 interpolate: True run_current: 1.000 +sense_resistor: 0.051 idle_current_percent: 20 [stepper_z] @@ -132,6 +134,7 @@ spi_bus: 1 microsteps: 16 interpolate: True run_current: 1.000 +sense_resistor: 0.051 #On drive E4 [stepper_z1] @@ -146,6 +149,7 @@ spi_bus: 1 microsteps: 16 interpolate: True run_current: 1.000 +sense_resistor: 0.051 #On drive E5 [stepper_z2] @@ -160,6 +164,7 @@ spi_bus: 1 microsteps: 16 interpolate: True run_current: 1.000 +sense_resistor: 0.051 #On drive E6 [stepper_z3] @@ -174,6 +179,7 @@ spi_bus: 1 microsteps: 16 interpolate: True run_current: 1.000 +sense_resistor: 0.051 #On drive E0 [extruder0] @@ -199,6 +205,7 @@ spi_bus: 1 microsteps: 16 interpolate: True run_current: 1.000 +sense_resistor: 0.051 #On drive E1 [extruder1] @@ -224,6 +231,7 @@ spi_bus: 1 microsteps: 16 interpolate: True run_current: 1.000 +sense_resistor: 0.051 # On drive E2 [extruder2] @@ -249,6 +257,7 @@ spi_bus: 1 microsteps: 16 interpolate: True run_current: 1.000 +sense_resistor: 0.051 # On drive E3 [extruder3] @@ -274,6 +283,7 @@ spi_bus: 1 microsteps: 16 interpolate: True run_current: 1.000 +sense_resistor: 0.051 [heater_bed] heater_pin: !PA19 diff --git a/klippy/extras/tmc2660.py b/klippy/extras/tmc2660.py index 393b5443..746c3064 100644 --- a/klippy/extras/tmc2660.py +++ b/klippy/extras/tmc2660.py @@ -3,77 +3,122 @@ # Copyright (C) 2018-2019 Florian Heilmann # # This file may be distributed under the terms of the GNU GPLv3 license. -import math -import bus - -REG_DRVCONF = 0xe << 16 -REG_SGCSCONF = 0xc << 16 -REG_SMARTEN = 0xa << 16 -REG_CHOPCONF = 0x8 << 16 -REG_DRVCTRL = 0x0 << 16 - -def get_bits(reg, field, value): - return ((value << reg[field][0]) & reg[field][1]) - -def read_field(bits, reg, field): - return (bits & reg[field][1]) >> reg[field][0] +import math, collections +import bus, tmc2130 def current_to_reg(current, sense_resistor, vsense_on): vsense = 0.165 if vsense_on else 0.310 cs = int(32 * current * sense_resistor * math.sqrt(2.) / vsense - 1. + .5) - return max(0, min(31, cs)) + return max(0, min(cs, 31)) -DRVCTRL = { - "INTPOL" : (9, 0x00100), - "DEDGE" : (8, 0x00080), - "MRES" : (0, 0x0000F) +Registers = { + "DRVCONF": 0xE, "SGCSCONF": 0xC, "SMARTEN": 0xA, + "CHOPCONF": 0x8, "DRVCTRL": 0x0 } -CHOPCONF = { - "TBL" : (15, 0x18000), - "CHM" : (14, 0x04000), - "RNDTF" : (13, 0x02000), - "HDEC" : (11, 0x01800), - "HEND" : (7, 0x00780), - "HSTRT" : (4, 0x00070), - "TOFF" : (0, 0x0000F) +Fields = {} + +Fields["DRVCTRL"] = { + "MRES": 0x0f, + "DEDGE": 0x01 << 8, + "INTPOL": 0x01 << 9, } -SMARTEN = { - "SEIMIN" : (15, 0x08000), - "SEDN" : (14, 0x06000), - "SEMAX" : (8, 0x00F00), - "SEUP" : (5, 0x00060), - "SEMIN" : (0, 0x0000F) +Fields["CHOPCONF"] = { + "TOFF": 0x0f, + "HSTRT": 0x7 << 4, + "HEND": 0x0f << 7, + "HDEC": 0x03 << 11, + "RNDTF": 0x01 << 13, + "CHM": 0x01 << 14, + "TBL": 0x03 << 15 } -SGCSCONF = { - "SFILT" : (16, 0x10000), - "SGT" : (8, 0x07800), - "CS" : (0, 0x0001F) +Fields["SMARTEN"] = { + "SEMIN" : 0x0f, + "SEUP": 0x03 << 5, + "SEMAX": 0x0f << 8, + "SEDN": 0x03 << 13, + "SEIMIN": 0x01 << 15 } -DRVCONF = { - "TST" : (16, 0x10000), - "SLPH" : (14, 0x0C000), - "SLPL" : (12, 0x03000), - "DISS2G" : (10, 0x00400), - "TS2G" : (8, 0x00300), - "SDOFF" : (7, 0x00080), - "VSENSE" : (6, 0x00040), - "RDSEL" : (4, 0x00030) +Fields["SGCSCONF"] = { + "CS": 0x1f, + "SGT": 0x7F << 8, + "SFILT": 0x01 << 16 } -READRSP = { - "MSTEP" : (10, 0xFFC00), - "STST" : (7, 0x00080), - "OLB" : (6, 0x00040), - "OLA" : (5, 0x00020), - "S2GB" : (4, 0x00010), - "S2GA" : (3, 0x00008), - "OTPW" : (2, 0x00004), - "OT" : (1, 0x00002), - "SG" : (0, 0x00001) +Fields["DRVCONF"] = { + "RDSEL": 0x03 << 4, + "VSENSE": 0x01 << 6, + "SDOFF": 0x01 << 7, + "TS2G": 0x03 << 8, + "DISS2G": 0x01 << 10, + "SLPL": 0x03 << 12, + "SLPH": 0x03 << 14, + "TST": 0x01 << 16 +} + +Fields["READRSP@RDSEL0"] = { + "SG": 0x01, + "OT": 0x01 << 1, + "OTPW": 0x01 << 2, + "S2GA": 0x01 << 3, + "S2GB": 0x01 << 4, + "OLA": 0x01 << 5, + "OLB": 0x01 << 6, + "STST": 0x01 << 7, + "MSTEP": 0x3ff << 10 +} + +Fields["READRSP@RDSEL1"] = { + "SG": 0x01, + "OT": 0x01 << 1, + "OTPW": 0x01 << 2, + "S2GA": 0x01 << 3, + "S2GB": 0x01 << 4, + "OLA": 0x01 << 5, + "OLB": 0x01 << 6, + "STST": 0x01 << 7, + "SG@RDSEL1": 0x3ff << 10 +} + +Fields["READRSP@RDSEL2"] = { + "SG": 0x01, + "OT": 0x01 << 1, + "OTPW": 0x01 << 2, + "S2GA": 0x01 << 3, + "S2GB": 0x01 << 4, + "OLA": 0x01 << 5, + "OLB": 0x01 << 6, + "STST": 0x01 << 7, + "SG@RDSEL2": 0x1f << 15, + "SE": 0x1f << 10 +} + +FieldFormatters = { + "MRES": (lambda v: "%d(%dusteps)" % (v, 0x100 >> v)), + "DEDGE": (lambda v: + "1(Both Edges Active)" if v else "0(Only Rising Edge active)"), + "INTPOL": (lambda v: "1(On)" if v else "0(Off)"), + "TOFF": (lambda v: ("%d" % v) if v else "0(Driver Disabled!)"), + "CHM": (lambda v: "1(constant toff)" if v else "0(spreadCycle)"), + "SGT": (lambda v: "%d" % (v)), + "SFILT": (lambda v: "1(Filtered mode)" if v else "0(Standard mode)"), + "VSENSE": (lambda v: "%d(%dmV)" % (v, 165 if v else 305)), + "SDOFF": (lambda v: "1(Step/Dir disabled" if v else "0(Step/dir enabled)"), + "DISS2G": (lambda v: "%d(Short to GND protection %s)" % (v, + "disabled" if v else "enabled")), + "MSTEP": (lambda v: "%d(%d, OA1 %s OA2)" % (v, v & 0xff, + "<=" if v & 0x100 else "=>")), + "SG": (lambda v: "%d(%s)" % (v, "Stall!" if v else "No Stall!")), + "OT": (lambda v: "1(Overtemp shutdown!)" if v else ""), + "OTPW": (lambda v: "1(Overtemp warning!)" if v else ""), + "S2GA": (lambda v: "1(Short to GND Coil A!)" if v else ""), + "S2GB": (lambda v: "1(Short to GND Coil B!)" if v else ""), + "OLA": (lambda v: "1(Open Load Coil A at slow speed!)" if v else ""), + "OLB": (lambda v: "1(Open Load Coil B at slow speed!)" if v else ""), + "STST": (lambda v: "1(Standstill detected!)" if v else ""), } class TMC2660: @@ -90,121 +135,59 @@ class TMC2660: "DUMP_TMC", "STEPPER", self.name, self.cmd_DUMP_TMC, desc=self.cmd_DUMP_TMC_help) # Setup driver registers + self.regs = collections.OrderedDict() + self.fields = tmc2130.FieldHelper(Fields, FieldFormatters, self.regs) + set_config_field = self.fields.set_config_field + # DRVCTRL steps = {'256': 0, '128': 1, '64': 2, '32': 3, '16': 4, '8': 5, '4': 6, '2': 7, '1': 8} self.driver_mres = config.getchoice('microsteps', steps) - self.driver_intpol = config.getboolean('interpolate', default=True) - self.current = config.getfloat('run_current', minval=0.1, - maxval=2.4) - self.driver_dedge = config.getboolean('driver_DEDGE', default=False) - + self.fields.set_field("MRES", self.driver_mres) + set_config_field(config, "DEDGE", 0) + set_config_field(config, "INTPOL", True, 'interpolate') # CHOPCONF - btime = {'16': 0, '24': 1, '36': 2, '54': 3} - self.driver_tbl = config.getchoice('driver_TBL', btime, default='36') - chm = {'spreadcycle': 0, 'constant_toff': 1} - self.driver_chm = config.getchoice('driver_CHM', chm, - default='spreadcycle') - self.driver_rndtf = config.getboolean('driver_RNDTF', False) - if self.driver_chm: - self.driver_hend = config.getint('driver_HEND', default=7, - minval=-3, maxval=12) + 3 - self.driver_hstrt = config.getint('driver_HSTRT', default=5, - minval=0, maxval=15) - self.driver_toff = config.getint('driver_TOFF', default=7, - minval=0, maxval=15) - # if chm is 1, HDEC1 is the MSB of HSTRT - self.driver_hdec = (config.getboolean('driver_HDEC', default=False) - | ((self.driver_hstrt & 0x8) >> 1)) - else: - self.driver_hdec = config.getboolean('driver_HDEC', default=False) - self.driver_hend = config.getint('driver_HEND', default=3, - minval=-3, maxval=12) + 3 - self.driver_hstrt = config.getint('driver_HSTRT', default=4, - minval=1, maxval=8) - 1 - self.driver_toff = config.getint('driver_TOFF', default=4, - minval=0, maxval=15) - if self.driver_hstrt + self.driver_hend > 15: - raise config.error("driver_HEND + HSTRT must be <= 15") - + set_config_field(config, "TBL", 2) + set_config_field(config, "RNDTF", 0) + set_config_field(config, "HDEC", 0) + set_config_field(config, "CHM", 0) + set_config_field(config, "HEND", 6) + set_config_field(config, "HSTRT", 3) + set_config_field(config, "TOFF", 4) + if not self.fields.get_field("CHM"): + if (self.fields.get_field("HSTRT") + + self.fields.get_field("HEND")) > 15: + raise config.error("driver_HEND + driver_HSTRT must be <= 15") # SMARTEN - csc = {'quarter': 1, 'half': 0} - self.driver_seimin = config.getchoice('driver_SEIMIN', csc, - default='half') - cds = {'32': 0, '8': 1, '2': 2, '1': 3} - self.driver_sedn = config.getchoice('driver_SEDN', cds, default='32') - self.driver_semax = config.getint('driver_SEMAX', default=0, - minval=0, maxval=15) - cis = {'1': 0, '2': 1, '4': 2, '8': 3} - self.driver_seup = config.getchoice('driver_SEUP', cis, default='1') - self.driver_semin = config.getint('driver_SEMIN', default=0, - minval=0, maxval=15) + set_config_field(config, "SEIMIN", 0) + set_config_field(config, "SEDN", 0) + set_config_field(config, "SEMAX", 0) + set_config_field(config, "SEUP", 0) + set_config_field(config, "SEMIN", 0) # DRVCONF - slph = {'min': 0, 'min_tc': 1, 'med_tc': 2, 'max': 3} - self.driver_slph = config.getchoice('driver_SLPH', slph, default='min') - slpl = {'min': 0, 'med': 2, 'max': 3} - self.driver_slpl = config.getchoice('driver_SLPL', slpl, default='min') - self.driver_diss2g = config.getboolean('driver_DISS2G', default=False) - ts2g = {'3.2': 0, '1.6': 1, '1.2': 2, '0.8': 3} - self.driver_ts2g = config.getchoice('driver_TS2G', ts2g, default='0.8') - # since we don't support SPI mode yet, this has to be False - self.driver_sdoff = False - vsense = {'low': 0, 'high': 1} - self.driver_vsense = config.getchoice('driver_VSENSE', vsense, - default='high') - self.sense_resistor = config.getfloat('sense_resistor', default=0.051) - self.driver_rdsel = 0 # Microsteps (used by endstop phase) + set_config_field(config, "SLPH", 0) + set_config_field(config, "SLPL", 0) + set_config_field(config, "DISS2G", 0) + set_config_field(config, "TS2G", 3) + set_config_field(config, "VSENSE", 1) + self.fields.set_field("RDSEL", 0) # needed for phase calculations + self.fields.set_field("SDOFF", 0) # only step/dir mode supported + self.sense_resistor = config.getfloat('sense_resistor') # SGSCONF - self.driver_sfilt = config.getboolean('driver_SFILT', default=True) - self.driver_sgt = config.getint('driver_sgt', default=-64, - minval=-64, maxval=63) + 64 - self.driver_cs = current_to_reg(self.current, self.sense_resistor, self.driver_vsense) + set_config_field(config, "SFILT", 1) + set_config_field(config, "SGT", 0) + self.current = config.getfloat('run_current', minval=0.1, + maxval=2.4) + self.driver_cs = current_to_reg(self.current, + self.sense_resistor, self.fields.get_field("VSENSE")) + self.fields.set_field("CS", self.driver_cs) - # Build and send registers - self.reg_drvconf = REG_DRVCONF | \ - get_bits(DRVCONF, "TST", 0) | \ - get_bits(DRVCONF, "SLPH", self.driver_slph) | \ - get_bits(DRVCONF, "SLPL", self.driver_slpl) | \ - get_bits(DRVCONF, "DISS2G", self.driver_diss2g) | \ - get_bits(DRVCONF, "TS2G", self.driver_ts2g) | \ - get_bits(DRVCONF, "SDOFF", self.driver_sdoff) | \ - get_bits(DRVCONF, "VSENSE", self.driver_vsense) | \ - get_bits(DRVCONF, "RDSEL", self.driver_rdsel) - self.add_config_cmd(self.reg_drvconf) + # Init Registers + self._init_registers(self) - self.reg_drvctrl = REG_DRVCTRL | \ - get_bits(DRVCTRL, "INTPOL", self.driver_intpol) | \ - get_bits(DRVCTRL, "DEDGE", self.driver_dedge) | \ - get_bits(DRVCTRL, "MRES", self.driver_mres) - self.add_config_cmd(self.reg_drvctrl) - - self.reg_chopconf = REG_CHOPCONF | \ - get_bits(CHOPCONF, "TBL", self.driver_tbl) | \ - get_bits(CHOPCONF, "CHM", self.driver_chm) | \ - get_bits(CHOPCONF, "RNDTF", self.driver_rndtf) | \ - get_bits(CHOPCONF, "HDEC", self.driver_hdec) | \ - get_bits(CHOPCONF, "HEND", self.driver_hend) | \ - get_bits(CHOPCONF, "HSTRT", self.driver_hstrt) | \ - get_bits(CHOPCONF, "TOFF", self.driver_toff) - self.add_config_cmd(self.reg_chopconf) - - self.reg_sgcsconf = REG_SGCSCONF | \ - get_bits(SGCSCONF, "SFILT", self.driver_sfilt) | \ - get_bits(SGCSCONF, "SGT", self.driver_sgt) | \ - get_bits(SGCSCONF, "CS", self.driver_cs) - self.add_config_cmd(self.reg_sgcsconf) - - self.reg_smarten = REG_SMARTEN | \ - get_bits(SMARTEN, "SEIMIN", self.driver_seimin) | \ - get_bits(SMARTEN, "SEDN", self.driver_sedn) | \ - get_bits(SMARTEN, "SEMAX", self.driver_semax) | \ - get_bits(SMARTEN, "SEUP", self.driver_seup) | \ - get_bits(SMARTEN, "SEMIN", self.driver_semin) - self.add_config_cmd(self.reg_smarten) - - # Idle timeout + # 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: @@ -213,42 +196,45 @@ class TMC2660: self.printer.register_event_handler("idle_timeout:ready", self.handle_ready) - def add_config_cmd(self, val): - data = [(val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff] - self.spi.spi_send(data) + def _init_registers(self, min_clock=0): + for reg_name in Registers: + self.set_register(reg_name, self.regs[reg_name]) + + def set_register(self, reg_name, val, min_clock=0): + reg = Registers[reg_name] + self.spi.spi_send([((val >> 16) | reg) & 0xff, + (val >> 8) & 0xff, val & 0xff], min_clock) + + def get_response(self): + reg = Registers["DRVCTRL"] + val = self.regs["DRVCTRL"] + params = self.spi.spi_transfer([((val >> 16) | reg) & 0xff, + (val >> 8) & 0xff, val & 0xff]) + pr = bytearray(params['response']) + return (pr[0] << 16) | (pr[1] << 8) | pr[2] def get_microsteps(self): - return 256 >> self.driver_mres + return 256 >> self.fields.get_field("MRES") def get_phase(self): - # Send DRVCTRL to get a response - reg_data = [(self.reg_drvctrl >> 16) & 0xff, - (self.reg_drvctrl >> 8) & 0xff, self.reg_drvctrl & 0xff] - params = self.spi.spi_transfer(reg_data) - pr = bytearray(params['response']) - steps = (((pr[0] << 16) | (pr[1] << 8) | pr[2]) - & READRSP['MSTEP'][1]) >> READRSP['MSTEP'][0] - return steps >> self.driver_mres + mscnt = self.fields.get_field("MSTEP", self.get_response()) + return mscnt >> self.driver_mres def handle_printing(self, print_time): - self.set_current(print_time, self.current) + self.set_current(0., self.current) # workaround def handle_ready(self, print_time): self.set_current(print_time, (float(self.idle_current_percentage) * self.current / 100)) def set_current(self, print_time, current): - self.driver_cs = current_to_reg(self.current, self.sense_resistor, self.driver_vsense) - reg = self.reg_sgcsconf - reg &= ~(SGCSCONF["CS"][1]) - reg |= get_bits(SGCSCONF, "CS", self.driver_cs) - reg_data = [(reg >> 16) & 0xff, (reg >> 8) & 0xff, reg & 0xff] + self.driver_cs = current_to_reg(current, self.sense_resistor, + self.fields.get_field("VSENSE")) + self.fields.set_field("CS", self.driver_cs) clock = self.spi.get_mcu().print_time_to_clock(print_time) - self.spi.spi_send(reg_data, minclock=clock, reqclock=clock) + self.set_register("SGCSCONF", self.regs["SGCSCONF"], min_clock=clock) - cmd_SET_TMC_CURRENT_help = ( - "Set the current of a TMC2660 driver (between %d and %d)" % ( - 0.1, 2.4)) + cmd_SET_TMC_CURRENT_help = "Set the current of a TMC2660 driver" def cmd_SET_TMC_CURRENT(self, params): gcode = self.printer.lookup_object('gcode') if 'CURRENT' in params: @@ -262,20 +248,15 @@ class TMC2660: def cmd_DUMP_TMC(self, params): self.printer.lookup_object('toolhead').get_last_move_time() gcode = self.printer.lookup_object('gcode') - for reg_name , val in zip(["DRVCONF", "DRVCTRL", "CHOPCONF", - "SGCSCONF", "SMARTEN"], - [self.reg_drvconf, self.reg_drvctrl, - self.reg_chopconf, self.reg_sgcsconf, - self.reg_smarten]): - msg = "%-15s %08x" % (reg_name + " (cached):", val) + gcode.respond_info("========== Write-only registers ==========") + for reg_name in Registers: + msg = self.fields.pretty_format(reg_name, self.regs[reg_name]) gcode.respond_info(msg) + # Send one register to get the return data - reg_data = [(self.reg_drvctrl >> 16) & 0xff, - (self.reg_drvctrl >> 8) & 0xff, self.reg_drvctrl & 0xff] - params = self.spi.spi_transfer(reg_data) - pr = bytearray(params['response']) - msg = "%-15s %08x" % ( - "RESPONSE:", ((pr[0] << 16) | (pr[1] << 8) | pr[2])) + gcode.respond_info("========== Queried registers ==========") + return_format = "READRSP@RDSEL" + str(self.fields.get_field("RDSEL")) + msg = self.fields.pretty_format(return_format, self.get_response()) gcode.respond_info(msg) def load_config_prefix(config):