]> Repositories - Adafruit_Blinka-hackapet.git/blob - src/busio.py
7d32a8b5adeed50399aa86d53a7b83b22d27a728
[Adafruit_Blinka-hackapet.git] / src / busio.py
1 """
2 `busio` - Bus protocol support like I2C and SPI
3 =================================================
4
5 See `CircuitPython:busio` in CircuitPython for more details.
6
7 * Author(s): cefn
8 """
9
10 try:
11     import threading
12 except ImportError:
13     threading = None
14
15 import adafruit_platformdetect.constants.boards as ap_board
16 import adafruit_platformdetect.constants.chips as ap_chip
17 from adafruit_blinka import Enum, Lockable, agnostic
18 from adafruit_blinka.agnostic import board_id, detector
19
20 # pylint: disable=import-outside-toplevel,too-many-branches,too-many-statements
21 # pylint: disable=too-many-arguments,too-many-function-args,consider-using-with
22
23
24 class I2C(Lockable):
25     """
26     Busio I2C Class for CircuitPython Compatibility. Used
27     for both MicroPython and Linux.
28     """
29
30     def __init__(self, scl, sda, frequency=100000):
31         self.init(scl, sda, frequency)
32
33     def init(self, scl, sda, frequency):
34         """Initialization"""
35         self.deinit()
36         if detector.board.ftdi_ft232h:
37             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.i2c import I2C as _I2C
38
39             self._i2c = _I2C(frequency=frequency)
40             return
41         if detector.board.binho_nova:
42             from adafruit_blinka.microcontroller.nova.i2c import I2C as _I2C
43
44             self._i2c = _I2C(frequency=frequency)
45             return
46         if detector.board.microchip_mcp2221:
47             from adafruit_blinka.microcontroller.mcp2221.i2c import I2C as _I2C
48
49             self._i2c = _I2C(frequency=frequency)
50             return
51         if detector.board.greatfet_one:
52             from adafruit_blinka.microcontroller.nxp_lpc4330.i2c import I2C as _I2C
53
54             self._i2c = _I2C(frequency=frequency)
55             return
56         if detector.board.pico_u2if:
57             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import I2C_Pico as _I2C
58
59             self._i2c = _I2C(scl, sda, frequency=frequency)
60             return
61         if detector.board.feather_u2if:
62             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import I2C_Feather as _I2C
63
64             self._i2c = _I2C(scl, sda, frequency=frequency)
65             return
66         if detector.board.qt2040_trinkey_u2if:
67             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import I2C_QT2040_Trinkey as _I2C
68
69             self._i2c = _I2C(scl, sda, frequency=frequency)
70             return
71         if detector.chip.id == ap_chip.RP2040:
72             from adafruit_blinka.microcontroller.rp2040.i2c import I2C as _I2C
73
74             self._i2c = _I2C(scl, sda, frequency=frequency)
75             return
76         if detector.board.any_embedded_linux:
77             from adafruit_blinka.microcontroller.generic_linux.i2c import I2C as _I2C
78         elif detector.board.ftdi_ft2232h:
79             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.i2c import I2C as _I2C
80         else:
81             from adafruit_blinka.microcontroller.generic_micropython.i2c import (
82                 I2C as _I2C,
83             )
84         from microcontroller.pin import i2cPorts
85
86         for portId, portScl, portSda in i2cPorts:
87             try:
88                 # pylint: disable=unexpected-keyword-arg
89                 if scl == portScl and sda == portSda:
90                     self._i2c = _I2C(portId, mode=_I2C.MASTER, baudrate=frequency)
91                     break
92                 # pylint: enable=unexpected-keyword-arg
93             except RuntimeError:
94                 pass
95         else:
96             raise ValueError(
97                 "No Hardware I2C on (scl,sda)={}\nValid I2C ports: {}".format(
98                     (scl, sda), i2cPorts
99                 )
100             )
101         if threading is not None:
102             self._lock = threading.RLock()
103
104     def deinit(self):
105         """Deinitialization"""
106         try:
107             del self._i2c
108         except AttributeError:
109             pass
110
111     def __enter__(self):
112         if threading is not None:
113             self._lock.acquire()
114         return self
115
116     def __exit__(self, exc_type, exc_value, traceback):
117         if threading is not None:
118             self._lock.release()
119         self.deinit()
120
121     def scan(self):
122         """Scan for attached devices"""
123         return self._i2c.scan()
124
125     def readfrom_into(self, address, buffer, *, start=0, end=None):
126         """Read from a device at specified address into a buffer"""
127         if start != 0 or end is not None:
128             if end is None:
129                 end = len(buffer)
130             buffer = memoryview(buffer)[start:end]
131         stop = True  # remove for efficiency later
132         return self._i2c.readfrom_into(address, buffer, stop=stop)
133
134     def writeto(self, address, buffer, *, start=0, end=None, stop=True):
135         """Write to a device at specified address from a buffer"""
136         if isinstance(buffer, str):
137             buffer = bytes([ord(x) for x in buffer])
138         if start != 0 or end is not None:
139             if end is None:
140                 return self._i2c.writeto(address, memoryview(buffer)[start:], stop=stop)
141             return self._i2c.writeto(address, memoryview(buffer)[start:end], stop=stop)
142         return self._i2c.writeto(address, buffer, stop=stop)
143
144     def writeto_then_readfrom(
145         self,
146         address,
147         buffer_out,
148         buffer_in,
149         *,
150         out_start=0,
151         out_end=None,
152         in_start=0,
153         in_end=None,
154         stop=False,
155     ):
156         """ "Write to a device at specified address from a buffer then read
157         from a device at specified address into a buffer
158         """
159         return self._i2c.writeto_then_readfrom(
160             address,
161             buffer_out,
162             buffer_in,
163             out_start=out_start,
164             out_end=out_end,
165             in_start=in_start,
166             in_end=in_end,
167             stop=stop,
168         )
169
170
171 class SPI(Lockable):
172     """
173     Busio SPI Class for CircuitPython Compatibility. Used
174     for both MicroPython and Linux.
175     """
176
177     def __init__(self, clock, MOSI=None, MISO=None):
178         self.deinit()
179         if detector.board.ftdi_ft232h:
180             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import SPI as _SPI
181             from adafruit_blinka.microcontroller.ftdi_mpsse.ft232h.pin import (
182                 SCK,
183                 MOSI,
184                 MISO,
185             )
186
187             self._spi = _SPI()
188             self._pins = (SCK, MOSI, MISO)
189             return
190         if detector.board.binho_nova:
191             from adafruit_blinka.microcontroller.nova.spi import SPI as _SPI
192             from adafruit_blinka.microcontroller.nova.pin import SCK, MOSI, MISO
193
194             self._spi = _SPI(clock)
195             self._pins = (SCK, MOSI, MISO)
196             return
197         if detector.board.greatfet_one:
198             from adafruit_blinka.microcontroller.nxp_lpc4330.spi import SPI as _SPI
199             from adafruit_blinka.microcontroller.nxp_lpc4330.pin import SCK, MOSI, MISO
200
201             self._spi = _SPI()
202             self._pins = (SCK, MOSI, MISO)
203             return
204         if detector.board.pico_u2if:
205             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_Pico as _SPI
206
207             self._spi = _SPI(clock)  # this is really all that's needed
208             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
209             return
210         if detector.board.feather_u2if:
211             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_Feather as _SPI
212
213             self._spi = _SPI(clock)  # this is really all that's needed
214             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
215             return
216         if detector.chip.id == ap_chip.RP2040:
217             from adafruit_blinka.microcontroller.rp2040.spi import SPI as _SPI
218
219             self._spi = _SPI(clock, MOSI, MISO)  # Pins configured on instantiation
220             self._pins = (clock, clock, clock)  # These don't matter, they're discarded
221             return
222         if detector.board.any_embedded_linux:
223             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
224         elif detector.board.ftdi_ft2232h:
225             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import SPI as _SPI
226         else:
227             from adafruit_blinka.microcontroller.generic_micropython.spi import (
228                 SPI as _SPI,
229             )
230         from microcontroller.pin import spiPorts
231
232         for portId, portSck, portMosi, portMiso in spiPorts:
233             if (
234                 (clock == portSck)
235                 and MOSI in (portMosi, None)  # Clock is required!
236                 and MISO in (portMiso, None)  # But can do with just output
237             ):  # Or just input
238                 self._spi = _SPI(portId)
239                 self._pins = (portSck, portMosi, portMiso)
240                 break
241         else:
242             raise ValueError(
243                 "No Hardware SPI on (SCLK, MOSI, MISO)={}\nValid SPI ports:{}".format(
244                     (clock, MOSI, MISO), spiPorts
245                 )
246             )
247
248     def configure(self, baudrate=100000, polarity=0, phase=0, bits=8):
249         """Update the configuration"""
250         if detector.board.any_raspberry_pi or detector.board.any_raspberry_pi_40_pin:
251             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
252         elif detector.board.BEAGLEBONE_AI:
253             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
254         elif detector.board.any_beaglebone:
255             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
256         elif detector.board.any_orange_pi:
257             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
258         elif detector.board.any_nanopi and detector.chip.id == ap_chip.SUN8I:
259             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
260         elif board_id == ap_board.GIANT_BOARD:
261             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
262         elif board_id == ap_board.CORAL_EDGE_TPU_DEV:
263             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
264         elif board_id == ap_board.CORAL_EDGE_TPU_DEV_MINI:
265             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
266         elif board_id == ap_board.ODROID_C2:
267             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
268         elif board_id == ap_board.ODROID_C4:
269             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
270         elif board_id == ap_board.ODROID_XU4:
271             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
272         elif board_id == ap_board.DRAGONBOARD_410C:
273             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
274         elif board_id == ap_board.JETSON_NANO:
275             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
276         elif board_id == ap_board.JETSON_TX1:
277             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
278         elif board_id == ap_board.JETSON_TX2:
279             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
280         elif board_id == ap_board.JETSON_XAVIER:
281             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
282         elif board_id == ap_board.JETSON_NX:
283             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
284         elif detector.board.ROCK_PI_S:
285             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
286         elif detector.board.ROCK_PI_4:
287             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
288         elif detector.board.ROCK_PI_E:
289             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
290         elif detector.board.SIFIVE_UNLEASHED:
291             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
292         elif detector.board.ftdi_ft232h:
293             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import (
294                 SPI as _SPI,
295             )
296         elif detector.board.ftdi_ft2232h:
297             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import (
298                 SPI as _SPI,
299             )
300         elif detector.board.binho_nova:
301             from adafruit_blinka.microcontroller.nova.spi import SPI as _SPI
302         elif detector.board.greatfet_one:
303             from adafruit_blinka.microcontroller.nxp_lpc4330.spi import SPI as _SPI
304         elif board_id in (
305             ap_board.PINE64,
306             ap_board.PINEBOOK,
307             ap_board.PINEPHONE,
308             ap_board.SOPINE,
309         ):
310             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
311         elif board_id == ap_board.PINEH64:
312             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
313         elif board_id == ap_board.CLOCKWORK_CPI3:
314             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
315         elif board_id == ap_board.ONION_OMEGA2:
316             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
317         elif detector.board.any_lubancat and detector.chip.id == ap_chip.IMX6ULL:
318             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
319         elif detector.board.pico_u2if:
320             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_Pico as _SPI
321         elif detector.board.feather_u2if:
322             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_Feather as _SPI
323         elif detector.chip.id == ap_chip.RP2040:
324             from adafruit_blinka.microcontroller.rp2040.spi import SPI as _SPI
325         else:
326             from adafruit_blinka.microcontroller.generic_micropython.spi import (
327                 SPI as _SPI,
328             )
329
330         if self._locked:
331             # TODO check if #init ignores MOSI=None rather than unsetting, to save _pinIds attribute
332             self._spi.init(
333                 baudrate=baudrate,
334                 polarity=polarity,
335                 phase=phase,
336                 bits=bits,
337                 firstbit=_SPI.MSB,
338             )
339         else:
340             raise RuntimeError("First call try_lock()")
341
342     def deinit(self):
343         """Deinitialization"""
344         self._spi = None
345         self._pinIds = None
346
347     @property
348     def frequency(self):
349         """Return the baud rate if implemented"""
350         try:
351             return self._spi.frequency
352         except AttributeError:
353             raise NotImplementedError(
354                 "Frequency attribute not implemented for this platform"
355             ) from AttributeError
356
357     def write(self, buf, start=0, end=None):
358         """Write to the SPI device"""
359         return self._spi.write(buf, start, end)
360
361     def readinto(self, buf, start=0, end=None, write_value=0):
362         """Read from the SPI device into a buffer"""
363         return self._spi.readinto(buf, start, end, write_value=write_value)
364
365     def write_readinto(
366         self, buffer_out, buffer_in, out_start=0, out_end=None, in_start=0, in_end=None
367     ):
368         """Write to the SPI device and read from the SPI device into a buffer"""
369         return self._spi.write_readinto(
370             buffer_out, buffer_in, out_start, out_end, in_start, in_end
371         )
372
373
374 class UART(Lockable):
375     """
376     Busio UART Class for CircuitPython Compatibility. Used
377     for MicroPython and a few other non-Linux boards.
378     """
379
380     class Parity(Enum):
381         """Parity Enumeration"""
382
383         pass  # pylint: disable=unnecessary-pass
384
385     Parity.ODD = Parity()
386     Parity.EVEN = Parity()
387
388     def __init__(
389         self,
390         tx,
391         rx,
392         baudrate=9600,
393         bits=8,
394         parity=None,
395         stop=1,
396         timeout=1000,
397         receiver_buffer_size=64,
398         flow=None,
399     ):
400         if detector.board.any_embedded_linux:
401             raise RuntimeError(
402                 "busio.UART not supported on this platform. Please use pyserial instead."
403             )
404         if detector.board.binho_nova:
405             from adafruit_blinka.microcontroller.nova.uart import UART as _UART
406         elif detector.board.greatfet_one:
407             from adafruit_blinka.microcontroller.nxp_lpc4330.uart import UART as _UART
408         elif detector.chip.id == ap_chip.RP2040:
409             from adafruit_blinka.microcontroller.rp2040.uart import UART as _UART
410         else:
411             from machine import UART as _UART
412
413         from microcontroller.pin import uartPorts
414
415         self.baudrate = baudrate
416
417         if flow is not None:  # default 0
418             raise NotImplementedError(
419                 "Parameter '{}' unsupported on {}".format("flow", agnostic.board_id)
420             )
421
422         # translate parity flag for Micropython
423         if parity is UART.Parity.ODD:
424             parity = 1
425         elif parity is UART.Parity.EVEN:
426             parity = 0
427         elif parity is None:
428             pass
429         else:
430             raise ValueError("Invalid parity")
431
432         if detector.chip.id == ap_chip.RP2040:
433             self._uart = _UART(
434                 tx,
435                 rx,
436                 baudrate=baudrate,
437                 bits=bits,
438                 parity=parity,
439                 stop=stop,
440             )
441         else:
442             # check tx and rx have hardware support
443             for portId, portTx, portRx in uartPorts:  #
444                 if portTx == tx and portRx == rx:
445                     self._uart = _UART(
446                         portId,
447                         baudrate,
448                         bits=bits,
449                         parity=parity,
450                         stop=stop,
451                         timeout=timeout,
452                         read_buf_len=receiver_buffer_size,
453                     )
454                     break
455             else:
456                 raise ValueError(
457                     "No Hardware UART on (tx,rx)={}\nValid UART ports: {}".format(
458                         (tx, rx), uartPorts
459                     )
460                 )
461
462     def deinit(self):
463         """Deinitialization"""
464         if detector.board.binho_nova:
465             self._uart.deinit()
466         self._uart = None
467
468     def read(self, nbytes=None):
469         """Read from the UART"""
470         return self._uart.read(nbytes)
471
472     def readinto(self, buf, nbytes=None):
473         """Read from the UART into a buffer"""
474         return self._uart.readinto(buf, nbytes)
475
476     def readline(self):
477         """Read a line of characters up to a newline charater from the UART"""
478         return self._uart.readline()
479
480     def write(self, buf):
481         """Write to the UART from a buffer"""
482         return self._uart.write(buf)
483
484
485 class OneWire:
486     """
487     Stub class for OneWire, which is currently not implemented
488     """
489
490     def __init__(self, pin):
491         raise NotImplementedError("OneWire has not been implemented")
492
493     def deinit(self):
494         """
495         Deinitialize the OneWire bus and release any hardware resources for reuse.
496         """
497         raise NotImplementedError("OneWire has not been implemented")
498
499     def reset(self):
500         """
501         Reset the OneWire bus and read presence
502         """
503         raise NotImplementedError("OneWire has not been implemented")
504
505     def read_bit(self):
506         """
507         Read in a bit
508         """
509         raise NotImplementedError("OneWire has not been implemented")
510
511     def write_bit(self, value):
512         """
513         Write out a bit based on value.
514         """
515         raise NotImplementedError("OneWire has not been implemented")