Skip to content

Commit 2bef50c

Browse files
committed
getting close to done, save work!
added extended address support file state such as line number, ext address, and EOF detection for early-quit init now takes an spi bus object linted examples
1 parent 314f084 commit 2bef50c

File tree

8 files changed

+656
-88
lines changed

8 files changed

+656
-88
lines changed

.pylintrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ max-attributes=11
405405
max-bool-expr=5
406406

407407
# Maximum number of branch for function / method body
408-
max-branches=12
408+
max-branches=15
409409

410410
# Maximum number of locals for function / method body
411411
max-locals=15

adafruit_avrprog.py

Lines changed: 77 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,10 @@
3333
__version__ = "0.0.0-auto.0"
3434
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AVRprog.git"
3535

36-
import busio
3736
from digitalio import Direction, DigitalInOut
3837

3938
_SLOW_CLOCK = 100000
40-
_FAST_CLOCK = 2000000
39+
_FAST_CLOCK = 1000000
4140

4241
class AVRprog:
4342
"""
@@ -47,25 +46,14 @@ class AVRprog:
4746
"""
4847
_spi = None
4948
_rst = None
50-
_mosi = None
51-
_miso = None
52-
_sck = None
5349

54-
def init(self, sck_pin, mosi_pin, miso_pin, rst_pin):
50+
def init(self, spi_bus, rst_pin):
5551
"""
5652
Initialize the programmer with SPI pins that will be used to
5753
communicate with the chip. Currently only hardware-SPI pins are
5854
supported!
5955
"""
60-
self._spi = busio.SPI(sck_pin, mosi_pin, miso_pin)
61-
#except:
62-
# pins = [DigitalInOut(p) for p in (sck_pin, mosi_pin, miso_pin)]
63-
# self._sck, self._mosi, self._miso = pins
64-
# self._sck.direction = Direction.OUTPUT
65-
# self._sck.value = False
66-
# self._mosi.direction = Direction.OUTPUT
67-
# self._miso.direction = Direction.INPUT
68-
56+
self._spi = spi_bus
6957
self._rst = DigitalInOut(rst_pin)
7058
self._rst.direction = Direction.OUTPUT
7159
self._rst.value = True
@@ -99,28 +87,33 @@ def program_file(self, chip, file_name, verbose=False, verify=True):
9987
self.erase_chip()
10088

10189
self.begin()
102-
hexfile = open(file_name, 'r')
90+
91+
# create a file state dictionary
92+
file_state = {'line': 0, 'ext_addr': 0, 'eof': False}
93+
file_state['f'] = open(file_name, 'r')
10394

10495
page_size = chip['page_size']
10596

10697
for page_addr in range(0, chip['flash_size'], page_size):
107-
#print("Programming page $%04X" % page_addr)
98+
if verbose:
99+
print("Programming page $%04X..." % page_addr, end="")
108100
page_buffer = bytearray(page_size)
109101
for b in range(page_size):
110102
page_buffer[b] = 0xFF # make an empty page
111103

112-
read_hex_page(hexfile, page_addr, page_size, page_buffer)
104+
read_hex_page(file_state, page_addr, page_size, page_buffer)
113105

114106
if all([v == 0xFF for v in page_buffer]):
115-
#print("Skipping empty page")
107+
if verbose:
108+
print("skipping")
116109
continue
117110

118-
if verbose:
119-
print("Programming page @ $%04X" % (page_addr))
120111
#print("From HEX file: ", page_buffer)
121112
self._flash_page(bytearray(page_buffer), page_addr, page_size)
122113

123114
if not verify:
115+
if verbose:
116+
print("done!")
124117
continue
125118

126119
if verbose:
@@ -137,7 +130,10 @@ def program_file(self, chip, file_name, verbose=False, verify=True):
137130
self.end()
138131
return False
139132

140-
hexfile.close()
133+
if file_state['eof']:
134+
break # we're done, bail!
135+
136+
file_state['f'].close()
141137
self.end()
142138
return True
143139

@@ -149,21 +145,25 @@ def verify_file(self, chip, file_name, verbose=False):
149145
if not self.verify_sig(chip):
150146
raise RuntimeError("Signature read failure")
151147

