tmc: Add support for periodic checking of driver status
Check the status of all Trinamic stepper motor drivers once a second. If the driver reports an error then invoke a shutdown. Also log any serious warnings. Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
53b10d3ae7
commit
f035de264f
|
@ -6,6 +6,11 @@ All dates in this document are approximate.
|
|||
|
||||
# Changes
|
||||
|
||||
20210227: TMC stepper motor drivers in UART or SPI mode are now
|
||||
queried once per second whenever they are enabled - if the driver can
|
||||
not be contacted or if the driver reports an error, then Klipper will
|
||||
transition to a shutdown state.
|
||||
|
||||
20210219: The `rpi_temperature` module has been renamed to
|
||||
`temperature_host`. Replace any occurrences of `sensor_type:
|
||||
rpi_temperature` with `sensor_type: temperature_host`. The path to
|
||||
|
|
|
@ -76,17 +76,90 @@ class FieldHelper:
|
|||
return "%-11s %08x%s" % (reg_name + ":", reg_value, "".join(fields))
|
||||
|
||||
|
||||
######################################################################
|
||||
# Periodic error checking
|
||||
######################################################################
|
||||
|
||||
class TMCErrorCheck:
|
||||
def __init__(self, config, mcu_tmc, clear_gstat=True):
|
||||
self.printer = config.get_printer()
|
||||
self.stepper_name = ' '.join(config.get_name().split()[1:])
|
||||
self.mcu_tmc = mcu_tmc
|
||||
self.fields = mcu_tmc.get_fields()
|
||||
self.check_timer = None
|
||||
# Setup for GSTAT query
|
||||
self.clear_gstat = clear_gstat
|
||||
reg_name = self.fields.lookup_register("drv_err")
|
||||
if reg_name is not None:
|
||||
self.gstat_reg_info = [0, reg_name, 0xffffffff, 0xffffffff]
|
||||
else:
|
||||
self.gstat_reg_info = None
|
||||
# Setup for DRV_STATUS query
|
||||
reg_name = self.fields.lookup_register("ot")
|
||||
mask = err_mask = 0
|
||||
err_fields = ["ot", "s2ga", "s2gb", "s2vsa", "s2vsb"]
|
||||
warn_fields = ["otpw", "t120", "t143", "t150", "t157"]
|
||||
for f in err_fields + warn_fields:
|
||||
if f in self.fields.all_fields[reg_name]:
|
||||
mask |= self.fields.all_fields[reg_name][f]
|
||||
if f in err_fields:
|
||||
err_mask |= self.fields.all_fields[reg_name][f]
|
||||
self.drv_status_reg_info = [0, reg_name, mask, err_mask]
|
||||
def _query_register(self, reg_info, try_clear=False):
|
||||
last_value, reg_name, mask, err_mask = reg_info
|
||||
count = 0
|
||||
while 1:
|
||||
val = self.mcu_tmc.get_register(reg_name)
|
||||
if val & mask != last_value & mask:
|
||||
fmt = self.fields.pretty_format(reg_name, val)
|
||||
logging.info("TMC '%s' reports %s", self.stepper_name, fmt)
|
||||
reg_info[0] = last_value = val
|
||||
if not val & err_mask:
|
||||
break
|
||||
count += 1
|
||||
if count >= 3:
|
||||
fmt = self.fields.pretty_format(reg_name, val)
|
||||
raise self.printer.command_error("TMC '%s' reports error: %s"
|
||||
% (self.stepper_name, fmt))
|
||||
if try_clear:
|
||||
try_clear = False
|
||||
self.mcu_tmc.set_register(reg_name, val & err_mask)
|
||||
def _do_periodic_check(self, eventtime, try_clear=False):
|
||||
try:
|
||||
self._query_register(self.drv_status_reg_info)
|
||||
if self.gstat_reg_info is not None:
|
||||
self._query_register(self.gstat_reg_info, try_clear=try_clear)
|
||||
except self.printer.command_error as e:
|
||||
self.printer.invoke_shutdown(str(e))
|
||||
return self.printer.get_reactor().NEVER
|
||||
return eventtime + 1.
|
||||
def stop_checks(self):
|
||||
if self.check_timer is None:
|
||||
return
|
||||
self.printer.get_reactor().unregister_timer(self.check_timer)
|
||||
self.check_timer = None
|
||||
def start_checks(self):
|
||||
if self.check_timer is not None:
|
||||
self.stop_checks()
|
||||
self._do_periodic_check(0., try_clear=self.clear_gstat)
|
||||
reactor = self.printer.get_reactor()
|
||||
curtime = reactor.monotonic()
|
||||
self.check_timer = reactor.register_timer(self._do_periodic_check,
|
||||
curtime + 1.)
|
||||
|
||||
|
||||
######################################################################
|
||||
# G-Code command helpers
|
||||
######################################################################
|
||||
|
||||
class TMCCommandHelper:
|
||||
def __init__(self, config, mcu_tmc, current_helper):
|
||||
def __init__(self, config, mcu_tmc, current_helper, clear_gstat=True):
|
||||
self.printer = config.get_printer()
|
||||
self.stepper_name = ' '.join(config.get_name().split()[1:])
|
||||
self.name = config.get_name().split()[-1]
|
||||
self.mcu_tmc = mcu_tmc
|
||||
self.current_helper = current_helper
|
||||
self.echeck_helper = TMCErrorCheck(config, mcu_tmc, clear_gstat)
|
||||
self.fields = mcu_tmc.get_fields()
|
||||
self.read_registers = self.read_translate = None
|
||||
self.toff = None
|
||||
|
@ -153,6 +226,7 @@ class TMCCommandHelper:
|
|||
# Shared enable via comms handling
|
||||
val = self.fields.set_field("toff", self.toff)
|
||||
self._init_registers(print_time)
|
||||
self.echeck_helper.start_checks()
|
||||
except self.printer.command_error as e:
|
||||
self.printer.invoke_shutdown(str(e))
|
||||
def _do_disable(self, print_time):
|
||||
|
@ -161,6 +235,7 @@ class TMCCommandHelper:
|
|||
val = self.fields.set_field("toff", 0)
|
||||
reg_name = self.fields.lookup_register("toff")
|
||||
self.mcu_tmc.set_register(reg_name, val, print_time)
|
||||
self.echeck_helper.stop_checks()
|
||||
except self.printer.command_error as e:
|
||||
self.printer.invoke_shutdown(str(e))
|
||||
def handle_stepper_enable(self, print_time, is_enable):
|
||||
|
|
|
@ -241,7 +241,8 @@ class TMC2130:
|
|||
tmc.TMCVirtualPinHelper(config, self.mcu_tmc)
|
||||
# Register commands
|
||||
current_helper = TMCCurrentHelper(config, self.mcu_tmc)
|
||||
cmdhelper = tmc.TMCCommandHelper(config, self.mcu_tmc, current_helper)
|
||||
cmdhelper = tmc.TMCCommandHelper(config, self.mcu_tmc, current_helper,
|
||||
clear_gstat=False)
|
||||
cmdhelper.setup_register_dump(ReadRegisters)
|
||||
# Setup basic register values
|
||||
mh = tmc.TMCMicrostepHelper(config, self.mcu_tmc)
|
||||
|
|
Loading…
Reference in New Issue