Initial commit

This commit is contained in:
Grigori Goronzy 2014-01-06 20:35:27 +01:00
commit cdbb6eee7c
6 changed files with 1020 additions and 0 deletions

BIN
doc/hello.bin Normal file

Binary file not shown.

100
doc/stc11f08xe.txt Normal file
View File

@ -0,0 +1,100 @@
MCU: STC11F08XE
Data: hello.bin
Handshake: 9600
Transfer: 9600
Clock: 20 MHz
2014-01-06 17:13:42.017505: host2mcu
7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F
7F 7F 7F 7F
2014-01-06 17:13:42.315631: mcu2host
46 B9 68 00 39 50 04 BC 04 BD 04 BD 04 BC 04 BC
04 BD 04 BC 04 BC 65 4C 00 D3 64 8C BF 7F F7 FF
FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 6B 0B D3 00 6A 82 80 11 4F 16
2014-01-06 17:13:42.455472: host2mcu
46 B9 6A 00 0D 50 00 00 36 01 D3 64 02 35 16
2014-01-06 17:13:42.479849: mcu2host
46 B9 68 00 07 8F 00 FE 16
2014-01-06 17:13:42.504527: host2mcu
46 B9 6A 00 0D 8F C0 7E 3F FE A0 83 04 A4 16
2014-01-06 17:13:42.768346: mcu2host
46 B9 68 00 0E 8F C0 7E 3F FE A0 83 04 04 A7 16
2014-01-06 17:13:42.987584: host2mcu
46 B9 6A 00 0C 8E C0 7E 3F FE A0 04 1F 16
2014-01-06 17:13:43.244111: mcu2host
46 B9 68 00 0D 84 C0 7E 3F FE A0 04 04 18 16
2014-01-06 17:13:43.286557: host2mcu
46 B9 6A 00 8C 84 00 00 02 00 00 20 00 00 00 00
00 00 00 00 00 00 00 00 80 7F 7E 7D 7C 7B 7A 79
78 77 76 75 74 73 72 71 70 6F 6E 6D 6C 6B 6A 69
68 67 66 65 64 63 62 61 60 5F 5E 5D 5C 5B 5A 59
58 57 56 55 54 53 52 51 50 4F 4E 4D 4C 4B 4A 49
48 47 46 45 44 43 42 41 40 3F 3E 3D 3C 3B 3A 39
38 37 36 35 34 33 32 31 30 2F 2E 2D 2C 2B 2A 29
28 27 26 25 24 23 22 21 20 1F 1E 1D 1C 1B 1A 19
18 17 16 15 14 13 12 11 10 0F 0E 21 81 16
2014-01-06 17:13:43.718954: mcu2host
46 B9 68 00 0E 00 00 08 00 8E 00 A8 2E 01 E2 16
2014-01-06 17:13:43.758507: host2mcu
46 B9 6A 00 8D 00 00 00 00 00 00 80 02 00 08 12
00 3F 80 FE 75 81 07 12 00 4C E5 82 60 03 02 00
03 E4 78 FF F6 D8 FD 02 00 03 AE 82 AF 83 8E 04
8F 05 1E BE FF 01 1F EC 4D 60 0F 7C 90 7D 01 1C
BC FF 01 1D EC 4D 70 F7 80 E4 22 90 03 E8 12 00
1E E5 80 F4 F5 80 80 F3 75 82 00 22 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 22 7A 16
2014-01-06 17:13:44.050030: mcu2host
46 B9 68 00 08 00 03 00 73 16
2014-01-06 17:13:44.063502: host2mcu
46 B9 6A 00 8D 00 00 00 00 80 00 80 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 01 F7 16
2014-01-06 17:13:44.261910: mcu2host
46 B9 68 00 08 00 00 00 70 16
2014-01-06 17:13:44.279527: host2mcu
46 B9 6A 00 8D 00 00 00 01 00 00 80 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 01 78 16
2014-01-06 17:13:44.486933: mcu2host
46 B9 68 00 08 00 00 00 70 16
2014-01-06 17:13:44.503472: host2mcu
46 B9 6A 00 8D 00 00 00 01 80 00 80 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 01 F8 16
2014-01-06 17:13:44.698788: mcu2host
46 B9 68 00 08 00 00 00 70 16
2014-01-06 17:13:44.716462: host2mcu
46 B9 6A 00 0D 69 00 00 36 01 D3 64 02 4E 16
2014-01-06 17:13:44.755837: mcu2host
46 B9 68 00 07 8D 00 FC 16
2014-01-06 17:13:44.786562: host2mcu
46 B9 6A 00 1B 8D BF 7F F7 FF FF FF FF FF FF FF
FF FF FF FF FF FF 01 30 9A 92 11 97 16
2014-01-06 17:13:44.843366: mcu2host
46 B9 68 00 24 50 BF 7F F7 FF FF 03 FF 65 4C BF
7F F7 FF FF FF FF 01 00 6B 0B D3 00 6A 82 80 00
00 00 00 0F A9 16
2014-01-06 17:13:44.968518: host2mcu
46 B9 6A 00 07 82 00 F3 16

