18 Commits
v1.0 ... usbisp

Author SHA1 Message Date
adcb8d8ced README: add details of usb15 protocol mode 2016-05-14 13:21:43 +02:00
81c890337e usb15: make USB support optional 2016-05-14 13:03:03 +02:00
9af984a191 Add USB-ISP support for STC15W4 series
The STC15W4 series have an additional USB BSL mode which kind of wraps
the serial protocol. It just uses basic USB control transfers. This
adds support for this mode, based on the existing STC15 code. pyusb is
required as an additional dependency. The new protocol mode is called
"usb15". There are some shortcomings, for instance timing of erase
and programming operations is problematic and right now only delays
are used. Also, only a single USB-ISP device is supported and the
first one found is always used.

Flash programming and setting options has been mildly tested on an
STC15W4K56S4 MCU.
2016-05-14 12:50:14 +02:00
2738118e8f stc15: extract build_options method
This will allow use to reuse code for upcoming USB-ISP support.
2016-05-14 12:43:15 +02:00
eedf9169a7 stc15: add error reporting for locked MCUs
It is possible to lock devices with a password against flashing. This
feature is not currently supported by stcgal, so at least output a
sensible message.
2016-05-13 02:54:43 +02:00
cb739e6f94 Add exit status to documentation 2016-05-12 01:48:02 +02:00
2a770bb37f Add missing license header 2016-05-12 01:25:22 +02:00
8fe94e001f Update list of compatible MCUs 2016-05-12 01:19:31 +02:00
b1ed017137 Add protocol detection to documentation 2016-05-12 01:07:27 +02:00
678d001ec5 Print informational message after protocol detection 2016-05-12 01:04:34 +02:00
f8e8d66baa Merge pull request #13 from laborer/master
Add automatic protocol detection
2016-05-12 00:11:10 +02:00
afba6c6805 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.
2016-05-11 15:14:13 -04:00
d1f464c387 Raise default handshake baudrate to 2400
See grigorig/stcgal#12 for more information.
2016-04-15 11:13:54 +02:00
e268b282cb Update dump-mcu.py for stc-isp 6.85K
v2: squash "Update model DB"
2016-04-14 18:53:45 +02:00
f9a19c927c Update README.md 2016-04-02 23:13:51 +02:00
372f854c77 Add autoreset feature via DTR control line
This is inspired by Arduino.  Directly before connect, DTR is asserted
for 0.5 seconds and then deasserted again.  With a small external
circuit, the MCU can be power-cycled with this control signal.
In case the MCU draws very little power, it may even be possible to
supply power directly from the DTR pin.
2016-04-02 23:00:07 +02:00
41cabb587b frontend: also catch I/O errors in pulse phase 2016-01-17 15:16:43 +01:00
6a38127c0d Fix regression with some STC15 parts
The cpu core voltage setting is only available on newer parts, so do not
try to support it on older ones. The option packet is too short on some
parts, which resulted in an assertion hit.

There may be a nicer solution, but this works for now.

Fixes grigorig/stcgal#6.
2016-01-05 07:03:59 +01:00
6 changed files with 568 additions and 238 deletions

View File

