Added support for all STC8 and STC32 series

This commit is contained in:
Vincent DEFERT 2023-05-29 09:15:07 +02:00
parent 796a17f7cf
commit 20ae770f8f
5 changed files with 1650 additions and 1325 deletions

View File

@ -33,6 +33,8 @@ from stcgal.protocols import Stc15Protocol
from stcgal.protocols import Stc15AProtocol from stcgal.protocols import Stc15AProtocol
from stcgal.protocols import StcUsb15Protocol from stcgal.protocols import StcUsb15Protocol
from stcgal.protocols import Stc8Protocol from stcgal.protocols import Stc8Protocol
from stcgal.protocols import Stc8dProtocol
from stcgal.protocols import Stc8gProtocol
from stcgal.protocols import StcAutoProtocol from stcgal.protocols import StcAutoProtocol
from stcgal.protocols import StcProtocolException from stcgal.protocols import StcProtocolException
from stcgal.protocols import StcFramingException from stcgal.protocols import StcFramingException
@ -43,6 +45,8 @@ class StcGal:
def __init__(self, opts): def __init__(self, opts):
self.opts = opts self.opts = opts
self.hexFileType = 8
self.linearBaseAddress = 0
self.initialize_protocol(opts) self.initialize_protocol(opts)
def initialize_protocol(self, opts): def initialize_protocol(self, opts):
@ -56,14 +60,19 @@ class StcGal:
elif opts.protocol == "stc12": elif opts.protocol == "stc12":
self.protocol = Stc12Protocol(opts.port, opts.handshake, opts.baud) self.protocol = Stc12Protocol(opts.port, opts.handshake, opts.baud)
elif opts.protocol == "stc15a": elif opts.protocol == "stc15a":
self.protocol = Stc15AProtocol(opts.port, opts.handshake, opts.baud, self.protocol = Stc15AProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
round(opts.trim * 1000))
elif opts.protocol == "stc15": elif opts.protocol == "stc15":
self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud, self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
round(opts.trim * 1000))
elif opts.protocol == "stc8": elif opts.protocol == "stc8":
self.protocol = Stc8Protocol(opts.port, opts.handshake, opts.baud, self.protocol = Stc8Protocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
round(opts.trim * 1000)) elif opts.protocol == "stc8d":
self.protocol = Stc8dProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
elif opts.protocol == "stc8g":
"""FIXME Ugly hack, but works until I fully implement the STC8G protocol"""
if opts.trim < 27360:
self.protocol = Stc8dProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
else:
self.protocol = Stc8gProtocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000))
elif opts.protocol == "usb15": elif opts.protocol == "usb15":
self.protocol = StcUsb15Protocol() self.protocol = StcUsb15Protocol()
else: else:
@ -90,6 +99,8 @@ class StcGal:
fname.endswith(".ihex")): fname.endswith(".ihex")):
try: try:
hexfile = IHex.read(fileobj) hexfile = IHex.read(fileobj)
self.hexFileType = hexfile.get_mode()
self.linearBaseAddress = hexfile.get_linearBaseAddress()
binary = hexfile.extract_data() binary = hexfile.extract_data()
print("%d bytes (Intel HEX)" %len(binary)) print("%d bytes (Intel HEX)" %len(binary))
return binary return binary
@ -103,45 +114,57 @@ class StcGal:
def program_mcu(self): def program_mcu(self):
"""Execute the standard programming flow.""" """Execute the standard programming flow."""
code_size = self.protocol.model.code if self.opts.option: self.emit_options(self.opts.option)
ee_size = self.protocol.model.eeprom
if self.protocol.split_code and self.protocol.model.iap:
code_size = self.protocol.split_code
ee_size = self.protocol.split_eeprom
else:
code_size = self.protocol.model.code
ee_size = self.protocol.model.eeprom
print("Loading flash: ", end="") print("Loading flash: ", end="")
sys.stdout.flush() sys.stdout.flush()
bindata = self.load_file_auto(self.opts.code_image) bindata = self.load_file_auto(self.opts.code_image)
if self.protocol.model.mcs251 and self.hexFileType != 32:
print("Invalid input file. MCU is an MCS-251, input file MUST specify a linear", file=sys.stderr)
print("base address, i.e. contain a type 04 record. More information at:", file=sys.stderr)
print("https://en.wikipedia.org/wiki/Intel_HEX", file=sys.stderr)
else:
self.protocol.linearBaseAddress = self.linearBaseAddress
# warn if it overflows # warn if it overflows
if len(bindata) > code_size: if len(bindata) > code_size:
print("WARNING: code_image overflows into eeprom segment!", file=sys.stderr) print("WARNING: code_image overflows into eeprom segment!", file=sys.stderr)
if len(bindata) > (code_size + ee_size): if len(bindata) > (code_size + ee_size):
print("WARNING: code_image truncated!", file=sys.stderr) print("WARNING: code_image truncated!", file=sys.stderr)
bindata = bindata[0:code_size + ee_size] bindata = bindata[0:code_size + ee_size]
# add eeprom data if supplied # add eeprom data if supplied
if self.opts.eeprom_image: if self.opts.eeprom_image:
print("Loading EEPROM: ", end="") print("Loading EEPROM: ", end="")
sys.stdout.flush() sys.stdout.flush()
eedata = self.load_file_auto(self.opts.eeprom_image) eedata = self.load_file_auto(self.opts.eeprom_image)
if len(eedata) > ee_size: if len(eedata) > ee_size:
print("WARNING: eeprom_image truncated!", file=sys.stderr) print("WARNING: eeprom_image truncated!", file=sys.stderr)
eedata = eedata[0:ee_size] eedata = eedata[0:ee_size]
if len(bindata) < code_size: if len(bindata) < code_size:
bindata += bytes([0xff] * (code_size - len(bindata))) bindata += bytes([0xff] * (code_size - len(bindata)))
elif len(bindata) > code_size: elif len(bindata) > code_size:
print("WARNING: eeprom_image overlaps code_image!", file=sys.stderr) print("WARNING: eeprom_image overlaps code_image!", file=sys.stderr)
bindata = bindata[0:code_size] bindata = bindata[0:code_size]
bindata += eedata bindata += eedata
# pad to 512 byte boundary # pad to 512 byte boundary
if len(bindata) % 512: if len(bindata) % 512:
bindata += b'\xff' * (512 - len(bindata) % 512) bindata += b'\xff' * (512 - len(bindata) % 512)
if self.opts.option: self.emit_options(self.opts.option) self.protocol.handshake()
self.protocol.erase_flash(len(bindata), code_size)
self.protocol.handshake() self.protocol.program_flash(bindata)
self.protocol.erase_flash(len(bindata), code_size) self.protocol.program_options()
self.protocol.program_flash(bindata)
self.protocol.program_options()
self.protocol.disconnect() self.protocol.disconnect()
def erase_mcu(self): def erase_mcu(self):
@ -161,7 +184,7 @@ class StcGal:
return 0 return 0
try: try:
self.protocol.connect(autoreset=self.opts.autoreset, resetcmd=self.opts.resetcmd) self.protocol.connect(autoreset=self.opts.autoreset, resetcmd=self.opts.resetcmd, resetpin=self.opts.resetpin)
if isinstance(self.protocol, StcAutoProtocol): if isinstance(self.protocol, StcAutoProtocol):
if not self.protocol.protocol_name: if not self.protocol.protocol_name:
raise StcProtocolException("cannot detect protocol") raise StcProtocolException("cannot detect protocol")
@ -240,11 +263,13 @@ def cli():
parser.add_argument("eeprom_image", help="eeprom segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?') parser.add_argument("eeprom_image", help="eeprom segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?')
exclusives.add_argument("-e", "--erase", help="only erase flash memory", action="store_true") exclusives.add_argument("-e", "--erase", help="only erase flash memory", action="store_true")
parser.add_argument("-a", "--autoreset", help="cycle power automatically by asserting DTR", action="store_true") parser.add_argument("-a", "--autoreset", help="cycle power automatically by asserting DTR", action="store_true")
parser.add_argument("-A", "--resetpin", help="pin to hold down when using --autoreset (default: DTR)",
choices=["dtr", "rts"], default="dtr")
parser.add_argument("-r", "--resetcmd", help="shell command for board power-cycling (instead of DTR assertion)", action="store") parser.add_argument("-r", "--resetcmd", help="shell command for board power-cycling (instead of DTR assertion)", action="store")
parser.add_argument("-P", "--protocol", help="protocol version (default: auto)", parser.add_argument("-P", "--protocol", help="protocol version (default: auto)",
choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "stc8", "usb15", "auto"], default="auto") choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "stc8", "stc8d", "stc8g", "usb15", "auto"], default="auto")
parser.add_argument("-p", "--port", help="serial port device", default="/dev/ttyUSB0") parser.add_argument("-p", "--port", help="serial port device", default="/dev/ttyUSB0")
parser.add_argument("-b", "--baud", help="transfer baud rate (default: 19200)", type=BaudType(), default=19200) parser.add_argument("-b", "--baud", help="transfer baud rate (default: 19200)", type=BaudType(), default=115200)
parser.add_argument("-l", "--handshake", help="handshake baud rate (default: 2400)", type=BaudType(), default=2400) parser.add_argument("-l", "--handshake", help="handshake baud rate (default: 2400)", type=BaudType(), default=2400)
parser.add_argument("-o", "--option", help="set option (can be used multiple times, see documentation)", action="append") parser.add_argument("-o", "--option", help="set option (can be used multiple times, see documentation)", action="append")
parser.add_argument("-t", "--trim", help="RC oscillator frequency in kHz (STC15+ series only)", type=float, default=0.0) parser.add_argument("-t", "--trim", help="RC oscillator frequency in kHz (STC15+ series only)", type=float, default=0.0)

