Skip to content

Commit 86f2b35

Browse files
committed
Annotation and hinting for BitbangIO
1 parent c7d2c1c commit 86f2b35

File tree

3 files changed

+57
-46
lines changed

3 files changed

+57
-46
lines changed

adafruit_bitbangio.py

Lines changed: 55 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,15 @@
2222
https://github.com/adafruit/circuitpython/releases
2323
2424
"""
25+
import microcontroller
26+
27+
try:
28+
from typing import Optional
29+
from typing_extensions import Literal
30+
from circuitpython_typing import WriteableBuffer, ReadableBuffer
31+
from microcontroller import Pin
32+
except ImportError:
33+
pass
2534

2635
# imports
2736
from time import monotonic
@@ -37,36 +46,36 @@
3746
class _BitBangIO:
3847
"""Base class for subclassing only"""
3948

40-
def __init__(self):
49+
def __init__(self) -> None:
4150
self._locked = False
4251

43-
def try_lock(self):
52+
def try_lock(self) -> bool:
4453
"""Attempt to grab the lock. Return True on success, False if the lock is already taken."""
4554
if self._locked:
4655
return False
4756
self._locked = True
4857
return True
4958

50-
def unlock(self):
59+
def unlock(self) -> None:
5160
"""Release the lock so others may use the resource."""
5261
if self._locked:
5362
self._locked = False
5463
else:
5564
raise ValueError("Not locked")
5665

57-
def _check_lock(self):
66+
def _check_lock(self) -> Optional[bool]:
5867
if not self._locked:
5968
raise RuntimeError("First call try_lock()")
6069
return True
6170

6271
def __enter__(self):
6372
return self
6473

65-
def __exit__(self, exc_type, exc_value, traceback):
74+
def __exit__(self, exc_type, exc_value, traceback) -> None:
6675
self.deinit()
6776

6877
# pylint: disable=no-self-use
69-
def deinit(self):
78+
def deinit(self) -> None:
7079
"""Free any hardware used by the object."""
7180
return
7281

@@ -76,7 +85,7 @@ def deinit(self):
7685
class I2C(_BitBangIO):
7786
"""Software-based implementation of the I2C protocol over GPIO pins."""
7887

79-
def __init__(self, scl, sda, *, frequency=400000, timeout=1):
88+
def __init__(self, scl: microcontroller.Pin, sda: microcontroller.Pin, *, frequency: int = 400000, timeout: int = 1) -> None:
8089
"""Initialize bitbang (or software) based I2C. Must provide the I2C
8190
clock, and data pin numbers.
8291
"""
@@ -95,17 +104,17 @@ def __init__(self, scl, sda, *, frequency=400000, timeout=1):
95104
self._delay = (1 / frequency) / 2 # half period
96105
self._timeout = timeout
97106

98-
def deinit(self):
107+
def deinit(self) -> None:
99108
"""Free any hardware used by the object."""
100109
self._sda.deinit()
101110
self._scl.deinit()
102111

103-
def _wait(self):
112+
def _wait(self) -> None:
104113
end = monotonic() + self._delay # half period
105114
while end > monotonic():
106115
pass
107116

108-
def scan(self):
117+
def scan(self) -> List[int]:
109118
"""Perform an I2C Device Scan"""
110119
found = []
111120
if self._check_lock():
@@ -114,14 +123,14 @@ def scan(self):
114123
found.append(address)
115124
return found
116125

117-
def writeto(self, address, buffer, *, start=0, end=None):
126+
def writeto(self, address: int, buffer: ReadableBuffer, *, start=0, end=None) -> None:
118127
"""Write data from the buffer to an address"""
119128
if end is None:
120129
end = len(buffer)
121130
if self._check_lock():
122131
self._write(address, buffer[start:end], True)
123132

124-
def readfrom_into(self, address, buffer, *, start=0, end=None):
133+
def readfrom_into(self, address: int, buffer: WriteableBuffer, *, start: int = 0, end: Optional[int] = None) -> None:
125134
"""Read data from an address and into the buffer"""
126135
if end is None:
127136
end = len(buffer)
@@ -133,15 +142,15 @@ def readfrom_into(self, address, buffer, *, start=0, end=None):
133142

134143
def writeto_then_readfrom(
135144
self,
136-
address,
137-
buffer_out,
138-
buffer_in,
145+
address: int,
146+
buffer_out: ReadableBuffer,
147+
buffer_in: WriteableBuffer,
139148
*,
140-
out_start=0,
141-
out_end=None,
142-
in_start=0,
143-
in_end=None
144-
):
149+
out_start: int = 0,
150+
out_end: Optional[int] = None,
151+
in_start: int = 0,
152+
in_end: Optional[int] = None
153+
) -> None:
145154
"""Write data from buffer_out to an address and then
146155
read data from an address and into buffer_in
147156
"""
@@ -153,30 +162,30 @@ def writeto_then_readfrom(
153162
self._write(address, buffer_out[out_start:out_end], False)
154163
self.readfrom_into(address, buffer_in, start=in_start, end=in_end)
155164

156-
def _scl_low(self):
165+
def _scl_low(self) -> None:
157166
self._scl.switch_to_output(value=False)
158167

159-
def _sda_low(self):
168+
def _sda_low(self) -> None:
160169
self._sda.switch_to_output(value=False)
161170

162-
def _scl_release(self):
171+
def _scl_release(self) -> None:
163172
"""Release and let the pullups lift"""
164173
# Use self._timeout to add clock stretching
165174
self._scl.switch_to_input()
166175

167-
def _sda_release(self):
176+
def _sda_release(self) -> None:
168177
"""Release and let the pullups lift"""
169178
# Use self._timeout to add clock stretching
170179
self._sda.switch_to_input()
171180

172-
def _start(self):
181+
def _start(self) -> None:
173182
self._sda_release()
174183
self._scl_release()
175184
self._wait()
176185
self._sda_low()
177186
self._wait()
178187

179-
def _stop(self):
188+
def _stop(self) -> None:
180189
self._scl_low()
181190
self._wait()
182191
self._sda_low()
@@ -186,7 +195,7 @@ def _stop(self):
186195
self._sda_release()
187196
self._wait()
188197

189-
def _repeated_start(self):
198+
def _repeated_start(self) -> None:
190199
self._scl_low()
191200
self._wait()
192201
self._sda_release()
@@ -196,7 +205,7 @@ def _repeated_start(self):
196205
self._sda_low()
197206
self._wait()
198207

199-
def _write_byte(self, byte):
208+
def _write_byte(self, byte: int ) -> bool:
200209
for bit_position in range(8):
201210
self._scl_low()
202211

@@ -222,7 +231,7 @@ def _write_byte(self, byte):
222231

223232
return not ack
224233

225-
def _read_byte(self, ack=False):
234+
def _read_byte(self, ack: bool = False) -> int:
226235
self._scl_low()
227236
self._wait()
228237
# sda will already be an input as we are simulating open drain
@@ -246,13 +255,13 @@ def _read_byte(self, ack=False):
246255

247256
return data & 0xFF
248257

249-
def _probe(self, address):
258+
def _probe(self, address: int) -> bool:
250259
self._start()
251260
ok = self._write_byte(address << 1)
252261
self._stop()
253262
return ok > 0
254263

255-
def _write(self, address, buffer, transmit_stop):
264+
def _write(self, address: int, buffer: ReadableBuffer, transmit_stop: bool) -> None:
256265
self._start()
257266
if not self._write_byte(address << 1):
258267
raise RuntimeError("Device not responding at 0x{:02X}".format(address))
@@ -261,7 +270,7 @@ def _write(self, address, buffer, transmit_stop):
261270
if transmit_stop:
262271
self._stop()
263272

264-
def _read(self, address, length):
273+
def _read(self, address: int, length: int) -> ReadableBuffer:
265274
self._start()
266275
if not self._write_byte(address << 1 | 1):
267276
raise RuntimeError("Device not responding at 0x{:02X}".format(address))
@@ -275,7 +284,7 @@ def _read(self, address, length):
275284
class SPI(_BitBangIO):
276285
"""Software-based implementation of the SPI protocol over GPIO pins."""
277286

278-
def __init__(self, clock, MOSI=None, MISO=None):
287+
def __init__(self, clock: microcontroller.Pin, MOSI: Optional[microcontroller.Pin] = None, MISO: Optional[microcontroller.Pin] = None) -> None:
279288
"""Initialize bit bang (or software) based SPI. Must provide the SPI
280289
clock, and optionally MOSI and MISO pin numbers. If MOSI is set to None
281290
then writes will be disabled and fail with an error, likewise for MISO
@@ -304,15 +313,15 @@ def __init__(self, clock, MOSI=None, MISO=None):
304313
self._miso = DigitalInOut(MISO)
305314
self._miso.switch_to_input()
306315

