34 Commits

Author SHA1 Message Date
a414bfb864 protocols.py: Increase clock_safety_factor to 2.5
This fixes cp2102 and ch341a baudswitch on mac sierra

Signed-off-by: Andrew 'ncrmnt' Andrianov <andrew@ncrmnt.org>
2017-10-14 21:08:26 +03:00
68d19f7b88 Use calculated delays
Some serial drivers don't handle draining the transmit buffer
correctly. This has been handled with a long delay so far, which might
be problematic. There's a race condition with some protocol versions.

Until STC15, the baud rate switch is initiated with a command sent by
stcgal, which is replied to by the MCU with the new baud rate. So the
switch of the baud rate has to be done after the command has finished
transmission, but before the MCU has started to transmit the response.

This change calculates the minimum delay needed (with some tolerance
added) so that it's unlikely that the baud rate switch will happen
too late.
2017-10-10 22:11:07 +02:00
ebcfeb467c Merge pull request #27 from nekromant/fixes
Implement power-cycling via a custom shell cmd, update models.py
2017-10-09 00:05:55 +02:00
d7e226df6b README.md: Document -r option properly
Signed-off-by: Andrew Andrianov <andrew@ncrmnt.org>
2017-10-08 23:58:31 +03:00
191a580469 protocols: Move device reset logic to a separate method
Signed-off-by: Andrew Andrianov <andrew@ncrmnt.org>
2017-10-08 23:20:17 +03:00
c131a9d901 frontend: Use command instead of cmd in description
Signed-off-by: Andrew Andrianov <andrew@ncrmnt.org>
2017-10-08 23:19:55 +03:00
3f4263e8fe models.py: Add some STC15xxx definitions from stcdude's mcudb
Signed-off-by: Andrew Andrianov <andrew@ncrmnt.org>
2017-10-07 23:30:03 +03:00
ba4faf9c43 Implement power-cycling via custom shell command
Sometimes instead of DTR line some custom way (e.g SoC gpio line)
may be used to reset the device. This commit implements
automated power-cycling using a a custom shell command that can
be specified via -r option

Signed-off-by: Andrew Andrianov <andrew@ncrmnt.org>
2017-10-07 22:41:49 +03:00
f1bafb1e0d Update version to 1.4 2017-09-19 18:00:02 +02:00
fdd6707d2d Add Travis CI for CI and CD
Run tests for each commit and deploy packages (deb/rpm) for each
release tag.
2017-09-19 17:54:26 +02:00
532363d97b Exclude test directory from build 2017-09-18 15:42:08 +02:00
5865b06f7f Add STC15W4K56S4 programming test 2017-09-02 10:23:06 +02:00
1b69257cd3 Add missing STC15F104E trace
Currently broken, needs retracing.
2017-09-02 10:12:14 +02:00
38ac5f0788 Add STC15L104W programming test 2017-09-02 08:05:13 +02:00
6dccf13fb6 Add missing test dependency
PyYAML is needed to parse the programming simulation data.
2017-09-02 00:08:28 +02:00
0ca8b2ea2d Ensure hexstr helper actually received a list of bytes
Otherwise, the formatted output is rather bogus. Found with a test case.
2017-09-02 00:00:27 +02:00
53184b549e Handle None in to_bool utility
None is commonly used, so we want to be able to handle it with this helper.
Found with a test case.
2017-09-01 23:59:05 +02:00
cf68e3c6dc Add initial tests
This adds various tests, integrated into setuptools. These use "monkey
patching" where needed to mock the pyserial and packet reader/writer
functionality to allow for testing with no changes.

The code should be refactored to simplify testing, but this is good enough
to stop regressions for now.
2017-09-01 23:55:46 +02:00
5d10c06f1e Add Visual Studio Code to gitignore 2017-08-31 21:08:57 +02:00
7e84b8e0fb Fix some additional code smells
No functional change intended.
2017-08-31 21:07:56 +02:00
f34ba6644f Fix option error handling for STC12A LVD 2017-06-16 10:22:24 +02:00
f15b64f4f7 Fix some minor code smells reported by pylint
No functional change intended.
2017-06-16 10:21:43 +02:00
2e822375e0 Update Debian packaging scripts for 1.3 2017-06-10 10:02:53 +02:00
7d6e8e9bfd Update version to 1.3 2017-06-10 09:55:38 +02:00
506289b8ee Add __main__ module
This allows stcgal to be started with "python3 -m stcgal" or similar.
Addresses stcgal#24.
2017-06-09 21:03:07 +02:00
f417b6eed5 Add new compatibility report
Closes #20.
2016-11-22 10:23:47 +01:00
86e289b65c usb15: add basic protocol information
These are just my notes from reverse engineering.
2016-06-10 21:12:17 +02:00
53f9544281 stc15: fix RC oscillator baudrate switch packet
It looks like it wasn't correct. The last value sent is probably
supposed to be the trim value for the chosen trim frequency.

Found while investigating grigorig/stcgal#16.
2016-06-10 12:45:58 +02:00
65a7759647 stc12+: drop checksum verification for flashing
It's not needed on STC12 and up. All transfers are error checked with
parity and a 16-bit modular sum already. STC15 dropped the verification
checksum on the protocol level, it's not sent with the write status
packet, which is a testament to this not being needed.

Some parts store the UID in the last bytes of flash memory and this
verification actually caused incorrect verification failures because
of the verification, which apparently read the UID on verification
readback.

Fixes grigorig/stcgal#15.
2016-05-28 11:25:44 +02:00
8ad77586d4 Update version to 1.2 2016-05-20 03:22:24 +02:00
276c696fa4 frontend: enable protocol autodetection by default
It seems to work rather well after some extended testing. Also clean
up and update the protocol documentation while at it.
2016-05-20 02:59:35 +02:00
26ef34991b usb15: abort if permission denied
Don't ignore permission denied when looking for a suitable USB device.
Otherwise users don't notice what the problem is, stcgal will just
keep waiting on the prompt.
2016-05-20 02:55:23 +02:00
f90fe4152b Add STC12B protocol variant
This is just like STC12, but with the STC12A option packet. Used by
STC12xx52 series, STC12xx56 series and possibly others.

Fixes grigorig/stcgal#14.
2016-05-20 02:54:57 +02:00
d6ef028dc7 Extract mix-ins for STC12 and STC12A options
This simplifies code down the line. No functional change intended.
2016-05-20 02:44:50 +02:00
24 changed files with 782 additions and 189 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@ __pycache__
/build /build
/dist /dist
/deb_dist /deb_dist
/.vscode

33
.travis.yml Normal file
View File

