32
32
__version__ = "0.0.0+auto.0"
33
33
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AVRprog.git"
34
34
35
- from digitalio import Direction , DigitalInOut
35
+ try :
36
+ from typing import List , Optional , Tuple , Union , TypedDict
37
+ from typing_extensions import TypeAlias
38
+ from os import PathLike
39
+ from busio import SPI
40
+ from microcontroller import Pin
41
+
42
+ # Technically this type should come from: from _typeshed import FileDescriptorOrPath
43
+ # Unfortunately _typeshed is only in the standard library in newer releases of Python, e.g. 3.11
44
+ # Thus have to define a placeholder
45
+ FileDescriptorOrPath : TypeAlias = Union [
46
+ int , str , bytes , PathLike [str ], PathLike [bytes ]
47
+ ]
48
+
49
+ from io import TextIOWrapper
50
+
51
+ class ChipDictionary (TypedDict ):
52
+ """
53
+ Dictionary representing a specific target chip type
54
+ """
55
+
56
+ name : str
57
+ sig : List [int ]
58
+ flash_size : int
59
+ page_size : int
60
+ fuse_mask : Tuple [int , int , int , int ]
61
+
62
+ class FileState (TypedDict ):
63
+ """
64
+ Dictionary representing a File State
65
+ """
66
+
67
+ # pylint: disable=invalid-name
68
+ line : int
69
+ ext_addr : int
70
+ eof : bool
71
+ f : Optional [TextIOWrapper ]
72
+
73
+ except ImportError :
74
+ pass
75
+
76
+
77
+ from digitalio import DigitalInOut , Direction
36
78
37
- _SLOW_CLOCK = 100000
38
- _FAST_CLOCK = 1000000
79
+ _SLOW_CLOCK : int = 100000
80
+ _FAST_CLOCK : int = 1000000
39
81
40
82
41
83
class AVRprog :
@@ -93,10 +135,10 @@ class Boards:
93
135
"fuse_mask" : (0xFF , 0xFF , 0x07 , 0x3F ),
94
136
}
95
137
96
- _spi = None
97
- _rst = None
138
+ _spi : Optional [ SPI ] = None
139
+ _rst : Optional [ DigitalInOut ] = None
98
140
99
- def init (self , spi_bus , rst_pin ) :
141
+ def init (self , spi_bus : SPI , rst_pin : Pin ) -> None :
100
142
"""
101
143
Initialize the programmer with an SPI port that will be used to
102
144
communicate with the chip. Make sure your SPI supports 'write_readinto'
@@ -107,7 +149,7 @@ def init(self, spi_bus, rst_pin):
107
149
self ._rst .direction = Direction .OUTPUT
108
150
self ._rst .value = True
109
151
110
- def verify_sig (self , chip , verbose = False ):
152
+ def verify_sig (self , chip : ChipDictionary , verbose : bool = False ) -> bool :
111
153
"""
112
154
Verify that the chip is connected properly, responds to commands,
113
155
and has the correct signature. Returns True/False based on success
@@ -122,7 +164,13 @@ def verify_sig(self, chip, verbose=False):
122
164
return True
123
165
124
166
# pylint: disable=too-many-branches
125
- def program_file (self , chip , file_name , verbose = False , verify = True ):
167
+ def program_file (
168
+ self ,
169
+ chip : ChipDictionary ,
170
+ file_name : FileDescriptorOrPath ,
171
+ verbose : bool = False ,
172
+ verify : bool = True ,
173
+ ) -> bool :
126
174
"""
127
175
Perform a chip erase and program from a file that
128
176
contains Intel HEX data. Returns true on verify-success, False on
@@ -139,10 +187,8 @@ def program_file(self, chip, file_name, verbose=False, verify=True):
139
187
self .begin (clock = clock_speed )
140
188
141
189
# create a file state dictionary
142
- file_state = {"line" : 0 , "ext_addr" : 0 , "eof" : False }
143
- with open (file_name , "r" ) as file_state [ # pylint: disable=unspecified-encoding
144
- "f"
145
- ]:
190
+ file_state = {"line" : 0 , "ext_addr" : 0 , "eof" : False , "f" : None }
191
+ with open (file_name , "r" ) as file_state ["f" ]:
146
192
page_size = chip ["page_size" ]
147
193
148
194
for page_addr in range (0 , chip ["flash_size" ], page_size ):
@@ -190,7 +236,12 @@ def program_file(self, chip, file_name, verbose=False, verify=True):
190
236
self .end ()
191
237
return True
192
238
193
- def verify_file (self , chip , file_name , verbose = False ):
239
+ def verify_file (
240
+ self ,
241
+ chip : ChipDictionary ,
242
+ file_name : FileDescriptorOrPath ,
243
+ verbose : bool = False ,
244
+ ) -> bool :
194
245
"""
195
246
Perform a chip full-flash verification from a file that
196
247
contains Intel HEX data. Returns True/False on success/fail.
@@ -199,10 +250,8 @@ def verify_file(self, chip, file_name, verbose=False):
199
250
raise RuntimeError ("Signature read failure" )
200
251
201
252
# create a file state dictionary
202
- file_state = {"line" : 0 , "ext_addr" : 0 , "eof" : False }
203
- with open (file_name , "r" ) as file_name [ # pylint: disable=unspecified-encoding
204
- "f"
205
- ]:
253
+ file_state = {"line" : 0 , "ext_addr" : 0 , "eof" : False , "f" : None }
254
+ with open (file_name , "r" ) as file_state ["f" ]:
206
255
page_size = chip ["page_size" ]
207
256
clock_speed = chip .get ("clock_speed" , _FAST_CLOCK )
208
257
self .begin (clock = clock_speed )
@@ -237,12 +286,12 @@ def verify_file(self, chip, file_name, verbose=False):
237
286
self .end ()
238
287
return True
239
288
240
- def read_fuses (self , chip ) :
289
+ def read_fuses (self , chip : ChipDictionary ) -> Tuple [ int , int , int , int ] :
241
290
"""
242
- Read the 4 fuses and return them in a list (low, high, ext, lock)
291
+ Read the 4 fuses and return them in a tuple (low, high, ext, lock)
243
292
Each fuse is bitwise-&'s with the chip's fuse mask for simplicity
244
293
"""
245
- mask = chip ["fuse_mask" ]
294
+ mask : Tuple [ int , int , int , int ] = chip ["fuse_mask" ]
246
295
self .begin (clock = _SLOW_CLOCK )
247
296
low = self ._transaction ((0x50 , 0 , 0 , 0 ))[2 ] & mask [0 ]
248
297
high = self ._transaction ((0x58 , 0x08 , 0 , 0 ))[2 ] & mask [1 ]
@@ -252,7 +301,14 @@ def read_fuses(self, chip):
252
301
return (low , high , ext , lock )
253
302
254
303
# pylint: disable=unused-argument,too-many-arguments
255
- def write_fuses (self , chip , low = None , high = None , ext = None , lock = None ):
304
+ def write_fuses (
305
+ self ,
306
+ chip : ChipDictionary ,
307
+ low : Optional [int ] = None ,
308
+ high : Optional [int ] = None ,
309
+ ext : Optional [int ] = None ,
310
+ lock : Optional [int ] = None ,
311
+ ) -> None :
256
312
"""
257
313
Write any of the 4 fuses. If the kwarg low/high/ext/lock is not
258
314
passed in or is None, that fuse is skipped
@@ -267,7 +323,14 @@ def write_fuses(self, chip, low=None, high=None, ext=None, lock=None):
267
323
self .end ()
268
324
269
325
# pylint: disable=too-many-arguments
270
- def verify_fuses (self , chip , low = None , high = None , ext = None , lock = None ):
326
+ def verify_fuses (
327
+ self ,
328
+ chip : ChipDictionary ,
329
+ low : Optional [int ] = None ,
330
+ high : Optional [int ] = None ,
331
+ ext : Optional [int ] = None ,
332
+ lock : Optional [int ] = None ,
333
+ ) -> bool :
271
334
"""
272
335
Verify the 4 fuses. If the kwarg low/high/ext/lock is not
273
336
passed in or is None, that fuse is not checked.
@@ -282,7 +345,7 @@ def verify_fuses(self, chip, low=None, high=None, ext=None, lock=None):
282
345
return False
283
346
return True
284
347
285
- def erase_chip (self ):
348
+ def erase_chip (self ) -> None :
286
349
"""
287
350
Fully erases the chip.
288
351
"""
@@ -293,7 +356,7 @@ def erase_chip(self):
293
356
294
357
#################### Mid level
295
358
296
- def begin (self , clock = _FAST_CLOCK ):
359
+ def begin (self , clock : int = _FAST_CLOCK ) -> None :
297
360
"""
298
361
Begin programming mode: pull reset pin low, initialize SPI, and
299
362
send the initialization command to get the AVR's attention.
@@ -304,14 +367,14 @@ def begin(self, clock=_FAST_CLOCK):
304
367
self ._spi .configure (baudrate = clock )
305
368
self ._transaction ((0xAC , 0x53 , 0 , 0 ))
306
369
307
- def end (self ):
370
+ def end (self ) -> None :
308
371
"""
309
372
End programming mode: SPI is released, and reset pin set high.
310
373
"""
311
374
self ._spi .unlock ()
312
375
self ._rst .value = True
313
376
314
- def read_signature (self ):
377
+ def read_signature (self ) -> List [ int ] :
315
378
"""
316
379
Read and return the signature of the chip as two bytes in an array.
317
380
Requires calling begin() beforehand to put in programming mode.
@@ -322,7 +385,7 @@ def read_signature(self):
322
385
sig .append (self ._transaction ((0x30 , 0 , i , 0 ))[2 ])
323
386
return sig
324
387
325
- def read (self , addr , read_buffer ) :
388
+ def read (self , addr : int , read_buffer : bytearray ) -> None :
326
389
"""
327
390
Read a chunk of memory from address 'addr'. The amount read is the
328
391
same as the size of the bytearray 'read_buffer'. Data read is placed
@@ -346,13 +409,15 @@ def read(self, addr, read_buffer):
346
409
last_addr = read_addr
347
410
348
411
#################### Low level
349
- def _flash_word (self , addr , low , high ) :
412
+ def _flash_word (self , addr : int , low : int , high : int ) -> None :
350
413
self ._transaction ((0x40 , addr >> 8 , addr , low ))
351
414
self ._transaction ((0x48 , addr >> 8 , addr , high ))
352
415
353
- def _flash_page (self , page_buffer , page_addr , page_size ):
416
+ def _flash_page (
417
+ self , page_buffer : bytearray , page_addr : int , page_size : int
418
+ ) -> None :
354
419
page_addr //= 2 # address is by 'words' not bytes!
355
- for i in range (page_size / 2 ): # page indexed by words, not bytes
420
+ for i in range (page_size // 2 ): # page indexed by words, not bytes
356
421
lo_byte , hi_byte = page_buffer [2 * i : 2 * i + 2 ]
357
422
self ._flash_word (i , lo_byte , hi_byte )
358
423
@@ -364,23 +429,25 @@ def _flash_page(self, page_buffer, page_addr, page_size):
364
429
raise RuntimeError ("Failed to commit page to flash" )
365
430
self ._busy_wait ()
366
431
367
- def _transaction (self , command ) :
432
+ def _transaction (self , command : Tuple [ int , int , int , int ]) -> bytearray :
368
433
reply = bytearray (4 )
369
- command = bytearray ([i & 0xFF for i in command ])
434
+ command_bytes = bytearray ([i & 0xFF for i in command ])
370
435
371
- self ._spi .write_readinto (command , reply )
372
- # s = [hex(i) for i in command ]
373
- # print("Sending %s reply %s" % ([hex(i) for i in command ], [hex(i) for i in reply]))
374
- if reply [2 ] != command [1 ]:
436
+ self ._spi .write_readinto (command_bytes , reply )
437
+ # s = [hex(i) for i in command_bytes ]
438
+ # print("Sending %s reply %s" % ([hex(i) for i in command_bytes ], [hex(i) for i in reply]))
439
+ if reply [2 ] != command_bytes [1 ]:
375
440
raise RuntimeError ("SPI transaction failed" )
376
441
return reply [1 :] # first byte is ignored
377
442
378
- def _busy_wait (self ):
443
+ def _busy_wait (self ) -> None :
379
444
while self ._transaction ((0xF0 , 0 , 0 , 0 ))[2 ] & 0x01 :
380
445
pass
381
446
382
447
383
- def read_hex_page (file_state , page_addr , page_size , page_buffer ):
448
+ def read_hex_page (
449
+ file_state : FileState , page_addr : int , page_size : int , page_buffer : bytearray
450
+ ) -> bool :
384
451
# pylint: disable=too-many-branches
385
452
"""
386
453
Helper function that does the Intel Hex parsing. Takes in a dictionary
0 commit comments