diff --git a/config/example.cfg b/config/example.cfg index c579864e..b93dda6f 100644 --- a/config/example.cfg +++ b/config/example.cfg @@ -288,15 +288,16 @@ pin_map: arduino # default is to not enable the aliases. #restart_method: # This controls the mechanism the host will use to reset the -# micro-controller. The choices are 'arduino', 'rpi_usb', and -# 'command'. The 'arduino' method (toggle DTR) is common on Arduino -# boards and clones. The 'rpi_usb' method is useful on Raspberry Pi -# boards with micro-controllers powered over USB - it briefly -# disables power to all USB ports to accomplish a micro-controller -# reset. The 'command' method involves sending a Klipper command to -# the micro-controller so that it can reset itself. The default is -# 'arduino' if the micro-controller communicates over a serial port, -# 'command' otherwise. +# micro-controller. The choices are 'arduino', 'cheetah', 'rpi_usb', +# and 'command'. The 'arduino' method (toggle DTR) is common on +# Arduino boards and clones. The 'cheetah' method is a special +# method needed for some Fysetc Cheetah boards. The 'rpi_usb' method +# is useful on Raspberry Pi boards with micro-controllers powered +# over USB - it briefly disables power to all USB ports to +# accomplish a micro-controller reset. The 'command' method involves +# sending a Klipper command to the micro-controller so that it can +# reset itself. The default is 'arduino' if the micro-controller +# communicates over a serial port, 'command' otherwise. # The printer section controls high level printer settings. [printer] diff --git a/klippy/mcu.py b/klippy/mcu.py index 49ec6cb0..36e9c48d 100644 --- a/klippy/mcu.py +++ b/klippy/mcu.py @@ -386,16 +386,22 @@ class MCU: self._disconnect) # Serial port self._serialport = config.get('serial', '/dev/ttyS0') + serial_rts = True + if config.get('restart_method', None) == "cheetah": + # Special case: Cheetah boards require RTS to be deasserted, else + # a reset will trigger the built-in bootloader. + serial_rts = False baud = 0 if not (self._serialport.startswith("/dev/rpmsg_") or self._serialport.startswith("/tmp/klipper_host_")): baud = config.getint('baud', 250000, minval=2400) self._serial = serialhdl.SerialReader( - self._reactor, self._serialport, baud) + self._reactor, self._serialport, baud, serial_rts) # Restarts self._restart_method = 'command' if baud: - rmethods = {m: m for m in [None, 'arduino', 'command', 'rpi_usb']} + rmethods = {m: m for m in [None, 'arduino', 'cheetah', 'command', + 'rpi_usb']} self._restart_method = config.getchoice( 'restart_method', rmethods, None) self._reset_cmd = self._config_reset_cmd = None @@ -683,6 +689,10 @@ class MCU: logging.info("Attempting MCU '%s' reset", self._name) self._disconnect() serialhdl.arduino_reset(self._serialport, self._reactor) + def _restart_cheetah(self): + logging.info("Attempting MCU '%s' Cheetah-style reset", self._name) + self._disconnect() + serialhdl.cheetah_reset(self._serialport, self._reactor) def _restart_via_command(self): if ((self._reset_cmd is None and self._config_reset_cmd is None) or not self._clocksync.is_active()): @@ -713,6 +723,8 @@ class MCU: self._restart_rpi_usb() elif self._restart_method == 'command': self._restart_via_command() + elif self._restart_method == 'cheetah': + self._restart_cheetah() else: self._restart_arduino() # Misc external commands diff --git a/klippy/serialhdl.py b/klippy/serialhdl.py index bc8a9d3c..46e31ed4 100644 --- a/klippy/serialhdl.py +++ b/klippy/serialhdl.py @@ -13,12 +13,13 @@ class error(Exception): class SerialReader: BITS_PER_BYTE = 10. - def __init__(self, reactor, serialport, baud): + def __init__(self, reactor, serialport, baud, rts=True): self.reactor = reactor self.serialport = serialport self.baud = baud # Serial port self.ser = None + self.rts = rts self.msgparser = msgproto.MessageParser() # C interface self.ffi_main, self.ffi_lib = chelper.get_ffi() @@ -85,7 +86,10 @@ class SerialReader: try: if self.baud: self.ser = serial.Serial( - self.serialport, self.baud, timeout=0, exclusive=True) + baudrate=self.baud, timeout=0, exclusive=True) + self.ser.port = self.serialport + self.ser.rts = self.rts + self.ser.open() else: self.ser = open(self.serialport, 'rb+') except (OSError, IOError, serial.SerialException) as e: @@ -266,6 +270,32 @@ def stk500v2_leave(ser, reactor): logging.debug("Got %s from stk500v2", repr(res)) ser.baudrate = origbaud +def cheetah_reset(serialport, reactor): + # Fysetc Cheetah v1.2 boards have a weird stateful circuitry for + # configuring the bootloader. This sequence takes care of disabling it for + # sure. + # Open the serial port with RTS asserted + ser = serial.Serial(baudrate=2400, timeout=0, exclusive=True) + ser.port = serialport + ser.rts = True + ser.open() + ser.read(1) + reactor.pause(reactor.monotonic() + 0.100) + # Toggle DTR + ser.dtr = True + reactor.pause(reactor.monotonic() + 0.100) + ser.dtr = False + # Deassert RTS + reactor.pause(reactor.monotonic() + 0.100) + ser.rts = False + reactor.pause(reactor.monotonic() + 0.100) + # Toggle DTR again + ser.dtr = True + reactor.pause(reactor.monotonic() + 0.100) + ser.dtr = False + reactor.pause(reactor.monotonic() + 0.100) + ser.close() + # Attempt an arduino style reset on a serial port def arduino_reset(serialport, reactor): # First try opening the port at a different baud