@ -0,0 +1,33 @@
sudo: required
dist: trusty
language: python
cache:
- pip
python:
- "3.4"
- "3.5"
- "3.6"
- "pypy3"
before_install:
- sudo apt install rpm dpkg-dev debhelper dh-python python3-setuptools fakeroot python3-serial python3-yaml
install:
- pip install pyserial pyusb
script:
- python setup.py build
- python setup.py test
before_deploy:
- deactivate
- python3 setup.py bdist_rpm
- dpkg-buildpackage -uc -us
- cp ../*.deb dist/
deploy:
provider: releases
api_key: $GH_TOKEN
file_glob: true
file:
- dist/stcgal*_all.deb
- dist/stcgal*.noarch.rpm
skip_cleanup: true
on:
tags: true
python: "3.4"

View File

@ -1,3 +1,5 @@
[![Build Status](https://travis-ci.org/grigorig/stcgal.svg)](https://travis-ci.org/grigorig/stcgal)
stcgal - STC MCU ISP flash tool stcgal - STC MCU ISP flash tool
=============================== ===============================
@ -25,14 +27,14 @@ 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/6.6C)
* STC90C52RC (BSL version: 4.3C) * STC90C52RC (BSL version: 4.3C)
* STC89C54RD+ (BSL version: 4.3C) * STC89C54RD+ (BSL version: 4.3C)
* STC12C2052 (BSL version: 5.8D) * STC12C2052 (BSL version: 5.8D)
* STC12C2052AD (BSL version: 5.8D) * STC12C2052AD (BSL version: 5.8D)
* STC12C5608AD (BSL version: 6.0G) * STC12C5608AD (BSL version: 6.0G)
* STC12C5A16S2 (BSL version: 6.2I) * STC12C5A16S2 (BSL version: 6.2I)
* STC12C5A60S2 (BSL version: 6.2I) * STC12C5A60S2 (BSL version: 6.2I/7.1I)
* STC11F02E (BSL version: 6.5K) * STC11F02E (BSL version: 6.5K)
* STC10F04XE (BSL version: 6.5J) * STC10F04XE (BSL version: 6.5J)
* STC11F08XE (BSL version: 6.5M) * STC11F08XE (BSL version: 6.5M)
@ -59,7 +61,7 @@ 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 power-cycling with DTR toggle or a custom shell command
* Automatic UART protocol detection * Automatic UART protocol detection
Installation Installation
@ -94,6 +96,9 @@ positional arguments:
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
-a, --autoreset cycle power automatically by asserting DTR -a, --autoreset cycle power automatically by asserting DTR
-r RESETCMD, --resetcmd RESETCMD
Use this shell command for board power-cycling
(instead of DTR assertion)
-P {stc89,stc12a,stc12,stc15a,stc15,auto}, --protocol {stc89,stc12a,stc12,stc15a,stc15,auto} -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
@ -111,17 +116,18 @@ 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 can be specified with the ```-P``` flag. Optionally, BSL. The protocol can be specified with the ```-P``` flag. By default
experimental protocol autodetection can be used. The mapping between UART protocol autodetection is used. The mapping between protocols
protocols and MCU series is as follows: and MCU series is as follows:
* ```stc89``` STC 89/90 series * ```stc89``` STC89/90 series
* ```stc12a``` STC12Cx052AD and possibly others * ```stc12a``` STC12x052 series and possibly others
* ```stc12``` Most STC10/11/12 series (default) * ```stc12b``` STC12x52 series, STC12x56 series and possibly others
* ```stc12``` Most STC10/11/12 series
* ```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 * ```usb15``` USB support on STC15W4 series
* ```auto``` Automatic detection of UART based protocols * ```auto``` Automatic detection of UART based protocols (default)
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,
@ -273,7 +279,22 @@ serial interface to automate this. The DTR signal is asserted for
approximately 500 ms when the autoreset feature is enabled with the approximately 500 ms when the autoreset feature is enabled with the
```-a``` flag. This requires external circuitry to actually switch the ```-a``` flag. This requires external circuitry to actually switch the
power. In some cases, when the microcontroller draws only little power, power. In some cases, when the microcontroller draws only little power,
it is possible to directly supply power from the DTR signal, however. it is possible to directly supply power from the DTR signal.
As an alternative to DTR, you can use a custom shell command or an external
script (via -r option) to reset the device. You should specify the command
along with -a option. Do not forget the quotes!
Example:
```
$ ./stcgal.py -P stc15 -a -r "echo 1 > /sys/class/gpio/gpio666/value"
```
or
```
$ ./stcgal.py -P stc15 -a -r "./powercycle.sh"
```
### Exit status ### Exit status

19
debian/changelog vendored
View File

@ -1,3 +1,22 @@
stcgal (1.4) unstable; urgency=low
* Update to 1.4
-- Grigori <greg@chown.ath.cx> Tue, 19 Sep 2017 17:57:11 +0200
stcgal (1.3) unstable; urgency=low
* Update to 1.3
-- Grigori Goronzy <greg@chown.ath.cx> Sat, 10 Jun 2017 10:01:07 +0200
stcgal (1.2) unstable; urgency=low
* Update to 1.2
* Add optional python3-usb dependency
-- Grigori Goronzy <greg@chown.ath.cx> Fri, 20 May 2016 03:21:25 +0200
stcgal (1.0git) unstable; urgency=low stcgal (1.0git) unstable; urgency=low
* Initial Debianized Release * Initial Debianized Release

18
debian/control vendored
View File

@ -10,17 +10,17 @@ X-Python3-Version: >= 3.2
Package: stcgal Package: stcgal
Architecture: all Architecture: all
Depends: ${misc:Depends}, python3, python3-serial Depends: ${misc:Depends}, python3, python3-serial
Recommends: python3-usb (>= 1.0.0~b2)
Description: STC MCU ISP flash tool Description: STC MCU ISP flash tool
stcgal is a command line flash programming tool for STC MCU Ltd. 8051 stcgal is a command line flash programming tool for STC MCU Ltd.
compatible microcontrollers. The name was inspired by avrdude. 8051 compatible microcontrollers. The name was inspired by avrdude.
. .
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 utilizes a packet-based protocol to flash the code memory and IAP
IAP memory over a serial link. This is referred to as in-system memory over a serial link. This is referred to as in-system programming
programming (ISP). The BSL is also used to configure various (ISP). The BSL is also used to configure various (fuse-like) device
(fuse-like) device options. Unfortunately, this protocol is not options. Unfortunately, this protocol is not publicly documented and
publicly documented and STC only provide a (crude) Windows GUI STC only provide a (crude) Windows GUI application for programming.
application for programming.
. .
stcgal is a full-featured Open Source replacement for STC's Windows stcgal is a full-featured Open Source replacement for STC's Windows
software; it supports a wide range of MCUs, it is very portable and software; it supports a wide range of MCUs, it is very portable and

View File

@ -0,0 +1,46 @@
STC15 series USB ISP protocol
=============================
General principle
-----------------
- host does OUT and IN control transfers for write and read
- IN transfer with wLength = 132, wValue = 0, wIndex = 0, bRequest = 0 are used for all reads
- OUT transfers with with specific bRequest, wValue, wIndex are used for writes
Packet coding
-------------
- packets from MCU
always start with 0x46 0xb9, similar to serial protocols
third byte is packet length, followed by data bytes
checksum at the end: 8 bit modular sum
- packets from host
no header bytes
bRequest sets packet type
wValue, wIndex interpretation according to packet type
8 bit modular checksum for every 7 bytes, interleaved
- packet types derived from the serial protocol
Specific packet information
---------------------------
- flash data
wIndex specifies write address
wValue is 0xa55a
bRequest is 0x22 for first packet, 0x02 for the following ones
unusually encoded: a total of 128 bytes per packet,
with every 7 byte checksummed in some way,
for a total of 18x7 byte segments and a final 2 byte segment
checksum: 8 bit modular sum
- option packet
generally same as with serial protocol, some header stuff omitted
wIndex is 0
wValue is 0xa55a
bRequest is 4
seems to use the same checksumming scheme as flash writes

View File

@ -27,7 +27,7 @@ from setuptools import setup, find_packages
setup( setup(
name = "stcgal", name = "stcgal",
version = stcgal.__version__, version = stcgal.__version__,
packages = find_packages(exclude=["doc"]), packages = find_packages(exclude=["doc", "test"]),
install_requires = ["pyserial"], install_requires = ["pyserial"],
extras_require = { extras_require = {
"usb": ["pyusb>=1.0.0"] "usb": ["pyusb>=1.0.0"]
@ -55,4 +55,6 @@ setup(
"Topic :: Software Development :: Embedded Systems", "Topic :: Software Development :: Embedded Systems",
"Topic :: Software Development", "Topic :: Software Development",
], ],
test_suite = "test",
tests_require = ["PyYAML"],
) )

View File

@ -1 +1 @@
__version__ = "1.0" __version__ = "1.4"

27
stcgal/__main__.py Executable file
View File

@ -0,0 +1,27 @@
#
# Copyright (c) 2013-2015 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 sys
import stcgal.frontend
if __name__ == "__main__":
sys.exit(stcgal.frontend.cli())

View File

@ -20,10 +20,10 @@
# SOFTWARE. # SOFTWARE.
# #
import sys, os, time, struct import sys
import argparse import argparse
import stcgal import stcgal
from stcgal.utils import Utils, BaudType from stcgal.utils import BaudType
from stcgal.protocols import * from stcgal.protocols import *
from stcgal.ihex import IHex from stcgal.ihex import IHex
@ -37,6 +37,8 @@ class StcGal:
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":
self.protocol = Stc12AProtocol(opts.port, opts.handshake, opts.baud) self.protocol = Stc12AProtocol(opts.port, opts.handshake, opts.baud)
elif opts.protocol == "stc12b":
self.protocol = Stc12BProtocol(opts.port, opts.handshake, opts.baud)
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":
@ -53,13 +55,16 @@ class StcGal:
self.protocol.debug = opts.debug self.protocol.debug = opts.debug
def emit_options(self, options): def emit_options(self, options):
for o in options: """Set options from command line to protocol handler."""
for opt in options:
try: try:
kv = o.split("=", 1) kv = opt.split("=", 1)
if len(kv) < 2: raise ValueError("incorrect format") if len(kv) < 2:
raise ValueError("incorrect format")
self.protocol.set_option(kv[0], kv[1]) self.protocol.set_option(kv[0], kv[1])
except ValueError as e: except ValueError as ex:
raise NameError("invalid option '%s' (%s)" % (kv[0], e)) raise NameError("invalid option '%s' (%s)" % (kv[0], ex))
def load_file_auto(self, fileobj): def load_file_auto(self, fileobj):
"""Load file with Intel Hex autodetection.""" """Load file with Intel Hex autodetection."""
@ -72,14 +77,16 @@ class StcGal:
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
except ValueError as e: except ValueError as ex:
raise IOError("invalid Intel HEX file (%s)" %e) raise IOError("invalid Intel HEX file (%s)" %ex)
else: else:
binary = fileobj.read() binary = fileobj.read()
print("%d bytes (Binary)" %len(binary)) print("%d bytes (Binary)" %len(binary))
return binary return binary
def program_mcu(self): def program_mcu(self):
"""Execute the standard programming flow."""
code_size = self.protocol.model.code code_size = self.protocol.model.code
ee_size = self.protocol.model.eeprom ee_size = self.protocol.model.eeprom
@ -122,9 +129,10 @@ class StcGal:
self.protocol.disconnect() self.protocol.disconnect()
def run(self): def run(self):
try: """Run programmer, main entry point."""
self.protocol.connect(autoreset=self.opts.autoreset)
try:
self.protocol.connect(autoreset=self.opts.autoreset, resetcmd=self.opts.resetcmd)
if self.opts.protocol == "auto": if self.opts.protocol == "auto":
if not self.protocol.protocol_name: if not self.protocol.protocol_name:
raise StcProtocolException("cannot detect protocol") raise StcProtocolException("cannot detect protocol")
@ -138,21 +146,21 @@ class StcGal:
self.protocol.initialize(base_protocol) self.protocol.initialize(base_protocol)
except KeyboardInterrupt: except KeyboardInterrupt:
sys.stdout.flush(); sys.stdout.flush()
print("interrupted") print("interrupted")
return 2 return 2
except (StcFramingException, StcProtocolException) as e: except (StcFramingException, StcProtocolException) as ex:
sys.stdout.flush(); sys.stdout.flush()
print("Protocol error: %s" % e, file=sys.stderr) print("Protocol error: %s" % ex, file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 1 return 1
except serial.SerialException as e: except serial.SerialException as ex:
sys.stdout.flush(); sys.stdout.flush()
print("Serial port error: %s" % e, file=sys.stderr) print("Serial port error: %s" % ex, file=sys.stderr)
return 1 return 1
except IOError as e: except IOError as ex:
sys.stdout.flush(); sys.stdout.flush()
print("I/O error: %s" % e, file=sys.stderr) print("I/O error: %s" % ex, file=sys.stderr)
return 1 return 1
try: try:
@ -162,27 +170,27 @@ class StcGal:
else: else:
self.protocol.disconnect() self.protocol.disconnect()
return 0 return 0
except NameError as e: except NameError as ex:
sys.stdout.flush(); sys.stdout.flush()
print("Option error: %s" % e, file=sys.stderr) print("Option error: %s" % ex, file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 1 return 1
except (StcFramingException, StcProtocolException) as e: except (StcFramingException, StcProtocolException) as ex:
sys.stdout.flush(); sys.stdout.flush()
print("Protocol error: %s" % e, file=sys.stderr) print("Protocol error: %s" % ex, file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 1 return 1
except KeyboardInterrupt: except KeyboardInterrupt:
sys.stdout.flush(); sys.stdout.flush()
print("interrupted", file=sys.stderr) print("interrupted", file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 2 return 2
except serial.SerialException as e: except serial.SerialException as ex:
print("Serial port error: %s" % e, file=sys.stderr) print("Serial port error: %s" % ex, file=sys.stderr)
return 1 return 1
except IOError as e: except IOError as ex:
sys.stdout.flush(); sys.stdout.flush()
print("I/O error: %s" % e, file=sys.stderr) print("I/O error: %s" % ex, file=sys.stderr)
self.protocol.disconnect() self.protocol.disconnect()
return 1 return 1
@ -190,11 +198,12 @@ class StcGal:
def cli(): def cli():
# check arguments # check arguments
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
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-2017 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("-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("-P", "--protocol", help="protocol version", choices=["stc89", "stc12a", "stc12", "stc15a", "stc15", "usb15", "auto"], default="stc12") parser.add_argument("-r", "--resetcmd", help="Use this 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", "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=19200)
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)

View File

@ -973,6 +973,46 @@ class MCUModelDatabase:
MCUModel(name='STC90LE513AD', magic=0xf18d, total=65536, code=53248, eeprom=10240), MCUModel(name='STC90LE513AD', magic=0xf18d, total=65536, code=53248, eeprom=10240),
MCUModel(name='STC90LE514AD', magic=0xf18e, total=65536, code=57344, eeprom=6144), MCUModel(name='STC90LE514AD', magic=0xf18e, total=65536, code=57344, eeprom=6144),
MCUModel(name='STC90LE516AD', magic=0xf190, total=65536, code=63488, eeprom=0), MCUModel(name='STC90LE516AD', magic=0xf190, total=65536, code=63488, eeprom=0),
# Warning, these definitions lack a valid eeprom size.
MCUModel(name='STC15F04AD', magic=0xd444, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15F06AD', magic=0xd446, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15F08AD', magic=0xd448, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15F10AD', magic=0xd44a, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15F12AD', magic=0xd44c, total=12288, code=12288, eeprom=0),
MCUModel(name='STC15F04CCP', magic=0xd434, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15F06CCP', magic=0xd436, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15F08CCP', magic=0xd438, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15F10CCP', magic=0xd43a, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15F12CCP', magic=0xd43c, total=12288, code=12288, eeprom=0),
MCUModel(name='STC15F04', magic=0xd404, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15F06', magic=0xd406, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15F08', magic=0xd408, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15F10', magic=0xd40a, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15F12', magic=0xd40c, total=12288, code=12288, eeprom=0),
MCUModel(name='IAP15F08AD', magic=0xd458, total=8192, code=8192, eeprom=0),
MCUModel(name='IAP15F10AD', magic=0xd45a, total=10240, code=10240, eeprom=0),
MCUModel(name='IAP15F12AD', magic=0xd45c, total=12288, code=12288, eeprom=0),
MCUModel(name='IAP15F14AD', magic=0xd45e, total=14336, code=14336, eeprom=0),
MCUModel(name='STC15L04AD', magic=0xd4c4, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15L06AD', magic=0xd4c6, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15L08AD', magic=0xd4c8, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15L10AD', magic=0xd4ca, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15L12AD', magic=0xd4cc, total=12288, code=12288, eeprom=0),
MCUModel(name='STC15L04CCP', magic=0xd4b4, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15L06CCP', magic=0xd4b6, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15L08CCP', magic=0xd4b8, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15L10CCP', magic=0xd4ba, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15L12CCP', magic=0xd4bc, total=12288, code=12288, eeprom=0),
MCUModel(name='STC15L04', magic=0xd484, total=4096, code=4096, eeprom=0),
MCUModel(name='STC15L06', magic=0xd486, total=6144, code=6144, eeprom=0),
MCUModel(name='STC15L08', magic=0xd488, total=8192, code=8192, eeprom=0),
MCUModel(name='STC15L10', magic=0xd48a, total=10240, code=10240, eeprom=0),
MCUModel(name='STC15L12', magic=0xd48c, total=12288, code=12288, eeprom=0),
MCUModel(name='IAP15L08AD', magic=0xd4d8, total=8192, code=8192, eeprom=0),
MCUModel(name='IAP15L10AD', magic=0xd4da, total=10240, code=10240, eeprom=0),
MCUModel(name='IAP15L12AD', magic=0xd4dc, total=12288, code=12288, eeprom=0),
MCUModel(name='IAP15L14AD', magic=0xd4de, total=14336, code=14336, eeprom=0),
) )
@classmethod @classmethod
@ -988,6 +1028,3 @@ class MCUModelDatabase:
print(" Magic: %02X%02X" % (model.magic >> 8, model.magic & 0xff)) print(" Magic: %02X%02X" % (model.magic >> 8, model.magic & 0xff))
print(" Code flash: %.1f KB" % (model.code / 1024.0)) print(" Code flash: %.1f KB" % (model.code / 1024.0))
print(" EEPROM flash: %.1f KB" % (model.eeprom / 1024.0)) print(" EEPROM flash: %.1f KB" % (model.eeprom / 1024.0))

View File

@ -69,7 +69,7 @@ class Stc89Option(BaseOption):
return not bool(self.msr & 1) return not bool(self.msr & 1)
def set_t6(self, val): def set_t6(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr &= 0xfe self.msr &= 0xfe
self.msr |= 0x01 if not bool(val) else 0x00 self.msr |= 0x01 if not bool(val) else 0x00
@ -77,7 +77,7 @@ class Stc89Option(BaseOption):
return not bool(self.msr & 4) return not bool(self.msr & 4)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr &= 0xfb self.msr &= 0xfb
self.msr |= 0x04 if not bool(val) else 0x00 self.msr |= 0x04 if not bool(val) else 0x00
@ -85,7 +85,7 @@ class Stc89Option(BaseOption):
return not bool(self.msr & 8) return not bool(self.msr & 8)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr &= 0xf7 self.msr &= 0xf7
self.msr |= 0x08 if not bool(val) else 0x00 self.msr |= 0x08 if not bool(val) else 0x00
@ -104,7 +104,7 @@ class Stc89Option(BaseOption):
return bool(self.msr & 32) return bool(self.msr & 32)
def set_ale(self, val): def set_ale(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr &= 0xdf self.msr &= 0xdf
self.msr |= 0x20 if bool(val) else 0x00 self.msr |= 0x20 if bool(val) else 0x00
@ -112,7 +112,7 @@ class Stc89Option(BaseOption):
return bool(self.msr & 64) return bool(self.msr & 64)
def set_xram(self, val): def set_xram(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr &= 0xbf self.msr &= 0xbf
self.msr |= 0x40 if bool(val) else 0x00 self.msr |= 0x40 if bool(val) else 0x00
@ -120,7 +120,7 @@ class Stc89Option(BaseOption):
return not bool(self.msr & 128) return not bool(self.msr & 128)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr &= 0x7f self.msr &= 0x7f
self.msr |= 0x80 if not bool(val) else 0x00 self.msr |= 0x80 if not bool(val) else 0x00
@ -150,7 +150,7 @@ class Stc12AOption(BaseOption):
def set_low_voltage_detect(self, val): def set_low_voltage_detect(self, val):
lvds = {"low": 1, "high": 0} lvds = {"low": 1, "high": 0}
if val not in lvds.keys(): if val not in lvds.keys():
raise ValueError("must be one of %s" % list(sources.keys())) raise ValueError("must be one of %s" % list(lvds.keys()))
self.msr[3] &= 0xbf self.msr[3] &= 0xbf
self.msr[3] |= lvds[val] << 6 self.msr[3] |= lvds[val] << 6
@ -169,7 +169,7 @@ class Stc12AOption(BaseOption):
return not bool(self.msr[1] & 32) return not bool(self.msr[1] & 32)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[1] &= 0xdf self.msr[1] &= 0xdf
self.msr[1] |= 0x20 if not val else 0x00 self.msr[1] |= 0x20 if not val else 0x00
@ -177,7 +177,7 @@ class Stc12AOption(BaseOption):
return not bool(self.msr[1] & 8) return not bool(self.msr[1] & 8)
def set_watchdog_idle(self, val): def set_watchdog_idle(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[1] &= 0xf7 self.msr[1] &= 0xf7
self.msr[1] |= 0x08 if not val else 0x00 self.msr[1] |= 0x08 if not val else 0x00
@ -196,7 +196,7 @@ class Stc12AOption(BaseOption):
return not bool(self.msr[2] & 2) return not bool(self.msr[2] & 2)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[2] &= 0xfd self.msr[2] &= 0xfd
self.msr[2] |= 0x02 if not val else 0x00 self.msr[2] |= 0x02 if not val else 0x00
@ -204,7 +204,7 @@ class Stc12AOption(BaseOption):
return not bool(self.msr[2] & 1) return not bool(self.msr[2] & 1)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[2] &= 0xfe self.msr[2] &= 0xfe
self.msr[2] |= 0x01 if not val else 0x00 self.msr[2] |= 0x01 if not val else 0x00
@ -235,7 +235,7 @@ class Stc12Option(BaseOption):
return bool(self.msr[0] & 1) return bool(self.msr[0] & 1)
def set_reset_pin_enabled(self, val): def set_reset_pin_enabled(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[0] &= 0xfe self.msr[0] &= 0xfe
self.msr[0] |= 0x01 if bool(val) else 0x00 self.msr[0] |= 0x01 if bool(val) else 0x00
@ -243,7 +243,7 @@ class Stc12Option(BaseOption):
return not bool(self.msr[0] & 64) return not bool(self.msr[0] & 64)
def set_low_voltage_detect(self, val): def set_low_voltage_detect(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[0] &= 0xbf self.msr[0] &= 0xbf
self.msr[0] |= 0x40 if not val else 0x00 self.msr[0] |= 0x40 if not val else 0x00
@ -295,7 +295,7 @@ class Stc12Option(BaseOption):
return not bool(self.msr[2] & 32) return not bool(self.msr[2] & 32)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[2] &= 0xdf self.msr[2] &= 0xdf
self.msr[2] |= 0x20 if not val else 0x00 self.msr[2] |= 0x20 if not val else 0x00
@ -303,7 +303,7 @@ class Stc12Option(BaseOption):
return not bool(self.msr[2] & 8) return not bool(self.msr[2] & 8)
def set_watchdog_idle(self, val): def set_watchdog_idle(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[2] &= 0xf7 self.msr[2] &= 0xf7
self.msr[2] |= 0x08 if not val else 0x00 self.msr[2] |= 0x08 if not val else 0x00
@ -322,7 +322,7 @@ class Stc12Option(BaseOption):
return not bool(self.msr[3] & 2) return not bool(self.msr[3] & 2)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[3] &= 0xfd self.msr[3] &= 0xfd
self.msr[3] |= 0x02 if not val else 0x00 self.msr[3] |= 0x02 if not val else 0x00
@ -330,7 +330,7 @@ class Stc12Option(BaseOption):
return not bool(self.msr[3] & 1) return not bool(self.msr[3] & 1)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[3] &= 0xfe self.msr[3] &= 0xfe
self.msr[3] |= 0x01 if not val else 0x00 self.msr[3] |= 0x01 if not val else 0x00
@ -359,7 +359,7 @@ class Stc15AOption(BaseOption):
return bool(self.msr[0] & 16) return bool(self.msr[0] & 16)
def set_reset_pin_enabled(self, val): def set_reset_pin_enabled(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[0] &= 0xef self.msr[0] &= 0xef
self.msr[0] |= 0x10 if bool(val) else 0x00 self.msr[0] |= 0x10 if bool(val) else 0x00
@ -367,7 +367,7 @@ class Stc15AOption(BaseOption):
return not bool(self.msr[2] & 32) return not bool(self.msr[2] & 32)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[2] &= 0xdf self.msr[2] &= 0xdf
self.msr[2] |= 0x20 if not val else 0x00 self.msr[2] |= 0x20 if not val else 0x00
@ -375,7 +375,7 @@ class Stc15AOption(BaseOption):
return not bool(self.msr[2] & 8) return not bool(self.msr[2] & 8)
def set_watchdog_idle(self, val): def set_watchdog_idle(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[2] &= 0xf7 self.msr[2] &= 0xf7
self.msr[2] |= 0x08 if not val else 0x00 self.msr[2] |= 0x08 if not val else 0x00
@ -394,7 +394,7 @@ class Stc15AOption(BaseOption):
return bool(self.msr[1] & 64) return bool(self.msr[1] & 64)
def set_lvrs(self, val): def set_lvrs(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[1] &= 0xbf self.msr[1] &= 0xbf
self.msr[1] |= 0x40 if val else 0x00 self.msr[1] |= 0x40 if val else 0x00
@ -402,7 +402,7 @@ class Stc15AOption(BaseOption):
return bool(self.msr[1] & 128) return bool(self.msr[1] & 128)
def set_eeprom_lvd(self, val): def set_eeprom_lvd(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[1] &= 0x7f self.msr[1] &= 0x7f
self.msr[1] |= 0x80 if val else 0x00 self.msr[1] |= 0x80 if val else 0x00
@ -420,7 +420,7 @@ class Stc15AOption(BaseOption):
return not bool(self.msr[12] & 2) return not bool(self.msr[12] & 2)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[12] &= 0xfd self.msr[12] &= 0xfd
self.msr[12] |= 0x02 if not val else 0x00 self.msr[12] |= 0x02 if not val else 0x00
@ -428,7 +428,7 @@ class Stc15AOption(BaseOption):
return not bool(self.msr[12] & 1) return not bool(self.msr[12] & 1)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[12] &= 0xfe self.msr[12] &= 0xfe
self.msr[12] |= 0x01 if not val else 0x00 self.msr[12] |= 0x01 if not val else 0x00
@ -463,7 +463,7 @@ class Stc15Option(BaseOption):
return not bool(self.msr[2] & 16) return not bool(self.msr[2] & 16)
def set_reset_pin_enabled(self, val): def set_reset_pin_enabled(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[2] &= 0xef self.msr[2] &= 0xef
self.msr[2] |= 0x10 if not bool(val) else 0x00 self.msr[2] |= 0x10 if not bool(val) else 0x00
@ -493,7 +493,7 @@ class Stc15Option(BaseOption):
return not bool(self.msr[0] & 32) return not bool(self.msr[0] & 32)
def set_watchdog(self, val): def set_watchdog(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[0] &= 0xdf self.msr[0] &= 0xdf
self.msr[0] |= 0x20 if not val else 0x00 self.msr[0] |= 0x20 if not val else 0x00
@ -501,7 +501,7 @@ class Stc15Option(BaseOption):
return not bool(self.msr[0] & 8) return not bool(self.msr[0] & 8)
def set_watchdog_idle(self, val): def set_watchdog_idle(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[0] &= 0xf7 self.msr[0] &= 0xf7
self.msr[0] |= 0x08 if not val else 0x00 self.msr[0] |= 0x08 if not val else 0x00
@ -520,7 +520,7 @@ class Stc15Option(BaseOption):
return not bool(self.msr[1] & 64) return not bool(self.msr[1] & 64)
def set_lvrs(self, val): def set_lvrs(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[1] &= 0xbf self.msr[1] &= 0xbf
self.msr[1] |= 0x40 if not val else 0x00 self.msr[1] |= 0x40 if not val else 0x00
@ -528,7 +528,7 @@ class Stc15Option(BaseOption):
return bool(self.msr[1] & 128) return bool(self.msr[1] & 128)
def set_eeprom_lvd(self, val): def set_eeprom_lvd(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[1] &= 0x7f self.msr[1] &= 0x7f
self.msr[1] |= 0x80 if val else 0x00 self.msr[1] |= 0x80 if val else 0x00
@ -546,7 +546,7 @@ class Stc15Option(BaseOption):
return bool(self.msr[3] & 2) return bool(self.msr[3] & 2)
def set_ee_erase(self, val): def set_ee_erase(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[3] &= 0xfd self.msr[3] &= 0xfd
self.msr[3] |= 0x02 if val else 0x00 self.msr[3] |= 0x02 if val else 0x00
@ -554,7 +554,7 @@ class Stc15Option(BaseOption):
return not bool(self.msr[3] & 1) return not bool(self.msr[3] & 1)
def set_pindetect(self, val): def set_pindetect(self, val):
val = Utils.to_bool(val); val = Utils.to_bool(val)
self.msr[3] &= 0xfe self.msr[3] &= 0xfe
self.msr[3] |= 0x01 if not val else 0x00 self.msr[3] |= 0x01 if not val else 0x00

View File

@ -21,7 +21,7 @@
# #
import serial import serial
import sys, os, time, struct, re import sys, os, time, struct, re, errno
import argparse import argparse
import collections import collections
from stcgal.models import MCUModelDatabase from stcgal.models import MCUModelDatabase
@ -142,7 +142,7 @@ class StcBaseProtocol:
packet += self.read_bytes_safe(packet_len - 3) packet += self.read_bytes_safe(packet_len - 3)
# verify checksum and extract payload # verify checksum and extract payload
payload = self.extract_payload(packet); payload = self.extract_payload(packet)
self.dump_packet(packet, receive=True) self.dump_packet(packet, receive=True)
@ -192,11 +192,12 @@ class StcBaseProtocol:
mcu_name += "E" if self.status_packet[17] < 0x70 else "W" mcu_name += "E" if self.status_packet[17] < 0x70 else "W"
self.model = self.model._replace(name = mcu_name) self.model = self.model._replace(name = mcu_name)
protocol_database = [("stc89", "STC(89|90)(C|LE)\d"), protocol_database = [("stc89", r"STC(89|90)(C|LE)\d"),
("stc12a", "STC12(C|LE)\d052"), ("stc12a", r"STC12(C|LE)\d052"),
("stc12", "(STC|IAP)(10|11|12)\D"), ("stc12b", r"STC12(C|LE)(52|56)"),
("stc15a", "(STC|IAP)15[FL][01]0\d(E|EA|)$"), ("stc12", r"(STC|IAP)(10|11|12)\D"),
("stc15", "(STC|IAP|IRC)15\D")] ("stc15a", r"(STC|IAP)15[FL][01]0\d(E|EA|)$"),
("stc15", r"(STC|IAP|IRC)15\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):
@ -237,10 +238,39 @@ class StcBaseProtocol:
return iap_wait return iap_wait
def delay_safely_written(self, length):
"""
Delay until data has been safely written and sent to device.
Some buggy serial drivers don't implement tcdrain/flush correctly.
That is, they wait until all data has been written to USB, but they
do not wait until the data has actually finished transmission.
Add additional delay to work around.
"""
bit_time = 1.0 / self.ser.baudrate
byte_time = bit_time * 11.0 # start, 8 data bits, stop, parity
clock_safety_factor = 2.5 # additional delay in case clock is slow
time.sleep(byte_time * length * clock_safety_factor)
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 connect(self, autoreset=False): def reset_device(self, resetcmd=False):
if not resetcmd:
print("Cycling power: ", end="")
sys.stdout.flush()
self.ser.setDTR(True)
time.sleep(0.5)
self.ser.setDTR(False)
print("done")
else:
print("Cycling power via shell cmd: " + resetcmd)
os.system(resetcmd)
print("Waiting for MCU: ", end="")
sys.stdout.flush()
def connect(self, autoreset=False, resetcmd=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.
@ -259,14 +289,7 @@ class StcBaseProtocol:
self.ser.flushInput() self.ser.flushInput()
if autoreset: if autoreset:
print("Cycling power: ", end="") self.reset_device(resetcmd)
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: else:
print("Waiting for MCU, please cycle power: ", end="") print("Waiting for MCU, please cycle power: ", end="")
sys.stdout.flush() sys.stdout.flush()
@ -421,7 +444,7 @@ class Stc89Protocol(StcBaseProtocol):
bl_version, bl_stepping = struct.unpack("BB", packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
chr(bl_stepping)) chr(bl_stepping))
def handshake(self): def handshake(self):
"""Switch to transfer baudrate """Switch to transfer baudrate
@ -439,7 +462,7 @@ class Stc89Protocol(StcBaseProtocol):
packet += struct.pack(">H", brt) packet += struct.pack(">H", brt)
packet += bytes([0xff - (brt >> 8), brt_csum, delay, iap]) packet += bytes([0xff - (brt >> 8), brt_csum, delay, iap])
self.write_packet(packet) self.write_packet(packet)
time.sleep(0.2) self.delay_safely_written(len(packet))
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
response = self.read_packet() response = self.read_packet()
self.ser.baudrate = self.baud_handshake self.ser.baudrate = self.baud_handshake
@ -453,7 +476,7 @@ class Stc89Protocol(StcBaseProtocol):
packet += struct.pack(">H", brt) packet += struct.pack(">H", brt)
packet += bytes([0xff - (brt >> 8), brt_csum, delay]) packet += bytes([0xff - (brt >> 8), brt_csum, delay])
self.write_packet(packet) self.write_packet(packet)
time.sleep(0.2) self.delay_safely_written(len(packet))
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
response = self.read_packet() response = self.read_packet()
if response[0] != 0x8e: if response[0] != 0x8e:
@ -529,7 +552,37 @@ class Stc89Protocol(StcBaseProtocol):
print("done") print("done")
class Stc12AProtocol(Stc89Protocol): class Stc12AOptionsMixIn:
def program_options(self):
print("Setting options: ", end="")
sys.stdout.flush()
msr = self.options.get_msr()
packet = bytes([0x8d, msr[0], msr[1], msr[2], 0xff, msr[3]])
packet += struct.pack(">I", int(self.mcu_clock_hz))
packet += bytes([msr[3]])
packet += bytes([0xff, msr[0], msr[1], 0xff, 0xff, 0xff, 0xff, msr[2]])
packet += bytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
packet += struct.pack(">I", int(self.mcu_clock_hz))
packet += bytes([0xff, 0xff, 0xff])
self.write_packet(packet)
response = self.read_packet()
if response[0] != 0x80:
raise StcProtocolException("incorrect magic in option packet")
# XXX: this is done by STC-ISP on newer parts. not sure why, but let's
# just replicate it, just to be sure.
if self.bsl_version >= 0x66:
packet = bytes([0x50])
self.write_packet(packet)
response = self.read_packet()
if response[0] != 0x10:
raise StcProtocolException("incorrect magic in option packet")
print("done")
class Stc12AProtocol(Stc12AOptionsMixIn, Stc89Protocol):
"""countdown value for flash erase""" """countdown value for flash erase"""
ERASE_COUNTDOWN = 0x0d ERASE_COUNTDOWN = 0x0d
@ -548,7 +601,7 @@ class Stc12AProtocol(Stc89Protocol):
bl_version, bl_stepping = struct.unpack("BB", packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
chr(bl_stepping)) chr(bl_stepping))
self.bsl_version = bl_version self.bsl_version = bl_version
@ -599,7 +652,7 @@ class Stc12AProtocol(Stc89Protocol):
sys.stdout.flush() sys.stdout.flush()
packet = bytes([0x8f, 0xc0, brt, 0x3f, brt_csum, delay, iap]) packet = bytes([0x8f, 0xc0, brt, 0x3f, brt_csum, delay, iap])
self.write_packet(packet) self.write_packet(packet)
time.sleep(0.2) self.delay_safely_written(len(packet))
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
response = self.read_packet() response = self.read_packet()
self.ser.baudrate = self.baud_handshake self.ser.baudrate = self.baud_handshake
@ -611,7 +664,7 @@ class Stc12AProtocol(Stc89Protocol):
sys.stdout.flush() sys.stdout.flush()
packet = bytes([0x8e, 0xc0, brt, 0x3f, brt_csum, delay]) packet = bytes([0x8e, 0xc0, brt, 0x3f, brt_csum, delay])
self.write_packet(packet) self.write_packet(packet)
time.sleep(0.2) self.delay_safely_written(len(packet))
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
response = self.read_packet() response = self.read_packet()
if response[0] != 0x8e: if response[0] != 0x8e:
@ -650,37 +703,34 @@ class Stc12AProtocol(Stc89Protocol):
raise StcProtocolException("incorrect magic in erase packet") raise StcProtocolException("incorrect magic in erase packet")
print("done") print("done")
class Stc12OptionsMixIn:
def program_options(self): def program_options(self):
print("Setting options: ", end="") print("Setting options: ", end="")
sys.stdout.flush() sys.stdout.flush()
msr = self.options.get_msr() msr = self.options.get_msr()
packet = bytes([0x8d, msr[0], msr[1], msr[2], 0xff, msr[3]]) # XXX: it's not 100% clear if the index of msr[3] is consistent
packet += struct.pack(">I", int(self.mcu_clock_hz)) # between devices, so write it to both indices.
packet += bytes([msr[3]]) packet = bytes([0x8d, msr[0], msr[1], msr[2], msr[3],
packet += bytes([0xff, msr[0], msr[1], 0xff, 0xff, 0xff, 0xff, msr[2]]) 0xff, 0xff, 0xff, 0xff, msr[3], 0xff,
packet += bytes([0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]) 0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
packet += struct.pack(">I", int(self.mcu_clock_hz))
packet += bytes([0xff, 0xff, 0xff])
packet += struct.pack(">I", int(self.mcu_clock_hz))
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if response[0] != 0x80: if response[0] != 0x50:
raise StcProtocolException("incorrect magic in option packet") raise StcProtocolException("incorrect magic in option packet")
# XXX: this is done by STC-ISP on newer parts. not sure why, but let's
# just replicate it, just to be sure.
if self.bsl_version >= 0x66:
packet = bytes([0x50])
self.write_packet(packet)
response = self.read_packet()
if response[0] != 0x10:
raise StcProtocolException("incorrect magic in option packet")
print("done") print("done")
# If UID wasn't sent with erase acknowledge, it should be in this packet
if not self.uid:
self.uid = response[18:25]
class Stc12Protocol(StcBaseProtocol): print("Target UID: %s" % Utils.hexstr(self.uid))
"""Protocol handler for STC 10/11/12 series"""
class Stc12BaseProtocol(StcBaseProtocol):
"""Base class for STC 10/11/12 series protocol handlers"""
"""block size for programming flash""" """block size for programming flash"""
PROGRAM_BLOCKSIZE = 128 PROGRAM_BLOCKSIZE = 128
@ -740,7 +790,9 @@ class Stc12Protocol(StcBaseProtocol):
bl_version, bl_stepping = struct.unpack("BB", packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
chr(bl_stepping)) chr(bl_stepping))
self.bsl_version = bl_version
def calculate_baud(self): def calculate_baud(self):
"""Calculate MCU baudrate setting. """Calculate MCU baudrate setting.
@ -797,7 +849,7 @@ class Stc12Protocol(StcBaseProtocol):
sys.stdout.flush() sys.stdout.flush()
packet = bytes([0x8f, 0xc0, brt, 0x3f, brt_csum, delay, iap]) packet = bytes([0x8f, 0xc0, brt, 0x3f, brt_csum, delay, iap])
self.write_packet(packet) self.write_packet(packet)
time.sleep(0.2) self.delay_safely_written(len(packet))
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
response = self.read_packet() response = self.read_packet()
self.ser.baudrate = self.baud_handshake self.ser.baudrate = self.baud_handshake
@ -809,7 +861,7 @@ class Stc12Protocol(StcBaseProtocol):
sys.stdout.flush() sys.stdout.flush()
packet = bytes([0x8e, 0xc0, brt, 0x3f, brt_csum, delay]) packet = bytes([0x8e, 0xc0, brt, 0x3f, brt_csum, delay])
self.write_packet(packet) self.write_packet(packet)
time.sleep(0.2) self.delay_safely_written(len(packet))
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
response = self.read_packet() response = self.read_packet()
if response[0] != 0x84: if response[0] != 0x84:
@ -856,13 +908,10 @@ class Stc12Protocol(StcBaseProtocol):
packet += struct.pack(">H", self.PROGRAM_BLOCKSIZE) packet += struct.pack(">H", self.PROGRAM_BLOCKSIZE)
packet += data[i:i+self.PROGRAM_BLOCKSIZE] packet += data[i:i+self.PROGRAM_BLOCKSIZE]
while len(packet) < self.PROGRAM_BLOCKSIZE + 7: packet += b"\x00" while len(packet) < self.PROGRAM_BLOCKSIZE + 7: packet += b"\x00"
csum = sum(packet[7:]) & 0xff
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if response[0] != 0x00: if response[0] != 0x00:
raise StcProtocolException("incorrect magic in write packet") raise StcProtocolException("incorrect magic in write packet")
elif response[1] != csum:
raise StcProtocolException("verification checksum mismatch")
print(".", end="") print(".", end="")
sys.stdout.flush() sys.stdout.flush()
print(" done") print(" done")
@ -877,28 +926,19 @@ class Stc12Protocol(StcBaseProtocol):
raise StcProtocolException("incorrect magic in finish packet") raise StcProtocolException("incorrect magic in finish packet")
print("done") print("done")
def program_options(self):
print("Setting options: ", end="")
sys.stdout.flush()
msr = self.options.get_msr()
# XXX: it's not 100% clear if the index of msr[3] is consistent
# between devices, so write it to both indices.
packet = bytes([0x8d, msr[0], msr[1], msr[2], msr[3],
0xff, 0xff, 0xff, 0xff, msr[3], 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
packet += struct.pack(">I", int(self.mcu_clock_hz)) class Stc12Protocol(Stc12OptionsMixIn, Stc12BaseProtocol):
self.write_packet(packet) """STC 10/11/12 series protocol handler"""
response = self.read_packet()
if response[0] != 0x50:
raise StcProtocolException("incorrect magic in option packet")
print("done")
# If UID wasn't sent with erase acknowledge, it should be in this packet def __init__(self, port, handshake, baud):
if not self.uid: Stc12BaseProtocol.__init__(self, port, handshake, baud)
self.uid = response[18:25]
print("Target UID: %s" % Utils.hexstr(self.uid))
class Stc12BProtocol(Stc12AOptionsMixIn, Stc12BaseProtocol):
"""STC 10/11/12 variant protocol handler"""
def __init__(self, port, handshake, baud):
Stc12BaseProtocol.__init__(self, port, handshake, baud)
class Stc15AProtocol(Stc12Protocol): class Stc15AProtocol(Stc12Protocol):
@ -946,7 +986,7 @@ class Stc15AProtocol(Stc12Protocol):
bl_version, bl_stepping = struct.unpack("BB", packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
chr(bl_stepping)) chr(bl_stepping))
self.trim_data = packet[51:58] self.trim_data = packet[51:58]
self.freq_counter = freq_counter self.freq_counter = freq_counter
@ -1000,7 +1040,8 @@ class Stc15AProtocol(Stc12Protocol):
""" """
user_speed = self.trim_frequency user_speed = self.trim_frequency
if user_speed <= 0: user_speed = self.mcu_clock_hz if user_speed <= 0:
user_speed = self.mcu_clock_hz
program_speed = 22118400 program_speed = 22118400
user_count = int(self.freq_counter * (user_speed / self.mcu_clock_hz)) user_count = int(self.freq_counter * (user_speed / self.mcu_clock_hz))
@ -1094,7 +1135,7 @@ class Stc15AProtocol(Stc12Protocol):
packet += struct.pack(">B", 230400 // self.baud_transfer) packet += struct.pack(">B", 230400 // self.baud_transfer)
packet += bytes([0xa1, 0x64, 0xb8, 0x00, iap_wait, 0x20, 0xff, 0x00]) packet += bytes([0xa1, 0x64, 0xb8, 0x00, iap_wait, 0x20, 0xff, 0x00])
self.write_packet(packet) self.write_packet(packet)
time.sleep(0.2) self.delay_safely_written(len(packet))
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
response = self.read_packet() response = self.read_packet()
if response[0] != 0x84: if response[0] != 0x84:
@ -1158,8 +1199,7 @@ class Stc15Protocol(Stc15AProtocol):
bl_version, bl_stepping = struct.unpack("BB", packet[17:19]) bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
bl_minor = packet[22] & 0x0f bl_minor = packet[22] & 0x0f
self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f, self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
bl_minor, bl_minor, chr(bl_stepping))
chr(bl_stepping))
self.bsl_version = bl_version self.bsl_version = bl_version
def print_mcu_info(self): def print_mcu_info(self):
@ -1274,14 +1314,13 @@ class Stc15Protocol(Stc15AProtocol):
# This is a bit of a hack, but it works. # This is a bit of a hack, but it works.
bauds = self.baud_transfer if (self.mcu_magic >> 8) == 0xf2 else self.baud_transfer * 4 bauds = self.baud_transfer if (self.mcu_magic >> 8) == 0xf2 else self.baud_transfer * 4
packet += struct.pack(">H", int(65535 - program_speed / bauds)) packet += struct.pack(">H", int(65535 - program_speed / bauds))
packet += struct.pack(">H", int(65535 - (program_speed / bauds) * 1.5)) packet += bytes(user_trim)
iap_wait = self.get_iap_delay(program_speed) iap_wait = self.get_iap_delay(program_speed)
packet += bytes([iap_wait]) packet += bytes([iap_wait])
self.write_packet(packet) self.write_packet(packet)
response = self.read_packet() response = self.read_packet()
if response[0] != 0x01: if response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
time.sleep(0.2)
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
def switch_baud_ext(self): def switch_baud_ext(self):
@ -1298,7 +1337,6 @@ class Stc15Protocol(Stc15AProtocol):
response = self.read_packet() response = self.read_packet()
if response[0] != 0x01: if response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet") raise StcProtocolException("incorrect magic in handshake packet")
time.sleep(0.2)
self.ser.baudrate = self.baud_transfer self.ser.baudrate = self.baud_transfer
# for switching back to RC, program factory values # for switching back to RC, program factory values
@ -1388,7 +1426,7 @@ class Stc15Protocol(Stc15AProtocol):
configuration.""" configuration."""
msr = self.options.get_msr() msr = self.options.get_msr()
packet = bytes([0xff] * 23) 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,
@ -1442,8 +1480,9 @@ class StcUsb15Protocol(Stc15Protocol):
def dump_packet(self, data, request=0, value=0, index=0, receive=True): def dump_packet(self, data, request=0, value=0, index=0, receive=True):
if self.debug: if self.debug:
print("%s bRequest=%02X wValue=%04X wIndex=%04X data: %s" % (("<-" if receive else "->"), print("%s bRequest=%02X wValue=%04X wIndex=%04X data: %s" %
request, value, index, Utils.hexstr(data, " ")), file=sys.stderr) (("<-" if receive else "->"), request, value, index,
Utils.hexstr(data, " ")), file=sys.stderr)
def read_packet(self): def read_packet(self):
"""Read a packet from the MCU""" """Read a packet from the MCU"""
@ -1483,15 +1522,16 @@ class StcUsb15Protocol(Stc15Protocol):
self.dump_packet(chunks, request, value, index, receive=False) self.dump_packet(chunks, request, value, index, receive=False)
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): def connect(self, autoreset=False, resetcmd=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.
if _usb_available == False: if not _usb_available:
raise StcProtocolException("USB support not available. " raise StcProtocolException(
+ "pyusb is not installed or not working correctly.") "USB support not available. " +
"pyusb is not installed or not working correctly.")
print("Waiting for MCU, please cycle power: ", end="") print("Waiting for MCU, please cycle power: ", end="")
sys.stdout.flush() sys.stdout.flush()
@ -1507,7 +1547,11 @@ class StcUsb15Protocol(Stc15Protocol):
self.status_packet = None self.status_packet = None
raise StcFramingException raise StcFramingException
else: raise StcFramingException else: raise StcFramingException
except (StcFramingException, usb.core.USBError): time.sleep(0.5) except StcFramingException:
time.sleep(0.5)
except usb.core.USBError as err:
if err.errno == errno.EACCES:
raise IOError(err.strerror)
self.initialize_model() self.initialize_model()
print("done") print("done")

View File

@ -19,31 +19,41 @@
# SOFTWARE. # SOFTWARE.
# #
import serial
import argparse import argparse
import serial
class Utils: class Utils:
"""Common utility functions"""
@classmethod @classmethod
def to_bool(self, val): def to_bool(cls, val):
"""make sensible boolean from string or other type value""" """make sensible boolean from string or other type value"""
if isinstance(val, bool): return val if val is None:
if isinstance(val, int): return bool(val) return False
if len(val) == 0: return False if isinstance(val, bool):
return True if val[0].lower() == "t" or val[0] == "1" else False return val
elif isinstance(val, int):
return bool(val)
elif len(val) == 0:
return False
else:
return True if val[0].lower() == "t" or val[0] == "1" else False
@classmethod @classmethod
def to_int(self, val): def to_int(cls, val):
"""make int from any value, nice error message if not possible""" """make int from any value, nice error message if not possible"""
try: return int(val, 0) try:
except: raise ValueError("invalid integer") return int(val, 0)
except:
raise ValueError("invalid integer")
@classmethod @classmethod
def hexstr(self, bytestr, sep=""): def hexstr(cls, bytestr, sep=""):
"""make formatted hex string output from byte sequence""" """make formatted hex string output from byte sequence"""
return sep.join(["%02X" % x for x in bytestr]) return sep.join(["%02X" % x for x in bytes(bytestr)])
class BaudType: class BaudType:
@ -55,5 +65,5 @@ class BaudType:
raise argparse.ArgumentTypeError("illegal baudrate") raise argparse.ArgumentTypeError("illegal baudrate")
return baud return baud
def __repr__(self): return "baudrate" def __repr__(self):
return "baudrate"

0
test/__init__.py Normal file
View File

19
test/iap15f2k61s2.yml Normal file
View File

@ -0,0 +1,19 @@
name: IAP15F2K61S2 programming test
protocol: stc15
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x87, 0xD3, 0x75, 0x9C, 0xF5, 0x3B, 0x17, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x09, 0x81, 0x00, 0x00, 0x71, 0x53, 0x00, 0xF4, 0x49, 0x04, 0x06, 0x58, 0x9C, 0x02, 0x0E, 0x14, 0x17, 0x19, 0x19, 0x00, 0xF4, 0xF4, 0x04, 0xD2]
- [0x00, 0x0B, 0x03, 0x37, 0x04, 0x9A, 0x06, 0x02, 0x06, 0x6B, 0x09, 0x27, 0x0B, 0xE8, 0x0D, 0x0A, 0x12, 0x5A, 0x17, 0x9B, 0x14, 0x8F, 0x1C, 0x96, 0x00, 0x00]
- [0x00, 0x0C, 0x09, 0x04, 0x09, 0x09, 0x09, 0x0E, 0x09, 0x0E, 0x09, 0x18, 0x09, 0x1D, 0x12, 0x00, 0x12, 0x0F, 0x12, 0x19, 0x12, 0x23, 0x12, 0x2D, 0x12, 0x37]
- [0x01]
- [0x05]
- [0x03, 0x0D, 0x00, 0x00, 0x21, 0x02, 0x26, 0x32]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x04, 0x54]

19
test/stc12c2052ad.yml Normal file
View File

@ -0,0 +1,19 @@
name: STC12C2052AD programming test
protocol: stc12a
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x00, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEC, 0x04, 0xEB, 0x04, 0xEB, 0x58, 0x44, 0x00, 0xF2, 0x12, 0x83, 0xFD, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFD, 0xF7, 0xF7, 0xFF]
- [0x8F, 0xC0, 0x79, 0x3F, 0xFE, 0x28, 0x85]
- [0x8E, 0xC0, 0x79, 0x3F, 0xFE, 0x28]
- [0x80]
- [0x80]
- [0x80]
- [0x80]
- [0x80]
- [0x80, 0x66]
- [0x80, 0x80]
- [0x80, 0x80]
- [0x80, 0x80]
- [0x80, 0xEE]
- [0x10, 0xC0, 0x16, 0xF7, 0xFF, 0xBF, 0x03, 0xFF, 0x58, 0x44, 0xFD, 0xF7, 0xF7, 0xFF, 0xFF, 0xFF, 0xBF, 0xFF, 0xFD, 0xF7, 0xF7, 0xFF]
- [0x80]

15
test/stc12c5a60s2.yml Normal file
View File

@ -0,0 +1,15 @@
name: STC12C5A60S2 programming test
protocol: stc12
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x04, 0xBD, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBD, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBC, 0x04, 0xBC, 0x62, 0x49, 0x00, 0xD1, 0x7E, 0x8C, 0xFF, 0x7F, 0xF7, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x03, 0x00, 0xB0, 0x02, 0x2E, 0x6B, 0x00, 0xCD, 0x80, 0x00, 0x00]
- [0x8F]
- [0x8F, 0xC0, 0x7E, 0x3F, 0xFE, 0xA0, 0x83, 0x04]
- [0x84, 0xC0, 0x7E, 0x3F, 0xFE, 0xA0, 0x04]
- [0x00]
- [0x00, 0x03]
- [0x00, 0x00]
- [0x00, 0x00]
- [0x00, 0x00]
- [0x8D]
- [0x50, 0xFF, 0x7F, 0xF7, 0xFF, 0xFF, 0x03, 0xFF, 0x62, 0x49, 0xFF, 0x7F, 0xF7, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x03, 0x00, 0xB0, 0x02, 0x2E, 0x6B, 0x00, 0xCD, 0x80, 0x00, 0x00]

20
test/stc15f104e.yml Normal file
View File

@ -0,0 +1,20 @@
name: STC15F104E programming test
protocol: stc15a
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x02, 0xB0, 0x02, 0xB0, 0x02, 0xAF, 0x02, 0xB0, 0x02, 0xE6, 0x02, 0xE7, 0x00, 0x00, 0x00, 0x00, 0x67, 0x51, 0xFF, 0xF2, 0x94, 0x8C, 0xEF, 0x3B, 0xF5, 0x58, 0x34, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x58, 0x50, 0x0C, 0x94, 0x21, 0xFF, 0x29]
- [0x8f]
- [0x65, 0x58, 0x50, 0x0C, 0x95, 0x21, 0xFF, 0x2B, 0xFF, 0xFF, 0x06, 0x06, 0x58, 0x00, 0x02, 0x00, 0x58, 0x80, 0x02, 0x00, 0x58, 0x80, 0x02, 0x00, 0x58, 0xFF, 0x02, 0x00, 0x58, 0x00, 0x02, 0x00, 0x58, 0x80, 0x02, 0x00]
- [0x65, 0x58, 0x50, 0x0C, 0x95, 0x21, 0xFF, 0x2B, 0xFF, 0xFF, 0x06, 0x0B, 0x58, 0x24, 0x02, 0x00, 0x58, 0x25, 0x02, 0x00, 0x58, 0x26, 0x02, 0x00, 0x58, 0x27, 0x02, 0x00, 0x58, 0x28, 0x02, 0x00, 0x58, 0x29, 0x02, 0x00, 0x58, 0x2A, 0x02, 0x00, 0x58, 0x2B, 0x02, 0x00, 0x58, 0x2C, 0x02, 0x00, 0x58, 0x2D, 0x02, 0x00, 0x58, 0x2E, 0x02, 0x00]
- [0x01]
- [0x05]
- [0x03, 0x0C, 0x00, 0x00, 0x17, 0x01, 0xA0, 0xE0]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x04, 0x54]

19
test/stc15l104w.yml Normal file
View File

@ -0,0 +1,19 @@
name: STC15L104W programming test
protocol: stc15
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x66, 0x3C, 0x93, 0xBA, 0xF7, 0xBB, 0x9F, 0x00, 0x5B, 0x68, 0x00, 0xFD, 0x00, 0x00, 0x00, 0x00, 0x71, 0x51, 0x03, 0xF2, 0xD4, 0x04, 0x06, 0x58, 0xBA, 0x02, 0x2A, 0x31, 0x32, 0x38, 0x30, 0x80, 0x14, 0x10, 0x04, 0xD9]
- [0x00, 0x0B, 0x03, 0x0A, 0x04, 0x4F, 0x05, 0x9E, 0x06, 0x20, 0x08, 0xB9, 0x0B, 0x5C, 0x0C, 0x6A, 0x11, 0x7E, 0x16, 0x79, 0x13, 0x77, 0x1A, 0xB1, 0x00, 0x00]
- [0x00, 0x0C, 0x04, 0xD6, 0x04, 0xDB, 0x04, 0xE0, 0x04, 0xE0, 0x04, 0xE0, 0x04, 0xE5, 0x11, 0xE2, 0x11, 0xF1, 0x11, 0xFB, 0x12, 0x05, 0x12, 0x0A, 0x12, 0x19]
- [0x01]
- [0x05]
- [0x03, 0x0C, 0x00, 0x00, 0x17, 0x01, 0xA0, 0xE0]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x04, 0x54]

20
test/stc15w4k56s4.yml Normal file
View File

@ -0,0 +1,20 @@
name: STC15W4K56S4 programming test
protocol: stc15
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x50, 0x8D, 0xFF, 0x73, 0x96, 0xF5, 0x7B, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x27, 0xED, 0x00, 0x00, 0x73, 0x54, 0x00, 0xF5, 0x28, 0x04, 0x06, 0x70, 0x96, 0x02, 0x15, 0x19, 0x1C, 0x1E, 0x23, 0x00, 0xEC, 0xE0, 0x04, 0xD7, 0xF8, 0x73, 0xBF, 0xFF, 0xFF, 0x15, 0x09, 0x25, 0x60]
- [0x00, 0x0B, 0x0D, 0x21, 0x12, 0xBC, 0x18, 0x3E, 0x1A, 0x05, 0x24, 0xFA, 0x2F, 0xB3, 0x34, 0xD1, 0x4A, 0x52, 0x5E, 0xC0, 0x52, 0xDB, 0x73, 0x1A, 0x00, 0x00]
- [0x00, 0x0C, 0x23, 0xBF, 0x23, 0xD3, 0x23, 0xE7, 0x23, 0xF6, 0x24, 0x0F, 0x24, 0x23, 0x47, 0x73, 0x47, 0xB9, 0x47, 0xE1, 0x48, 0x09, 0x48, 0x36, 0x48, 0x59]
- [0x01]
- [0x05]
- [0x03, 0xF5, 0x28, 0x00, 0xA5, 0x03, 0x27, 0x49]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x02, 0x54]
- [0x07, 0x54]
- [0x04, 0x54]

19
test/stc89c52rc.yml Normal file
View File

@ -0,0 +1,19 @@
name: STC89C52RC programming test
protocol: stc89
code_data: [49, 50, 51, 52, 53, 54, 55, 56, 57]
responses:
- [0x00, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE6, 0x25, 0xE2, 0x25, 0xE6, 0x43, 0x43, 0xFC, 0xF0, 0x02, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
- [0x8F, 0xFD, 0xF8, 0x02, 0x10, 0x28, 0x81]
- [0x8E, 0xFD, 0xF8, 0x02, 0x10, 0x28]
- [0x80]
- [0x80]
- [0x80]
- [0x80]
- [0x80]
- [0x80, 0x66]
- [0x80, 0x80]
- [0x80, 0x80]
- [0x80, 0x80]
- [0x8D, 0xFC, 0xFF, 0xF6, 0xFF]
- [0x10, 0xC0, 0x16, 0xF6, 0xFF, 0xF1, 0x03, 0xFF, 0x43, 0x43, 0xFC]
- [0x80]

136
test/test_program.py Normal file
View File

@ -0,0 +1,136 @@
#
# Copyright (c) 2017 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.
#
"""Tests that simulate a whole programming cycle"""
import unittest
from unittest.mock import patch
import yaml
import stcgal.frontend
import stcgal.protocols
def convert_to_bytes(list_of_lists):
"""Convert lists of integer lists to list of byte lists"""
return [bytes(x) for x in list_of_lists]
def get_default_opts():
"""Get a default preconfigured option object"""
opts = unittest.mock.MagicMock()
opts.protocol = "stc89"
opts.autoreset = False
opts.port = ""
opts.baud = 19200
opts.handshake = 9600
opts.trim = 22118
opts.eeprom_image = None
opts.debug = False
opts.code_image.name = "test.bin"
opts.code_image.read.return_value = b"123456789"
return opts
class ProgramTests(unittest.TestCase):
"""Test MCU programming cycles for different families, based on traces"""
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc89(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC89 protocol"""
self._program_yml("./test/stc89c52rc.yml", serial_mock, read_mock)
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc12(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC12 protocol"""
self._program_yml("./test/stc12c5a60s2.yml", serial_mock, read_mock)
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc12a(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC12A protocol"""
self._program_yml("./test/stc12c2052ad.yml", serial_mock, read_mock)
def test_program_stc12b(self):
"""Test a programming cycle with STC12B protocol"""
self.skipTest("trace missing")
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc15f2(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC15 protocol, F2 series"""
self._program_yml("./test/iap15f2k61s2.yml", serial_mock, read_mock)
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc15w4(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC15 protocol, W4 series"""
self._program_yml("./test/stc15w4k56s4.yml", serial_mock, read_mock)
@unittest.skip("trace is broken")
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc15a(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC15A protocol"""
self._program_yml("./test/stc15f104e.yml", serial_mock, read_mock)
@patch("stcgal.protocols.StcBaseProtocol.read_packet")
@patch("stcgal.protocols.Stc89Protocol.write_packet")
@patch("stcgal.protocols.serial.Serial", autospec=True)
@patch("stcgal.protocols.time.sleep")
@patch("sys.stdout")
def test_program_stc15l1(self, out, sleep_mock, serial_mock, write_mock, read_mock):
"""Test a programming cycle with STC15 protocol, L1 series"""
self._program_yml("./test/stc15l104w.yml", serial_mock, read_mock)
def test_program_stc15w4_usb(self):
"""Test a programming cycle with STC15W4 USB protocol"""
self.skipTest("USB not supported yet, trace missing")
def _program_yml(self, yml, serial_mock, read_mock):
"""Program MCU with data from YAML file"""
with open(yml) as test_file:
test_data = yaml.load(test_file.read())
opts = get_default_opts()
opts.protocol = test_data["protocol"]
opts.code_image.read.return_value = bytes(test_data["code_data"])
serial_mock.return_value.inWaiting.return_value = 1
read_mock.side_effect = convert_to_bytes(test_data["responses"])
gal = stcgal.frontend.StcGal(opts)
self.assertEqual(gal.run(), 0)

77
test/test_utils.py Normal file
View File

@ -0,0 +1,77 @@
#
# Copyright (c) 2017 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.
#
"""Tests for utility functions and other misc parts"""
import argparse
import unittest
from unittest.mock import patch
from stcgal.utils import Utils, BaudType
class TestUtils(unittest.TestCase):
"""Test for utility functions in the Utils class"""
def test_to_bool(self):
"""Test special utility function for bool conversion"""
self.assertTrue(Utils.to_bool(True))
self.assertTrue(Utils.to_bool("true"))
self.assertTrue(Utils.to_bool("True"))
self.assertTrue(Utils.to_bool("t"))
self.assertTrue(Utils.to_bool("T"))
self.assertTrue(Utils.to_bool(1))
self.assertTrue(Utils.to_bool(-1))
self.assertFalse(Utils.to_bool(0))
self.assertFalse(Utils.to_bool(None))
self.assertFalse(Utils.to_bool("false"))
self.assertFalse(Utils.to_bool("False"))
self.assertFalse(Utils.to_bool("f"))
self.assertFalse(Utils.to_bool("F"))
self.assertFalse(Utils.to_bool(""))
def test_to_int(self):
"""Test wrapped integer conversion"""
self.assertEqual(Utils.to_int("2"), 2)
self.assertEqual(Utils.to_int("0x10"), 16)
with self.assertRaises(ValueError):
Utils.to_int("a")
with self.assertRaises(ValueError):
Utils.to_int("")
with self.assertRaises(ValueError):
Utils.to_int(None)
def test_hexstr(self):
"""Test byte array formatter"""
self.assertEqual(Utils.hexstr([10]), "0A")
self.assertEqual(Utils.hexstr([1, 2, 3]), "010203")
with self.assertRaises(Exception):
Utils.hexstr([400, 500])
class TestBaudType(unittest.TestCase):
"""Test BaudType class"""
def test_create_baud_type(self):
"""Test creation of BaudType instances"""
baud_type = BaudType()
self.assertEqual(baud_type("2400"), 2400)
self.assertEqual(baud_type("115200"), 115200)
with self.assertRaises(argparse.ArgumentTypeError):
baud_type("2374882")