307-
def deinit(self):
316+
def deinit(self) -> None:
308317
"""Free any hardware used by the object."""
309318
self._sclk.deinit()
310319
if self._miso:
311320
self._miso.deinit()
312321
if self._mosi:
313322
self._mosi.deinit()
314323

315-
def configure(self, *, baudrate=100000, polarity=0, phase=0, bits=8):
324+
def configure(self, *, baudrate: int = 100000, polarity: Literal[0,1] = 0, phase: Literal[0,1] = 0, bits: int = 8) -> None:
316325
"""Configures the SPI bus. Only valid when locked."""
317326
if self._check_lock():
318327
if not isinstance(baudrate, int):
@@ -331,13 +340,13 @@ def configure(self, *, baudrate=100000, polarity=0, phase=0, bits=8):
331340
self._bits = bits
332341
self._half_period = (1 / self._baudrate) / 2 # 50% Duty Cyle delay
333342

334-
def _wait(self, start=None):
343+
def _wait(self, start: Optional[int] = None) -> float:
335344
"""Wait for up to one half cycle"""
336345
while (start + self._half_period) > monotonic():
337346
pass
338347
return monotonic() # Return current time
339348

340-
def write(self, buffer, start=0, end=None):
349+
def write(self, buffer: ReadableBuffer, start: int = 0, end: Optional[int] = None) -> None:
341350
"""Write the data contained in buf. Requires the SPI being locked.
342351
If the buffer is empty, nothing happens.
343352
"""
@@ -369,7 +378,7 @@ def write(self, buffer, start=0, end=None):
369378
self._sclk.value = self._polarity
370379

