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