@ -4,15 +4,16 @@ stcgal - STC MCU ISP flash tool
stcgal is a command line flash programming tool for STC MCU Ltd. [1] stcgal is a command line flash programming tool for STC MCU Ltd. [1]
8051 compatible microcontrollers. The name was inspired by avrdude [2]. 8051 compatible microcontrollers. The name was inspired by avrdude [2].
STC microcontrollers have a UART based boot strap loader (BSL). It STC microcontrollers have an UART/USB based boot strap loader (BSL). It
utilizes a packet-based protocol to flash the code memory and IAP utilizes a packet-based protocol to flash the code memory and IAP
memory over a serial link. This is referred to as in-system programming (ISP). memory over a serial link. This is referred to as in-system programming
The BSL is also used to configure various (fuse-like) device (ISP). The BSL is also used to configure various (fuse-like) device
options. Unfortunately, this protocol is not publicly documented and options. Unfortunately, this protocol is not publicly documented and
STC only provide a (crude) Windows GUI application for programming. STC only provide a (crude) Windows GUI application for programming.
stcgal is a full-featured Open Source replacement for STC's Windows software; stcgal is a full-featured Open Source replacement for STC's Windows
it supports a wide range of MCUs, it is very portable and suitable for automation. software; it supports a wide range of MCUs, it is very portable and
suitable for automation.
[1] http://stcmcu.com/ [1] http://stcmcu.com/
[2] http://www.nongnu.org/avrdude/ [2] http://www.nongnu.org/avrdude/
@ -25,23 +26,32 @@ stcgal should fully support STC 89/90/10/11/12/15 series MCUs.
So far, stcgal was tested with the following MCU models: So far, stcgal was tested with the following MCU models:
* STC89C52RC (BSL version: 4.3C) * STC89C52RC (BSL version: 4.3C)
* STC90C52RC (BSL version: 4.3C)
* STC89C54RD+ (BSL version: 4.3C)
* STC12C2052 (BSL version: 5.8D)
* STC12C2052AD (BSL version: 5.8D) * STC12C2052AD (BSL version: 5.8D)
* STC12C5608AD (BSL version: 6.0G)
* STC12C5A16S2 (BSL version: 6.2I)
* STC12C5A60S2 (BSL version: 6.2I) * STC12C5A60S2 (BSL version: 6.2I)
* STC11F02E (BSL version: 6.5K) * STC11F02E (BSL version: 6.5K)
* STC10F04XE (BSL version: 6.5J)
* STC11F08XE (BSL version: 6.5M) * STC11F08XE (BSL version: 6.5M)
* STC12C5204AD (BSL version: 6.6H)
* STC15F104E (BSL version: 6.7Q) * STC15F104E (BSL version: 6.7Q)
* STC15F204EA (BSL version: 6.7R) * STC15F204EA (BSL version: 6.7R)
* STC15L104W (BSL version: 7.1Q) * STC15L104W (BSL version: 7.1.4Q)
* IAP15F2K61S2 (BSL version: 7.1S) * STC15F104W (BSL version: 7.1.4Q)
* IAP15F2K61S2 (BSL version: 7.1.4S)
* STC15L2K16S2 (BSL version: 7.2.4S) * STC15L2K16S2 (BSL version: 7.2.4S)
* STC15W408AS (BSL version: 7.2.4T) * STC15W408AS (BSL version: 7.2.4T)
* STC15W4K56S4 (BSL version: 7.3.4T) * STC15W4K56S4 (BSL version: 7.3.4T, UART and USB mode)
More compatibility testing is going to happen soon. Compatibility reports, both negative and positive, are welcome.
Features Features
-------- --------
* UART and USB BSL support
* Display part info * Display part info
* Determine operating frequency * Determine operating frequency
* Program flash memory * Program flash memory
@ -49,11 +59,14 @@ Features
* Set device options * Set device options
* Read unique device ID (STC 10/11/12/15) * Read unique device ID (STC 10/11/12/15)
* Trim RC oscillator frequency (STC 15) * Trim RC oscillator frequency (STC 15)
* Automatic power-cycling with DTR toggle
* Automatic UART protocol detection
Installation Installation
------------ ------------
stcgal requires Python 3.2 (or later) and pySerial. You can run stcgal stcgal requires Python 3.2 (or later) and pySerial. USB support is
optional and requires pyusb 1.0.0b2 or later. You can run stcgal
directly with the included ```stcgal.py``` script. The recommended directly with the included ```stcgal.py``` script. The recommended
method for permanent installation is to use Python's setuptools. Run method for permanent installation is to use Python's setuptools. Run
```./setup.py build``` to build and ```sudo ./setup.py install``` ```./setup.py build``` to build and ```sudo ./setup.py install```
@ -66,24 +79,27 @@ Usage
Call stcgal with ```-h``` for usage information. Call stcgal with ```-h``` for usage information.
``` ```
usage: stcgal.py [-h] [-P {stc89,stc12a,stc12,stc15a,stc15}] [-p PORT] usage: stcgal.py [-h] [-a] [-P {stc89,stc12a,stc12,stc15a,stc15,auto}]
[-b BAUD] [-l HANDSHAKE] [-o OPTION] [-t TRIM] [-D] [-p PORT] [-b BAUD] [-l HANDSHAKE] [-o OPTION] [-t TRIM] [-D]
[code_binary] [eeprom_binary] [code_image] [eeprom_image]
stcgal 1.0 - an STC MCU ISP flash tool stcgal 1.0 - an STC MCU ISP flash tool
(C) 2014-2015 Grigori Goronzy
https://github.com/grigorig/stcgal
positional arguments: positional arguments:
code_binary code segment binary file to flash code_image code segment file to flash (BIN/HEX)
eeprom_binary eeprom segment binary file to flash eeprom_image eeprom segment file to flash (BIN/HEX)
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
-P {stc89,stc12a,stc12,stc15a,stc15}, --protocol {stc89,stc12a,stc12,stc15a,stc15} -a, --autoreset cycle power automatically by asserting DTR
-P {stc89,stc12a,stc12,stc15a,stc15,auto}, --protocol {stc89,stc12a,stc12,stc15a,stc15,auto}
protocol version protocol version
-p PORT, --port PORT serial port device -p PORT, --port PORT serial port device
-b BAUD, --baud BAUD transfer baud rate (default: 19200) -b BAUD, --baud BAUD transfer baud rate (default: 19200)
-l HANDSHAKE, --handshake HANDSHAKE -l HANDSHAKE, --handshake HANDSHAKE
handshake baud rate (default: 1200) handshake baud rate (default: 2400)
-o OPTION, --option OPTION -o OPTION, --option OPTION
set option (can be used multiple times) set option (can be used multiple times)
-t TRIM, --trim TRIM RC oscillator frequency in kHz (STC15 series only) -t TRIM, --trim TRIM RC oscillator frequency in kHz (STC15 series only)
@ -95,14 +111,17 @@ Most importantly, ```-p``` sets the serial port to be used for programming.
### Protocols ### Protocols
STC MCUs use a variety of related but incompatible protocols for the STC MCUs use a variety of related but incompatible protocols for the
BSL. The protocol must be specified with the ```-P``` flag. Here's BSL. The protocol can be specified with the ```-P``` flag. Optionally,
the general mapping between protocols and MCU series: experimental protocol autodetection can be used. The mapping between
protocols and MCU series is as follows:
* ```stc89``` STC 89/90 series * ```stc89``` STC 89/90 series
* ```stc12a``` STC12Cx052AD and possibly others * ```stc12a``` STC12Cx052AD and possibly others
* ```stc12``` Most STC10/11/12 series * ```stc12``` Most STC10/11/12 series (default)
* ```stc15a``` STC15x104E and STC15x204E(A) series * ```stc15a``` STC15x104E and STC15x204E(A) series
* ```stc15``` Most STC15 series * ```stc15``` Most STC15 series
* ```usb15``` USB support on STC15W4 series
* ```auto``` Automatic detection of UART based protocols
The text files in the doc/ subdirectory provide an overview over The text files in the doc/ subdirectory provide an overview over
the reverse engineered protocols used by the BSLs. For more details, the reverse engineered protocols used by the BSLs. For more details,
@ -244,6 +263,31 @@ device options. Use the ```-t``` flag to request trimming to a certain
value. Generally, frequencies between 4 and 35 MHz can be achieved. If value. Generally, frequencies between 4 and 35 MHz can be achieved. If
trimming fails, stcgal will abort. trimming fails, stcgal will abort.
### Automatic power-cycling
STC's microcontrollers require a power-on reset to invoke the bootloader,
which can be inconvenient. stcgal can use the DTR control signal of a
serial interface to automate this. The DTR signal is asserted for
approximately 500 ms when the autoreset feature is enabled with the
```-a``` flag. This requires external circuitry to actually switch the
power. In some cases, when the microcontroller draws only little power,
it is possible to directly supply power from the DTR signal, however.
### Exit status
The exit status is 0 if no error occured while executing stcgal. Any
error, such as a protocol error or I/O error, results in an exit
status of 1. If the the user aborted stcgal by pressing CTRL-C,
that results in an exit status of 2.
### USB support
STC15W4 series have an USB-based BSL that can be optionally
used. USB support in stcgal is experimental and might change in the
future. USB mode is enabled by using the ```usb15``` protocol. The
port (```-p```) flag as well as the baudrate options are ignored for
the USB protocol. RC frequency trimming is not supported.
License License
------- -------