371380
# pylint: disable=too-many-branches
372-
def readinto(self, buffer, start=0, end=None, write_value=0):
381+
def readinto(self, buffer: WriteableBuffer, start: int = 0, end: Optional[int] = None, write_value: int = 0) -> None:
373382
"""Read into the buffer specified by buf while writing zeroes. Requires the SPI being
374383
locked. If the number of bytes to read is 0, nothing happens.
375384
"""
@@ -417,14 +426,14 @@ def readinto(self, buffer, start=0, end=None, write_value=0):
417426

418427
def write_readinto(
419428
self,
420-
buffer_out,
421-
buffer_in,
429+
buffer_out: ReadableBuffer,
430+
buffer_in: WriteableBuffer,
422431
*,
423-
out_start=0,
424-
out_end=None,
425-
in_start=0,
426-
in_end=None
427-
):
432+
out_start: int = 0,
433+
out_end: Optional[int] = None,
434+
in_start: int = 0,
435+
in_end: Optional[int] = None
436+
) -> None:
428437
"""Write out the data in buffer_out while simultaneously reading data into buffer_in.
429438
The lengths of the slices defined by buffer_out[out_start:out_end] and
430439
buffer_in[in_start:in_end] must be equal. If buffer slice lengths are
@@ -482,6 +491,6 @@ def write_readinto(
482491
# pylint: enable=too-many-branches
483492

484493
@property
485-
def frequency(self):
494+
def frequency(self) -> int:
486495
"""Return the currently configured baud rate"""
487496
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

setup.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
author_email="circuitpython@adafruit.com",
3636
install_requires=[
3737
"Adafruit-Blinka",
38+
"typing-extensions",
3839
],
3940
# Choose your license
4041
license="MIT",

0 commit comments

Comments
 (0)