3232__version__ = "0.0.0+auto.0"
3333__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_AVRprog.git"
3434
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
3678
37- _SLOW_CLOCK = 100000
38- _FAST_CLOCK = 1000000
79+ _SLOW_CLOCK : int = 100000
80+ _FAST_CLOCK : int = 1000000
3981
4082
4183class AVRprog :
@@ -93,10 +135,10 @@ class Boards:
93135 "fuse_mask" : (0xFF , 0xFF , 0x07 , 0x3F ),
94136 }
95137
96- _spi = None
97- _rst = None
138+ _spi : Optional [ SPI ] = None
139+ _rst : Optional [ DigitalInOut ] = None
98140
99- def init (self , spi_bus , rst_pin ) :
141+ def init (self , spi_bus : SPI , rst_pin : Pin ) -> None :
100142 """
101143 Initialize the programmer with an SPI port that will be used to
102144 communicate with the chip. Make sure your SPI supports 'write_readinto'
@@ -107,7 +149,7 @@ def init(self, spi_bus, rst_pin):
107149 self ._rst .direction = Direction .OUTPUT
108150 self ._rst .value = True
109151
110- def verify_sig (self , chip , verbose = False ):
152+ def verify_sig (self , chip : ChipDictionary , verbose : bool = False ) -> bool :
111153 """
112154 Verify that the chip is connected properly, responds to commands,
113155 and has the correct signature. Returns True/False based on success
@@ -122,7 +164,13 @@ def verify_sig(self, chip, verbose=False):
122164 return True
123165
124166 # 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 :
126174 """
127175 Perform a chip erase and program from a file that
128176 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):
139187 self .begin (clock = clock_speed )
140188
141189 # 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" ]:
146192 page_size = chip ["page_size" ]
147193
148194 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):
190236 self .end ()
191237 return True
192238
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 :
194245 """
195246 Perform a chip full-flash verification from a file that
196247 contains Intel HEX data. Returns True/False on success/fail.
@@ -199,10 +250,8 @@ def verify_file(self, chip, file_name, verbose=False):
199250 raise RuntimeError ("Signature read failure" )
200251
201252 # 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" ]:
206255 page_size = chip ["page_size" ]
207256 clock_speed = chip .get ("clock_speed" , _FAST_CLOCK )
208257 self .begin (clock = clock_speed )
@@ -237,12 +286,12 @@ def verify_file(self, chip, file_name, verbose=False):
237286 self .end ()
238287 return True
239288
240- def read_fuses (self , chip ) :
289+ def read_fuses (self , chip : ChipDictionary ) -> Tuple [ int , int , int , int ] :
241290 """
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)
243292 Each fuse is bitwise-&'s with the chip's fuse mask for simplicity
244293 """
245- mask = chip ["fuse_mask" ]
294+ mask : Tuple [ int , int , int , int ] = chip ["fuse_mask" ]
246295 self .begin (clock = _SLOW_CLOCK )
247296 low = self ._transaction ((0x50 , 0 , 0 , 0 ))[2 ] & mask [0 ]
248297 high = self ._transaction ((0x58 , 0x08 , 0 , 0 ))[2 ] & mask [1 ]
@@ -252,7 +301,14 @@ def read_fuses(self, chip):
252301 return (low , high , ext , lock )
253302
254303 # 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 :
256312 """
257313 Write any of the 4 fuses. If the kwarg low/high/ext/lock is not
258314 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):
267323 self .end ()
268324
269325 # 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 :
271334 """
272335 Verify the 4 fuses. If the kwarg low/high/ext/lock is not
273336 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):
282345 return False
283346 return True
284347
285- def erase_chip (self ):
348+ def erase_chip (self ) -> None :
286349 """
287350 Fully erases the chip.
288351 """
@@ -293,7 +356,7 @@ def erase_chip(self):
293356
294357 #################### Mid level
295358
296- def begin (self , clock = _FAST_CLOCK ):
359+ def begin (self , clock : int = _FAST_CLOCK ) -> None :
297360 """
298361 Begin programming mode: pull reset pin low, initialize SPI, and
299362 send the initialization command to get the AVR's attention.
@@ -304,14 +367,14 @@ def begin(self, clock=_FAST_CLOCK):
304367 self ._spi .configure (baudrate = clock )
305368 self ._transaction ((0xAC , 0x53 , 0 , 0 ))
306369
307- def end (self ):
370+ def end (self ) -> None :
308371 """
309372 End programming mode: SPI is released, and reset pin set high.
310373 """
311374 self ._spi .unlock ()
312375 self ._rst .value = True
313376
314- def read_signature (self ):
377+ def read_signature (self ) -> List [ int ] :
315378 """
316379 Read and return the signature of the chip as two bytes in an array.
317380 Requires calling begin() beforehand to put in programming mode.
@@ -322,7 +385,7 @@ def read_signature(self):
322385 sig .append (self ._transaction ((0x30 , 0 , i , 0 ))[2 ])
323386 return sig
324387
325- def read (self , addr , read_buffer ) :
388+ def read (self , addr : int , read_buffer : bytearray ) -> None :
326389 """
327390 Read a chunk of memory from address 'addr'. The amount read is the
328391 same as the size of the bytearray 'read_buffer'. Data read is placed
@@ -346,13 +409,15 @@ def read(self, addr, read_buffer):
346409 last_addr = read_addr
347410
348411 #################### Low level
349- def _flash_word (self , addr , low , high ) :
412+ def _flash_word (self , addr : int , low : int , high : int ) -> None :
350413 self ._transaction ((0x40 , addr >> 8 , addr , low ))
351414 self ._transaction ((0x48 , addr >> 8 , addr , high ))
352415
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 :
354419 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
356421 lo_byte , hi_byte = page_buffer [2 * i : 2 * i + 2 ]
357422 self ._flash_word (i , lo_byte , hi_byte )
358423
@@ -364,23 +429,25 @@ def _flash_page(self, page_buffer, page_addr, page_size):
364429 raise RuntimeError ("Failed to commit page to flash" )
365430 self ._busy_wait ()
366431
367- def _transaction (self , command ) :
432+ def _transaction (self , command : Tuple [ int , int , int , int ]) -> bytearray :
368433 reply = bytearray (4 )
369- command = bytearray ([i & 0xFF for i in command ])
434+ command_bytes = bytearray ([i & 0xFF for i in command ])
370435
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 ]:
375440 raise RuntimeError ("SPI transaction failed" )
376441 return reply [1 :] # first byte is ignored
377442
378- def _busy_wait (self ):
443+ def _busy_wait (self ) -> None :
379444 while self ._transaction ((0xF0 , 0 , 0 , 0 ))[2 ] & 0x01 :
380445 pass
381446
382447
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 :
384451 # pylint: disable=too-many-branches
385452 """
386453 Helper function that does the Intel Hex parsing. Takes in a dictionary
0 commit comments