Skip to content

Commit

Permalink
feat(bootloader reset): Tighter transitions on Unix systems
Browse files Browse the repository at this point in the history
Closes #712
  • Loading branch information
radimkarnis committed Jan 4, 2023
1 parent cee66a2 commit 353cefc
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 0 deletions.
9 changes: 9 additions & 0 deletions esptool/loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
DEFAULT_RESET_DELAY,
HardReset,
USBJTAGSerialReset,
UnixTightReset,
)
from .util import FatalError, NotImplementedInROMError, UnsupportedCommandError
from .util import byte, hexify, mask_to_shift, pad_to
Expand Down Expand Up @@ -573,6 +574,14 @@ def _construct_reset_strategy_sequence(self, mode):
return (USBJTAGSerialReset(self._port),)

# USB-to-Serial bridge
if os.name != "nt" and not self._port.name.startswith("rfc2217:"):
return (
UnixTightReset(self._port, delay),
UnixTightReset(self._port, extra_delay),
ClassicReset(self._port, delay),
ClassicReset(self._port, extra_delay),
)

return (
ClassicReset(self._port, delay),
ClassicReset(self._port, extra_delay),
Expand Down
45 changes: 45 additions & 0 deletions esptool/reset.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,23 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later

import os
import struct
import time

# Used for resetting into bootloader on Unix-like systems
if os.name != "nt":
import fcntl
import termios

# Constants used for terminal status lines reading/setting.
# Taken from pySerial's backend for IO:
# https://github.com/pyserial/pyserial/blob/master/serial/serialposix.py
TIOCMSET = getattr(termios, "TIOCMSET", 0x5418)
TIOCMGET = getattr(termios, "TIOCMGET", 0x5415)
TIOCM_DTR = getattr(termios, "TIOCM_DTR", 0x002)
TIOCM_RTS = getattr(termios, "TIOCM_RTS", 0x004)

DEFAULT_RESET_DELAY = 0.05 # default time to wait before releasing boot pin after reset


Expand All @@ -26,6 +41,20 @@ def _setRTS(self, state):
# request is sent with the updated RTS state and the same DTR state
self.port.setDTR(self.port.dtr)

def _setDTRandRTS(self, dtr=False, rts=False):
status = struct.unpack(
"I", fcntl.ioctl(self.port.fileno(), TIOCMGET, struct.pack("I", 0))
)[0]
if dtr:
status |= TIOCM_DTR
else:
status &= ~TIOCM_DTR
if rts:
status |= TIOCM_RTS
else:
status &= ~TIOCM_RTS
fcntl.ioctl(self.port.fileno(), TIOCMSET, struct.pack("I", status))


class ClassicReset(ResetStrategy):
"""
Expand All @@ -42,6 +71,22 @@ def __call__(self):
self._setDTR(False) # IO0=HIGH, done


class UnixTightReset(ResetStrategy):
"""
UNIX-only reset sequence with custom implementation,
which allows setting DTR and RTS lines at the same time.
"""

def __call__(self):
self._setDTRandRTS(False, False)
self._setDTRandRTS(True, True)
self._setDTRandRTS(False, True) # IO0=HIGH & EN=LOW, chip in reset
time.sleep(0.1)
self._setDTRandRTS(True, False) # IO0=LOW & EN=HIGH, chip out of reset
time.sleep(self.reset_delay)
self._setDTRandRTS(False, False) # IO0=HIGH, done


class USBJTAGSerialReset(ResetStrategy):
"""
Custom reset sequence, which is required when the device
Expand Down

0 comments on commit 353cefc

Please sign in to comment.