Skip to content

Commit 16decd6

Browse files
authored
Merge pull request #23 from tcfranks/main
Annotation and hinting for BitbangIO
2 parents b883831 + 96c9d07 commit 16decd6

File tree

2 files changed

+92
-48
lines changed

2 files changed

+92
-48
lines changed

adafruit_bitbangio.py

Lines changed: 91 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@
2323
2424
"""
2525

26+
try:
27+
from typing import Optional, List
28+
from typing_extensions import Literal
29+
from circuitpython_typing import WriteableBuffer, ReadableBuffer
30+
from microcontroller import Pin
31+
except ImportError:
32+
pass
33+
2634
# imports
2735
from time import monotonic
2836
from digitalio import DigitalInOut
@@ -37,36 +45,36 @@
3745
class _BitBangIO:
3846
"""Base class for subclassing only"""
3947

40-
def __init__(self):
48+
def __init__(self) -> None:
4149
self._locked = False
4250

43-
def try_lock(self):
51+
def try_lock(self) -> bool:
4452
"""Attempt to grab the lock. Return True on success, False if the lock is already taken."""
4553
if self._locked:
4654
return False
4755
self._locked = True
4856
return True
4957

50-
def unlock(self):
58+
def unlock(self) -> None:
5159
"""Release the lock so others may use the resource."""
5260
if self._locked:
5361
self._locked = False
5462
else:
5563
raise ValueError("Not locked")
5664

57-
def _check_lock(self):
65+
def _check_lock(self) -> Literal[True]:
5866
if not self._locked:
5967
raise RuntimeError("First call try_lock()")
6068
return True
6169

6270
def __enter__(self):
6371
return self
6472

65-
def __exit__(self, exc_type, exc_value, traceback):
73+
def __exit__(self, exc_type, exc_value, traceback) -> None:
6674
self.deinit()
6775

6876
# pylint: disable=no-self-use
69-
def deinit(self):
77+
def deinit(self) -> None:
7078
"""Free any hardware used by the object."""
7179
return
7280

@@ -76,7 +84,9 @@ def deinit(self):
7684
class I2C(_BitBangIO):
7785
"""Software-based implementation of the I2C protocol over GPIO pins."""
7886

79-
def __init__(self, scl, sda, *, frequency=400000, timeout=1):
87+
def __init__(
88+
self, scl: Pin, sda: Pin, *, frequency: int = 400000, timeout: float = 1
89+
) -> None:
8090
"""Initialize bitbang (or software) based I2C. Must provide the I2C
8191
clock, and data pin numbers.
8292
"""
@@ -95,17 +105,17 @@ def __init__(self, scl, sda, *, frequency=400000, timeout=1):
95105
self._delay = (1 / frequency) / 2 # half period
96106
self._timeout = timeout
97107

98-
def deinit(self):
108+
def deinit(self) -> None:
99109
"""Free any hardware used by the object."""
100110
self._sda.deinit()
101111
self._scl.deinit()
102112

103-
def _wait(self):
113+
def _wait(self) -> None:
104114
end = monotonic() + self._delay # half period
105115
while end > monotonic():
106116
pass
107117

108-
def scan(self):
118+
def scan(self) -> List[int]:
109119
"""Perform an I2C Device Scan"""
110120
found = []
111121
if self._check_lock():
@@ -114,14 +124,28 @@ def scan(self):
114124
found.append(address)
115125
return found
116126

117-
def writeto(self, address, buffer, *, start=0, end=None):
127+
def writeto(
128+
self,
129+
address: int,
130+
buffer: ReadableBuffer,
131+
*,
132+
start: int = 0,
133+
end: Optional[int] = None,
134+
) -> None:
118135
"""Write data from the buffer to an address"""
119136
if end is None:
120137
end = len(buffer)
121138
if self._check_lock():
122139
self._write(address, buffer[start:end], True)
123140

124-
def readfrom_into(self, address, buffer, *, start=0, end=None):
141+
def readfrom_into(
142+
self,
143+
address: int,
144+
buffer: WriteableBuffer,
145+
*,
146+
start: int = 0,
147+
end: Optional[int] = None,
148+
) -> None:
125149
"""Read data from an address and into the buffer"""
126150
if end is None:
127151
end = len(buffer)
@@ -133,15 +157,15 @@ def readfrom_into(self, address, buffer, *, start=0, end=None):
133157

134158
def writeto_then_readfrom(
135159
self,
136-
address,
137-
buffer_out,
138-
buffer_in,
160+
address: int,
161+
buffer_out: ReadableBuffer,
162+
buffer_in: WriteableBuffer,
139163
*,
140-
out_start=0,
141-
out_end=None,
142-
in_start=0,
143-
in_end=None
144-
):
164+
out_start: int = 0,
165+
out_end: Optional[int] = None,
166+
in_start: int = 0,
167+
in_end: Optional[int] = None,
168+
) -> None:
145169
"""Write data from buffer_out to an address and then
146170
read data from an address and into buffer_in
147171
"""
@@ -153,30 +177,30 @@ def writeto_then_readfrom(
153177
self._write(address, buffer_out[out_start:out_end], False)
154178
self.readfrom_into(address, buffer_in, start=in_start, end=in_end)
155179

156-
def _scl_low(self):
180+
def _scl_low(self) -> None:
157181
self._scl.switch_to_output(value=False)
158182

159-
def _sda_low(self):
183+
def _sda_low(self) -> None:
160184
self._sda.switch_to_output(value=False)
161185

162-
def _scl_release(self):
186+
def _scl_release(self) -> None:
163187
"""Release and let the pullups lift"""
164188
# Use self._timeout to add clock stretching
165189
self._scl.switch_to_input()
166190

167-
def _sda_release(self):
191+
def _sda_release(self) -> None:
168192
"""Release and let the pullups lift"""
169193
# Use self._timeout to add clock stretching
170194
self._sda.switch_to_input()
171195

172-
def _start(self):
196+
def _start(self) -> None:
173197
self._sda_release()
174198
self._scl_release()
175199
self._wait()
176200
self._sda_low()
177201
self._wait()
178202

179-
def _stop(self):
203+
def _stop(self) -> None:
180204
self._scl_low()
181205
self._wait()
182206
self._sda_low()
@@ -186,7 +210,7 @@ def _stop(self):
186210
self._sda_release()
187211
self._wait()
188212

189-
def _repeated_start(self):
213+
def _repeated_start(self) -> None:
190214
self._scl_low()
191215
self._wait()
192216
self._sda_release()
@@ -196,7 +220,7 @@ def _repeated_start(self):
196220
self._sda_low()
197221
self._wait()
198222

199-
def _write_byte(self, byte):
223+
def _write_byte(self, byte: int) -> bool:
200224
for bit_position in range(8):
201225
self._scl_low()
202226

@@ -222,7 +246,7 @@ def _write_byte(self, byte):
222246

223247
return not ack
224248

225-
def _read_byte(self, ack=False):
249+
def _read_byte(self, ack: bool = False) -> int:
226250
self._scl_low()
227251
self._wait()
228252
# sda will already be an input as we are simulating open drain
@@ -246,25 +270,27 @@ def _read_byte(self, ack=False):
246270

247271
return data & 0xFF
248272

249-
def _probe(self, address):
273+
def _probe(self, address: int) -> bool:
250274
self._start()
251275
ok = self._write_byte(address << 1)
252276
self._stop()
253277
return ok > 0
254278

255-
def _write(self, address, buffer, transmit_stop):
279+
def _write(self, address: int, buffer: ReadableBuffer, transmit_stop: bool) -> None:
256280
self._start()
257281
if not self._write_byte(address << 1):
258-
raise RuntimeError("Device not responding at 0x{:02X}".format(address))
282+
# raise RuntimeError("Device not responding at 0x{:02X}".format(address))
283+
raise RuntimeError(f"Device not responding at 0x{address:02X}")
259284
for byte in buffer:
260285
self._write_byte(byte)
261286
if transmit_stop:
262287
self._stop()
263288

264-
def _read(self, address, length):
289+
def _read(self, address: int, length: int) -> bytearray:
265290
self._start()
266291
if not self._write_byte(address << 1 | 1):
267-
raise RuntimeError("Device not responding at 0x{:02X}".format(address))
292+
# raise RuntimeError("Device not responding at 0x{:02X}".format(address))
293+
raise RuntimeError(f"Device not responding at 0x{address:02X}")
268294
buffer = bytearray(length)
269295
for byte_position in range(length):
270296
buffer[byte_position] = self._read_byte(ack=(byte_position != length - 1))
@@ -275,7 +301,9 @@ def _read(self, address, length):
275301
class SPI(_BitBangIO):
276302
"""Software-based implementation of the SPI protocol over GPIO pins."""
277303

278-
def __init__(self, clock, MOSI=None, MISO=None):
304+
def __init__(
305+
self, clock: Pin, MOSI: Optional[Pin] = None, MISO: Optional[Pin] = None
306+
) -> None:
279307
"""Initialize bit bang (or software) based SPI. Must provide the SPI
280308
clock, and optionally MOSI and MISO pin numbers. If MOSI is set to None
281309
then writes will be disabled and fail with an error, likewise for MISO
@@ -304,15 +332,22 @@ def __init__(self, clock, MOSI=None, MISO=None):
304332
self._miso = DigitalInOut(MISO)
305333
self._miso.switch_to_input()
306334

