stm32isp 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stm32isp/__init__.py +1 -0
- stm32isp/stm32isp.py +418 -0
- stm32isp-0.8.0.dist-info/LICENSE +21 -0
- stm32isp-0.8.0.dist-info/METADATA +128 -0
- stm32isp-0.8.0.dist-info/RECORD +8 -0
- stm32isp-0.8.0.dist-info/WHEEL +5 -0
- stm32isp-0.8.0.dist-info/entry_points.txt +2 -0
- stm32isp-0.8.0.dist-info/top_level.txt +1 -0
stm32isp/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .stm32isp import _main
|
stm32isp/stm32isp.py
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# ===================================================================================
|
|
3
|
+
# Project: stm32isp - Programming Tool for some STM32 Microcontrollers
|
|
4
|
+
# Version: v0.8
|
|
5
|
+
# Year: 2023
|
|
6
|
+
# Author: Stefan Wagner
|
|
7
|
+
# Github: https://github.com/wagiminator
|
|
8
|
+
# License: MIT License
|
|
9
|
+
# ===================================================================================
|
|
10
|
+
#
|
|
11
|
+
# Description:
|
|
12
|
+
# ------------
|
|
13
|
+
# Python tool for flashing some entry-level STM32 microcontrollers via USB-to-serial
|
|
14
|
+
# converter utilizing the factory built-in UART boot loader. Supported devices:
|
|
15
|
+
# - STM32C011/031
|
|
16
|
+
# - STM32F03xx4/6
|
|
17
|
+
# - STM32G03x/04x
|
|
18
|
+
# - STM32L01x/02x
|
|
19
|
+
# !!! THIS TOOL IS IN AN EARLY STAGE OF DEVELOPMENT !!!
|
|
20
|
+
#
|
|
21
|
+
# Dependencies:
|
|
22
|
+
# -------------
|
|
23
|
+
# - pyserial
|
|
24
|
+
#
|
|
25
|
+
# Operating Instructions:
|
|
26
|
+
# -----------------------
|
|
27
|
+
# You need to install PySerial to use stm32isp.
|
|
28
|
+
# Install it via "python3 -m pip install pyserial".
|
|
29
|
+
# You may need to install a driver for your USB-to-serial converter.
|
|
30
|
+
#
|
|
31
|
+
# On some STM32, the BOOT0 pin is disabled by default. As soon as the chip is brand
|
|
32
|
+
# new and/or the main flash memory is deleted, this is not a problem, the embedded
|
|
33
|
+
# bootloader will automatically start. Using this stm32isp tool will automatically
|
|
34
|
+
# activate the BOOT0 pin so that it can also be used in the following. However, if
|
|
35
|
+
# the chip has already been written to before using a different software tool, it
|
|
36
|
+
# is likely that the bootloader can no longer be activated via the BOOT0 pin. In
|
|
37
|
+
# this case, the bit nBOOT_SEL in the User Option Bytes must be deleted (set to 0)
|
|
38
|
+
# using an SWD programmer (e.g. ST-Link) and appropriate software.
|
|
39
|
+
#
|
|
40
|
+
# Connect your USB-to-serial converter to your MCU:
|
|
41
|
+
# USB2SERIAL STM32C011/031
|
|
42
|
+
# RXD <--- PA9 (PA11)
|
|
43
|
+
# TXD ---> PA10 (PA12)
|
|
44
|
+
# VCC ---> VCC
|
|
45
|
+
# GND ---> GND
|
|
46
|
+
#
|
|
47
|
+
# USB2SERIAL STM32F03xx4/6
|
|
48
|
+
# RXD <--- PA9 or PA14
|
|
49
|
+
# TXD ---> PA10 or PA15
|
|
50
|
+
# VCC ---> VCC
|
|
51
|
+
# GND ---> GND
|
|
52
|
+
#
|
|
53
|
+
# USB2SERIAL STM32G03x/04x
|
|
54
|
+
# RXD <--- PA2 or PA9
|
|
55
|
+
# TXD ---> PA3 or PA10
|
|
56
|
+
# VCC ---> VCC
|
|
57
|
+
# GND ---> GND
|
|
58
|
+
#
|
|
59
|
+
# USB2SERIAL STM32L01x/02x
|
|
60
|
+
# RXD <--- PA2 or PA9
|
|
61
|
+
# TXD ---> PA3 or PA10
|
|
62
|
+
# VCC ---> VCC
|
|
63
|
+
# GND ---> GND
|
|
64
|
+
#
|
|
65
|
+
# Set your MCU to boot mode by using the following method:
|
|
66
|
+
# - Connect your USB-to-serial converter to your USB port. Pull BOOT0 pin (PA14)
|
|
67
|
+
# to VCC, then pull nRST shortly to GND (or press and hold the BOOT button,
|
|
68
|
+
# then press and release the RESET button and then release the BOOT button, if
|
|
69
|
+
# your board has them).
|
|
70
|
+
#
|
|
71
|
+
# Run "python3 stm32isp.py -f firmware.bin".
|
|
72
|
+
|
|
73
|
+
# If the PID/VID of the USB-to-Serial converter is known, it can be defined here,
|
|
74
|
+
# which can make the auto-detection a lot faster. If not, comment out or delete.
|
|
75
|
+
#ST_VID = '1A86'
|
|
76
|
+
#ST_PID = '7523'
|
|
77
|
+
|
|
78
|
+
# Define BAUD rate here, range: 1200 - 115200, default: 115200
|
|
79
|
+
ST_BAUD = 115200
|
|
80
|
+
|
|
81
|
+
# Libraries
|
|
82
|
+
import sys
|
|
83
|
+
import argparse
|
|
84
|
+
import serial
|
|
85
|
+
from serial import Serial
|
|
86
|
+
from serial.tools.list_ports import comports
|
|
87
|
+
|
|
88
|
+
# ===================================================================================
|
|
89
|
+
# Main Function
|
|
90
|
+
# ===================================================================================
|
|
91
|
+
|
|
92
|
+
def _main():
|
|
93
|
+
# Parse command line arguments
|
|
94
|
+
parser = argparse.ArgumentParser(description='Minimal command line interface for STM32 ISP')
|
|
95
|
+
parser.add_argument('-u', '--unlock', action='store_true', help='unlock chip (remove read protection)')
|
|
96
|
+
parser.add_argument('-l', '--lock', action='store_true', help='lock chip (set read protection)')
|
|
97
|
+
parser.add_argument('-e', '--erase', action='store_true', help='perform a chip erase (implied with -f)')
|
|
98
|
+
parser.add_argument('-f', '--flash', help='write BIN file to flash and verify')
|
|
99
|
+
args = parser.parse_args(sys.argv[1:])
|
|
100
|
+
|
|
101
|
+
# Check arguments
|
|
102
|
+
if not any( (args.unlock, args.lock, args.erase, args.flash) ):
|
|
103
|
+
print('No arguments - no action!')
|
|
104
|
+
sys.exit(0)
|
|
105
|
+
|
|
106
|
+
# Establish connection to MCU via USB-to-serial converter
|
|
107
|
+
try:
|
|
108
|
+
print('Connecting to MCU via USB-to-serial converter ...')
|
|
109
|
+
isp = Programmer()
|
|
110
|
+
print('SUCCESS: Connection established via', isp.port + '.')
|
|
111
|
+
except Exception as ex:
|
|
112
|
+
sys.stderr.write('ERROR: ' + str(ex) + '!\n')
|
|
113
|
+
sys.exit(1)
|
|
114
|
+
|
|
115
|
+
# Performing actions
|
|
116
|
+
try:
|
|
117
|
+
# Get chip info
|
|
118
|
+
print('Getting chip info ...')
|
|
119
|
+
isp.readinfo()
|
|
120
|
+
print('SUCCESS: Found', isp.chipname, 'with bootloader v' + isp.verstr + '.')
|
|
121
|
+
|
|
122
|
+
# Check if chip is locked, unlock if so; erase chip
|
|
123
|
+
if not isp.readoption():
|
|
124
|
+
print('Chip is locked, unlocking and erasing ...')
|
|
125
|
+
isp.unlock()
|
|
126
|
+
isp.sendcommand(ST_SYNCH)
|
|
127
|
+
isp.readoption()
|
|
128
|
+
print('SUCCESS: Chip is unlocked and erased.')
|
|
129
|
+
else:
|
|
130
|
+
if args.unlock:
|
|
131
|
+
print('INFO: Chip is already unlocked.')
|
|
132
|
+
if (args.erase) or (args.flash is not None):
|
|
133
|
+
print('Performing chip erase ...')
|
|
134
|
+
isp.erase()
|
|
135
|
+
print('SUCCESS: Chip is erased.')
|
|
136
|
+
|
|
137
|
+
# Flash binary file
|
|
138
|
+
if args.flash is not None:
|
|
139
|
+
print('Flashing', args.flash, 'to', isp.chipname, '...')
|
|
140
|
+
with open(args.flash, 'rb') as f: data = f.read()
|
|
141
|
+
isp.writeflash(ST_CODE_ADDR, data)
|
|
142
|
+
print('Verifying ...')
|
|
143
|
+
isp.verifyflash(ST_CODE_ADDR, data)
|
|
144
|
+
print('SUCCESS:', len(data), 'bytes written and verified.')
|
|
145
|
+
|
|
146
|
+
# Enable BOOT0 pin in OPTION bytes (nBOOT_SEL = 0) for STM32C/G
|
|
147
|
+
if (isp.pid in (0x443, 0x453, 0x466)) and isp.checkbootpin():
|
|
148
|
+
print('Enabling BOOT0 pin in OPTION bytes ...')
|
|
149
|
+
isp.enablebootpin()
|
|
150
|
+
print('SUCCESS: Modified OPTION bytes written.')
|
|
151
|
+
isp.close()
|
|
152
|
+
|
|
153
|
+
# Lock chip (set readout protection)
|
|
154
|
+
if args.lock:
|
|
155
|
+
print('Locking chip ...')
|
|
156
|
+
isp.lock()
|
|
157
|
+
print('SUCCESS: Chip is locked.')
|
|
158
|
+
print('INFO: A power-on reset may need to be performed.')
|
|
159
|
+
isp.close()
|
|
160
|
+
|
|
161
|
+
# Run firmware
|
|
162
|
+
if isp.isOpen():
|
|
163
|
+
isp.run()
|
|
164
|
+
|
|
165
|
+
except Exception as ex:
|
|
166
|
+
sys.stderr.write('ERROR: ' + str(ex) + '!\n')
|
|
167
|
+
isp.close()
|
|
168
|
+
sys.exit(1)
|
|
169
|
+
|
|
170
|
+
print('DONE.')
|
|
171
|
+
sys.exit(0)
|
|
172
|
+
|
|
173
|
+
# ===================================================================================
|
|
174
|
+
# Programmer Class
|
|
175
|
+
# ===================================================================================
|
|
176
|
+
|
|
177
|
+
class Programmer(Serial):
|
|
178
|
+
def __init__(self):
|
|
179
|
+
# BAUD rate: 1200 - 115200bps (default: 115200), will be auto-detected
|
|
180
|
+
# Data frame: 1 start bit, 8 data bit, 1 parity bit set to even, 1 stop bit
|
|
181
|
+
super().__init__(baudrate = ST_BAUD, parity = serial.PARITY_EVEN, timeout = 1)
|
|
182
|
+
self.identify()
|
|
183
|
+
|
|
184
|
+
# Identify port of programmer and enter programming mode
|
|
185
|
+
def identify(self):
|
|
186
|
+
for p in comports():
|
|
187
|
+
if (('ST_VID' not in globals()) or (ST_VID in p.hwid)) and (('ST_PID' not in globals()) or (ST_PID in p.hwid)):
|
|
188
|
+
self.port = p.device
|
|
189
|
+
try:
|
|
190
|
+
self.open()
|
|
191
|
+
except:
|
|
192
|
+
continue
|
|
193
|
+
self.reset_input_buffer()
|
|
194
|
+
self.write([ST_SYNCH])
|
|
195
|
+
if not self.checkreply():
|
|
196
|
+
self.close()
|
|
197
|
+
continue
|
|
198
|
+
return
|
|
199
|
+
raise Exception('No MCU in boot mode found')
|
|
200
|
+
|
|
201
|
+
# Send command
|
|
202
|
+
def sendcommand(self, command):
|
|
203
|
+
self.write([command, command ^ 0xff])
|
|
204
|
+
if not self.checkreply():
|
|
205
|
+
raise Exception('Device has not acknowledged the command 0x%02x' % command)
|
|
206
|
+
|
|
207
|
+
# Send address
|
|
208
|
+
def sendaddress(self, addr):
|
|
209
|
+
stream = addr.to_bytes(4, byteorder='big')
|
|
210
|
+
parity = 0x00
|
|
211
|
+
for x in range(4):
|
|
212
|
+
parity ^= stream[x]
|
|
213
|
+
self.write(stream)
|
|
214
|
+
self.write([parity])
|
|
215
|
+
if not self.checkreply():
|
|
216
|
+
raise Exception('Failed to send address')
|
|
217
|
+
|
|
218
|
+
# Check if device acknowledged
|
|
219
|
+
def checkreply(self):
|
|
220
|
+
reply = self.read(1)
|
|
221
|
+
return (len(reply) == 1 and reply[0] == ST_REPLY_ACK)
|
|
222
|
+
|
|
223
|
+
#--------------------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
# Read info stream
|
|
226
|
+
def readinfostream(self, command):
|
|
227
|
+
self.sendcommand(command)
|
|
228
|
+
size = self.read(1)[0]
|
|
229
|
+
stream = self.read(size + 1)
|
|
230
|
+
if not self.checkreply():
|
|
231
|
+
raise Exception('Failed to read info')
|
|
232
|
+
return stream
|
|
233
|
+
|
|
234
|
+
# Get chip info
|
|
235
|
+
def readinfo(self):
|
|
236
|
+
self.info = self.readinfostream(ST_CMD_GET)
|
|
237
|
+
self.ver = self.info[0]
|
|
238
|
+
self.verstr = '%x.%x' % (self.ver >> 4, self.ver & 7)
|
|
239
|
+
self.cmds = list(self.info[1:])
|
|
240
|
+
self.pid = int.from_bytes(self.readinfostream(ST_CMD_PID), byteorder='big')
|
|
241
|
+
self.device = None
|
|
242
|
+
|
|
243
|
+
# Find device in dictionary
|
|
244
|
+
for d in DEVICES:
|
|
245
|
+
if d['id'] == self.pid:
|
|
246
|
+
self.device = d
|
|
247
|
+
if self.device is None:
|
|
248
|
+
raise Exception('Unsupported chip (ID: 0x%04x)' % self.chipid)
|
|
249
|
+
|
|
250
|
+
# Set parameters according to dictionary
|
|
251
|
+
self.chipname = self.device['name']
|
|
252
|
+
self.option_address = self.device['opt_addr']
|
|
253
|
+
|
|
254
|
+
# Read OPTION bytes
|
|
255
|
+
def readoption(self):
|
|
256
|
+
try:
|
|
257
|
+
self.option = list(self.readflash(self.option_address, 8))
|
|
258
|
+
except:
|
|
259
|
+
return False
|
|
260
|
+
if self.pid == 0x457:
|
|
261
|
+
self.optionbytes = int.from_bytes(self.option[0:2], byteorder='little') \
|
|
262
|
+
| (int.from_bytes(self.option[4:6], byteorder='little') << 16)
|
|
263
|
+
else:
|
|
264
|
+
self.optionbytes = int.from_bytes(self.option[:4], byteorder='little')
|
|
265
|
+
return True
|
|
266
|
+
|
|
267
|
+
# Check if BOOT0 pin ist disabled in OPTION bytes (STM32C/G only)
|
|
268
|
+
def checkbootpin(self):
|
|
269
|
+
return ((self.option[3] & 0x01) == 0x01)
|
|
270
|
+
|
|
271
|
+
# Enable BOOT0 pin in OPTION bytes (nBOOT_SEL = 0), STM32C/G only
|
|
272
|
+
def enablebootpin(self):
|
|
273
|
+
if self.pid in (0x443, 0x453, 0x466):
|
|
274
|
+
self.option[3] = (self.option[3] & 0b11111000) | 0b00000110
|
|
275
|
+
for x in range(4):
|
|
276
|
+
self.option[x+4] = self.option[x] ^ 0xff
|
|
277
|
+
self.writeflash(self.option_address, self.option)
|
|
278
|
+
|
|
279
|
+
# Unlock chip (remove readout protection), erase and reset
|
|
280
|
+
def unlock(self):
|
|
281
|
+
self.sendcommand(ST_CMD_R_UNLOCK)
|
|
282
|
+
if not self.checkreply():
|
|
283
|
+
raise Exception('Failed to unlock chip')
|
|
284
|
+
|
|
285
|
+
# Lock chip (set readout protection)
|
|
286
|
+
def lock(self):
|
|
287
|
+
self.sendcommand(ST_CMD_R_LOCK)
|
|
288
|
+
if not self.checkreply():
|
|
289
|
+
raise Exception('Failed to lock chip')
|
|
290
|
+
|
|
291
|
+
# Start firmware and disconnect
|
|
292
|
+
def run(self):
|
|
293
|
+
self.sendcommand(ST_CMD_GO)
|
|
294
|
+
self.sendaddress(ST_CODE_ADDR)
|
|
295
|
+
self.close()
|
|
296
|
+
|
|
297
|
+
#--------------------------------------------------------------------------------
|
|
298
|
+
|
|
299
|
+
# Erase whole chip
|
|
300
|
+
def erase(self):
|
|
301
|
+
if self.pid == 0x457:
|
|
302
|
+
self.erasepages(128)
|
|
303
|
+
return
|
|
304
|
+
elif ST_CMD_ERASE_X in self.cmds:
|
|
305
|
+
self.sendcommand(ST_CMD_ERASE_X)
|
|
306
|
+
self.write(b'\xff\xff\x00')
|
|
307
|
+
else:
|
|
308
|
+
self.sendcommand(ST_CMD_ERASE)
|
|
309
|
+
self.write(b'\xff\x00')
|
|
310
|
+
if not self.checkreply():
|
|
311
|
+
raise Exception('Failed to erase chip')
|
|
312
|
+
|
|
313
|
+
# Erase number of pages
|
|
314
|
+
def erasepages(self, pagecount):
|
|
315
|
+
pagecount -= 1
|
|
316
|
+
self.sendcommand(ST_CMD_ERASE_X)
|
|
317
|
+
self.write([pagecount >> 16, pagecount & 0xff])
|
|
318
|
+
parity = (pagecount >> 16) ^ (pagecount & 0xff)
|
|
319
|
+
for x in range(pagecount + 1):
|
|
320
|
+
self.write([x >> 16, x & 0xff])
|
|
321
|
+
parity ^= (x >> 16) ^ (x & 0xff)
|
|
322
|
+
self.write([parity])
|
|
323
|
+
if not self.checkreply():
|
|
324
|
+
raise Exception('Failed to erase pages')
|
|
325
|
+
|
|
326
|
+
# Read flash
|
|
327
|
+
def readflash(self, addr, size):
|
|
328
|
+
data = bytes()
|
|
329
|
+
while size > 0:
|
|
330
|
+
blocksize = min(size, ST_PAGE_SIZE)
|
|
331
|
+
self.sendcommand(ST_CMD_READ)
|
|
332
|
+
self.sendaddress(addr)
|
|
333
|
+
self.sendcommand(blocksize - 1)
|
|
334
|
+
data += self.read(blocksize)
|
|
335
|
+
addr += blocksize
|
|
336
|
+
size -= blocksize
|
|
337
|
+
return data
|
|
338
|
+
|
|
339
|
+
# Write to flash
|
|
340
|
+
def writeflash(self, addr, data):
|
|
341
|
+
size = len(data)
|
|
342
|
+
while size > 0:
|
|
343
|
+
blocksize = min(size, ST_PAGE_SIZE)
|
|
344
|
+
block = data[:blocksize]
|
|
345
|
+
parity = blocksize - 1
|
|
346
|
+
for x in range(blocksize):
|
|
347
|
+
parity ^= block[x]
|
|
348
|
+
self.sendcommand(ST_CMD_WRITE)
|
|
349
|
+
self.sendaddress(addr)
|
|
350
|
+
self.write([blocksize - 1])
|
|
351
|
+
self.write(block)
|
|
352
|
+
self.write([parity])
|
|
353
|
+
if not self.checkreply():
|
|
354
|
+
raise Exception('Failed to write to address 0x%08x' % addr)
|
|
355
|
+
data = data[blocksize:]
|
|
356
|
+
addr += blocksize
|
|
357
|
+
size -= blocksize
|
|
358
|
+
|
|
359
|
+
# Verify flash
|
|
360
|
+
def verifyflash(self, addr, data):
|
|
361
|
+
flash = self.readflash(addr, len(data))
|
|
362
|
+
if set(flash) != set(data):
|
|
363
|
+
raise Exception('Verification failed')
|
|
364
|
+
|
|
365
|
+
# Pad data
|
|
366
|
+
def paddata(self, data, pagesize):
|
|
367
|
+
if (len(data) % pagesize) > 0:
|
|
368
|
+
data += b'\xff' * (pagesize - (len(data) % pagesize))
|
|
369
|
+
return data
|
|
370
|
+
|
|
371
|
+
# ===================================================================================
|
|
372
|
+
# Device Constants
|
|
373
|
+
# ===================================================================================
|
|
374
|
+
|
|
375
|
+
# Device and Memory constants
|
|
376
|
+
ST_PAGE_SIZE = 256
|
|
377
|
+
ST_FLASH_ADDR = 0x08000000
|
|
378
|
+
ST_CODE_ADDR = 0x08000000
|
|
379
|
+
ST_SRAM_ADDR = 0x20000000
|
|
380
|
+
|
|
381
|
+
# Command codes
|
|
382
|
+
ST_CMD_GET = 0x00
|
|
383
|
+
ST_CMD_VER = 0x01
|
|
384
|
+
ST_CMD_PID = 0x02
|
|
385
|
+
ST_CMD_READ = 0x11
|
|
386
|
+
ST_CMD_WRITE = 0x31
|
|
387
|
+
ST_CMD_ERASE = 0x43
|
|
388
|
+
ST_CMD_ERASE_X = 0x44
|
|
389
|
+
ST_CMD_GO = 0x21
|
|
390
|
+
ST_CMD_W_LOCK = 0x63
|
|
391
|
+
ST_CMD_W_UNLOCK = 0x73
|
|
392
|
+
ST_CMD_R_LOCK = 0x82
|
|
393
|
+
ST_CMD_R_UNLOCK = 0x92
|
|
394
|
+
|
|
395
|
+
# Reply codes
|
|
396
|
+
ST_REPLY_ACK = 0x79
|
|
397
|
+
ST_REPLY_NACK = 0x1f
|
|
398
|
+
ST_REPLY_BUSY = 0xaa
|
|
399
|
+
|
|
400
|
+
# Other codes
|
|
401
|
+
ST_SYNCH = 0x7f
|
|
402
|
+
|
|
403
|
+
# ===================================================================================
|
|
404
|
+
# Device definitions
|
|
405
|
+
# ===================================================================================
|
|
406
|
+
|
|
407
|
+
DEVICES = [
|
|
408
|
+
{'name': 'STM32C011xx', 'id': 0x443, 'opt_addr': 0x1fff7800, 'opt_default': 0xfffffeaa},
|
|
409
|
+
{'name': 'STM32C031xx', 'id': 0x453, 'opt_addr': 0x1fff7800, 'opt_default': 0xfffffeaa},
|
|
410
|
+
{'name': 'STM32G03x/04x', 'id': 0x466, 'opt_addr': 0x1fff7800, 'opt_default': 0xfffffeaa},
|
|
411
|
+
{'name': 'STM32L01x/02x', 'id': 0x457, 'opt_addr': 0x1ff80000, 'opt_default': 0x807000aa},
|
|
412
|
+
{'name': 'STM32F03xx4/6', 'id': 0x444, 'opt_addr': 0x1ffff800, 'opt_default': 0x00ff55aa}
|
|
413
|
+
]
|
|
414
|
+
|
|
415
|
+
# ===================================================================================
|
|
416
|
+
|
|
417
|
+
if __name__ == "__main__":
|
|
418
|
+
_main()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Stefan Wagner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: stm32isp
|
|
3
|
+
Version: 0.8.0
|
|
4
|
+
Summary: Programming Tool for some STM32 Microcontrollers
|
|
5
|
+
Home-page: https://github.com/wagiminator/MCU-Flash-Tools
|
|
6
|
+
Author: Stefan Wagner
|
|
7
|
+
Author-email: Stefan Wagner <wagiminator@web.de>
|
|
8
|
+
Maintainer-email: Stefan Wagner <wagiminator@web.de>
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2023 Stefan Wagner
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
Project-URL: Homepage, https://wagiminator.github.io/
|
|
31
|
+
Project-URL: Repository, https://github.com/wagiminator/MCU-Flash-Tools
|
|
32
|
+
Project-URL: Issues, https://github.com/wagiminator/MCU-Flash-Tools/issues
|
|
33
|
+
Keywords: STM32,STM32C011,STM32F030,STM32G030,STM32L011,microcontroller
|
|
34
|
+
Classifier: Development Status :: 4 - Beta
|
|
35
|
+
Classifier: Programming Language :: Python :: 3
|
|
36
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
37
|
+
Classifier: Operating System :: OS Independent
|
|
38
|
+
Classifier: Intended Audience :: Developers
|
|
39
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
40
|
+
Requires-Python: >=3.8
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
License-File: LICENSE
|
|
43
|
+
Requires-Dist: pyserial
|
|
44
|
+
|
|
45
|
+
# stm32isp - Programming Tool for some STM32 Microcontrollers
|
|
46
|
+
## Description
|
|
47
|
+
With this tool, some entry-level STM32 microcontrollers can be flashed via a simple USB-to-serial converter by utilizing the factory built-in UART bootloader. It currently supports the following devices:
|
|
48
|
+
- STM32C011/031
|
|
49
|
+
- STM32F03xx4/6
|
|
50
|
+
- STM32G03x/04x
|
|
51
|
+
- STM32L01x/02x
|
|
52
|
+
|
|
53
|
+
## Preparations
|
|
54
|
+
If necessary, a driver for the USB-to-serial converter used must be installed.
|
|
55
|
+
|
|
56
|
+
## Installation
|
|
57
|
+
- Install [Python3](https://www.pythontutorial.net/getting-started/install-python/).
|
|
58
|
+
- Install [pip](https://pip.pypa.io/en/stable/installation/).
|
|
59
|
+
- Install stm32isp:
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
pip install stm32isp
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Usage
|
|
66
|
+
Connect your USB-to-serial converter to your STM32 MCU as follows:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
USB2SERIAL STM32C011/031
|
|
70
|
+
+--------+ +------------+
|
|
71
|
+
| RXD| <--- |PA9 (PA11) |
|
|
72
|
+
| TXD| ---> |PA10 (PA12) |
|
|
73
|
+
| 3V3| ---> |VDD (3V3) |
|
|
74
|
+
| GND| ---> |GND |
|
|
75
|
+
+--------+ +------------+
|
|
76
|
+
|
|
77
|
+
USB2SERIAL STM32F03xx4/6
|
|
78
|
+
+--------+ +------------+
|
|
79
|
+
| RXD| <--- |PA9 or PA14|
|
|
80
|
+
| TXD| ---> |PA10 or PA15|
|
|
81
|
+
| 3V3| ---> |VDD (3V3) |
|
|
82
|
+
| GND| ---> |GND |
|
|
83
|
+
+--------+ +------------+
|
|
84
|
+
|
|
85
|
+
USB2SERIAL STM32G03x/04x
|
|
86
|
+
+--------+ +------------+
|
|
87
|
+
| RXD| <--- |PA2 or PA9 |
|
|
88
|
+
| TXD| ---> |PA3 or PA10 |
|
|
89
|
+
| 3V3| ---> |VDD (3V3) |
|
|
90
|
+
| GND| ---> |GND |
|
|
91
|
+
+--------+ +------------+
|
|
92
|
+
|
|
93
|
+
USB2SERIAL STM32L01x/02x
|
|
94
|
+
+--------+ +------------+
|
|
95
|
+
| RXD| <--- |PA2 or PA9 |
|
|
96
|
+
| TXD| ---> |PA3 or PA10 |
|
|
97
|
+
| 3V3| ---> |VDD (3V3) |
|
|
98
|
+
| GND| ---> |GND |
|
|
99
|
+
+--------+ +------------+
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Set your MCU to boot mode by using ONE of the following method:
|
|
103
|
+
- Disconnect your board from all power supplies, pull BOOT0 pin to VCC (or press and hold the BOOT button if your board has one), then connect the board to your USB port. The BOOT button can be released now.
|
|
104
|
+
- Connect your USB-to-serial converter to your USB port. Pull BOOT0 pin to VCC, then pull nRST shortly to GND (or press and hold the BOOT button, then press and release the RESET button and then release the BOOT button, if your board has them).
|
|
105
|
+
|
|
106
|
+
On STM32G03x/04x microcontrollers, the BOOT0 pin is initially disabled. When the chip is brand new or the main flash memory is erased, this isn't an issue as the embedded bootloader automatically kicks in. By using the stm32isp tool, the BOOT0 pin will be activated for subsequent use. However, if the chip has been previously programmed using a different software tool, the bootloader might not be accessible through the BOOT0 pin anymore. In such cases, the nBOOT_SEL bit in the User Option Bytes must be cleared (set to 0) using an SWD programmer like ST-Link and the appropriate software.
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
Usage: stm32isp [-h] [-u] [-l] [-e] [-f FLASH]
|
|
110
|
+
|
|
111
|
+
Optional arguments:
|
|
112
|
+
-h, --help show this help message and exit
|
|
113
|
+
-u, --unlock unlock chip (remove read protection)
|
|
114
|
+
-l, --lock lock chip (set read protection)
|
|
115
|
+
-e, --erase perform chip erase (implied with -f)
|
|
116
|
+
-f FLASH, --flash FLASH write BIN file to flash and verify
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
stm32isp -f firmware.bin
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Links
|
|
123
|
+
- [MCU Flash Tools](https://github.com/wagiminator/MCU-Flash-Tools)
|
|
124
|
+
- [MCU Templates](https://github.com/wagiminator/MCU-Templates)
|
|
125
|
+
- [MCU Development Boards](https://github.com/wagiminator/Development-Boards)
|
|
126
|
+
- [AVR Development Boards](https://github.com/wagiminator/AVR-Development-Boards)
|
|
127
|
+
- [AVR Programmers](https://github.com/wagiminator/AVR-Programmer)
|
|
128
|
+
- [SAMD Development Boards](https://github.com/wagiminator/SAMD-Development-Boards)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
stm32isp/__init__.py,sha256=Bh6Qq09dlS7NyC3YsfgRld6Jg8grZHBLROxjlFjt14Q,27
|
|
2
|
+
stm32isp/stm32isp.py,sha256=AGZgDaag8PbnzhmZ1LOeEQOhgcM4mbnSH6r0w_SYb6A,15335
|
|
3
|
+
stm32isp-0.8.0.dist-info/LICENSE,sha256=kph2e2d1k_-VvrU6YbwdbUJ2GKXsFgBGCSym5wVv2j8,1089
|
|
4
|
+
stm32isp-0.8.0.dist-info/METADATA,sha256=v1W6gjfeb8oENbfkJK0Ch_u7NixPXcasbn-vwJaNO2g,5809
|
|
5
|
+
stm32isp-0.8.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
|
|
6
|
+
stm32isp-0.8.0.dist-info/entry_points.txt,sha256=KrPwmj9BZ7OgNdmaoWGFSce4I2Q4zZcdLMUJ2V9A7kk,44
|
|
7
|
+
stm32isp-0.8.0.dist-info/top_level.txt,sha256=ymspw44mJvCL5yOuXL5c6ExSZ6oHZB5qumMZW9wtRAI,9
|
|
8
|
+
stm32isp-0.8.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
stm32isp
|