Add automatic protocol detection

The model of the target MCU and its protocol is detected using model ID provided in the first packet (status packet) from the MCU.

Tested working models include,
IAP15F2K61S2 (BSL version: 7.1.4S, protocol: stc15)
STC15F104W (BSL version: 7.1.4Q, protocol: stc15)
TC11F02E (BSL version: 6.5K, protocol: stc12)
STC10F04XE (BSL version: 6.5J, protocol: stc12)
STC12C5A16S2 (BSL version: 6.2I, protocol: stc12)
STC12C5608AD (BSL version: 6.0G, protocol: stc12)
STC12C2052 (BSL version: 5.8D, protocol: stc12a)
STC90C52RC (BSL version: 4.3C, protocol: stc89)
STC89C52RC (BSL version: 4.3C, protocol: stc89)
STC89C54RD+ (BSL version: 4.3C, protocol: stc89)
STC15F104E (BSL version: 6.7Q, protocol: stc15a)

STC15F104E uses a different status packet protocol than other MCUs; it waits for an ACK packet before sending out the status packet.  Another problem is that STC15F104E shares the same model magic with STC15F104W.  Fortunately, these two models can be differentiated by their BSL version numbers.
This commit is contained in:
laborer 2016-05-06 12:04:49 -04:00
parent d1f464c387
commit afba6c6805
2 changed files with 155 additions and 124 deletions

View File

@ -32,6 +32,7 @@ class StcGal:
def __init__(self, opts):
self.opts = opts
if opts.protocol == "stc89":
self.protocol = Stc89Protocol(opts.port, opts.handshake, opts.baud)
elif opts.protocol == "stc12a":
@ -40,10 +41,12 @@ class StcGal:
self.protocol = Stc12Protocol(opts.port, opts.handshake, opts.baud)
elif opts.protocol == "stc15a":
self.protocol = Stc15AProtocol(opts.port, opts.handshake, opts.baud,
round(opts.trim * 1000))
else:
round(opts.trim * 1000))
elif opts.protocol == "stc15":
self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud,
round(opts.trim * 1000))
else:
self.protocol = StcBaseProtocol(opts.port, opts.handshake, opts.baud)
self.protocol.debug = opts.debug
@ -117,7 +120,20 @@ class StcGal:
self.protocol.disconnect()
def run(self):
try: self.protocol.connect(autoreset=self.opts.autoreset)
try:
self.protocol.connect(autoreset=self.opts.autoreset)
if self.opts.protocol == "auto":
if not self.protocol.protocol_name:
raise StcProtocolException("cannot detect protocol")
base_protocol = self.protocol
self.opts.protocol = self.protocol.protocol_name
# recreate self.protocol with proper protocol class
self.__init__(self.opts)
else:
base_protocol = None
self.protocol.initialize(base_protocol)
except KeyboardInterrupt:
sys.stdout.flush();
print("interrupted")
@ -175,7 +191,7 @@ def cli():
parser.add_argument("code_image", help="code 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='?')
parser.add_argument("-a", "--autoreset", help="cycle power automatically by asserting DTR", action="store_true")
parser.add_argument("-P", "--protocol", help="protocol version", choices=["stc89", "stc12a", "stc12", "stc15a", "stc15"], default="stc12")
parser.add_argument("-P", "--protocol", help="protocol version", choices=["stc89", "stc12a", "stc12", "stc15a", "stc15", "auto"], default="stc12")
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)
@ -183,7 +199,7 @@ def cli():
parser.add_argument("-t", "--trim", help="RC oscillator frequency in kHz (STC15 series only)", type=float, default=0.0)
parser.add_argument("-D", "--debug", help="enable debug output", action="store_true")
opts = parser.parse_args()
# run programmer
gal = StcGal(opts)
return gal.run()

View File