307-
def deinit(self):
335+
def deinit(self) -> None:
308336
"""Free any hardware used by the object."""
309337
self._sclk.deinit()
310338
if self._miso:
311339
self._miso.deinit()
312340
if self._mosi:
313341
self._mosi.deinit()
314342

315-
def configure(self, *, baudrate=100000, polarity=0, phase=0, bits=8):
343+
def configure(
344+
self,
345+
*,
346+
baudrate: int = 100000,
347+
polarity: Literal[0, 1] = 0,
348+
phase: Literal[0, 1] = 0,
349+
bits: int = 8,
350+
) -> None:
316351
"""Configures the SPI bus. Only valid when locked."""
317352
if self._check_lock():
318353
if not isinstance(baudrate, int):
@@ -331,13 +366,15 @@ def configure(self, *, baudrate=100000, polarity=0, phase=0, bits=8):
331366
self._bits = bits
332367
self._half_period = (1 / self._baudrate) / 2 # 50% Duty Cyle delay
333368

334-
def _wait(self, start=None):
369+
def _wait(self, start: Optional[int] = None) -> float:
335370
"""Wait for up to one half cycle"""
336371
while (start + self._half_period) > monotonic():
337372
pass
338373
return monotonic() # Return current time
339374

340-
def write(self, buffer, start=0, end=None):
375+
def write(
376+
self, buffer: ReadableBuffer, start: int = 0, end: Optional[int] = None
377+
) -> None:
341378
"""Write the data contained in buf. Requires the SPI being locked.
342379
If the buffer is empty, nothing happens.
343380
"""
@@ -369,7 +406,13 @@ def write(self, buffer, start=0, end=None):
369406
self._sclk.value = self._polarity
370407

