serial: Add Fysetc Cheetah board specific reset sequence

Fysetc Cheetah v1.2 boards require a special sequence to reset reliably.
This sequence works for me in all cases. Simpler sequences without
double reset did not work correctly. This is likely because of a weird
stateful circuitry for toggling the bootloader state.

Cheetah boards use RTS to configure bootloader triggering. By default,
pySerial sets RTS on connect, which unfortunately configures the board
to start the bootloader on reset.

Add a toggle for the RTS state to allow users to workaround. The RTS state
is set before the serial connection is opened, so there are no glitches.

Addresses #2026.

Signed-off-by: Grigori Goronzy <greg@chown.ath.cx>
This commit is contained in:
Grigori Goronzy 2020-04-01 23:52:55 +02:00 committed by KevinOConnor
parent 5c8d15bbee
commit 0a20430e07
3 changed files with 56 additions and 13 deletions

View File

@ -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]

View File

@ -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

View File

@ -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