stc8: implement option handling
Implement option handling for STC8 series, based on STC8A8K64S4A12 reverse engineering. This mostly wraps up all important parts of the STC8 implementation. Interoperability was tested with STC-ISP V6.86O. v2: update documentation
This commit is contained in:
parent
0ffcbd197b
commit
fe60e647bf
17
README.md
17
README.md
@ -23,7 +23,7 @@ suitable for automation.
|
||||
Supported MCU models
|
||||
--------------------
|
||||
|
||||
stcgal should fully support STC 89/90/10/11/12/15 series MCUs.
|
||||
stcgal should fully support STC 89/90/10/11/12/15 series MCUs. Support for STC8 series MCUs is work in progress.
|
||||
|
||||
So far, stcgal was tested with the following MCU models:
|
||||
|
||||
@ -47,6 +47,7 @@ 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.
|
||||
|
||||
@ -59,8 +60,8 @@ Features
|
||||
* Program flash memory
|
||||
* Program IAP/EEPROM
|
||||
* Set device options
|
||||
* Read unique device ID (STC 10/11/12/15)
|
||||
* Trim RC oscillator frequency (STC 15)
|
||||
* Read unique device ID (STC 10/11/12/15/8)
|
||||
* Trim RC oscillator frequency (STC 15/8)
|
||||
* Automatic power-cycling with DTR toggle or a custom shell command
|
||||
* Automatic UART protocol detection
|
||||
|
||||
@ -126,6 +127,7 @@ 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)
|
||||
|
||||
@ -257,17 +259,20 @@ 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 pin state after power-on reset
|
||||
```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)
|
||||
```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. The trim values are stored with
|
||||
device options. Use the ```-t``` flag to request trimming to a certain
|
||||
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
|
||||
value. Generally, frequencies between 4 and 35 MHz can be achieved. If
|
||||
trimming fails, stcgal will abort.
|
||||
|
||||
|
71
doc/stc8-options.txt
Normal file
71
doc/stc8-options.txt
Normal file
@ -0,0 +1,71 @@
|
||||
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: ?
|
@ -31,6 +31,10 @@ Clock set to 20 MHz by STC-ISP (encoding is different compared to STC15):
|
||||
^^ 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
|
||||
----------
|
||||
|
@ -622,3 +622,170 @@ class Stc15Option(BaseOption):
|
||||
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
|
@ -34,6 +34,7 @@ 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
|
||||
import functools
|
||||
@ -1554,9 +1555,14 @@ class Stc8Protocol(Stc15Protocol):
|
||||
Stc15Protocol.__init__(self, port, handshake, baud, trim)
|
||||
self.trim_divider = None
|
||||
|
||||
def program_options(self):
|
||||
# XXX: not yet implemented
|
||||
pass
|
||||
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"""
|
||||
@ -1657,10 +1663,20 @@ class Stc8Protocol(Stc15Protocol):
|
||||
raise StcProtocolException("incorrect magic in handshake packet")
|
||||
self.ser.baudrate = self.baud_transfer
|
||||
|
||||
def initialize_options(self, status_packet):
|
||||
"""Initialize options"""
|
||||
# XXX: not implemented yet
|
||||
pass
|
||||
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"""
|
||||
|
Loading…
Reference in New Issue
Block a user