Skip to content

Commit 8a19b59

Browse files
authored
Merge pull request #34 from RossK1/main
Type annotations and suspected minor bug fix
2 parents 17a9e9b + 2493023 commit 8a19b59

File tree

1 file changed

+105
-38
lines changed

1 file changed

+105
-38
lines changed

adafruit_avrprog.py

Lines changed: 105 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,52 @@
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

4183
class 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

Comments
 (0)