70
doc/stc12-options.txt Normal file
View File

@ -0,0 +1,70 @@
Model-specific configuration registers
Placement of configuration values
"~" means the bit is a negated boolean. Sometimes values overlap,
depending on MCU model.
In STC10/11/12 series, the first 4 MCS bytes have active
values. Generally, unused bits should be set to 1.
MCS0
----
MSB 7 6 5 4 3 2 1 0 LSB
~RS2LV OSC1 OSC0 RSPEN
~LVD
RSPEN := RESET pin enable
~RS2LV := RESET2 pin low voltage detect enable
~LVD := low voltage detect enable
OSC0, OSC1 := oscillator stabilization delay
OSC1 OSC0 delay
0 0 4096
0 1 8192
1 0 16384
1 1 32768
MCS1
----
MSB 7 6 5 4 3 2 1 0 LSB
~PORD OSCG CLKSRC
~PORD := power-on-reset (POR) delay (0 = long, 1 = short)
OSCG := high oscillator gain
CLKSRC := clock source (0 = internal RC, 1 = external crystal)
MCS2
----
MSB 7 6 5 4 3 2 1 0 LSB
~WDEN ~WDSTP WDPS2 WDPS1 WDPS0
~WDEN := watchdog enable after power-on-reset
~WDSTP := stop watchdog counter in idle mode
WDPS2...WDPS0 := watchdog counter prescaler
WDPS2 WDPS1 WDPS0 divisior
0 0 0 2
0 0 1 4
0 1 0 8
0 1 1 16
1 0 0 32
1 0 1 64
1 1 0 128
1 1 1 256
MCS3
----
MSB 7 6 5 4 3 2 1 0 LSB
~EREE ~BSLD
~EREE := enable eeprom erase next time MCU is programmed
~BSLD := enable BSL pin detect; i.e. BSL is only enabled if P1.0/P1.1
(or others, depends on MCU model) are held low on POR.

182
doc/stc12-protocol.txt Normal file
View File

