Merge pull request #13 from laborer/master

Add automatic protocol detection
This commit is contained in:
Grigori Goronzy 2016-05-12 00:11:10 +02:00
commit f8e8d66baa
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))