1 Commits

Author SHA1 Message Date
1a5cf18590 debian: Update Build-Depends and Depends
This closes #32

Signed-off-by: Andrew Andrianov <andrew@ncrmnt.org>
2017-10-25 21:31:14 +03:00
9 changed files with 82 additions and 630 deletions

View File

@ -23,7 +23,7 @@ suitable for automation.
Supported MCU models
--------------------
stcgal should fully support STC 89/90/10/11/12/15 series MCUs. Support for STC8 series MCUs is work in progress.
stcgal should fully support STC 89/90/10/11/12/15 series MCUs.
So far, stcgal was tested with the following MCU models:
@ -47,7 +47,6 @@ So far, stcgal was tested with the following MCU models:
* STC15L2K16S2 (BSL version: 7.2.4S)
* STC15W408AS (BSL version: 7.2.4T)
* STC15W4K56S4 (BSL version: 7.3.4T, UART and USB mode)
* STC8A8K64S4A12 (BSL version: 7.3.9U)
Compatibility reports, both negative and positive, are welcome.
@ -60,8 +59,8 @@ Features
* Program flash memory
* Program IAP/EEPROM
* Set device options
* Read unique device ID (STC 10/11/12/15/8)
* Trim RC oscillator frequency (STC 15/8)
* Read unique device ID (STC 10/11/12/15)
* Trim RC oscillator frequency (STC 15)
* Automatic power-cycling with DTR toggle or a custom shell command
* Automatic UART protocol detection
@ -127,7 +126,6 @@ and MCU series is as follows:
* ```stc12``` Most STC10/11/12 series
* ```stc15a``` STC15x104E and STC15x204E(A) series
* ```stc15``` Most STC15 series
* ```stc8``` STC8 series
* ```usb15``` USB support on STC15W4 series
* ```auto``` Automatic detection of UART based protocols (default)
@ -259,20 +257,17 @@ Option key | Possible values | Protocols/Models | Descri
```por_reset_delay``` | short/long | STC12+ | Power-on reset (POR) delay
```low_voltage_threshold``` | 0...7 | STC15A+ | Low-voltage detection threshold. Model specific.
```eeprom_lvd_inhibit``` | true/false | STC15A+ | Ignore EEPROM writes in low-voltage situations
```rstout_por_state``` | low/high | STC15+ | RSTOUT/RSTSV pin state after power-on reset
```uart1_remap``` | true/false | STC8 | Remap UART1 pins (P3.0/P3.1) to UART2 pins (P3.6/P3.7)
```rstout_por_state``` | low/high | STC15+ | RSTOUT pin state after power-on reset
```uart2_passthrough``` | true/false | STC15+ | Pass-through UART1 to UART2 pins (for single-wire UART mode)
```uart2_pin_mode``` | push-pull/normal | STC15+ | Output mode of UART2 TX pin
```cpu_core_voltage``` | low/mid/high | STC15W+ | CPU core voltage (low: ~2.7V, mid: ~3.3V, high: ~3.6V)
```epwm_open_drain``` | true/false | STC8 | Use open-drain pin mode for EPWM pins after power-on reset
```program_eeprom_split``` | 512 - 65024 | STC8A8 w/ 64 KB | Select split between code flash and EEPROM flash (in 512 byte blocks)
### Frequency trimming
If the internal RC oscillator is used (```clock_source=internal```),
stcgal can execute a trim procedure to adjust it to a given value. This
is only supported by STC15 series and newer. The trim values are stored
with device options. Use the ```-t``` flag to request trimming to a certain
is only supported by STC15 series. The trim values are stored with
device options. Use the ```-t``` flag to request trimming to a certain
value. Generally, frequencies between 4 and 35 MHz can be achieved. If
trimming fails, stcgal will abort.

View File

@ -1,71 +0,0 @@
MCS bytes
=========
46 b9 6a 00 33 04 00 00 5a a5 ff ff ff 00 ff ff
00 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff
00 ff 01 31 20 80 34 00 01 ff ff ff ff ff 8b bf
^^^^^^^^^^^ ^^ ^^ ^^ ^^
frequency clkdiv 5) 1) 3)
^^^^^
trim?
f7 fe 1f cc 16
^^ ^^
4) 2)
1) not stricty related to some register
aka MCS1
bit 0: ? always 1
bit 1: oscillator high gain
bit 2: EPWM push-pull enabled
bit 3: p2.0 state after boot
bit 4: TXD signal source from RXD
bit 5: p3.7 push-pull enabled
bit 6: UART1 remap enabled
bit 7: long power-on reset delay
2) not strictly related to some register
aka MCS4
eeprom size / code space upper limit (in pages)
only seems to apply to devices with max. flash size
e.g. fe -> 63.5K, e0 -> 56K
3) like RSTCFG? inverted?
aka MCS2
bit 0: LVD0
bit 1: LVD1
bit 2: ? always 1
bit 3: ? always 1
bit 4: ~reset pin enabled
bit 5: ? always 1
bit 6: ~enable lvd reset
bit 7: ? always 1
LVD:
2.20V -> 0xbf
2.40V -> 0xbe
2.70V -> 0xbd
3.00V -> 0xbc
4) like WDT_CONTR
aka MCS3
bit 0: WDPS0
bit 1: WDPS1
bit 2: WDPS2
bit 3: ~stop wdt in idle
bit 4: ? always 1
bit 5: ~enable wdt on por
bit 6: ? always 1
bit 7: ? always 1
WDPS like in datasheet
5)
aka MCS0
bit 0: ? ~BSLD / bootloader enabled
bit 1: erase eeprom enabled
bit 2: ?
bit 3: ?
bit 4: ?
bit 5: ?
bit 6: ?
bit 7: ?