@ -0,0 +1,182 @@
STC10/11/12 reverse engineering
Initialisation/Synchronisation
------------------------------
Send a constant stream of 0x7f bytes, and wait for an initial response
by the MCU.
Basic frame format
------------------
M0 M1 DR L0 L1 D0 ... Dn C0 C1 ME
M0 := 0x46
M1 := 0xb9
DR := 0x6a if host2mcu else 0x68
L := 16 bit big endian packet length, counted from DR to ME
C := 16 big endian modular sum from DR to Dn
ME := 0x16
D0..Dn is the packet payload
In most cases, the first byte of the payload marks the type of packet
or type of command. Responses by the MCU often use this type to tell
the programmer software which kind of command should follow. For
instance, after the baudrate handshake, the MCU replies with a
type 0x84 packet, and 0x84 is used for "erase" command packets from
the host.
Fun fact: The start marker (0x46, 0xb9) interpreted as UTF-16 is the
Unicode character U+46B9, which is an unusual CJK ideograph (䚹)
which translates as "to prepare" or "all ready" into English. How
fitting! This might not be a coincidence.
Packets host2mcu
----------------
1. Initiate baudrate handshake
Payload: 0x50, 0x07, 0x00, 0x36, 0x01, ID0, ID1
^ is 0x00 with current STC software and 11F08XE, what gives?
ID0 = MCU ID, byte 1
ID1 = MCU ID, byte 2
2. Test baudrate setting
Payload: 0x8f, 0xc0, brt, 0x3f, brt_csum, delay, iap
brt := MCU baudrate timer compare
brt_csum := (2 * (256 - brt)) & 0xff
delay := delay after baudrate change (0x40 seems to be fine),
STC software always seems to use 0xa0
iap := MCU IAP wait state register value
3. Switch to baudrate setting
Payload: 0x8e, 0xc0, brt, 0x3f, brt_csum, delay, iap
^ current STC software *omits* this here!
Almost the same as the test packet.
4. Erase flash memory
Payload: 0x84, 0xff, 0x00, blks, 0x00, 0x00, size,
^ no idea what that is for, current STC software uses 0x00
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
0x80, ..., 0x0e
blks := 256 byte blocks to clear
size := total number of 256 byte blocks (size of flash memory)
The 0x80..0x0e sequence seems to be some kind of magic code
to stop flaky connections and the like from erasing the flash
by accident.
"size" specifies the number of flash memory blocks. if blks > size,
eeprom will be erased.
5. Program flash memory
Payload: 0x00, 0x00, 0x00, addr0, addr1, size0, size1, D0, ..., Dn
addr0, addr1 := big-endian 16 bit address
size0, size1 := big-endian 16 bit block size, always 128
D0...Dn := block data
Current STC software always seems to write at least 4 128 byte blocks
for some reason. Data is zero-padded.
Current STC software always writes a sequential set of memory. Since
flash and eeprom are essentially the same, any free space between
flash to be written and eeprom to be written is padded with zeros,
and then the whole batch is sent at once.
6. Finish flash programming
Payload: 0x69, 0x00, 0x00, 0x36, 0x01, ID0, ID1
^ kSTC-ISP uses 0x07
This should be sent after all flash programming is done. I am not
entirely sure why, though. Programming also works without it.
7. Set options
Payload: 0x8d, MS0, ..., MS15, CLK0, CLK1, CLK2, CLK3
MS0...MS15 := configuration registers specific to MCU model,
not documented here.
CLK0...CLK3 := 32 bit big endian measured clock, in Hz
8. Reset MCU
Payload: 0x82
Packets mcu2host
----------------
1. Info packet
Payload: 0x50, SYNC00, SYNC01, ..., SYNC70, SYNC71,
V1, V2, 0x00, ID0, ID1, 0x8c,
MS0, ..., MS7,
UID0, ..., UID6,
unknown bytes follow
SYNC* := sequence of 8 16-bit big-endian counter values, recorded
from the initial 0x7f sync sequence. this can be used to
determine the MCU clock frequency.
V1 := version number, two digits packed BCD.
V2 := stepping, one ASCII character.
ID0 := MCU model ID, byte 1
ID1 := MCU model ID, byte 2
UID0...UID6 := 7 bytes of unique id
UID is only sent by some BSL versions, others send zero bytes.
2. Acknowledge baudrate handshake start
Payload: 0x8f
This means the programming software should erase the flash memory as
the next step.
3. Acknowledge baudrate test
Payload: request packet payload with some pad byte appended to payload
4. Acknowledge baudrate switch
Payload: request packet payload with some pad byte appended to payload, and
first payload byte changed to 0x84
5. Acknowledge erase
Payload: 0x00, [UID0, ..., UID6]
The UID is optional, not sent by all BSL versions.
6. Acknowledge block write
Payload: 0x00, csum
csum := 8 bit modular sum of flash block data
7. Acknowledge finish flash writing
Payload: 0x8d
This means the programming software should set options as the next
step.
8. Acknowledge set options
Payload: 0x50, MS0, ..., MS4, 0x03, 0xff, V1, V2, MS0, ..., MS7,
UID0, ..., UID6,
unknown bytes follow
Some of the model-specific bytes are repeated twice (MS0-MS4).

100
doc/stc12c5a60s2.txt Normal file
View File