152-
hexfile = open(file_name, 'r')
148+
# create a file state dictionary
149+
file_state = {'line': 0, 'ext_addr': 0, 'eof': False}
150+
file_state['f'] = open(file_name, 'r')
151+
153152
page_size = chip['page_size']
154153
self.begin()
155-
for page_addr in range(0, chip['flash_size'], page_size):
154+
for page_addr in range(0x0, chip['flash_size'], page_size):
156155
page_buffer = bytearray(page_size)
157156
for b in range(page_size):
158157
page_buffer[b] = 0xFF # make an empty page
159158

160-
read_hex_page(hexfile, page_addr, page_size, page_buffer)
159+
read_hex_page(file_state, page_addr, page_size, page_buffer)
161160

162161
if verbose:
163162
print("Verifying page @ $%04X" % page_addr)
164163
read_buffer = bytearray(page_size)
165164
self.read(page_addr, read_buffer)
166165
#print("From memory: ", read_buffer)
166+
#print("From file : ", page_buffer)
167167

168168
if page_buffer != read_buffer:
169169
if verbose:
@@ -172,7 +172,11 @@ def verify_file(self, chip, file_name, verbose=False):
172172
# pylint: enable=line-too-long
173173
self.end()
174174
return False
175-
hexfile.close()
175+
176+
if file_state['eof']:
177+
break # we're done, bail!
178+
179+
file_state['f'].close()
176180
self.end()
177181
return True
178182