View File

@ -1,13 +1,13 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# This curious script dumps all model info from STC-ISP. # This curious script dumps all model info from STC-ISP.
# Data is directly read from the binary. # Data is directly read from the binary.
# Offsets are for STC-ISP 6.85I, sha1sum a1a625d6c491fe98d0286ebac0a8d78b94dca81d # Offsets are for stc-isp-15xx-v6.85K.exe, sha1sum aa66e4c1ab49de27369b83c954a7c202acce0950
MCU_TABLE_OFFSET = 0x00063550 MCU_TABLE_OFFSET = 0x00064550
MCU_TABLE_SIZE = 914 MCU_TABLE_SIZE = 941
MCU_RECORD_SIZE = 32 MCU_RECORD_SIZE = 32
MCU_NAMES_OFFSET = 0x0007d708 MCU_NAMES_OFFSET = 0x0007e80c
MCU_NAMES_PTR_OFFSET = 0x0047d708 MCU_NAMES_PTR_OFFSET = 0x0047e80c
import struct import struct
import sys import sys

View File

@ -1,4 +1,25 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
#
# Copyright (c) 2016 Grigori Goronzy <greg@chown.ath.cx>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
import stcgal import stcgal
from setuptools import setup, find_packages from setuptools import setup, find_packages
@ -8,6 +29,9 @@ setup(
version = stcgal.__version__, version = stcgal.__version__,
packages = find_packages(exclude=["doc"]), packages = find_packages(exclude=["doc"]),
install_requires = ["pyserial"], install_requires = ["pyserial"],
extras_require = {
"usb": ["pyusb>=1.0.0"]
},
entry_points = { entry_points = {
"console_scripts": [ "console_scripts": [
"stcgal = stcgal.frontend:cli", "stcgal = stcgal.frontend:cli",

View File

@ -32,6 +32,7 @@ class StcGal:
def __init__(self, opts): def __init__(self, opts):
self.opts = opts self.opts = opts
if opts.protocol == "stc89": if opts.protocol == "stc89":
self.protocol = Stc89Protocol(opts.port, opts.handshake, opts.baud) self.protocol = Stc89Protocol(opts.port, opts.handshake, opts.baud)
elif opts.protocol == "stc12a": elif opts.protocol == "stc12a":
@ -40,10 +41,14 @@ class StcGal:
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))
else: 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 == "usb15":
self.protocol = StcUsb15Protocol()
else:
self.protocol = StcBaseProtocol(opts.port, opts.handshake, opts.baud)
self.protocol.debug = opts.debug self.protocol.debug = opts.debug
@ -117,7 +122,21 @@ class StcGal:
self.protocol.disconnect() self.protocol.disconnect()
def run(self): def run(self):
try: self.protocol.connect() 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
print("Protocol detected: %s" % self.opts.protocol)
# recreate self.protocol with proper protocol class
self.__init__(self.opts)
else:
base_protocol = None
self.protocol.initialize(base_protocol)
except KeyboardInterrupt: except KeyboardInterrupt:
sys.stdout.flush(); sys.stdout.flush();
print("interrupted") print("interrupted")
@ -131,6 +150,10 @@ class StcGal:
sys.stdout.flush(); sys.stdout.flush();
print("Serial port error: %s" % e, file=sys.stderr) print("Serial port error: %s" % e, file=sys.stderr)
return 1 return 1
except IOError as e:
sys.stdout.flush();
print("I/O error: %s" % e, file=sys.stderr)
return 1
try: try:
if self.opts.code_image: if self.opts.code_image:
@ -170,15 +193,16 @@ def cli():
description="stcgal %s - an STC MCU ISP flash tool\n(C) 2014-2015 Grigori Goronzy\nhttps://github.com/grigorig/stcgal" %stcgal.__version__) description="stcgal %s - an STC MCU ISP flash tool\n(C) 2014-2015 Grigori Goronzy\nhttps://github.com/grigorig/stcgal" %stcgal.__version__)
parser.add_argument("code_image", help="code segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?') 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("eeprom_image", help="eeprom segment file to flash (BIN/HEX)", type=argparse.FileType("rb"), nargs='?')
parser.add_argument("-P", "--protocol", help="protocol version", choices=["stc89", "stc12a", "stc12", "stc15a", "stc15"], default="stc12") 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", "usb15", "auto"], default="stc12")
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=19200)
parser.add_argument("-l", "--handshake", help="handshake baud rate (default: 1200)", type=BaudType(), default=1200) 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)", action="append") parser.add_argument("-o", "--option", help="set option (can be used multiple times)", 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)
parser.add_argument("-D", "--debug", help="enable debug output", action="store_true") parser.add_argument("-D", "--debug", help="enable debug output", action="store_true")
opts = parser.parse_args() opts = parser.parse_args()
# run programmer # run programmer
gal = StcGal(opts) gal = StcGal(opts)
return gal.run() return gal.run()

View File

