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