@ -21,7 +21,7 @@
#
import serial
import sys, os, time, struct
import sys, os, time, struct, re
import argparse
import collections
from stcgal.models import MCUModelDatabase
@ -634,6 +634,8 @@ class StcBaseProtocol:
"""magic byte for packets sent by host"""
PACKET_HOST = bytes([0x6a])
PARITY = serial.PARITY_NONE
def __init__(self, port, baud_handshake, baud_transfer):
self.port = port
self.baud_handshake = baud_handshake
@ -646,19 +648,14 @@ class StcBaseProtocol:
self.model = None
self.uid = None
self.debug = False
self.status_packet = None
self.protocol_name = None
def dump_packet(self, data, receive=True):
if self.debug:
print("%s Packet data: %s" % (("<-" if receive else "->"),
Utils.hexstr(data, " ")), file=sys.stderr)
def modular_sum(self, data):
"""modular 16-bit sum"""
s = 0
for b in data: s += b
return s & 0xffff
def read_bytes_safe(self, num):
"""Read data from serial port with timeout handling
@ -670,6 +667,61 @@ class StcBaseProtocol:
return data
def extract_payload(self, packet):
"""Extract the payload of a packet"""
if packet[-1] != self.PACKET_END[0]:
self.dump_packet(packet)
raise StcFramingException("incorrect frame end")
return packet[5:-1]
def read_packet(self):
"""Read and check packet from MCU.
Reads a packet of data from the MCU and and do
validity and checksum checks on it.
Returns packet payload or None in case of an error.
"""
# read and check frame start magic
packet = bytes()
packet += self.read_bytes_safe(1)
# Some (?) BSL versions don't send a frame start with the status
# packet. Let's be liberal and accept that always, just in case.
if packet[0] == self.PACKET_MCU[0]:
packet = self.PACKET_START + self.PACKET_MCU
else:
if packet[0] != self.PACKET_START[0]:
self.dump_packet(packet)
raise StcFramingException("incorrect frame start")
packet += self.read_bytes_safe(1)
if packet[1] != self.PACKET_START[1]:
self.dump_packet(packet)
raise StcFramingException("incorrect frame start")
# read direction
packet += self.read_bytes_safe(1)
if packet[2] != self.PACKET_MCU[0]:
self.dump_packet(packet)
raise StcFramingException("incorrect packet direction magic")
# read length
packet += self.read_bytes_safe(2)
# read packet data
packet_len, = struct.unpack(">H", packet[3:5])
packet += self.read_bytes_safe(packet_len - 3)
# verify checksum and extract payload
payload = self.extract_payload(packet);
self.dump_packet(packet, receive=True)
# payload only is returned
return payload
def print_mcu_info(self):
"""Print MCU status information"""
@ -693,6 +745,7 @@ class StcBaseProtocol:
def initialize_model(self):
"""Initialize model-specific information"""
self.mcu_magic, = struct.unpack(">H", self.status_packet[20:22])
try:
self.model = MCUModelDatabase.find_model(self.mcu_magic)
except NameError:
@ -701,15 +754,47 @@ class StcBaseProtocol:
print(msg, file=sys.stderr)
self.model = MCUModelDatabase.MCUModel(name="UNKNOWN",
magic=self.mcu_magic, total=63488, code=63488, eeprom=0)
self.print_mcu_info()
# special case for duplicated mcu magic,
# 0xf294 (STC15F104W, STC15F104E)
# 0xf2d4 (STC15L104W, STC15L104E)
# duplicated mcu magic can be found using command,
# grep -o 'magic=[^,]*' models.py | sort | uniq -d
if self.mcu_magic in (0xF294, 0xF2D4):
mcu_name = self.model.name[:-1]
mcu_name += "E" if self.status_packet[17] < 0x70 else "W"
self.model = self.model._replace(name = mcu_name)
protocol_database = [("stc89", "STC(89|90)(C|LE)\d"),
("stc12a", "STC12(C|LE)\d052"),
("stc12", "(STC|IAP)(10|11|12)\D"),
("stc15a", "(STC|IAP)15[FL][01]0\d(E|EA|)$"),
("stc15", "(STC|IAP|IRC)15\D")]
for protocol_name, pattern in protocol_database:
if re.match(pattern, self.model.name):
self.protocol_name = protocol_name
break
else:
self.protocol_name = None
def get_status_packet(self):
"""Read and decode status packet"""
status_packet = self.read_packet()
if status_packet[0] != 0x50:
raise StcProtocolException("incorrect magic in status packet")
return status_packet
packet = self.read_packet()
if packet[0] == 0x80:
# need to re-ack
self.ser.parity = serial.PARITY_EVEN
packet = (self.PACKET_START
+ self.PACKET_HOST
+ bytes([0x00, 0x07, 0x80, 0x00, 0xF1])
+ self.PACKET_END)
self.dump_packet(packet, receive=False)
self.ser.write(packet)
self.ser.flush()
self.pulse()
packet = self.read_packet()
return packet
def get_iap_delay(self, clock_hz):
"""IAP wait states for STC12A+ (according to datasheet(s))"""
@ -761,11 +846,11 @@ class StcBaseProtocol:
# send sync, and wait for MCU response
# ignore errors until we see a valid response
status_packet = None
while not status_packet:
self.status_packet = None
while not self.status_packet:
try:
self.pulse()
status_packet = self.get_status_packet()
self.status_packet = self.get_status_packet()
except (StcFramingException, serial.SerialTimeoutException): pass
print("done")
@ -773,9 +858,25 @@ class StcBaseProtocol:
self.ser.timeout = 15.0
self.ser.interCharTimeout = 1.0
self.initialize_status(status_packet)
self.initialize_model()
self.initialize_options(status_packet)
def initialize(self, base_protocol = None):
if base_protocol:
self.ser = base_protocol.ser
self.ser.parity = self.PARITY
packet = base_protocol.status_packet
packet = (self.PACKET_START
+ self.PACKET_MCU
+ struct.pack(">H", len(packet) + 4)
+ packet
+ self.PACKET_END)
self.status_packet = self.extract_payload(packet)
self.mcu_magic = base_protocol.mcu_magic
self.model = base_protocol.model
self.initialize_status(self.status_packet)
self.print_mcu_info()
self.initialize_options(self.status_packet)
def disconnect(self):
"""Disconnect from MCU"""
@ -801,60 +902,17 @@ class Stc89Protocol(StcBaseProtocol):
self.cpu_6t = None
def read_packet(self):
"""Read and check packet from MCU.
def extract_payload(self, packet):
"""Verify the checksum of packet and return its payload"""
Reads a packet of data from the MCU and and do
validity and checksum checks on it.
Returns packet payload or None in case of an error.
"""
# read and check frame start magic
packet = bytes()
packet += self.read_bytes_safe(1)
# Some (?) BSL versions don't send a frame start with the status
# packet. Let's be liberal and accept that always, just in case.
if packet[0] == self.PACKET_MCU[0]:
packet = self.PACKET_START + self.PACKET_MCU
else:
if packet[0] != self.PACKET_START[0]:
self.dump_packet(packet)
raise StcFramingException("incorrect frame start")
packet += self.read_bytes_safe(1)
if packet[1] != self.PACKET_START[1]:
self.dump_packet(packet)
raise StcFramingException("incorrect frame start")
# read direction
packet += self.read_bytes_safe(1)
if packet[2] != self.PACKET_MCU[0]:
self.dump_packet(packet)
raise StcFramingException("incorrect packet direction magic")
# read length
packet += self.read_bytes_safe(2)
# read packet data
packet_len, = struct.unpack(">H", packet[3:5])
packet += self.read_bytes_safe(packet_len - 3)
# verify end code
if packet[packet_len+1] != self.PACKET_END[0]:
self.dump_packet(packet)
raise StcFramingException("incorrect frame end")
# verify checksum
packet_csum = packet[packet_len]
calc_csum = sum(packet[2:packet_len]) & 0xff
packet_csum = packet[-2]
calc_csum = sum(packet[2:-2]) & 0xff
if packet_csum != calc_csum:
self.dump_packet(packet)
raise StcFramingException("packet checksum mismatch")
self.dump_packet(packet, receive=True)
# payload only is returned
return packet[5:packet_len]
payload = StcBaseProtocol.extract_payload(self, packet)
return payload[:-1]
def write_packet(self, data):
"""Send packet to MCU.
@ -925,7 +983,6 @@ class Stc89Protocol(StcBaseProtocol):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
self.mcu_magic, = struct.unpack(">H", packet[20:22])
self.cpu_6t = not bool(packet[19] & 1)
cpu_t = 6.0 if self.cpu_6t else 12.0
@ -1055,8 +1112,6 @@ class Stc12AProtocol(Stc89Protocol):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
self.mcu_magic, = struct.unpack(">H", packet[20:22])
freq_counter = 0
for i in range(8):
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
@ -1103,7 +1158,7 @@ class Stc12AProtocol(Stc89Protocol):
def handshake(self):
"""Do baudrate handshake
Initate and do the (rather complicated) baudrate handshake.
Initiate and do the (rather complicated) baudrate handshake.
"""
# start baudrate handshake
@ -1195,50 +1250,17 @@ class Stc12Protocol(StcBaseProtocol):
def __init__(self, port, baud_handshake, baud_transfer):
StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer)
def read_packet(self):
"""Read and check packet from MCU.
Reads a packet of data from the MCU and and do
validity and checksum checks on it.
def extract_payload(self, packet):
"""Verify the checksum of packet and return its payload"""
Returns packet payload or None in case of an error.
"""
# read and check frame start magic
packet = bytes()
packet += self.read_bytes_safe(1)
if packet[0] != self.PACKET_START[0]:
raise StcFramingException("incorrect frame start")
packet += self.read_bytes_safe(1)
if packet[1] != self.PACKET_START[1]:
raise StcFramingException("incorrect frame start")
# read direction and length
packet += self.read_bytes_safe(3)
if packet[2] != self.PACKET_MCU[0]:
self.dump_packet(packet)
raise StcFramingException("incorrect packet direction magic")
# read packet data
packet_len, = struct.unpack(">H", packet[3:5])
packet += self.read_bytes_safe(packet_len - 3)
# verify end code
if packet[packet_len+1] != self.PACKET_END[0]:
self.dump_packet(packet)
raise StcFramingException("incorrect frame end")
# verify checksum
packet_csum, = struct.unpack(">H", packet[packet_len-1:packet_len+1])
calc_csum = sum(packet[2:packet_len-1]) & 0xffff
packet_csum, = struct.unpack(">H", packet[-3:-1])
calc_csum = sum(packet[2:-3]) & 0xffff
if packet_csum != calc_csum:
self.dump_packet(packet)
raise StcFramingException("packet checksum mismatch")
self.dump_packet(packet, receive=True)
# payload only is returned
return packet[5:packet_len-1]
payload = StcBaseProtocol.extract_payload(self, packet)
return payload[:-2]
def write_packet(self, data):
"""Send packet to MCU.
@ -1266,8 +1288,6 @@ class Stc12Protocol(StcBaseProtocol):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
self.mcu_magic, = struct.unpack(">H", packet[20:22])
freq_counter = 0
for i in range(8):
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
@ -1303,7 +1323,7 @@ class Stc12Protocol(StcBaseProtocol):
delay = 0x80
return brt, brt_csum, iap_wait, delay
def initialize_options(self, status_packet):
"""Initialize options"""
@ -1472,8 +1492,6 @@ class Stc15AProtocol(Stc12Protocol):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
self.mcu_magic, = struct.unpack(">H", packet[20:22])
freq_counter = 0
for i in range(4):
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
@ -1653,6 +1671,7 @@ class Stc15AProtocol(Stc12Protocol):
print("Target UID: %s" % Utils.hexstr(self.uid))
class Stc15Protocol(Stc15AProtocol):
"""Protocol handler for later STC 15 series"""
@ -1671,8 +1690,6 @@ class Stc15Protocol(Stc15AProtocol):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
self.mcu_magic, = struct.unpack(">H", packet[20:22])
# check bit that control internal vs. external clock source
# get frequency either stored from calibration or from
# frequency counter
@ -1951,5 +1968,3 @@ class Stc15Protocol(Stc15AProtocol):
print("done")
print("Target UID: %s" % Utils.hexstr(self.uid))