@ -32,14 +32,38 @@ class MCUModelDatabase:
MCUModel = collections.namedtuple("MCUModel", ["name", "magic", "total", "code", "eeprom"]) MCUModel = collections.namedtuple("MCUModel", ["name", "magic", "total", "code", "eeprom"])
models = ( models = (
MCUModel(name='STC15H4K08S4', magic=0xf601, total=65536, code=8192, eeprom=0), MCUModel(name='STC8F8K08S4A10', magic=0xf611, total=65536, code=8192, eeprom=57344),
MCUModel(name='STC15H4K16S4', magic=0xf602, total=65536, code=16384, eeprom=0), MCUModel(name='STC8F8K16S4A10', magic=0xf612, total=65536, code=16384, eeprom=49152),
MCUModel(name='STC15H4K24S4', magic=0xf603, total=65536, code=24576, eeprom=0), MCUModel(name='STC8F8K24S4A10', magic=0xf613, total=65536, code=24576, eeprom=40960),
MCUModel(name='STC15H4K32S4', magic=0xf604, total=65536, code=32768, eeprom=0), MCUModel(name='STC8F8K32S4A10', magic=0xf614, total=65536, code=32768, eeprom=32768),
MCUModel(name='STC15H4K40S4', magic=0xf605, total=65536, code=40960, eeprom=0), MCUModel(name='STC8F8K40S4A10', magic=0xf615, total=65536, code=40960, eeprom=24576),
MCUModel(name='STC15H4K48S4', magic=0xf606, total=65536, code=49152, eeprom=0), MCUModel(name='STC8F8K48S4A10', magic=0xf616, total=65536, code=49152, eeprom=16384),
MCUModel(name='STC15H4K56S4', magic=0xf607, total=65536, code=57344, eeprom=0), MCUModel(name='STC8F8K56S4A10', magic=0xf617, total=65536, code=57344, eeprom=8192),
MCUModel(name='STC15H4K64S4', magic=0xf608, total=65536, code=65024, eeprom=0), MCUModel(name='STC8F8K64S4A10', magic=0xf618, total=65536, code=65024, eeprom=512),
MCUModel(name='STC8A8K08S4A12', magic=0xf621, total=65536, code=8192, eeprom=57344),
MCUModel(name='STC8A8K16S4A12', magic=0xf622, total=65536, code=16384, eeprom=49152),
MCUModel(name='STC8A8K24S4A12', magic=0xf623, total=65536, code=24576, eeprom=40960),
MCUModel(name='STC8A8K32S4A12', magic=0xf624, total=65536, code=32768, eeprom=32768),
MCUModel(name='STC8A8K40S4A12', magic=0xf625, total=65536, code=40960, eeprom=24576),
MCUModel(name='STC8A8K48S4A12', magic=0xf626, total=65536, code=49152, eeprom=16384),
MCUModel(name='STC8A8K56S4A12', magic=0xf627, total=65536, code=57344, eeprom=8192),
MCUModel(name='STC8A8K64S4A12', magic=0xf628, total=65536, code=65024, eeprom=512),
MCUModel(name='STC8F2K08S4', magic=0xf631, total=65536, code=8192, eeprom=57344),
MCUModel(name='STC8F2K16S4', magic=0xf632, total=65536, code=16384, eeprom=49152),
MCUModel(name='STC8F2K24S4', magic=0xf633, total=65536, code=24576, eeprom=40960),
MCUModel(name='STC8F2K32S4', magic=0xf634, total=65536, code=32768, eeprom=32768),
MCUModel(name='STC8F2K40S4', magic=0xf635, total=65536, code=40960, eeprom=24576),
MCUModel(name='STC8F2K48S4', magic=0xf636, total=65536, code=49152, eeprom=16384),
MCUModel(name='STC8F2K56S4', magic=0xf637, total=65536, code=57344, eeprom=8192),
MCUModel(name='STC8F2K64S4', magic=0xf638, 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),
MCUModel(name='STC15H4K32S4', magic=0xf604, total=65536, code=32768, eeprom=32768),
MCUModel(name='STC15H4K40S4', magic=0xf605, total=65536, code=40960, eeprom=24576),
MCUModel(name='STC15H4K48S4', magic=0xf606, total=65536, code=49152, eeprom=16384),
MCUModel(name='STC15H4K56S4', magic=0xf607, total=65536, code=57344, eeprom=8192),
MCUModel(name='STC15H4K64S4', magic=0xf608, total=65536, code=65024, eeprom=512),
MCUModel(name='STC15F2K08S2', magic=0xf401, total=65536, code=8192, eeprom=54272), MCUModel(name='STC15F2K08S2', magic=0xf401, total=65536, code=8192, eeprom=54272),
MCUModel(name='STC15F2K16S2', magic=0xf402, total=65536, code=16384, eeprom=46080), MCUModel(name='STC15F2K16S2', magic=0xf402, total=65536, code=16384, eeprom=46080),
MCUModel(name='STC15F2K24S2', magic=0xf403, total=65536, code=24576, eeprom=37888), MCUModel(name='STC15F2K24S2', magic=0xf403, total=65536, code=24576, eeprom=37888),
@ -209,6 +233,9 @@ class MCUModelDatabase:
MCUModel(name='STC15W1K08PWM', magic=0xf52d, total=65536, code=8192, eeprom=52224), MCUModel(name='STC15W1K08PWM', magic=0xf52d, total=65536, code=8192, eeprom=52224),
MCUModel(name='STC15W1K16PWM', magic=0xf52e, total=65536, code=16384, eeprom=44032), MCUModel(name='STC15W1K16PWM', magic=0xf52e, total=65536, code=16384, eeprom=44032),
MCUModel(name='STC15W1K20S', magic=0xf52f, total=65536, code=20480, eeprom=39936), MCUModel(name='STC15W1K20S', magic=0xf52f, total=65536, code=20480, eeprom=39936),
MCUModel(name='STC15W1K20AS', magic=0xf534, total=65536, code=20480, eeprom=39936),
MCUModel(name='STC15W1K32AS', magic=0xf535, total=65536, code=32768, eeprom=27648),
MCUModel(name='STC15W1K48AS', magic=0xf536, total=65536, code=49152, eeprom=11264),
MCUModel(name='STC15W2K32S2', magic=0xf530, total=65536, code=32768, eeprom=27648), MCUModel(name='STC15W2K32S2', magic=0xf530, total=65536, code=32768, eeprom=27648),
MCUModel(name='STC15W2K48S2', magic=0xf531, total=65536, code=49152, eeprom=11264), MCUModel(name='STC15W2K48S2', magic=0xf531, total=65536, code=49152, eeprom=11264),
MCUModel(name='STC15W2K32AS', magic=0xf532, total=65536, code=32768, eeprom=27648), MCUModel(name='STC15W2K32AS', magic=0xf532, total=65536, code=32768, eeprom=27648),

View File

@ -21,11 +21,19 @@
# #
import serial import serial
import sys, os, time, struct import sys, os, time, struct, re
import argparse import argparse
import collections import collections
from stcgal.models import MCUModelDatabase from stcgal.models import MCUModelDatabase
from stcgal.utils import Utils from stcgal.utils import Utils
import functools
try:
import usb.core, usb.util
_usb_available = True
except ImportError:
_usb_available = False
class StcFramingException(Exception): class StcFramingException(Exception):
"""Something wrong with packet framing or checksum""" """Something wrong with packet framing or checksum"""
@ -446,14 +454,13 @@ class Stc15AOption(BaseOption):
class Stc15Option(BaseOption): class Stc15Option(BaseOption):
def __init__(self, msr): def __init__(self, msr):
assert len(msr) == 5 assert len(msr) >= 4
self.msr = bytearray(msr) self.msr = bytearray(msr)
self.options = ( self.options = (
("reset_pin_enabled", self.get_reset_pin_enabled, self.set_reset_pin_enabled), ("reset_pin_enabled", self.get_reset_pin_enabled, self.set_reset_pin_enabled),
("clock_source", self.get_clock_source, self.set_clock_source), ("clock_source", self.get_clock_source, self.set_clock_source),
("clock_gain", self.get_clock_gain, self.set_clock_gain), ("clock_gain", self.get_clock_gain, self.set_clock_gain),
("cpu_core_voltage", self.get_core_voltage, self.set_core_voltage),
("watchdog_por_enabled", self.get_watchdog, self.set_watchdog), ("watchdog_por_enabled", self.get_watchdog, self.set_watchdog),
("watchdog_stop_idle", self.get_watchdog_idle, self.set_watchdog_idle), ("watchdog_stop_idle", self.get_watchdog_idle, self.set_watchdog_idle),
("watchdog_prescale", self.get_watchdog_prescale, self.set_watchdog_prescale), ("watchdog_prescale", self.get_watchdog_prescale, self.set_watchdog_prescale),
@ -468,6 +475,9 @@ class Stc15Option(BaseOption):
("uart2_pin_mode", self.get_uart_pin_mode, self.set_uart_pin_mode), ("uart2_pin_mode", self.get_uart_pin_mode, self.set_uart_pin_mode),
) )
if len(msr) > 4:
self.options += ("cpu_core_voltage", self.get_core_voltage, self.set_core_voltage),
def get_reset_pin_enabled(self): def get_reset_pin_enabled(self):
return not bool(self.msr[2] & 16) return not bool(self.msr[2] & 16)
@ -632,6 +642,8 @@ class StcBaseProtocol:
"""magic byte for packets sent by host""" """magic byte for packets sent by host"""
PACKET_HOST = bytes([0x6a]) PACKET_HOST = bytes([0x6a])
PARITY = serial.PARITY_NONE
def __init__(self, port, baud_handshake, baud_transfer): def __init__(self, port, baud_handshake, baud_transfer):
self.port = port self.port = port
self.baud_handshake = baud_handshake self.baud_handshake = baud_handshake
@ -644,19 +656,14 @@ class StcBaseProtocol:
self.model = None self.model = None
self.uid = None self.uid = None
self.debug = False self.debug = False
self.status_packet = None
self.protocol_name = None
def dump_packet(self, data, receive=True): def dump_packet(self, data, receive=True):
if self.debug: if self.debug:
print("%s Packet data: %s" % (("<-" if receive else "->"), print("%s Packet data: %s" % (("<-" if receive else "->"),
Utils.hexstr(data, " ")), file=sys.stderr) 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): def read_bytes_safe(self, num):
"""Read data from serial port with timeout handling """Read data from serial port with timeout handling
@ -668,126 +675,14 @@ class StcBaseProtocol:
return data return data
def print_mcu_info(self): def extract_payload(self, packet):
"""Print MCU status information""" """Extract the payload of a packet"""
MCUModelDatabase.print_model_info(self.model) if packet[-1] != self.PACKET_END[0]:
print("Target frequency: %.3f MHz" % (self.mcu_clock_hz / 1E6)) self.dump_packet(packet)
print("Target BSL version: %s" % self.mcu_bsl_version) raise StcFramingException("incorrect frame end")
def pulse(self, character=b"\x7f", timeout=0): return packet[5:-1]
"""Send a sequence of bytes for synchronization with MCU"""
duration = 0
while True:
if timeout > 0 and duration > timeout:
raise serial.SerialTimeoutException("pulse timeout")
self.ser.write(character)
self.ser.flush()
time.sleep(0.015)
duration += 0.015
if self.ser.inWaiting() > 0: break
def initialize_model(self):
"""Initialize model-specific information"""
try:
self.model = MCUModelDatabase.find_model(self.mcu_magic)
except NameError:
msg = ("WARNING: Unknown model %02X%02X!" %
(self.mcu_magic >> 8, self.mcu_magic & 0xff))
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()
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
def get_iap_delay(self, clock_hz):
"""IAP wait states for STC12A+ (according to datasheet(s))"""
iap_wait = 0x80
if clock_hz < 1E6: iap_wait = 0x87
elif clock_hz < 2E6: iap_wait = 0x86
elif clock_hz < 3E6: iap_wait = 0x85
elif clock_hz < 6E6: iap_wait = 0x84
elif clock_hz < 12E6: iap_wait = 0x83
elif clock_hz < 20E6: iap_wait = 0x82
elif clock_hz < 24E6: iap_wait = 0x81
return iap_wait
def set_option(self, name, value):
self.options.set_option(name, value)
def connect(self):
"""Connect to MCU and initialize communication.
Set up serial port, send sync sequence and get part info.
"""
self.ser = serial.Serial(port=self.port, parity=self.PARITY)
# set baudrate separately to work around a bug with the CH340 driver
# on older Linux kernels
self.ser.baudrate = self.baud_handshake
# fast timeout values to deal with detection errors
self.ser.timeout = 0.5
self.ser.interCharTimeout = 0.5
# avoid glitches if there is something in the input buffer
self.ser.flushInput()
print("Waiting for MCU, please cycle power: ", end="")
sys.stdout.flush()
# send sync, and wait for MCU response
# ignore errors until we see a valid response
status_packet = None
while not status_packet:
try:
self.pulse()
status_packet = self.get_status_packet()
except (StcFramingException, serial.SerialTimeoutException): pass
print("done")
# conservative timeout values
self.ser.timeout = 15.0
self.ser.interCharTimeout = 1.0
self.initialize_status(status_packet)
self.initialize_model()
self.initialize_options(status_packet)
def disconnect(self):
"""Disconnect from MCU"""
# reset mcu
packet = bytes([0x82])
self.write_packet(packet)
self.ser.close()
print("Disconnected!")
class Stc89Protocol(StcBaseProtocol):
"""Protocol handler for STC 89/90 series"""
"""These don't use any parity"""
PARITY = serial.PARITY_NONE
"""block size for programming flash"""
PROGRAM_BLOCKSIZE = 128
def __init__(self, port, baud_handshake, baud_transfer):
StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer)
self.cpu_6t = None
def read_packet(self): def read_packet(self):
"""Read and check packet from MCU. """Read and check packet from MCU.
@ -827,22 +722,205 @@ class Stc89Protocol(StcBaseProtocol):
packet_len, = struct.unpack(">H", packet[3:5]) packet_len, = struct.unpack(">H", packet[3:5])
packet += self.read_bytes_safe(packet_len - 3) packet += self.read_bytes_safe(packet_len - 3)
# verify end code # verify checksum and extract payload
if packet[packet_len+1] != self.PACKET_END[0]: payload = self.extract_payload(packet);
self.dump_packet(packet)
raise StcFramingException("incorrect frame end")
# verify checksum
packet_csum = packet[packet_len]
calc_csum = sum(packet[2:packet_len]) & 0xff
if packet_csum != calc_csum:
self.dump_packet(packet)
raise StcFramingException("packet checksum mismatch")
self.dump_packet(packet, receive=True) self.dump_packet(packet, receive=True)
# payload only is returned # payload only is returned
return packet[5:packet_len] return payload
def print_mcu_info(self):
"""Print MCU status information"""
MCUModelDatabase.print_model_info(self.model)
print("Target frequency: %.3f MHz" % (self.mcu_clock_hz / 1E6))
print("Target BSL version: %s" % self.mcu_bsl_version)
def pulse(self, character=b"\x7f", timeout=0):
"""Send a sequence of bytes for synchronization with MCU"""
duration = 0
while True:
if timeout > 0 and duration > timeout:
raise serial.SerialTimeoutException("pulse timeout")
self.ser.write(character)
self.ser.flush()
time.sleep(0.015)
duration += 0.015
if self.ser.inWaiting() > 0: break
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:
msg = ("WARNING: Unknown model %02X%02X!" %
(self.mcu_magic >> 8, self.mcu_magic & 0xff))
print(msg, file=sys.stderr)
self.model = MCUModelDatabase.MCUModel(name="UNKNOWN",
magic=self.mcu_magic, total=63488, code=63488, eeprom=0)
# 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"""
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))"""
iap_wait = 0x80
if clock_hz < 1E6: iap_wait = 0x87
elif clock_hz < 2E6: iap_wait = 0x86
elif clock_hz < 3E6: iap_wait = 0x85
elif clock_hz < 6E6: iap_wait = 0x84
elif clock_hz < 12E6: iap_wait = 0x83
elif clock_hz < 20E6: iap_wait = 0x82
elif clock_hz < 24E6: iap_wait = 0x81
return iap_wait
def set_option(self, name, value):
self.options.set_option(name, value)
def connect(self, autoreset=False):
"""Connect to MCU and initialize communication.
Set up serial port, send sync sequence and get part info.
"""
self.ser = serial.Serial(port=self.port, parity=self.PARITY)
# set baudrate separately to work around a bug with the CH340 driver
# on older Linux kernels
self.ser.baudrate = self.baud_handshake
# fast timeout values to deal with detection errors
self.ser.timeout = 0.5
self.ser.interCharTimeout = 0.5
# avoid glitches if there is something in the input buffer
self.ser.flushInput()
if autoreset:
print("Cycling power: ", end="")
sys.stdout.flush()
self.ser.setDTR(True)
time.sleep(0.5)
self.ser.setDTR(False)
print("done")
print("Waiting for MCU: ", end="")
sys.stdout.flush()
else:
print("Waiting for MCU, please cycle power: ", end="")
sys.stdout.flush()
# send sync, and wait for MCU response
# ignore errors until we see a valid response
self.status_packet = None
while not self.status_packet:
try:
self.pulse()
self.status_packet = self.get_status_packet()
except (StcFramingException, serial.SerialTimeoutException): pass
print("done")
# conservative timeout values
self.ser.timeout = 15.0
self.ser.interCharTimeout = 1.0
self.initialize_model()
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"""
# reset mcu
packet = bytes([0x82])
self.write_packet(packet)
self.ser.close()
print("Disconnected!")
class Stc89Protocol(StcBaseProtocol):
"""Protocol handler for STC 89/90 series"""
"""These don't use any parity"""
PARITY = serial.PARITY_NONE
"""block size for programming flash"""
PROGRAM_BLOCKSIZE = 128
def __init__(self, port, baud_handshake, baud_transfer):
StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer)
self.cpu_6t = None
def extract_payload(self, packet):
"""Verify the checksum of packet and return its payload"""
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")
payload = StcBaseProtocol.extract_payload(self, packet)
return payload[:-1]
def write_packet(self, data): def write_packet(self, data):
"""Send packet to MCU. """Send packet to MCU.
@ -913,7 +991,6 @@ class Stc89Protocol(StcBaseProtocol):
def initialize_status(self, packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """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) self.cpu_6t = not bool(packet[19] & 1)
cpu_t = 6.0 if self.cpu_6t else 12.0 cpu_t = 6.0 if self.cpu_6t else 12.0
@ -1043,8 +1120,6 @@ class Stc12AProtocol(Stc89Protocol):
def initialize_status(self, packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """Decode status packet and store basic MCU info"""
self.mcu_magic, = struct.unpack(">H", packet[20:22])
freq_counter = 0 freq_counter = 0
for i in range(8): for i in range(8):
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0] freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
@ -1091,7 +1166,7 @@ class Stc12AProtocol(Stc89Protocol):
def handshake(self): def handshake(self):
"""Do baudrate handshake """Do baudrate handshake
Initate and do the (rather complicated) baudrate handshake. Initiate and do the (rather complicated) baudrate handshake.
""" """
# start baudrate handshake # start baudrate handshake
@ -1183,50 +1258,17 @@ class Stc12Protocol(StcBaseProtocol):
def __init__(self, port, baud_handshake, baud_transfer): def __init__(self, port, baud_handshake, baud_transfer):
StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer) StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer)
def read_packet(self): def extract_payload(self, packet):
"""Read and check packet from MCU. """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. packet_csum, = struct.unpack(">H", packet[-3:-1])
""" calc_csum = sum(packet[2:-3]) & 0xffff
# 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
if packet_csum != calc_csum: if packet_csum != calc_csum:
self.dump_packet(packet) self.dump_packet(packet)
raise StcFramingException("packet checksum mismatch") raise StcFramingException("packet checksum mismatch")
self.dump_packet(packet, receive=True) payload = StcBaseProtocol.extract_payload(self, packet)
return payload[:-2]
# payload only is returned
return packet[5:packet_len-1]
def write_packet(self, data): def write_packet(self, data):
"""Send packet to MCU. """Send packet to MCU.
@ -1254,8 +1296,6 @@ class Stc12Protocol(StcBaseProtocol):
def initialize_status(self, packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """Decode status packet and store basic MCU info"""
self.mcu_magic, = struct.unpack(">H", packet[20:22])
freq_counter = 0 freq_counter = 0
for i in range(8): for i in range(8):
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0] freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
@ -1291,7 +1331,7 @@ class Stc12Protocol(StcBaseProtocol):
delay = 0x80 delay = 0x80
return brt, brt_csum, iap_wait, delay return brt, brt_csum, iap_wait, delay
def initialize_options(self, status_packet): def initialize_options(self, status_packet):
"""Initialize options""" """Initialize options"""
@ -1460,8 +1500,6 @@ class Stc15AProtocol(Stc12Protocol):
def initialize_status(self, packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """Decode status packet and store basic MCU info"""
self.mcu_magic, = struct.unpack(">H", packet[20:22])
freq_counter = 0 freq_counter = 0
for i in range(4): for i in range(4):
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0] freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
@ -1641,6 +1679,7 @@ class Stc15AProtocol(Stc12Protocol):
print("Target UID: %s" % Utils.hexstr(self.uid)) print("Target UID: %s" % Utils.hexstr(self.uid))
class Stc15Protocol(Stc15AProtocol): class Stc15Protocol(Stc15AProtocol):
"""Protocol handler for later STC 15 series""" """Protocol handler for later STC 15 series"""
@ -1659,8 +1698,6 @@ class Stc15Protocol(Stc15AProtocol):
def initialize_status(self, packet): def initialize_status(self, packet):
"""Decode status packet and store basic MCU info""" """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 # check bit that control internal vs. external clock source
# get frequency either stored from calibration or from # get frequency either stored from calibration or from
# frequency counter # frequency counter
@ -1847,6 +1884,8 @@ class Stc15Protocol(Stc15AProtocol):
packet += bytes([0x00, 0x00, 0x5a, 0xa5]) packet += bytes([0x00, 0x00, 0x5a, 0xa5])
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if response[0] == 0x0f:
raise StcProtocolException("MCU is locked")
if response[0] != 0x05: if response[0] != 0x05:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
@ -1906,15 +1945,12 @@ class Stc15Protocol(Stc15AProtocol):
raise StcProtocolException("incorrect magic in finish packet") raise StcProtocolException("incorrect magic in finish packet")
print("done") print("done")
def program_options(self): def build_options(self):
print("Setting options: ", end="") """Build a 64 byte packet of option data from the current
sys.stdout.flush() configuration."""
msr = self.options.get_msr()
packet = bytes([0x04, 0x00, 0x00]) msr = self.options.get_msr()
if self.bsl_version >= 0x72: packet = bytes([0xff] * 23)
packet += bytes([0x5a, 0xa5])
packet += bytes([0xff] * 23)
packet += bytes([(self.trim_frequency >> 24) & 0xff, packet += bytes([(self.trim_frequency >> 24) & 0xff,
0xff, 0xff,
(self.trim_frequency >> 16) & 0xff, (self.trim_frequency >> 16) & 0xff,
@ -1925,10 +1961,23 @@ class Stc15Protocol(Stc15AProtocol):
0xff]) 0xff])
packet += bytes([msr[3]]) packet += bytes([msr[3]])
packet += bytes([0xff] * 23) packet += bytes([0xff] * 23)
packet += bytes([msr[4]]) if len(msr) > 4:
packet += bytes([msr[4]])
else:
packet += bytes([0xff])
packet += bytes([0xff] * 3) packet += bytes([0xff] * 3)
packet += bytes([self.trim_value[0], self.trim_value[1] + 0x3f]) packet += bytes([self.trim_value[0], self.trim_value[1] + 0x3f])
packet += msr[0:3] packet += msr[0:3]
return packet
def program_options(self):
print("Setting options: ", end="")
sys.stdout.flush()
packet = bytes([0x04, 0x00, 0x00])
if self.bsl_version >= 0x72:
packet += bytes([0x5a, 0xa5])
packet += self.build_options()
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if response[0] != 0x04 or response[1] != 0x54: if response[0] != 0x04 or response[1] != 0x54:
@ -1938,3 +1987,165 @@ class Stc15Protocol(Stc15AProtocol):
print("Target UID: %s" % Utils.hexstr(self.uid)) print("Target UID: %s" % Utils.hexstr(self.uid))
class StcUsb15Protocol(Stc15Protocol):
"""USB should use large blocks"""
PROGRAM_BLOCKSIZE = 128
"""VID of STC devices"""
USB_VID = 0x5354
"""PID of STC devices"""
USB_PID = 0x4312
"""Control transfer from host to device"""
USB_HOST2DEV = usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.CTRL_OUT
"""Control transfer from device to host"""
USB_DEV2HOST = usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_RECIPIENT_DEVICE | usb.util.CTRL_IN
def __init__(self):
# XXX: this is really ugly!
Stc15Protocol.__init__(self, "", 0, 0, 0)
self.dev = None
def dump_packet(self, data, request=0, value=0, index=0, receive=True):
if self.debug:
print("%s bRequest=%02X wValue=%04X wIndex=%04X data: %s" % (("<-" if receive else "->"),
request, value, index, Utils.hexstr(data, " ")), file=sys.stderr)
def read_packet(self):
"""Read a packet from the MCU"""
packet = self.dev.ctrl_transfer(self.USB_DEV2HOST, 0, 0, 0, 132).tobytes()
if len(packet) < 5 or packet[0] != 0x46 or packet[1] != 0xb9:
self.dump_packet(packet)
raise StcFramingException("incorrect frame start")
data_len = packet[2]
if (data_len) > len(packet) + 3:
self.dump_packet(packet)
raise StcFramingException("frame length mismatch")
data = packet[2:-1]
csum = functools.reduce(lambda x, y: x - y, data, 0) & 0xff
if csum != packet[-1]:
self.dump_packet(packet)
raise StcFramingException("frame checksum mismatch")
self.dump_packet(packet, receive=True)
return packet[3:3+data_len]
def write_packet(self, request, value=0, index=0, data=bytes([0])):
"""Write USB control packet"""
# Control transfers are maximum of 8 bytes each, and every
# invidual partial transfer is checksummed individually.
i = 0
chunks = bytes()
while i < len(data):
c = data[i:i+7]
csum = functools.reduce(lambda x, y: x - y, c, 0) & 0xff
chunks += c + bytes([csum])
i += 7
self.dump_packet(chunks, request, value, index, receive=False)
self.dev.ctrl_transfer(self.USB_HOST2DEV, request, value, index, chunks);
def connect(self, autoreset=False):
"""Connect to USB device and read info packet"""
# USB support is optional. Provide an error if pyusb is not available.
if _usb_available == False:
raise StcProtocolException("USB support not available. "
+ "pyusb is not installed or not working correctly.")
print("Waiting for MCU, please cycle power: ", end="")
sys.stdout.flush()
self.status_packet = None
while not self.status_packet:
try:
self.dev = usb.core.find(idVendor=self.USB_VID, idProduct=self.USB_PID)
if self.dev:
self.dev.set_configuration()
self.status_packet = self.read_packet()
else:
time.sleep(0.5)
except (StcFramingException, usb.core.USBError): pass
self.initialize_model()
print("done")
def handshake(self):
print("Initializing: ", end="")
sys.stdout.flush()
# handshake
self.write_packet(0x01, 0, 0, bytes([0x03]))
response = self.read_packet()
if response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet")
# enable/unlock MCU
self.write_packet(0x05, 0xa55a, 0)
response = self.read_packet()
if response[0] == 0x0f:
raise StcProtocolException("MCU is locked")
if response[0] != 0x05:
raise StcProtocolException("incorrect magic in handshake packet")
print("done")
def erase_flash(self, code, eeprom):
print("Erasing flash: ", end="")
sys.stdout.flush()
self.write_packet(0x03, 0xa55a, 0)
# XXX: better way to detect MCU has finished
time.sleep(2)
packet = self.read_packet()
if packet[0] != 0x03:
raise StcProtocolException("incorrect magic in erase packet")
self.uid = packet[1:8]
print("done")
def program_flash(self, data):
"""Program the MCU's flash memory."""
print("Writing %d bytes: " % len(data), end="")
sys.stdout.flush()
for i in range(0, len(data), self.PROGRAM_BLOCKSIZE):
packet = data[i:i+self.PROGRAM_BLOCKSIZE]
while len(packet) < self.PROGRAM_BLOCKSIZE: packet += b"\x00"
self.write_packet(0x22 if i == 0 else 0x02, 0xa55a, i, packet)
# XXX: better way to detect MCU has finished
time.sleep(0.1)
response = self.read_packet()
if response[0] != 0x02 or response[1] != 0x54:
raise StcProtocolException("incorrect magic in write packet")
print(".", end="")
sys.stdout.flush()
print(" done")
def program_options(self):
print("Setting options: ", end="")
sys.stdout.flush()
# always use 24 MHz pre-tuned value for now
self.trim_value = (self.freq_count_24, 0x40)
self.trim_frequency = int(24E6)
packet = self.build_options()
self.write_packet(0x04, 0xa55a, 0, packet)
# XXX: better way to detect MCU has finished
time.sleep(0.5)
response = self.read_packet()
if response[0] != 0x04 or response[1] != 0x54:
raise StcProtocolException("incorrect magic in option packet")
print("done")
print("Target UID: %s" % Utils.hexstr(self.uid))
def disconnect(self):
if self.dev:
self.write_packet(0xff)
print("Disconnected!")