View File

@ -14,7 +14,6 @@ class IHex:
"""Read Intel HEX data from string or lines""" """Read Intel HEX data from string or lines"""
ihex = cls() ihex = cls()
segbase = 0
for line in lines: for line in lines:
line = line.strip() line = line.strip()
if not line: if not line:
@ -22,14 +21,14 @@ class IHex:
t, a, d = ihex.parse_line(line) t, a, d = ihex.parse_line(line)
if t == 0x00: if t == 0x00:
ihex.insert_data(segbase + a, d) ihex.insert_data(a, d)
elif t == 0x01: elif t == 0x01:
break # Should we check for garbage after this? break # Should we check for garbage after this?
elif t == 0x02: elif t == 0x02:
ihex.set_mode(16) ihex.set_mode(16)
segbase = struct.unpack(">H", d[0:2])[0] << 4 ihex.linearBaseAddress = struct.unpack(">H", d[0:2])[0] << 4
elif t == 0x03: elif t == 0x03:
ihex.set_mode(16) ihex.set_mode(16)
@ -39,7 +38,7 @@ class IHex:
elif t == 0x04: elif t == 0x04:
ihex.set_mode(32) ihex.set_mode(32)
segbase = struct.unpack(">H", d[0:2])[0] << 16 ihex.linearBaseAddress = struct.unpack(">H", d[0:2])[0] << 16
elif t == 0x05: elif t == 0x05:
ihex.set_mode(32) ihex.set_mode(32)
@ -63,6 +62,7 @@ class IHex:
self.start = None self.start = None
self.mode = 8 self.mode = 8
self.row_bytes = 16 self.row_bytes = 16
self.linearBaseAddress = 0
def set_row_bytes(self, row_bytes): def set_row_bytes(self, row_bytes):
"""Set output hex file row width (bytes represented per row).""" """Set output hex file row width (bytes represented per row)."""
@ -105,6 +105,12 @@ class IHex:
def set_mode(self, mode): def set_mode(self, mode):
self.mode = mode self.mode = mode
def get_mode(self):
return self.mode
def get_linearBaseAddress(self):
return self.linearBaseAddress
def get_area(self, addr): def get_area(self, addr):
for start, data in self.areas.items(): for start, data in self.areas.items():
end = start + len(data) end = start + len(data)
@ -193,6 +199,7 @@ class IHex:
output += self.make_line( output += self.make_line(
0x04, 0, struct.pack(">H", newsegbase)) 0x04, 0, struct.pack(">H", newsegbase))
segbase = newsegbase segbase = newsegbase
segbase = newsegbase
output += self.make_line(0x00, addr, chunk) output += self.make_line(0x00, addr, chunk)