View File

@ -1,137 +0,0 @@
Overview of changes
-------------------
The following changes have been observed compared to STC15:
- Many differences in the status packet
- At least some differences in MCS
- Different challenge
- no separate program speed
- clock division was introduced; calibration always in the ~20-30 MHz range, lower clocks
use division
- the meaning of the calibration ranges and trim has changed
The good:
- Erase, Program, etc. operations are apparently unchanged. :)
Status packet
-------------
46 B9 68 00 30 50 00 54 62 58 5D 00 04 FF FD 8B BF FF 27 4A F7 FE 73 55 00 F6 28 09 85 E3 5F 80 07 20 20 20 01 00 00 FE 05 3A 17 05 25 91 FF 10 AE 16
^^^^^ wakeup clock
Clock set to 20 MHz by STC-ISP (encoding is different compared to STC15):
46 B9 68 00 30 50 01 31 2E 90 38 01 01 FF FD 8B BF FF 27 35 F7 FE 73 55 00 F6 28 09 85 E3 5F 80 07 20 20 20 01 00 00 FE 05 3A 17 05 25 91 FF 10 54 16
46 B9 68 00 30 50 01 31 2E 90 38 01 01 FF FD 8B BF FF 27 3B F7 FE 73 55 00 F6 28 09 85 E3 5F 80 07 20 20 20 01 00 00 FE 05 3A 17 05 25 91 FF 10 5A 16
^^^^^ some 24 MHz reference or other clk measurement?
^^^^^ trim/adjust?
^^ clkdiv
^^^^^^^^^^^ clk
MCS bytes
46 B9 68 00 30 50 01 31 2E 90 38 01 01 FF FD 8B BF FF 27 35 F7 FE 73 55 00 F6 28 09 85 E3 5F 80 07 20 20 20 01 00 00 FE 05 3A 17 05 25 91 FF 10 54 16
^^^^^^^^ ^^^^^
Disconnect
----------
Uses FF command byte.
Basic challenge operation
-------------------------
Host sends a challenge of some kind, followed by 0xfe pulsing
46 B9 6A 00 0C 00 02 00 00 80 00 00 F8 16
Much simpler than in STC15
MCU sends back some response:
46 B9 68 00 0C 00 02 36 AD 4E 83 02 2A 16
Host now sends some longer challenge, followed by more pulses:
46 B9 6A 00 20 00 0C 7C 00 7C 01 7C 02 7C 03 7D 00 7D 01 7D 02 7D 03 7E 00 7E 01 7E 02 7E 03 06 84 16
MCU sends back some response:
46 B9 68 00 20 00 0C 4D C6 4D DB 4D E7 4D F3 4D F6 4E 0E 4E 11 4E 26 4E 26 4E 32 4E 41 4E 56 09 DC 16
Host now seems to initiate a baud switch or something like that
46 B9 6A 00 0E 01 00 00 FF CC 01 7C 80 03 41 16
MCU acknowlegdes it:
46 B9 68 00 07 01 00 70 16
Now the MCU switches to the new baud rate.
Challenges observed
-------------------
6 MHz:
46B96A0020000C 1400 1401 1402 1403 1500 1501 1502 1503 1600 1601 1602 1603 01A416
5.5 MHz:
46B96A0020000C 5C00 5C01 5C02 5C03 5D00 5D01 5D02 5D03 5E00 5E01 5E02 5E03 050416
11 MHz:
46B96A0020000C 5B00 5B01 5B02 5B03 5C00 5C01 5C02 5C03 5D00 5D01 5D02 5D03 04F816
20 MHz:
46B96A0020000C 3600 3601 3602 3603 3700 3701 3702 3703 3800 3801 3802 3803 033C16
24 MHz:
46B96A0020000C 7C00 7C01 7C02 7C03 7D00 7D01 7D02 7D03 7E00 7E01 7E02 7E03 068416
27 MHz:
46B96A0020000C B000 B001 B002 B003 B100 B101 B102 B103 B200 B201 B202 B203 08F416
Ranges vs trim value
--------------------
46 B9 6A 00 20 00 0C 00 00 80 00 FF 00 00 01 80 01 FF 01 00 02 80 02 FF 02 00 03 80 03 FF 03 06 A4 16
46 B9 68 00 20 00 0C 36 9B 4E 92 65 E4 36 CB 4E 7D 66 29 36 D1 4E 83 66 05 36 CB 4E C2 66 47 0A EA 16
first byte determines general trim value... range of ~16 to ~30 MHz, the second byte (00..03) is a fine adjustment.
Clock division?
---------------
5.5 MHz vs 11 Mhz: challenge is about the same. it's likely some kind of clock divider is used!
5.5 Mhz switch: 01 00 00 FF CC 01 5C 80 clkdiv = 4?
11 MHz switch: 01 00 00 FF CC 01 5B 80 clkdiv = 2?
22 MHz switch: 01 00 00 FF CC 01 5C 80 clkdiv = 1?
22 Mhz option packet: 0400005AA5FFFFFF00FFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FF01516D405D0201FFFDFFFFFF8BBFF7FE
11 MHz option packet: 0400005AA5FFFFFF00FFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FF00A8AF985D0102FFFDFFFFFF8BBFF7FE
5.5 MHz option packet: 0400005AA5FFFFFF00FFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FF005462585D0004FFFDFFFFFF8BBFF7FE
^^ clkdiv?
^^^^^^^^ clkspeed
Always 24 MHz for programming
-----------------------------
Calibration for anything but 24 Mhz (and around that) fails when switching baud. Another observation is that there is no
programming speed being calibrated anymore. This may suggest that a fixed speed is used for programming.
Adjusting BRT calculation to 24 MHz in the switch packet seems to work. So it is really using 24 MHz by default;
probably some pre-calibrated value.