371408
# pylint: disable=too-many-branches
372-
def readinto(self, buffer, start=0, end=None, write_value=0):
409+
def readinto(
410+
self,
411+
buffer: WriteableBuffer,
412+
start: int = 0,
413+
end: Optional[int] = None,
414+
write_value: int = 0,
415+
) -> None:
373416
"""Read into the buffer specified by buf while writing zeroes. Requires the SPI being
374417
locked. If the number of bytes to read is 0, nothing happens.
375418
"""
@@ -417,14 +460,14 @@ def readinto(self, buffer, start=0, end=None, write_value=0):
417460

418461
def write_readinto(
419462
self,
420-
buffer_out,
421-
buffer_in,
463+
buffer_out: ReadableBuffer,
464+
buffer_in: WriteableBuffer,
422465
*,
423-
out_start=0,
424-
out_end=None,
425-
in_start=0,
426-
in_end=None
427-
):
466+
out_start: int = 0,
467+
out_end: Optional[int] = None,
468+
in_start: int = 0,
469+
in_end: Optional[int] = None,
470+
) -> None:
428471
"""Write out the data in buffer_out while simultaneously reading data into buffer_in.
429472
The lengths of the slices defined by buffer_out[out_start:out_end] and
430473
buffer_in[in_start:in_end] must be equal. If buffer slice lengths are
@@ -482,6 +525,6 @@ def write_readinto(
482525
# pylint: enable=too-many-branches
483526

484527
@property
485-
def frequency(self):
528+
def frequency(self) -> int:
486529
"""Return the currently configured baud rate"""
487530
return self._baudrate

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
# SPDX-License-Identifier: Unlicense
44

55
Adafruit-Blinka
6+
typing-extensions

0 commit comments

Comments
 (0)