@@ -237,18 +241,16 @@ def begin(self, clock=_FAST_CLOCK):
237241
send the initialization command to get the AVR's attention.
238242
"""
239243
self._rst.value = False
240-
if self._spi:
241-
while self._spi and not self._spi.try_lock():
242-
pass
243-
self._spi.configure(baudrate=clock)
244+
while self._spi and not self._spi.try_lock():
245+
pass
246+
self._spi.configure(baudrate=clock)
244247
self._transaction((0xAC, 0x53, 0, 0))
245248

246249
def end(self):
247250
"""
248251
End programming mode: SPI is released, and reset pin set high.
249252
"""
250-
if self._spi:
251-
self._spi.unlock()
253+
self._spi.unlock()
252254
self._rst.value = True
253255

254256
def read_signature(self):
@@ -269,37 +271,48 @@ def read(self, addr, read_buffer):
269271
directly into 'read_buffer'
270272
Requires calling begin() beforehand to put in programming mode.
271273
"""
274+
last_addr = 0
272275
for i in range(len(read_buffer)//2):
273276
read_addr = addr//2 + i # read 'words' so address is half
277+
278+
if (last_addr >> 16) != (read_addr >> 16):
279+
# load extended byte
280+
#print("Loading extended address", read_addr >> 16)
281+
self._transaction((0x4D, 0, read_addr >> 16, 0))
274282
high = self._transaction((0x28, read_addr >> 8, read_addr, 0))[2]
275283
low = self._transaction((0x20, read_addr >> 8, read_addr, 0))[2]
276284
#print("%04X: %02X %02X" % (read_addr*2, low, high))
277285
read_buffer[i*2] = low
278286
read_buffer[i*2+1] = high
279287

288+
last_addr = read_addr
289+
280290
#################### Low level
281291
def _flash_word(self, addr, low, high):
282292
self._transaction((0x40, addr >> 8, addr, low))
283293
self._transaction((0x48, addr >> 8, addr, high))
284294

285295
def _flash_page(self, page_buffer, page_addr, page_size):
286-
for i in range(page_size/2):
296+
page_addr //= 2 # address is by 'words' not bytes!
297+
for i in range(page_size/2): # page indexed by words, not bytes
287298
lo_byte, hi_byte = page_buffer[2*i:2*i+2]
288299
self._flash_word(i, lo_byte, hi_byte)
289-
page_addr //= 2
300+
301+
# load extended byte
302+
self._transaction((0x4D, 0, page_addr >> 16, 0))
303+
290304
commit_reply = self._transaction((0x4C, page_addr >> 8, page_addr, 0))
291-
if ((commit_reply[1] << 8) + commit_reply[2]) != page_addr:
305+
if ((commit_reply[1] << 8) + commit_reply[2]) != (page_addr & 0xFFFF):
292306
raise RuntimeError("Failed to commit page to flash")
293307
self._busy_wait()
294308

295309
def _transaction(self, command):
296310
reply = bytearray(4)
297311
command = bytearray([i & 0xFF for i in command])
298312

299-
if self._spi:
300-
self._spi.write_readinto(command, reply)
313+
self._spi.write_readinto(command, reply)
301314
#s = [hex(i) for i in command]
302-
#print("Sending %s reply %s" % (command, reply))
315+
#print("Sending %s reply %s" % ([hex(i) for i in command], [hex(i) for i in reply]))
303316
if reply[2] != command[1]:
304317
raise RuntimeError("SPI transaction failed")
305318
return reply[1:] # first byte is ignored
@@ -308,7 +321,7 @@ def _busy_wait(self):
308321
while self._transaction((0xF0, 0, 0, 0))[2] & 0x01:
309322
pass
310323

311-
def read_hex_page(hexfile, page_addr, page_size, page_buffer):
324+
def read_hex_page(file_state, page_addr, page_size, page_buffer):
312325
"""
313326
Helper function that does the Intel Hex parsing. Given an open file
314327
'hexfile' and our desired buffer address start (page_addr), size
@@ -322,36 +335,51 @@ def read_hex_page(hexfile, page_addr, page_size, page_buffer):
322335
line does not contain any more data we can use.
323336
"""
324337
while True: # read until our page_buff is full!
325-
orig_loc = hexfile.tell() # in case we have to 'back up'
326-
line = hexfile.readline() # read one line from the HEX file
338+
orig_loc = file_state['f'].tell() # in case we have to 'back up'
339+
line = file_state['f'].readline() # read one line from the HEX file
340+
file_state['line'] += 1
341+
327342
if not line:
343+
file_state['eof'] = True
328344
return False
329345
#print(line)
330346
if line[0] != ':': # lines must start with ':'
331-
raise RuntimeError("HEX line doesn't start with :")
347+
raise RuntimeError("HEX line %d doesn't start with :" % file_state['line'])
332348

333349
# Try to parse the line length, address, and record type
334350
try:
335351
hex_len = int(line[1:3], 16)
336352
line_addr = int(line[3:7], 16)
353+
file_state['line_addr'] = line_addr
337354
rec_type = int(line[7:9], 16)
338355
except ValueError:
339-
raise RuntimeError("Could not parse HEX line addr")
356+
raise RuntimeError("Could not parse HEX line %d addr" % file_state['line'])
340357

358+
if file_state['ext_addr']:
359+
line_addr += file_state['ext_addr']
341360
#print("Hex len: %d, addr %04X, record type %d " % (hex_len, line_addr, rec_type))
342361

343362
# We should only look for data type records (0x00)
344-
if rec_type == 0x01:
345-
return False # reached end of file
346-
elif rec_type != 0x00:
347-
raise RuntimeError("Unsupported record type %d" % rec_type)
363+
if rec_type == 1:
364+
file_state['eof'] = True
365+
return False # reached end of file
366+
if rec_type == 2:
367+
file_state['ext_addr'] = int(line[9:13], 16) << 4
368+
#print("Extended addr: %05X" % file_state['ext_addr'])
369+
continue
370+
if rec_type == 3: # sometimes appears, we ignore this
371+
continue
372+
elif rec_type != 0: # if not the above or a data record...
373+
raise RuntimeError("Unsupported record type %d on line %d" %
374+
(rec_type, file_state['line']))
348375

349376
# check if this file file is either after the current page
350377
# (in which case, we've read all we can for this page and should
351-
# commence flashing...)
378+
# commence flasing...)
352379
if line_addr >= (page_addr + page_size):
353380
#print("Hex is past page address range")
354-
hexfile.seek(orig_loc) # back up!
381+
file_state['f'].seek(orig_loc) # back up!
382+
file_state['line'] -= 1
355383
return True
356384
# or, this line does not yet reach the current page address, in which
357385
# case which should just keep reading in hopes we reach the address

examples/program_mega2560.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Mega STK500 Bootloader programming example
2+
3+
import board
4+
import busio
5+
import adafruit_avrprog
6+
7+
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
8+
avrprog = adafruit_avrprog.AVRprog()
9+
avrprog.init(spi, board.D5)
10+
11+
# Each chip has to have a definition so the script knows how to find it
12+
atmega2560 = {'name': "ATmega2560"}
13+
atmega2560['sig'] = [0x1E, 0x98, 0x01]
14+
atmega2560['flash_size'] = 262144
15+
atmega2560['page_size'] = 256
16+
atmega2560['fuse_mask'] = (0xFF, 0xFF, 0x07, 0x3F)
17+
18+
# Helper to print out errors for us
19+
def error(err):
20+
print("ERROR: "+err)
21+
avrprog.end()
22+
while True:
23+
pass
24+
25+
if not avrprog.verify_sig(atmega2560, verbose=True):
26+
error("Signature read failure")
27+
print("Found", atmega2560['name'])
28+
29+
# Since we are unsetting the lock fuse, an erase is required!
30+
avrprog.erase_chip()
31+
32+
avrprog.write_fuses(atmega2560, low=0xFF, high=0xD8, ext=0x05, lock=0x3F)
33+
if not avrprog.verify_fuses(atmega2560, low=0xFF, high=0xD8, ext=0x05, lock=0x3F):
34+
error("Failure programming fuses: "+str([hex(i) for i in avrprog.read_fuses(atmega2560)]))
35+
36+
print("Programming flash from file")
37+
avrprog.program_file(atmega2560, "stk500boot_v2_mega2560.hex", verbose=True, verify=True)
38+
39+
avrprog.write_fuses(atmega2560, lock=0x0F)
40+
if not avrprog.verify_fuses(atmega2560,lock=0x0F):
41+
error("Failure verifying fuses!")
42+
43+
print("Done!")

examples/program_trinket85.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""
2+
Trinket/Gemma (ATtiny85) programming example, be sure you have the '85 wired up so:
3+
Trinket Ground to CircuitPython GND
4+
Trinket USB to CircuitPythong USB or make sure the Trinket is powered by USB
5+
Pin 2 -> CircuitPython SCK
6+
Pin 1 -> CircuitPython MISO
7+
Pin 0 -> CircuitPython MOSI
8+
RESET -> CircuitPython D5 (or change the init() below to change it!)
9+
Drag "trinket_boot.hex" onto the CircuitPython disk drive, then open REPL!
10+
"""
11+
12+
import board
13+
import busio
14+
import adafruit_avrprog
15+
16+
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
17+
avrprog = adafruit_avrprog.AVRprog()
18+
avrprog.init(spi, board.D5)
19+
20+
# Each chip has to have a definition so the script knows how to find it
21+
attiny85 = {'name': "ATtiny85"}
22+
attiny85['sig'] = [0x1E, 0x93, 0x0B]
23+
attiny85['flash_size'] = 8192
24+
attiny85['page_size'] = 64
25+
attiny85['fuse_mask'] = (0xFF, 0xFF, 0x07, 0x3F)
26+
27+
def error(err):
28+
""" Helper to print out errors for us and then halt """
29+
print("ERROR: "+err)
30+
avrprog.end()
31+
while True:
32+
pass
33+
34+
while input("Ready to GO, type 'G' here to start> ") != 'G':
35+
pass
36+
37+
if not avrprog.verify_sig(attiny85, verbose=True):
38+
error("Signature read failure")
39+
print("Found", attiny85['name'])
40+
41+
avrprog.write_fuses(attiny85, low=0xF1, high=0xD5, ext=0x06, lock=0x3F)
42+
if not avrprog.verify_fuses(attiny85, low=0xF1, high=0xD5, ext=0x06, lock=0x3F):
43+
error("Failure verifying fuses!")
44+
45+
print("Programming flash from file")
46+
avrprog.program_file(attiny85, "trinket_boot.hex", verbose=True, verify=True)
47+
48+
print("Done!")

0 commit comments

Comments
 (0)