View File

@ -24,9 +24,6 @@
import stcgal
from setuptools import setup, find_packages
with open("README.md", "r") as fh:
long_description = fh.read()
setup(
name = "stcgal",
version = stcgal.__version__,
@ -41,9 +38,6 @@ setup(
],
},
description = "STC MCU ISP flash tool",
long_description = long_description,
long_description_content_type = "text/markdown",
keywords = "stc mcu microcontroller 8051 mcs-51",
url = "https://github.com/grigorig/stcgal",
author = "Grigori Goronzy",
author_email = "greg@kinoho.net",
@ -58,9 +52,6 @@ setup(
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Topic :: Software Development :: Embedded Systems",
"Topic :: Software Development",
],

View File

@ -23,19 +23,8 @@
import sys
import argparse
import stcgal
import serial
from stcgal.utils import BaudType
from stcgal.protocols import Stc89Protocol
from stcgal.protocols import Stc12AProtocol
from stcgal.protocols import Stc12BProtocol
from stcgal.protocols import Stc12Protocol
from stcgal.protocols import Stc15Protocol
from stcgal.protocols import Stc15AProtocol
from stcgal.protocols import StcUsb15Protocol
from stcgal.protocols import Stc8Protocol
from stcgal.protocols import StcAutoProtocol
from stcgal.protocols import StcProtocolException
from stcgal.protocols import StcFramingException
from stcgal.protocols import *
from stcgal.ihex import IHex
class StcGal:
@ -61,9 +50,6 @@ class StcGal:
elif opts.protocol == "stc15":
self.protocol = Stc15Protocol(opts.port, opts.handshake, opts.baud,
round(opts.trim * 1000))
elif opts.protocol == "stc8":
self.protocol = Stc8Protocol(opts.port, opts.handshake, opts.baud,
round(opts.trim * 1000))
elif opts.protocol == "usb15":
self.protocol = StcUsb15Protocol()
else:
@ -183,8 +169,9 @@ class StcGal:
if self.opts.code_image:
self.program_mcu()
return 0
self.protocol.disconnect()
return 0
else:
self.protocol.disconnect()
return 0
except NameError as ex:
sys.stdout.flush()
print("Option error: %s" % ex, file=sys.stderr)
@ -213,14 +200,12 @@ class StcGal:
def cli():
# check arguments
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description="stcgal {} - an STC MCU ISP flash tool\n".format(stcgal.__version__) +
"(C) 2014-2017 Grigori Goronzy\nhttps://github.com/grigorig/stcgal")
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("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("-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", "stc8", "usb15", "auto"], default="auto")
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("-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)

View File

@ -87,17 +87,18 @@ class IHex:
return bytes(result)
result = bytearray()
else:
result = bytearray()
for addr, data in self.areas.items():
if addr >= start and addr < end:
data = data[:end - addr]
if len(result) < (addr - start):
result[len(result):addr - start] = bytes(
addr - start - len(result))
result[addr - start:addr - start + len(data)] = data
for addr, data in self.areas.items():
if addr >= start and addr < end:
data = data[:end - addr]
if len(result) < (addr - start):
result[len(result):addr - start] = bytes(
addr - start - len(result))
result[addr - start:addr - start + len(data)] = data
return bytes(result)
return bytes(result)
def set_start(self, start=None):
self.start = start
@ -136,7 +137,7 @@ class IHex:
try:
line = codecs.decode(rawline[1:], "hex_codec")
except ValueError:
except:
raise ValueError("Invalid hex data")
length, addr, line_type = struct.unpack(">BHB", line[:4])

View File

@ -473,7 +473,7 @@ class Stc15Option(BaseOption):
)
if len(msr) > 4:
self.options += (("cpu_core_voltage", self.get_core_voltage, self.set_core_voltage),)
self.options += ("cpu_core_voltage", self.get_core_voltage, self.set_core_voltage),
def get_reset_pin_enabled(self):
return not bool(self.msr[2] & 16)
@ -615,177 +615,10 @@ class Stc15Option(BaseOption):
if self.msr[4] == 0xea: return "low"
elif self.msr[4] == 0xf7: return "mid"
elif self.msr[4] == 0xfd: return "high"
return "unknown"
else: return "unknown"
def set_core_voltage(self, val):
volt_vals = {"low": 0xea, "mid": 0xf7, "high": 0xfd}
if val not in volt_vals.keys():
raise ValueError("must be one of %s" % list(volt_vals.keys()))
self.msr[4] = volt_vals[val]
class Stc8Option(BaseOption):
def __init__(self, msr):
super().__init__()
assert len(msr) >= 5
self.msr = bytearray(msr)
self.options = (
("reset_pin_enabled", self.get_reset_pin_enabled, self.set_reset_pin_enabled),
("clock_gain", self.get_clock_gain, self.set_clock_gain),
("watchdog_por_enabled", self.get_watchdog, self.set_watchdog),
("watchdog_stop_idle", self.get_watchdog_idle, self.set_watchdog_idle),
("watchdog_prescale", self.get_watchdog_prescale, self.set_watchdog_prescale),
("low_voltage_reset", self.get_lvrs, self.set_lvrs),
("low_voltage_threshold", self.get_low_voltage, self.set_low_voltage),
("eeprom_erase_enabled", self.get_ee_erase, self.set_ee_erase),
("bsl_pindetect_enabled", self.get_pindetect, self.set_pindetect),
("por_reset_delay", self.get_por_delay, self.set_por_delay),
("rstout_por_state", self.get_p20_state, self.set_p20_state),
("uart1_remap", self.get_uart1_remap, self.set_uart1_remap),
("uart2_passthrough", self.get_uart_passthrough, self.set_uart_passthrough),
("uart2_pin_mode", self.get_uart_pin_mode, self.set_uart_pin_mode),
("epwm_open_drain", self.get_epwm_pp, self.set_epwm_pp),
("program_eeprom_split", self.get_flash_split, self.set_flash_split),
)
def get_reset_pin_enabled(self):
return not bool(self.msr[2] & 16)
def set_reset_pin_enabled(self, val):
val = Utils.to_bool(val)
self.msr[2] &= 0xef
self.msr[2] |= 0x10 if not bool(val) else 0x00
def get_clock_gain(self):
gain = bool(self.msr[1] & 0x02)
return "high" if gain else "low"
def set_clock_gain(self, val):
gains = {"low": 0, "high": 1}
if val not in gains.keys():
raise ValueError("must be one of %s" % list(gains.keys()))
self.msr[1] &= 0xfd
self.msr[1] |= gains[val] << 1
def get_watchdog(self):
return not bool(self.msr[3] & 32)
def set_watchdog(self, val):
val = Utils.to_bool(val)
self.msr[3] &= 0xdf
self.msr[3] |= 0x20 if not val else 0x00
def get_watchdog_idle(self):
return not bool(self.msr[3] & 8)
def set_watchdog_idle(self, val):
val = Utils.to_bool(val)
self.msr[3] &= 0xf7
self.msr[3] |= 0x08 if not val else 0x00
def get_watchdog_prescale(self):
return 2 ** (((self.msr[3]) & 0x07) + 1)
def set_watchdog_prescale(self, val):
val = Utils.to_int(val)
wd_vals = {2: 0, 4: 1, 8: 2, 16: 3, 32: 4, 64: 5, 128: 6, 256: 7}
if val not in wd_vals.keys():
raise ValueError("must be one of %s" % list(wd_vals.keys()))
self.msr[3] &= 0xf8
self.msr[3] |= wd_vals[val]
def get_lvrs(self):
return not bool(self.msr[2] & 64)
def set_lvrs(self, val):
val = Utils.to_bool(val)
self.msr[2] &= 0xbf
self.msr[2] |= 0x40 if not val else 0x00
def get_low_voltage(self):
return 3 - self.msr[2] & 0x03
def set_low_voltage(self, val):
val = Utils.to_int(val)
if val not in range(0, 4):
raise ValueError("must be one of %s" % list(range(0, 4)))
self.msr[2] &= 0xfc
self.msr[2] |= 3 - val
def get_ee_erase(self):
return bool(self.msr[0] & 2)
def set_ee_erase(self, val):
val = Utils.to_bool(val)
self.msr[0] &= 0xfd
self.msr[0] |= 0x02 if val else 0x00
def get_pindetect(self):
return not bool(self.msr[0] & 1)
def set_pindetect(self, val):
val = Utils.to_bool(val)
self.msr[0] &= 0xfe
self.msr[0] |= 0x01 if not val else 0x00
def get_por_delay(self):
delay = bool(self.msr[1] & 128)
return "long" if delay else "short"
def set_por_delay(self, val):
delays = {"short": 0, "long": 1}
if val not in delays.keys():
raise ValueError("must be one of %s" % list(delays.keys()))
self.msr[1] &= 0x7f
self.msr[1] |= delays[val] << 7
def get_p20_state(self):
return "high" if self.msr[1] & 0x08 else "low"
def set_p20_state(self, val):
val = Utils.to_bool(val)
self.msr[1] &= 0xf7
self.msr[1] |= 0x08 if val else 0x00
def get_uart_passthrough(self):
return bool(self.msr[1] & 0x10)
def set_uart_passthrough(self, val):
val = Utils.to_bool(val)
self.msr[1] &= 0xef
self.msr[1] |= 0x10 if val else 0x00
def get_uart_pin_mode(self):
return "push-pull" if bool(self.msr[1] & 0x20) else "normal"
def set_uart_pin_mode(self, val):
modes = {"normal": 0, "push-pull": 1}
if val not in modes.keys():
raise ValueError("must be one of %s" % list(modes.keys()))
self.msr[1] &= 0xdf
self.msr[1] |= 0x20 if modes[val] else 0x00
def get_epwm_pp(self):
return bool(self.msr[1] & 0x04)
def set_epwm_pp(self, val):
val = Utils.to_bool(val)
self.msr[1] &= 0xfb
self.msr[1] |= 0x04 if val else 0x00
def get_uart1_remap(self):
return bool(self.msr[1] & 0x40)
def set_uart1_remap(self, val):
val = Utils.to_bool(val)
self.msr[1] &= 0xbf
self.msr[1] |= 0x40 if val else 0x00
def get_flash_split(self):
return self.msr[4] * 256
def set_flash_split(self, val):
num_val = Utils.to_int(val)
if num_val < 512 or num_val > 65024 or (num_val % 512) != 0:
raise ValueError("must be between 512 and 65024 bytes and a multiple of 512 bytes")
self.msr[4] = num_val // 256

View File

@ -27,22 +27,17 @@ import time
import struct
import re
import errno
import argparse
import collections
from stcgal.models import MCUModelDatabase
from stcgal.utils import Utils
from stcgal.options import Stc89Option
from stcgal.options import Stc12Option
from stcgal.options import Stc12AOption
from stcgal.options import Stc15Option
from stcgal.options import Stc15AOption
from stcgal.options import Stc8Option
from abc import ABC
from abc import abstractmethod
from stcgal.options import Stc89Option, Stc12Option, Stc12AOption, Stc15Option, Stc15AOption
from abc import ABC, abstractmethod
import functools
import tqdm
try:
import usb.core
import usb.util
import usb.core, usb.util
_usb_available = True
except ImportError:
_usb_available = False
@ -61,20 +56,19 @@ class StcProtocolException(Exception):
class StcBaseProtocol(ABC):
"""Basic functionality for STC BSL protocols"""
PACKET_START = bytes([0x46, 0xb9])
"""magic word that starts a packet"""
PACKET_START = bytes([0x46, 0xb9])
PACKET_END = bytes([0x16])
"""magic byte that ends a packet"""
PACKET_END = bytes([0x16])
PACKET_MCU = bytes([0x68])
"""magic byte for packets received from MCU"""
PACKET_MCU = bytes([0x68])
PACKET_HOST = bytes([0x6a])
"""magic byte for packets sent by host"""
PACKET_HOST = bytes([0x6a])
PARITY = serial.PARITY_NONE
"""parity configuration for serial communication"""
def __init__(self, port, baud_handshake, baud_transfer):
self.port = port
@ -90,22 +84,22 @@ class StcBaseProtocol(ABC):
self.debug = False
self.status_packet = None
self.protocol_name = None
self.progress = None
self.bar = None
self.progress_cb = self.progress_bar_cb
def progress_text_cb(self, current, written, maximum):
print(current, written, maximum)
def progress_bar_cb(self, current, written, maximum):
if not self.progress:
self.progress = tqdm.tqdm(
if not self.bar:
self.bar = tqdm.tqdm(
total = maximum,
unit = " Bytes",
desc = "Writing flash"
)
self.progress.update(written)
self.bar.update(written)
if current == maximum:
self.progress.close()
self.bar.close()
def dump_packet(self, data, receive=True):
if self.debug:
@ -372,9 +366,8 @@ class StcAutoProtocol(StcBaseProtocol):
("stc12a", r"STC12(C|LE)\d052"),
("stc12b", r"STC12(C|LE)(52|56)"),
("stc12", r"(STC|IAP)(10|11|12)\D"),
("stc15a", r"(STC|IAP)15[FL][012]0\d(E|EA|)$"),
("stc15", r"(STC|IAP|IRC)15\D"),
("stc8", r"(STC|IAP|IRC)8")]
("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:
if re.match(pattern, self.model.name):
@ -396,11 +389,11 @@ class StcAutoProtocol(StcBaseProtocol):
class Stc89Protocol(StcBaseProtocol):
"""Protocol handler for STC 89/90 series"""
"""These don't use any parity"""
PARITY = serial.PARITY_NONE
"""Parity configuration - these don't use any parity"""
PROGRAM_BLOCKSIZE = 128
"""block size for programming flash"""
PROGRAM_BLOCKSIZE = 128
def __init__(self, port, baud_handshake, baud_transfer):
StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer)
@ -419,7 +412,7 @@ class Stc89Protocol(StcBaseProtocol):
payload = StcBaseProtocol.extract_payload(self, packet)
return payload[:-1]
def write_packet(self, packet_data):
def write_packet(self, data):
"""Send packet to MCU.
Constructs a packet with supplied payload and sends it to the MCU.
@ -431,8 +424,8 @@ class Stc89Protocol(StcBaseProtocol):
packet += self.PACKET_HOST
# packet length and payload
packet += struct.pack(">H", len(packet_data) + 5)
packet += packet_data
packet += struct.pack(">H", len(data) + 5)
packet += data
# checksum and end code
packet += bytes([sum(packet[2:]) & 0xff])
@ -488,19 +481,19 @@ class Stc89Protocol(StcBaseProtocol):
return brt, brt_csum, iap_wait, delay
def initialize_status(self, status_packet):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
self.cpu_6t = not bool(status_packet[19] & 1)
self.cpu_6t = not bool(packet[19] & 1)
cpu_t = 6.0 if self.cpu_6t else 12.0
freq_counter = 0
for i in range(8):
freq_counter += struct.unpack(">H", status_packet[1+2*i:3+2*i])[0]
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
freq_counter /= 8.0
self.mcu_clock_hz = (self.baud_handshake * freq_counter * cpu_t) / 7.0
bl_version, bl_stepping = struct.unpack("BB", status_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,
chr(bl_stepping))
@ -545,7 +538,7 @@ class Stc89Protocol(StcBaseProtocol):
sys.stdout.flush()
packet = bytes([0x80, 0x00, 0x00, 0x36, 0x01])
packet += struct.pack(">H", self.mcu_magic)
for _ in range(4):
for i in range(4):
self.write_packet(packet)
response = self.read_packet()
if response[0] != 0x80:
@ -553,7 +546,7 @@ class Stc89Protocol(StcBaseProtocol):
print("done")
def erase_flash(self, erase_size, _):
def erase_flash(self, erase_size, flash_size):
"""Erase the MCU's flash memory.
Erase the flash memory with a block-erase command.
@ -645,16 +638,16 @@ class Stc12AProtocol(Stc12AOptionsMixIn, Stc89Protocol):
def __init__(self, port, baud_handshake, baud_transfer):
Stc89Protocol.__init__(self, port, baud_handshake, baud_transfer)
def initialize_status(self, status_packet):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
freq_counter = 0
for i in range(8):
freq_counter += struct.unpack(">H", status_packet[1+2*i:3+2*i])[0]
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
freq_counter /= 8.0
self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0
bl_version, bl_stepping = struct.unpack("BB", status_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,
chr(bl_stepping))
@ -733,7 +726,7 @@ class Stc12AProtocol(Stc12AOptionsMixIn, Stc89Protocol):
sys.stdout.flush()
packet = bytes([0x80, 0x00, 0x00, 0x36, 0x01])
packet += struct.pack(">H", self.mcu_magic)
for _ in range(4):
for i in range(4):
self.write_packet(packet)
response = self.read_packet()
if response[0] != 0x80:
@ -790,14 +783,14 @@ class Stc12OptionsMixIn:
class Stc12BaseProtocol(StcBaseProtocol):
"""Base class for STC 10/11/12 series protocol handlers"""
PROGRAM_BLOCKSIZE = 128
"""block size for programming flash"""
PROGRAM_BLOCKSIZE = 128
ERASE_COUNTDOWN = 0x0d
"""countdown value for flash erase"""
ERASE_COUNTDOWN = 0x0d
PARITY = serial.PARITY_EVEN
"""Parity for error correction was introduced with STC12"""
PARITY = serial.PARITY_EVEN
def __init__(self, port, baud_handshake, baud_transfer):
StcBaseProtocol.__init__(self, port, baud_handshake, baud_transfer)
@ -814,7 +807,7 @@ class Stc12BaseProtocol(StcBaseProtocol):
payload = StcBaseProtocol.extract_payload(self, packet)
return payload[:-2]
def write_packet(self, packet_data):
def write_packet(self, data):
"""Send packet to MCU.
Constructs a packet with supplied payload and sends it to the MCU.
@ -826,8 +819,8 @@ class Stc12BaseProtocol(StcBaseProtocol):
packet += self.PACKET_HOST
# packet length and payload
packet += struct.pack(">H", len(packet_data) + 6)
packet += packet_data
packet += struct.pack(">H", len(data) + 6)
packet += data
# checksum and end code
packet += struct.pack(">H", sum(packet[2:]) & 0xffff)
@ -837,16 +830,16 @@ class Stc12BaseProtocol(StcBaseProtocol):
self.ser.write(packet)
self.ser.flush()
def initialize_status(self, status_packet):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
freq_counter = 0
for i in range(8):
freq_counter += struct.unpack(">H", status_packet[1+2*i:3+2*i])[0]
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
freq_counter /= 8.0
self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0
bl_version, bl_stepping = struct.unpack("BB", status_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,
chr(bl_stepping))
@ -1036,20 +1029,20 @@ class Stc15AProtocol(Stc12Protocol):
raise StcProtocolException("incorrect magic in status packet")
return status_packet
def initialize_status(self, status_packet):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
freq_counter = 0
for i in range(4):
freq_counter += struct.unpack(">H", status_packet[1+2*i:3+2*i])[0]
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
freq_counter /= 4.0
self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0
bl_version, bl_stepping = struct.unpack("BB", status_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,
chr(bl_stepping))
self.trim_data = status_packet[51:58]
self.trim_data = packet[51:58]
self.freq_counter = freq_counter
def get_trim_sequence(self, frequency):
@ -1247,30 +1240,30 @@ class Stc15Protocol(Stc15AProtocol):
self.options = Stc15Option(status_packet[5:8] + status_packet[12:13] + status_packet[37:38])
self.options.print()
def initialize_status(self, status_packet):
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
# check bit that control internal vs. external clock source
# get frequency either stored from calibration or from
# frequency counter
self.external_clock = (status_packet[7] & 0x01) == 0
self.external_clock = (packet[7] & 0x01) == 0
if self.external_clock:
count, = struct.unpack(">H", status_packet[13:15])
count, = struct.unpack(">H", packet[13:15])
self.mcu_clock_hz = self.baud_handshake * count
else:
self.mcu_clock_hz, = struct.unpack(">I", status_packet[8:12])
self.mcu_clock_hz, = struct.unpack(">I", packet[8:12])
# all ones means no calibration
# new chips are shipped without any calibration
if self.mcu_clock_hz == 0xffffffff: self.mcu_clock_hz = 0
# pre-calibrated trim adjust for 24 MHz, range 0x40
self.freq_count_24 = status_packet[4]
self.freq_count_24 = packet[4]
# wakeup timer factory value
self.wakeup_freq, = struct.unpack(">H", status_packet[1:3])
self.wakeup_freq, = struct.unpack(">H", packet[1:3])
bl_version, bl_stepping = struct.unpack("BB", status_packet[17:19])
bl_minor = status_packet[22] & 0x0f
bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
bl_minor = packet[22] & 0x0f
self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
bl_minor, chr(bl_stepping))
self.bsl_version = bl_version
@ -1362,7 +1355,7 @@ class Stc15Protocol(Stc15AProtocol):
# select ranges and trim values
user_trim = self.choose_range(packet, response, target_user_count)
prog_trim = self.choose_range(packet, response, target_prog_count)
if user_trim is None or prog_trim is None:
if user_trim == None or prog_trim == None:
raise StcProtocolException("frequency trimming unsuccessful")
# calibration, round 2
@ -1380,7 +1373,7 @@ class Stc15Protocol(Stc15AProtocol):
# select final values
user_trim, user_count = self.choose_trim(packet, response, target_user_count)
prog_trim, _ = self.choose_trim(packet, response, target_prog_count)
prog_trim, prog_count = self.choose_trim(packet, response, target_prog_count)
self.trim_value = user_trim
self.trim_frequency = round(user_count * (self.baud_handshake / 2))
print("%.03f MHz" % (self.trim_frequency / 1E6))
@ -1548,145 +1541,6 @@ class Stc15Protocol(Stc15AProtocol):
print("Target UID: %s" % Utils.hexstr(self.uid))
class Stc8Protocol(Stc15Protocol):
"""Protocol handler for STC8 series"""
def __init__(self, port, handshake, baud, trim):
Stc15Protocol.__init__(self, port, handshake, baud, trim)
self.trim_divider = None
def initialize_options(self, status_packet):
"""Initialize options"""
if len(status_packet) < 17:
raise StcProtocolException("invalid options in status packet")
# create option state
self.options = Stc8Option(status_packet[9:12] + status_packet[15:17])
self.options.print()
def initialize_status(self, packet):
"""Decode status packet and store basic MCU info"""
self.mcu_clock_hz, = struct.unpack(">I", packet[1:5])
# XXX: external clock not supported nor tested
self.external_clock = False
# all ones means no calibration
# new chips are shipped without any calibration
# XXX: somehow check if that still holds
if self.mcu_clock_hz == 0xffffffff: self.mcu_clock_hz = 0
# pre-calibrated trim adjust for 24 MHz, range 0x40
self.freq_count_24 = packet[4]
# wakeup timer factory value
self.wakeup_freq, = struct.unpack(">H", packet[23:25])
bl_version, bl_stepping = struct.unpack("BB", packet[17:19])
bl_minor = packet[22] & 0x0f
self.mcu_bsl_version = "%d.%d.%d%s" % (bl_version >> 4, bl_version & 0x0f,
bl_minor, chr(bl_stepping))
self.bsl_version = bl_version
def calibrate(self):
"""Calibrate selected user frequency frequency and switch to selected baudrate."""
# handle uncalibrated chips
if self.mcu_clock_hz == 0 and self.trim_frequency <= 0:
raise StcProtocolException("uncalibrated, please provide a trim value")
# determine target counter
user_speed = self.trim_frequency
if user_speed <= 0: user_speed = self.mcu_clock_hz
target_user_count = round(user_speed / (self.baud_handshake/2))
# calibration, round 1
# XXX: challenges need work. the ranges and how they related to clock frequency
# is different. A clock divider is used for lower frequencies.
print("Trimming frequency: ", end="")
sys.stdout.flush()
packet = bytes([0x00])
packet += struct.pack(">B", 12)
packet += bytes([0x00, 0x00, 23*1, 0x00, 23*2, 0x00])
packet += bytes([23*3, 0x00, 23*4, 0x00, 23*5, 0x00])
packet += bytes([23*6, 0x00, 23*7, 0x00, 23*8, 0x00])
packet += bytes([23*9, 0x00, 23*10, 0x00, 255, 0x00])
self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select ranges and trim values
for divider in (1, 2, 3, 4, 5):
user_trim = self.choose_range(packet, response, target_user_count * divider)
if user_trim is not None:
self.trim_divider = divider
break
if user_trim is None:
raise StcProtocolException("frequency trimming unsuccessful")
# calibration, round 2
packet = bytes([0x00])
packet += struct.pack(">B", 12)
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x00])
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x01])
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x02])
for i in range(user_trim[0] - 1, user_trim[0] + 2):
packet += bytes([i & 0xff, 0x03])
self.write_packet(packet)
self.pulse(b"\xfe", timeout=1.0)
response = self.read_packet()
if len(response) < 2 or response[0] != 0x00:
raise StcProtocolException("incorrect magic in handshake packet")
# select final values
user_trim, user_count = self.choose_trim(packet, response, target_user_count)
self.trim_value = user_trim
self.trim_frequency = round(user_count * (self.baud_handshake / 2) / self.trim_divider)
print("%.03f MHz" % (self.trim_frequency / 1E6))
# switch to programming frequency
print("Switching to %d baud: " % self.baud_transfer, end="")
sys.stdout.flush()
packet = bytes([0x01, 0x00, 0x00])
bauds = self.baud_transfer * 4
packet += struct.pack(">H", int(65535 - 24E6 / bauds))
packet += bytes([user_trim[1], user_trim[0]])
iap_wait = self.get_iap_delay(24E6)
packet += bytes([iap_wait])
self.write_packet(packet)
response = self.read_packet()
if len(response) < 1 or response[0] != 0x01:
raise StcProtocolException("incorrect magic in handshake packet")
self.ser.baudrate = self.baud_transfer
def build_options(self):
"""Build a packet of option data from the current configuration."""
msr = self.options.get_msr()
packet = 40 * bytearray([0xff])
packet[3] = 0
packet[6] = 0
packet[22] = 0
packet[24:28] = struct.pack(">I", self.trim_frequency)
packet[28:30] = self.trim_value
packet[30] = self.trim_divider
packet[32] = msr[0]
packet[36:40] = msr[1:5]
return bytes(packet)
def disconnect(self):
"""Disconnect from MCU"""
# reset mcu
packet = bytes([0xff])
self.write_packet(packet)
self.ser.close()
print("Disconnected!")
class StcUsb15Protocol(Stc15Protocol):
"""USB should use large blocks"""
PROGRAM_BLOCKSIZE = 128

View File

@ -35,7 +35,8 @@ class Utils:
return val
elif isinstance(val, int):
return bool(val)
return True if val[0].lower() == "t" or val[0] == "1" else False
else:
return True if val[0].lower() == "t" or val[0] == "1" else False
@classmethod
def to_int(cls, val):
@ -43,7 +44,7 @@ class Utils:
try:
return int(val, 0)
except (TypeError, ValueError):
except:
raise ValueError("invalid integer")
@classmethod