@ -0,0 +1,100 @@
MCU: STC12C5A60S2
Data: hello.bin
Handshake: 9600
Transfer: 9600
Clock: 20 MHz
2014-01-06 17:19:52.426530: host2mcu
7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F 7F
7F 7F 7F 7F
2014-01-06 17:19:52.722646: mcu2host
46 B9 68 00 31 50 04 BD 04 BC 04 BC 04 BD 04 BC
04 BC 04 BC 04 BC 62 49 00 D1 7E 8C FF 7F F7 FF
FF FF 00 00 00 03 00 B0 02 2E 6B 00 CD 80 00 00
11 7E 16
2014-01-06 17:19:52.846519: host2mcu
46 B9 6A 00 0D 50 00 00 36 01 D1 7E 02 4D 16
2014-01-06 17:19:52.893712: mcu2host
46 B9 68 00 07 8F 00 FE 16
2014-01-06 17:19:52.928824: host2mcu
46 B9 6A 00 0D 8F C0 7E 3F FE A0 83 04 A4 16
2014-01-06 17:19:53.209386: mcu2host
46 B9 68 00 0E 8F C0 7E 3F FE A0 83 04 04 A7 16
2014-01-06 17:19:53.424544: host2mcu
46 B9 6A 00 0C 8E C0 7E 3F FE A0 04 1F 16
2014-01-06 17:19:53.679264: mcu2host
46 B9 68 00 0D 84 C0 7E 3F FE A0 04 04 18 16
2014-01-06 17:19:53.724472: host2mcu
46 B9 6A 00 8C 84 00 00 02 00 00 F0 00 00 00 00
00 00 00 00 00 00 00 00 80 7F 7E 7D 7C 7B 7A 79
78 77 76 75 74 73 72 71 70 6F 6E 6D 6C 6B 6A 69
68 67 66 65 64 63 62 61 60 5F 5E 5D 5C 5B 5A 59
58 57 56 55 54 53 52 51 50 4F 4E 4D 4C 4B 4A 49
48 47 46 45 44 43 42 41 40 3F 3E 3D 3C 3B 3A 39
38 37 36 35 34 33 32 31 30 2F 2E 2D 2C 2B 2A 29
28 27 26 25 24 23 22 21 20 1F 1E 1D 1C 1B 1A 19
18 17 16 15 14 13 12 11 10 0F 0E 22 51 16
2014-01-06 17:19:55.505307: mcu2host
46 B9 68 00 07 00 00 6F 16
2014-01-06 17:19:55.537548: host2mcu
46 B9 6A 00 8D 00 00 00 00 00 00 80 02 00 08 12
00 3F 80 FE 75 81 07 12 00 4C E5 82 60 03 02 00
03 E4 78 FF F6 D8 FD 02 00 03 AE 82 AF 83 8E 04
8F 05 1E BE FF 01 1F EC 4D 60 0F 7C 90 7D 01 1C
BC FF 01 1D EC 4D 70 F7 80 E4 22 90 03 E8 12 00
1E E5 80 F4 F5 80 80 F3 75 82 00 22 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 22 7A 16
2014-01-06 17:19:55.968298: mcu2host
46 B9 68 00 08 00 03 00 73 16
2014-01-06 17:19:55.986526: host2mcu
46 B9 6A 00 8D 00 00 00 00 80 00 80 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 01 F7 16
2014-01-06 17:19:56.412372: mcu2host
46 B9 68 00 08 00 00 00 70 16
2014-01-06 17:19:56.430530: host2mcu
46 B9 6A 00 8D 00 00 00 01 00 00 80 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 01 78 16
2014-01-06 17:19:56.865930: mcu2host
46 B9 68 00 08 00 00 00 70 16
2014-01-06 17:19:56.884481: host2mcu
46 B9 6A 00 8D 00 00 00 01 80 00 80 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 01 F8 16
2014-01-06 17:19:57.288219: mcu2host
46 B9 68 00 08 00 00 00 70 16
2014-01-06 17:19:57.306515: host2mcu
46 B9 6A 00 0D 69 00 00 36 01 D1 7E 02 66 16
2014-01-06 17:19:57.369302: mcu2host
46 B9 68 00 07 8D 00 FC 16
2014-01-06 17:19:57.412492: host2mcu
46 B9 6A 00 1B 8D FF 7F F7 FF FF FF FF FF FF FF
FF FF FF FF FF FF 01 30 5A 49 11 4E 16
2014-01-06 17:19:57.511742: mcu2host
46 B9 68 00 24 50 FF 7F F7 FF FF 03 FF 62 49 FF
7F F7 FF FF FF FF 01 00 03 00 B0 02 2E 6B 00 CD
80 00 00 10 09 16
2014-01-06 17:19:57.672474: host2mcu
46 B9 6A 00 07 82 00 F3 16