File diff suppressed because it is too large Load Diff

View File

@ -788,4 +788,4 @@ class Stc8Option(BaseOption):
num_val = Utils.to_int(val) num_val = Utils.to_int(val)
if num_val < 512 or num_val > 65024 or (num_val % 512) != 0: if num_val < 512 or num_val > 65024 or (num_val % 512) != 0:
raise ValueError("must be between 512 and 65024 bytes and a multiple of 512 bytes") raise ValueError("must be between 512 and 65024 bytes and a multiple of 512 bytes")
self.msr[4] = num_val // 256 self.msr[4] = num_val // 256

View File

@ -86,12 +86,15 @@ class StcBaseProtocol(ABC):
self.mcu_bsl_version = "" self.mcu_bsl_version = ""
self.options = None self.options = None
self.model = None self.model = None
self.split_eeprom = None
self.split_code = None
self.uid = None self.uid = None
self.debug = False self.debug = False
self.status_packet = None self.status_packet = None
self.protocol_name = None self.protocol_name = None
self.progress = None self.progress = None
self.progress_cb = self.progress_bar_cb self.progress_cb = self.progress_bar_cb
self.linearBaseAddress = 0
def progress_text_cb(self, current, written, maximum): def progress_text_cb(self, current, written, maximum):
print(current, written, maximum) print(current, written, maximum)
@ -133,7 +136,7 @@ class StcBaseProtocol(ABC):
return packet[5:-1] return packet[5:-1]
@abstractmethod @abstractmethod
def write_packet(self, packet_data): def write_packet(self, packet_data, epilogue_len = 0):
pass pass
def read_packet(self): def read_packet(self):
@ -262,13 +265,23 @@ class StcBaseProtocol(ABC):
def set_option(self, name, value): def set_option(self, name, value):
self.options.set_option(name, value) self.options.set_option(name, value)
def reset_device(self, resetcmd=False): def reset_device(self, resetcmd=False, resetpin=False):
if not resetcmd: if not resetcmd:
print("Cycling power: ", end="") print("Cycling power: ", end="")
sys.stdout.flush() sys.stdout.flush()
self.ser.setDTR(True)
time.sleep(0.5) if resetpin == "rts":
self.ser.setDTR(False) self.ser.setRTS(True)
else:
self.ser.setDTR(True)
time.sleep(0.25)
if resetpin == "rts":
self.ser.setRTS(False)
else:
self.ser.setDTR(False)
time.sleep(0.030) time.sleep(0.030)
print("done") print("done")
else: else:
@ -278,7 +291,7 @@ class StcBaseProtocol(ABC):
print("Waiting for MCU: ", end="") print("Waiting for MCU: ", end="")
sys.stdout.flush() sys.stdout.flush()
def connect(self, autoreset=False, resetcmd=False): def connect(self, autoreset=False, resetcmd=False, resetpin=False):
"""Connect to MCU and initialize communication. """Connect to MCU and initialize communication.
Set up serial port, send sync sequence and get part info. Set up serial port, send sync sequence and get part info.
@ -297,7 +310,7 @@ class StcBaseProtocol(ABC):
self.ser.flushInput() self.ser.flushInput()
if autoreset: if autoreset:
self.reset_device(resetcmd) self.reset_device(resetcmd, resetpin)
else: else:
print("Waiting for MCU, please cycle power: ", end="") print("Waiting for MCU, please cycle power: ", end="")
sys.stdout.flush() sys.stdout.flush()
@ -377,7 +390,12 @@ class StcAutoProtocol(StcBaseProtocol):
("stc12", r"(STC|IAP)(10|11|12)\D"), ("stc12", r"(STC|IAP)(10|11|12)\D"),
("stc15a", r"(STC|IAP)15[FL][012]0\d(E|EA|)$"), ("stc15a", r"(STC|IAP)15[FL][012]0\d(E|EA|)$"),
("stc15", r"(STC|IAP|IRC)15\D"), ("stc15", r"(STC|IAP|IRC)15\D"),
("stc8", r"(STC|IAP|IRC)8")] ("stc8d", r"STC8H(3|4|8)K"),
("stc8d", r"STC32G"),
("stc8d", r"STC8A8K\d\dD4"),
("stc8g", r"STC8H"),
("stc8g", r"STC8G"),
("stc8", r"STC8\D")]
for protocol_name, pattern in protocol_database: for protocol_name, pattern in protocol_database:
if re.match(pattern, self.model.name): if re.match(pattern, self.model.name):
@ -392,7 +410,7 @@ class StcAutoProtocol(StcBaseProtocol):
def initialize_status(self, status_packet): def initialize_status(self, status_packet):
raise NotImplementedError raise NotImplementedError
def write_packet(self, packet_data): def write_packet(self, packet_data, epilogue_len = 0):
raise NotImplementedError raise NotImplementedError
@ -422,7 +440,7 @@ class Stc89Protocol(StcBaseProtocol):
payload = StcBaseProtocol.extract_payload(self, packet) payload = StcBaseProtocol.extract_payload(self, packet)
return payload[:-1] return payload[:-1]
def write_packet(self, packet_data): def write_packet(self, packet_data, epilogue_len = 0):
"""Send packet to MCU. """Send packet to MCU.
Constructs a packet with supplied payload and sends it to the MCU. Constructs a packet with supplied payload and sends it to the MCU.
@ -817,7 +835,7 @@ class Stc12BaseProtocol(StcBaseProtocol):
payload = StcBaseProtocol.extract_payload(self, packet) payload = StcBaseProtocol.extract_payload(self, packet)
return payload[:-2] return payload[:-2]
def write_packet(self, packet_data): def write_packet(self, packet_data, epilogue_len = 0):
"""Send packet to MCU. """Send packet to MCU.
Constructs a packet with supplied payload and sends it to the MCU. Constructs a packet with supplied payload and sends it to the MCU.
@ -835,6 +853,11 @@ class Stc12BaseProtocol(StcBaseProtocol):
# checksum and end code # checksum and end code
packet += struct.pack(">H", sum(packet[2:]) & 0xffff) packet += struct.pack(">H", sum(packet[2:]) & 0xffff)
packet += self.PACKET_END packet += self.PACKET_END
i = 0
while i < epilogue_len:
packet += bytes([0x66])
i += 1
self.dump_packet(packet, receive=False) self.dump_packet(packet, receive=False)
self.ser.write(packet) self.ser.write(packet)
@ -1561,156 +1584,6 @@ class Stc15Protocol(Stc15AProtocol):
print("Target UID: %s" % Utils.hexstr(self.uid)) print("Target UID: %s" % Utils.hexstr(self.uid))
class Stc8Protocol(Stc15Protocol):
"""Protocol handler for STC8 series"""
def __init__(self, port, handshake, baud, trim):
Stc15Protocol.__init__(self, port, handshake, baud, trim)
self.trim_divider = None
self.reference_voltage = None
self.mfg_date = ()
def initialize_options(self, status_packet):
"""Initialize options"""
if len(status_packet) < 17:
raise StcProtocolException("invalid options in status packet")
# create option state
self.options = Stc8Option(status_packet[9:12] + status_packet[15:17])
self.options.print()
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
if len(packet) < 39:
raise StcProtocolException("invalid status packet")
self.mcu_clock_hz, = struct.unpack(">I", packet[1:5])
self.external_clock = False
# all ones means no calibration
# new chips are shipped without any calibration
# XXX: somehow check if that still holds
if self.mcu_clock_hz == 0xffffffff: self.mcu_clock_hz = 0
# wakeup timer factory value
self.wakeup_freq, = struct.unpack(">H", packet[23:25])
self.reference_voltage, = struct.unpack(">H", packet[35:37])
self.mfg_date = (
2000 + Utils.decode_packed_bcd(packet[37]),
Utils.decode_packed_bcd(packet[38]),
Utils.decode_packed_bcd(packet[39])
)
bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
bl_minor = packet[22] & 0x0f
self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
bl_minor, chr(bl_stepping))
self.bsl_version = bl_version
def print_mcu_info(self):
"""Print additional STC8 info"""
super().print_mcu_info()
print("Target ref. voltage: %d mV" % self.reference_voltage)
print("Target mfg. date: %04d-%02d-%02d" % self.mfg_date)
def calibrate(self):
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
# handle uncalibrated chips
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
raise StcProtocolException("uncalibrated, please provide a trim value")
# determine target counter
user_speed = self.trim_frequency
if user_speed <= 0: user_speed = self.mcu_clock_hz
target_user_count = round(user_speed / (self.baud_handshake/2))
# calibration, round 1
print("Trimming frequency: ", end="")
sys.stdout.flush()
packet = bytes([0x00])
packet += struct.pack(">B", 12)
packet += bytes([0x00, 0x00, 23*1, 0x00, 23*2, 0x00])
packet += bytes([23*3, 0x00, 23*4, 0x00, 23*5, 0x00])
packet += bytes([23*6, 0x00, 23*7, 0x00, 23*8, 0x00])
packet += bytes([23*9, 0x00, 23*10, 0x00, 255, 0x00])
self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select ranges and trim values
for divider in (1, 2, 3, 4, 5):
user_trim = self.choose_range(packet, response, target_user_count * divider)
if user_trim is not None:
self.trim_divider = divider
break
if user_trim is None:
raise StcProtocolException("frequency trimming unsuccessful")
# calibration, round 2
packet = bytes([0x00])
packet += struct.pack(">B", 12)
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x00])
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x01])
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x02])
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x03])
self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select final values
user_trim, user_count = self.choose_trim(packet, response, target_user_count)
self.trim_value = user_trim
self.trim_frequency = round(user_count * (self.baud_handshake / 2) / self.trim_divider)
print("%.03f MHz" % (self.trim_frequency / 1E6))
# switch to programming frequency
print("Switching to %d baud: " % self.baud_transfer, end="")
sys.stdout.flush()
packet = bytes([0x01, 0x00, 0x00])
bauds = self.baud_transfer * 4
packet += struct.pack(">H", round(65536 - 24E6 / bauds))
packet += bytes([user_trim[1], user_trim[0]])
iap_wait = self.get_iap_delay(24E6)
packet += bytes([iap_wait])
self.write_packet(packet)
response = self.read_packet()
if len(response) < 1 or response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet")
self.ser.baudrate = self.baud_transfer
def build_options(self):
"""Build a packet of option data from the current configuration."""
msr = self.options.get_msr()
packet = 40 * bytearray([0xff])
packet[3] = 0
packet[6] = 0
packet[22] = 0
packet[24:28] = struct.pack(">I", self.trim_frequency)
packet[28:30] = self.trim_value
packet[30] = self.trim_divider
packet[32] = msr[0]
packet[36:40] = msr[1:5]
return bytes(packet)
def disconnect(self):
"""Disconnect from MCU"""
# reset mcu
packet = bytes([0xff])
self.write_packet(packet)
self.ser.close()
print("Disconnected!")
class StcUsb15Protocol(Stc15Protocol): class StcUsb15Protocol(Stc15Protocol):
"""USB should use large blocks""" """USB should use large blocks"""
PROGRAM_BLOCKSIZE = 128 PROGRAM_BLOCKSIZE = 128
@ -1772,7 +1645,7 @@ class StcUsb15Protocol(Stc15Protocol):
host2dev = usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.CTRL_OUT host2dev = usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.CTRL_OUT
self.dev.ctrl_transfer(host2dev, request, value, index, chunks) self.dev.ctrl_transfer(host2dev, request, value, index, chunks)
def connect(self, autoreset=False, resetcmd=False): def connect(self, autoreset=False, resetcmd=False, resetpin=False):
"""Connect to USB device and read info packet""" """Connect to USB device and read info packet"""
# USB support is optional. Provide an error if pyusb is not available. # USB support is optional. Provide an error if pyusb is not available.
@ -1874,3 +1747,399 @@ class StcUsb15Protocol(Stc15Protocol):
if self.dev: if self.dev:
self.write_packet(0xff) self.write_packet(0xff)
print("Disconnected!") print("Disconnected!")
class Stc8Protocol(Stc15Protocol):
"""Protocol handler for STC8 series"""
def __init__(self, port, handshake, baud, trim):
Stc15Protocol.__init__(self, port, handshake, baud, trim)
self.trim_divider = None
self.reference_voltage = None
self.mfg_date = ()
def initialize_options(self, status_packet):
"""Initialize options"""
if len(status_packet) < 17:
raise StcProtocolException("invalid options in status packet")
# create option state
self.options = Stc8Option(status_packet[9:12] + status_packet[15:17])
self.options.print()
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
if len(packet) < 39:
raise StcProtocolException("invalid status packet")
self.mcu_clock_hz, = struct.unpack(">I", packet[1:5])
self.external_clock = False
# all ones means no calibration
# new chips are shipped without any calibration
# XXX: somehow check if that still holds
if self.mcu_clock_hz == 0xffffffff: self.mcu_clock_hz = 0
# wakeup timer factory value
self.wakeup_freq, = struct.unpack(">H", packet[23:25])
self.reference_voltage, = struct.unpack(">H", packet[35:37])
self.mfg_date = (
2000 + Utils.decode_packed_bcd(packet[37]),
Utils.decode_packed_bcd(packet[38]),
Utils.decode_packed_bcd(packet[39])
)
bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
bl_minor = packet[22] & 0x0f
self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
bl_minor, chr(bl_stepping))
self.bsl_version = bl_version
def print_mcu_info(self):
"""Print additional STC8 info"""
super().print_mcu_info()
print("Target ref. voltage: %d mV" % self.reference_voltage)
print("Target mfg. date: %04d-%02d-%02d" % self.mfg_date)
def set_option(self, name, value):
super().set_option(name, value)
def calibrate(self):
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
# handle uncalibrated chips
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
raise StcProtocolException("uncalibrated, please provide a trim value")
# determine target counter
user_speed = self.trim_frequency
if user_speed <= 0: user_speed = self.mcu_clock_hz
target_user_count = round(user_speed / (self.baud_handshake/2))
# calibration, round 1
print("Trimming frequency: ", end="")
sys.stdout.flush()
packet = bytes([0x00])
packet += struct.pack(">B", 12)
packet += bytes([0x00, 0x00, 23*1, 0x00, 23*2, 0x00])
packet += bytes([23*3, 0x00, 23*4, 0x00, 23*5, 0x00])
packet += bytes([23*6, 0x00, 23*7, 0x00, 23*8, 0x00])
packet += bytes([23*9, 0x00, 23*10, 0x00, 255, 0x00])
self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select ranges and trim values
for divider in (1, 2, 3, 4, 5):
user_trim = self.choose_range(packet, response, target_user_count * divider)
if user_trim is not None:
self.trim_divider = divider
break
if user_trim is None:
raise StcProtocolException("frequency trimming unsuccessful")
# calibration, round 2
packet = bytes([0x00])
packet += struct.pack(">B", 12)
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x00])
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x01])
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x02])
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x03])
self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select final values
user_trim, user_count = self.choose_trim(packet, response, target_user_count)
self.trim_value = user_trim
self.trim_frequency = round(user_count * (self.baud_handshake / 2) / self.trim_divider)
print("%.03f MHz" % (self.trim_frequency / 1E6))
# switch to programming frequency
print("Switching to %d baud: " % self.baud_transfer, end="")
sys.stdout.flush()
packet = bytes([0x01, 0x00, 0x00])
bauds = self.baud_transfer * 4
packet += struct.pack(">H", round(65536 - 24E6 / bauds))
packet += bytes([user_trim[1], user_trim[0]])
iap_wait = self.get_iap_delay(24E6)
packet += bytes([iap_wait])
self.write_packet(packet)
response = self.read_packet()
if len(response) < 1 or response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet")
self.ser.baudrate = self.baud_transfer
def build_options(self):
"""Build a packet of option data from the current configuration."""
msr = self.options.get_msr()
packet = 40 * bytearray([0xff])
packet[3] = 0
packet[6] = 0
packet[22] = 0
packet[24:28] = struct.pack(">I", self.trim_frequency)
packet[28:30] = self.trim_value
packet[30] = self.trim_divider
packet[32] = msr[0]
packet[36:40] = msr[1:5]
return bytes(packet)
def disconnect(self):
"""Disconnect from MCU"""
# reset mcu
packet = bytes([0xff])
self.write_packet(packet)
self.ser.close()
print("Disconnected!")
class Stc8dProtocol(Stc8Protocol):
"""Protocol handler for STC8A8K64D4 series"""
def __init__(self, port, handshake, baud, trim):
Stc8Protocol.__init__(self, port, handshake, baud, trim)
def set_option(self, name, value):
super().set_option(name, value)
if name=='program_eeprom_split':
split_point = Utils.to_int(value);
if self.model.mcs251:
"""Minimum size is 1K in STC-ISP"""
if split_point == 0 and self.model.iap:
split_point = 0x400;
# CODE starts at 0xFF0000
self.split_code = 0x10000;
# EEPROM starts at 0xFE0000
self.split_eeprom = split_point;
else:
if split_point == 0 and self.model.iap:
split_point = self.model.code;
self.split_code = split_point;
self.split_eeprom = self.model.total - self.split_code;
def choose_range(self, packet, response, target_count):
"""Choose appropriate trim value mean for next round from challenge
responses."""
challenge_data = packet[2:]
calib_data = response[2:]
calib_len = response[1]
if len(calib_data) < 2 * calib_len:
raise StcProtocolException("range calibration data missing")
for i in range(calib_len >> 1):
count_a, count_b = struct.unpack(
">HH", calib_data[4 * i: 4 * i + 4])
trim_a, trim_b, trim_range = struct.unpack(
">BxBB", challenge_data[4 * i:4 * i + 4])
if ((count_a <= target_count and count_b >= target_count)):
target_trim = round(
(target_count - count_a) * (trim_b - trim_a) / (count_b - count_a) + trim_a)
# target_trim will be set at the center of packet in the 2nd calibration
if target_trim < 6 or target_trim > 255 - 5:
raise StcProtocolException("frequency trimming failed")
return (target_trim, trim_range)
return None
def choose_trim(self, packet, response, target_count):
"""Choose best trim for given target count from challenge
responses."""
calib_data = response[2:]
challenge_data = packet[2:]
calib_len = response[1]
if len(calib_data) < 2 * calib_len:
raise StcProtocolException("trim calibration data missing")
best = None
best_count = sys.maxsize
for i in range(calib_len):
count, = struct.unpack(">H", calib_data[2 * i: 2 * i + 2])
trim_adj, trim_range = struct.unpack(
">BB", challenge_data[2 * i: 2 * i + 2])
if abs(count - target_count) < best_count:
best_count = abs(count - target_count)
best = (trim_adj, trim_range), count
if not best:
raise StcProtocolException("frequency trimming failed")
return best
def calibrate(self):
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
# handle uncalibrated chips
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
raise StcProtocolException(
"uncalibrated, please provide a trim value")
# determine target counter
user_speed = self.trim_frequency
if user_speed <= 0:
user_speed = self.mcu_clock_hz
target_user_count = round(user_speed / self.baud_handshake)
# calibration, round 1
print("Target frequency: ", end="")
sys.stdout.flush()
packet = bytes([0x00, 0x08])
packet += bytes([0x00, 0x00, 0xFF, 0x00])
packet += bytes([0x00, 0x10, 0xFF, 0x10])
packet += bytes([0x00, 0x20, 0xFF, 0x20])
packet += bytes([0x00, 0x30, 0xFF, 0x30])
self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select ranges and trim values
for divider in range(1, 6):
user_trim = self.choose_range(
packet, response, target_user_count * divider)
if user_trim is not None:
self.trim_divider = divider
break
if user_trim is None:
raise StcProtocolException("frequency trimming unsuccessful")
# calibration, round 2
packet = bytes([0x00, 0x0C])
for i in range(-6, 6):
packet += bytes([user_trim[0] + i, user_trim[1]])
self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select final values
user_trim, user_count = self.choose_trim(
packet, response, target_user_count * self.trim_divider)
self.trim_value = user_trim
self.trim_frequency = round(
user_count * self.baud_handshake/self.trim_divider)
print("Target %.03f MHz" % (user_speed / 1E6))
print("Adjusted frequency: %.03f MHz(%.03f%%)" % (
(self.trim_frequency / 1E6), (self.trim_frequency*100/user_speed-100)))
# switch to programming frequency
print("Switching to %d baud: " % self.baud_transfer, end="")
sys.stdout.flush()
packet = bytes([0x01, 0x00, 0x00])
bauds = self.baud_transfer * 4
packet += struct.pack(">H", round(65536 - 24E6 / bauds))
packet += bytes([user_trim[1], user_trim[0]])
# iap_wait = self.get_iap_delay(24E6)
iap_wait = 0x98 # iap_wait for "STC8A8K64D4"
packet += bytes([iap_wait])
self.write_packet(packet)
response = self.read_packet()
if len(response) < 1 or response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet")
self.ser.baudrate = self.baud_transfer
def build_options(self):
"""Build a packet of option data from the current configuration."""
msr = self.options.get_msr()
packet = 40 * bytearray([0xff])
packet[3] = 0x00
packet[6] = 0x00
packet[22] = 0x00
packet[24:28] = struct.pack(">I", self.trim_frequency)
packet[28:30] = self.trim_value
packet[30] = self.trim_divider
packet[32] = msr[0]
packet[36:40] = msr[1:5]
return bytes(packet)
class Stc8gProtocol(Stc8dProtocol):
"""Protocol handler for STC8G series"""
def __init__(self, port, handshake, baud, trim):
Stc8dProtocol.__init__(self, port, handshake, baud, trim)
def calibrate(self):
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
# handle uncalibrated chips
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
raise StcProtocolException(
"uncalibrated, please provide a trim value")
# determine target counter
user_speed = self.trim_frequency
if user_speed <= 0:
user_speed = self.mcu_clock_hz
target_user_count = round(user_speed / self.baud_handshake)
# calibration, round 1
print("Target frequency: ", end="")
sys.stdout.flush()
packet = bytes([0x00, 0x05])
packet += bytes([0x00, 0x00, 0x80, 0x00])
packet += bytes([0x00, 0x80, 0x80, 0x80])
packet += bytes([0xFF, 0x00])
self.write_packet(packet, 12)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select ranges and trim values
for divider in range(1, 6):
user_trim = self.choose_range(
packet, response, target_user_count * divider)
if user_trim is not None:
self.trim_divider = divider
break
if user_trim is None:
raise StcProtocolException("frequency trimming unsuccessful")
# calibration, round 2
packet = bytes([0x00, 0x0C])
for i in range(-6, 6):
packet += bytes([user_trim[0] + i, user_trim[1]])
self.write_packet(packet, 19)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select final values
user_trim, user_count = self.choose_trim(
packet, response, target_user_count * self.trim_divider)
self.trim_value = user_trim
self.trim_frequency = round(
user_count * self.baud_handshake/self.trim_divider)
print("Target %.03f MHz" % (user_speed / 1E6))
print("Adjusted frequency: %.03f MHz(%.03f%%)" % (
(self.trim_frequency / 1E6), (self.trim_frequency*100/user_speed-100)))
# switch to programming frequency
print("Switching to %d baud: " % self.baud_transfer, end="")
sys.stdout.flush()
packet = bytes([0x01, 0x00, 0x00])
bauds = self.baud_transfer * 4
packet += struct.pack(">H", round(65536 - 24E6 / bauds))
packet += bytes([user_trim[1], user_trim[0]])
# iap_wait = self.get_iap_delay(24E6)
iap_wait = 0x98 # iap_wait for "STC8A8K64D4"
packet += bytes([iap_wait])
self.write_packet(packet)
response = self.read_packet()
if len(response) < 1 or response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet")
self.ser.baudrate = self.baud_transfer