diff --git a/stcgal/frontend.py b/stcgal/frontend.py index 31a0978..e5932c1 100644 --- a/stcgal/frontend.py +++ b/stcgal/frontend.py @@ -33,6 +33,7 @@ from stcgal.protocols import Stc15Protocol from stcgal.protocols import Stc15AProtocol from stcgal.protocols import StcUsb15Protocol from stcgal.protocols import Stc8Protocol +from stcgal.protocols import Stc8dProtocol from stcgal.protocols import StcAutoProtocol from stcgal.protocols import StcProtocolException from stcgal.protocols import StcFramingException @@ -64,6 +65,9 @@ class StcGal: elif opts.protocol == "stc8": self.protocol = Stc8Protocol(opts.port, opts.handshake, opts.baud, round(opts.trim * 1000)) + elif opts.protocol == "stc8d": + self.protocol = Stc8dProtocol(opts.port, opts.handshake, opts.baud, + round(opts.trim * 1000)) elif opts.protocol == "usb15": self.protocol = StcUsb15Protocol() else: @@ -242,7 +246,7 @@ def cli(): parser.add_argument("-a", "--autoreset", help="cycle power automatically by asserting DTR", action="store_true") 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)", - choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "stc8", "usb15", "auto"], default="auto") + choices=["stc89", "stc12a", "stc12b", "stc12", "stc15a", "stc15", "stc8", "stc8d", "usb15", "auto"], default="auto") 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("-l", "--handshake", help="handshake baud rate (default: 2400)", type=BaudType(), default=2400) diff --git a/stcgal/models.py b/stcgal/models.py index 8507409..57aecdd 100644 --- a/stcgal/models.py +++ b/stcgal/models.py @@ -175,6 +175,7 @@ class MCUModelDatabase: MCUModel(name='STC8A8K56S4A12', magic=0xf627, total=65536, code=57344, eeprom=8192), MCUModel(name='STC8A8K60S4A12', magic=0xf629, total=65536, code=61440, eeprom=4096), MCUModel(name='STC8A8K64S4A12', magic=0xf628, total=65536, code=65024, eeprom=512), + MCUModel(name='STC8A8K64D4', magic=0xf7f4,total=65536, code=65024, eeprom=512), MCUModel(name='STC15H4K08S4', magic=0xf601, total=65536, code=8192, eeprom=57344), MCUModel(name='STC15H4K16S4', magic=0xf602, total=65536, code=16384, eeprom=49152), MCUModel(name='STC15H4K24S4', magic=0xf603, total=65536, code=24576, eeprom=40960), diff --git a/stcgal/protocols.py b/stcgal/protocols.py index 52a4262..e9cfa12 100644 --- a/stcgal/protocols.py +++ b/stcgal/protocols.py @@ -1874,3 +1874,151 @@ class StcUsb15Protocol(Stc15Protocol): if self.dev: self.write_packet(0xff) 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 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)