568
stcgal.py Executable file
View File

@ -0,0 +1,568 @@
#!/usr/bin/env python3
#
# Copyright (c) 2013 Grigori Goronzy
#
# 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.
#
# stcgal - STC MCU serial bootloader flash programmer
"""
TODO:
- Utils class?
- error/exception handling
- some more documentation / comments
- private member naming, other style issues
- MCU database
- Prepare for STC89/STC15 protocols
"""
import serial
import sys, os, time, struct
import argparse
DEBUG = False
class Utils:
"""make sensible boolean from string or other type value"""
@classmethod
def to_bool(self, val):
if isinstance(val, bool): return val
if isinstance(val, int): return bool(val)
if len(val) == 0: return False
return True if val[0].lower() == "t" or val[0] == "1" else False
class Stc12Option:
"""Manipulate STC10/11/12 series option bytes"""
def __init__(self, msr):
self.msr = bytearray(msr)
"""list of options and their handlers"""
self.options = (
("reset_pin_enabled", self.get_reset_pin_enabled, self.set_reset_pin_enabled),
("low_voltage_detect", self.get_low_voltage_detect, self.set_low_voltage_detect),
("oscillator_stable_delay", self.get_osc_stable_delay, self.set_osc_stable_delay),
("power_on_reset_delay", self.get_por_delay, self.set_por_delay),
("clock_gain", self.get_clock_gain, self.set_clock_gain),
("clock_source", self.get_clock_source, self.set_clock_source),
("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),
("eeprom_erase_enabled", self.get_ee_erase, self.set_ee_erase),
("bsl_pindetect_enabled", self.get_pindetect, self.set_pindetect),
)
def print(self):
print("Target options:")
for name, get_func, _ in self.options:
print(" %s=%s" % (name, get_func()))
def set_option(self, name, value):
for opt, _, set_func in self.options:
if opt == name:
print("Option %s=%s" % (name, value))
set_func(value)
return
raise ValueError
def get_option(self, name):
for opt, get_func, _ in self.options:
if opt == name:
return get_func(name)
raise ValueError
def get_msr(self):
return bytes(self.msr)
def get_reset_pin_enabled(self):
return bool(self.msr[0] & 1)
def set_reset_pin_enabled(self, val):
val = Utils.to_bool(val);
self.msr[0] &= 0xfe
self.msr[0] |= 0x01 if bool(val) else 0x00
def get_low_voltage_detect(self):
return not bool(self.msr[0] & 64)
def set_low_voltage_detect(self, val):
val = Utils.to_bool(val);
self.msr[0] &= 0xbf
self.msr[0] |= 0x40 if not val else 0x00
def get_osc_stable_delay(self):
return 2 ** (((self.msr[0] >> 4) & 0x03) + 12)
def set_osc_stable_delay(self, val):
val = int(val, 0)
osc_vals = {4096: 0, 8192: 1, 16384: 2, 32768: 3}
if val not in osc_vals.keys(): raise ValueError
self.msr[0] &= 0x8f
self.msr[0] |= osc_vals[val] << 4
def get_por_delay(self):
delay = not bool(self.msr[1] & 128)
return "long" if delay else "short"
def set_por_delay(self, val):
delays = {"short": 1, "long": 0}
if val not in delays.keys(): raise ValueError
self.msr[1] &= 0x7f
self.msr[1] |= delays[val] << 7
def get_clock_gain(self):
gain = bool(self.msr[1] & 64)
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
self.msr[1] &= 0xbf
self.msr[1] |= gains[val] << 6
def get_clock_source(self):
source = bool(self.msr[1] & 2)
return "external" if source else "internal"
def set_clock_source(self, val):
sources = {"internal": 0, "external": 1}
if val not in sources.keys(): raise ValueError
self.msr[1] &= 0xfd
self.msr[1] |= sources[val] << 1
def get_watchdog(self):
return not bool(self.msr[2] & 32)
def set_watchdog(self, val):
val = Utils.to_bool(val);
self.msr[2] &= 0xdf
self.msr[2] |= 0x20 if not val else 0x00
def get_watchdog_idle(self):
return not bool(self.msr[2] & 8)
def set_watchdog_idle(self, val):
val = Utils.to_bool(val);
self.msr[2] &= 0xf7
self.msr[2] |= 0x08 if not val else 0x00
def get_watchdog_prescale(self):
return 2 ** (((self.msr[2]) & 0x07) + 1)
def set_watchdog_prescale(self, val):
val = int(val, 0)
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
self.msr[2] &= 0xf8
self.msr[2] |= wd_vals[val]
def get_ee_erase(self):
return not bool(self.msr[3] & 2)
def set_ee_erase(self, val):
val = Utils.to_bool(val);
self.msr[3] &= 0xfd
self.msr[3] |= 0x02 if not val else 0x00
def get_pindetect(self):
return not bool(self.msr[3] & 1)
def set_pindetect(self, val):
val = Utils.to_bool(val);
self.msr[3] &= 0xfe
self.msr[3] |= 0x01 if not val else 0x00
class Stc12Protocol:
"""Protocol handler for STC 10/11/12 series"""
"""magic word that starts a packet"""
PACKET_START = bytes([0x46, 0xb9])
"""magic byte that ends a packet"""
PACKET_END = bytes([0x16])
"""magic byte for packets received from MCU"""
PACKET_MCU = bytes([0x68])
"""magic byte for packets sent by host"""
PACKET_HOST = bytes([0x6a])
"""block size for programming flash"""
PROGRAM_BLOCKSIZE = 128
def __init__(self, port, baud_handshake, baud_transfer):
self.port = port
self.baud_handshake = baud_handshake
self.baud_transfer = baud_transfer
self.mcu_magic = 0
self.mcu_clock_hz = 0.0
self.mcu_bsl_version = ""
self.options = None
def dump_packet(self, data, receive=True):
if DEBUG:
print("%s Packet data: " % ("<-" if receive else "->") +
" ".join(hex(x) for x in data), file=sys.stderr)
def modular_sum(self, data):
"""modular 16-bit sum"""
s = 0
for b in data: s += b
return s & 0xffff
def read_packet(self):
"""Read and check packet from MCU.
Reads a packet of data from the MCU and and do
validity and checksum checks on it.
Returns packet payload or None in case of an error.
"""
# read and check frame start magic
packet = bytes()
packet += self.ser.read(2)
if packet[0:2] != self.PACKET_START:
print("Wrong magic (%s), discarding packet!" %
packet[0:2], file=sys.stderr)
self.dump_packet(packet)
return None
# read direction and length
packet += self.ser.read(3)
if packet[2] != self.PACKET_MCU[0]:
print("Wrong direction (%s), discarding packet!" %
hex(packet[3]), file=sys.stderr)
self.dump_packet(packet)
return None
# read packet data
packet_len, = struct.unpack(">H", packet[3:5])
packet += self.ser.read(packet_len - 3)
# verify end code
if packet[packet_len+1] != self.PACKET_END[0]:
print("Wrong end code (%s), discarding packet!" %
hex(packet[packet_len+1]), file=sys.stderr)
self.dump_packet(packet)
return None
# verify checksum
packet_csum, = struct.unpack(">H", packet[packet_len-1:packet_len+1])
calc_csum = sum(packet[2:packet_len-1]) & 0xffff
if packet_csum != calc_csum:
print("Wrong checksum (%s, expected %s), discarding packet!" %
(hex(packet_csum), hex(calc_csum)), file=sys.stderr)
self.dump_packet(packet)
return None
self.dump_packet(packet, receive=True)
# payload only is returned
return packet[5:packet_len-1]
def write_packet(self, data):
"""Send packet to MCU.
Constructs a packet with supplied payload and sends it to the MCU.
"""
# frame start and direction magic
packet = bytes()
packet += self.PACKET_START
packet += self.PACKET_HOST
# packet length and payload
packet += struct.pack(">H", len(data) + 6)
packet += data
# checksum and end code
packet += struct.pack(">H", sum(packet[2:]) & 0xffff)
packet += self.PACKET_END
self.dump_packet(packet, receive=False)
self.ser.write(packet)
self.ser.flush()
def decode_status_packet(self, packet):
"""Decode status packet"""
self.mcu_magic, = struct.unpack(">H", packet[20:22])
freq_counter = 0
for i in range(8):
freq_counter += struct.unpack(">H", packet[1+2*i:3+2*i])[0]
freq_counter /= 8.0
self.mcu_clock_hz = (self.baud_handshake * freq_counter * 12.0) / 7.0
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))
def calculate_baud(self):
"""Calculate MCU baudrate setting.
Calculate appropriate baudrate settings for the MCU's UART,
according to clock frequency and requested baud rate.
"""
# baudrate is directly controlled by programming the MCU's BRT register
brt = 256 - round((self.mcu_clock_hz) / (self.baud_transfer * 16))
brt_csum = (2 * (256 - brt)) & 0xff
baud_actual = (self.mcu_clock_hz) / (16 * (256 - brt))
baud_error = (abs(self.baud_transfer - baud_actual) * 100.0) / self.baud_transfer
if baud_error > 5.0:
print("WARNING: baud rate error is %.2f%%. You may need to set a slower rate." %
baud_error, file=sys.stderr)
# IAP wait states (according to datasheet(s))
iap_wait = 0x80
if self.mcu_clock_hz < 1E6: iap_wait = 0x87
elif self.mcu_clock_hz < 2E6: iap_wait = 0x86
elif self.mcu_clock_hz < 3E6: iap_wait = 0x85
elif self.mcu_clock_hz < 6E6: iap_wait = 0x84
elif self.mcu_clock_hz < 12E6: iap_wait = 0x83
elif self.mcu_clock_hz < 20E6: iap_wait = 0x82
elif self.mcu_clock_hz < 24E6: iap_wait = 0x81
# MCU delay after switching baud rates
delay = 0x80
return brt, brt_csum, iap_wait, delay
def print_mcu_info(self):
"""Print MCU status information"""
print("Target magic: %s" % hex(self.mcu_magic))
print("Target frequency: %.3f MHz" % (self.mcu_clock_hz / 1E6))
print("Target bootloader version: %s" % self.mcu_bsl_version)
def connect(self):
"""Connect to MCU and initialize communication.
Set up serial port, send sync sequence and get part info.
"""
self.ser = serial.Serial(port=self.port, baudrate=self.baud_handshake,
parity=serial.PARITY_EVEN)
# send sync, and wait for MCU response
print("Waiting for MCU, please cycle power...", end="")
sys.stdout.flush()
while True:
self.ser.write(b"\x7f")
self.ser.flush()
time.sleep(0.015)
if self.ser.inWaiting() > 0: break
print("done")
# read status packet
status_packet = self.read_packet()
if status_packet == None or status_packet[0] != 0x50:
print("Error receiving status packet, aborting!", file=sys.stderr)
return False
self.decode_status_packet(status_packet)
self.print_mcu_info()
self.options = Stc12Option(status_packet[23:27])
self.options.print()
return True
def handshake(self):
"""Do baudrate handshake
Initate and do the (rather complicated) baudrate handshake.
"""
# start baudrate handshake
brt, brt_csum, iap, delay = self.calculate_baud()
print("Switching to %d baud..." % self.baud_transfer, end="")
sys.stdout.flush()
packet = bytes([0x50, 0x00, 0x00, 0x36, 0x01])
packet += struct.pack(">H", self.mcu_magic)
self.write_packet(packet)
response = self.read_packet()
if response == None or response[0] != 0x8f:
print("Error receiving handshake packet, aborting!", file=sys.stderr)
return False
# test new settings
print("testing...", end="")
sys.stdout.flush()
packet = bytes([0x8f, 0xc0, brt, 0x3f, brt_csum, delay, iap])
self.write_packet(packet)
time.sleep(0.2)
self.ser.baudrate = self.baud_transfer
response = self.read_packet()
self.ser.baudrate = self.baud_handshake
if response == None or response[0] != 0x8f:
print("Error receiving handshake packet, aborting!", file=sys.stderr)
return False
# switch to the settings
print("setting...", end="")
sys.stdout.flush()
packet = bytes([0x8e, 0xc0, brt, 0x3f, brt_csum, delay])
self.write_packet(packet)
time.sleep(0.2)
self.ser.baudrate = self.baud_transfer
response = self.read_packet()
if response == None or response[0] != 0x84:
print("Error receiving handshake packet, aborting!", file=sys.stderr)
return False
print("done")
return True
def erase_flash(self, erase_size, flash_size):
"""Erase the MCU's flash memory.
Erase the flash memory with a block-erase command.
"""
blks = (erase_size + 255) // 256
size = (flash_size + 255) // 256
print("Erasing %d blocks..." % blks)
packet = bytes([0x84, 0x00, 0x00, blks, 0x00, 0x00, size,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00])
for i in range(0x80, 0x0d, -1): packet += bytes([i])
self.write_packet(packet)
response = self.read_packet()
if response == None or response[0] != 0x00:
print("Error receiving erase response, aborting!", file=sys.stderr)
return False
return True
def program_flash(self, addr, data):
"""Program the MCU's flash memory.
Write data into flash memory, starting at the given address. The address
should be 128 byte aligned.
"""
print("Writing %d bytes..." % len(data), end="")
sys.stdout.flush()
for i in range(addr, addr+len(data), 128):
packet = bytes(3)
packet += struct.pack(">H", i)
packet += struct.pack(">H", self.PROGRAM_BLOCKSIZE)
packet += data[i-addr:i-addr+128]
while len(packet) < self.PROGRAM_BLOCKSIZE + 7: packet += b"\x00"
csum = sum(packet[7:]) & 0xff
self.write_packet(packet)
response = self.read_packet()
if response == None or response[0] != 0x00:
print("Error receiving program response packet, aborting!", file=sys.stderr)
return False
elif response[1] != csum:
print("Wrong checksum in program response (%s, expected %s), aborting!" %
(hex(response[1]), hex(csum)), file=sys.stderr)
print(".", end="")
sys.stdout.flush()
print()
packet = bytes([0x69, 0x00, 0x00, 0x36, 0x01])
packet += struct.pack(">H", self.mcu_magic)
self.write_packet(packet)
response = self.read_packet()
if response == None or response[0] != 0x8d:
print("Error receiving program finish response packet, aborting!", file=sys.stderr)
return False
print("Finished writing flash!")
return True
def set_option(self, name, value):
self.options.set_option(name, value)
def program_options(self):
#self.options.print()
print("Setting options...")
msr = self.options.get_msr()
packet = bytes([0x8d, msr[0], msr[1], msr[2], msr[3],
0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff])
packet += struct.pack(">I", int(self.mcu_clock_hz))
self.write_packet(packet)
response = self.read_packet()
if response == None or response[0] != 0x50:
print("Error receiving set options response packet, aborting!", file=sys.stderr)
return False
print("Target UID: %02x%02x%02x%02x%02x%02x%02x" %
(response[18], response[19], response[20], response[21],
response[22], response[23], response[24]))
return True
def disconnect(self):
"""Disconnect from MCU"""
# reset mcu
packet = bytes([0x82])
self.write_packet(packet)
self.ser.close()
print("Disconnected!")
class StcGal:
"""STC ISP flash tool frontend"""
def __init__(self, opts):
self.opts = opts
self.protocol = Stc12Protocol(opts.port, opts.handshake, opts.baud)
def run(self):
self.protocol.connect()
if opts.binary:
bindata = opts.binary.read()
if opts.option:
for o in opts.option:
k, v = o.split("=", 1)
self.protocol.set_option(k, v)
self.protocol.handshake()
self.protocol.erase_flash(len(bindata), 0xf0 * 256)
self.protocol.program_flash(0, bindata)
self.protocol.program_options()
self.protocol.disconnect()
if __name__ == "__main__":
# check arguments
parser = argparse.ArgumentParser(description="STC10/11/12 series MCU ISP flash tool")
parser.add_argument("binary", help="binary file to flash", type=argparse.FileType("rb"), nargs='?')
parser.add_argument("-p", "--port", help="serial port device", default="/dev/ttyUSB0")
parser.add_argument("-b", "--baud", help="transfer baud rate (default: 19200)", type=int, default=19200)
parser.add_argument("-l", "--handshake", help="handshake baud rate (default: 2400)", type=int, default=2400)
parser.add_argument("-o", "--option", help="set option (can be used multiple times)", action="append")
opts = parser.parse_args()
# run programmer
gal = StcGal(opts)
sys.exit(gal.run())