1 # SPDX-FileCopyrightText: 2021 Melissa LeBlanc-Williams for Adafruit Industries
 
   3 # SPDX-License-Identifier: MIT
 
   5 `busio` - Bus protocol support like I2C and SPI
 
   6 =================================================
 
   8 See `CircuitPython:busio` in CircuitPython for more details.
 
  18 # pylint: disable=unused-import
 
  19 import adafruit_platformdetect.constants.boards as ap_board
 
  20 import adafruit_platformdetect.constants.chips as ap_chip
 
  21 from adafruit_blinka import Enum, Lockable, agnostic
 
  22 from adafruit_blinka.agnostic import board_id, detector
 
  24 # pylint: disable=import-outside-toplevel,too-many-branches,too-many-statements
 
  25 # pylint: disable=too-many-arguments,too-many-function-args,too-many-return-statements
 
  30     Busio I2C Class for CircuitPython Compatibility. Used
 
  31     for both MicroPython and Linux.
 
  34     def __init__(self, scl, sda, frequency=100000):
 
  35         self.init(scl, sda, frequency)
 
  37     def init(self, scl, sda, frequency):
 
  40         if detector.board.ftdi_ft232h:
 
  41             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.i2c import I2C as _I2C
 
  43             self._i2c = _I2C(frequency=frequency)
 
  45         if detector.board.binho_nova:
 
  46             from adafruit_blinka.microcontroller.nova.i2c import I2C as _I2C
 
  48             self._i2c = _I2C(frequency=frequency)
 
  50         if detector.board.microchip_mcp2221:
 
  51             from adafruit_blinka.microcontroller.mcp2221.i2c import I2C as _I2C
 
  53             self._i2c = _I2C(frequency=frequency)
 
  55         if detector.board.greatfet_one:
 
  56             from adafruit_blinka.microcontroller.nxp_lpc4330.i2c import I2C as _I2C
 
  58             self._i2c = _I2C(frequency=frequency)
 
  60         if detector.board.pico_u2if:
 
  61             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import I2C_Pico as _I2C
 
  63             self._i2c = _I2C(scl, sda, frequency=frequency)
 
  65         if detector.board.feather_u2if:
 
  66             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
 
  70             self._i2c = _I2C(scl, sda, frequency=frequency)
 
  72         if detector.board.qtpy_u2if:
 
  73             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import I2C_QTPY as _I2C
 
  75             self._i2c = _I2C(scl, sda, frequency=frequency)
 
  77         if detector.board.itsybitsy_u2if:
 
  78             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
 
  79                 I2C_ItsyBitsy as _I2C,
 
  82             self._i2c = _I2C(scl, sda, frequency=frequency)
 
  84         if detector.board.macropad_u2if:
 
  85             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
 
  89             self._i2c = _I2C(scl, sda, frequency=frequency)
 
  91         if detector.board.qt2040_trinkey_u2if:
 
  92             from adafruit_blinka.microcontroller.rp2040_u2if.i2c import (
 
  93                 I2C_QT2040_Trinkey as _I2C,
 
  96             self._i2c = _I2C(scl, sda, frequency=frequency)
 
  98         if detector.chip.id == ap_chip.RP2040:
 
  99             from adafruit_blinka.microcontroller.rp2040.i2c import I2C as _I2C
 
 101             self._i2c = _I2C(scl, sda, frequency=frequency)
 
 103         if detector.board.any_embedded_linux:
 
 104             from adafruit_blinka.microcontroller.generic_linux.i2c import I2C as _I2C
 
 105         elif detector.board.ftdi_ft2232h:
 
 106             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.i2c import I2C as _I2C
 
 108             from adafruit_blinka.microcontroller.generic_micropython.i2c import (
 
 111         from microcontroller.pin import i2cPorts
 
 113         for portId, portScl, portSda in i2cPorts:
 
 115                 # pylint: disable=unexpected-keyword-arg
 
 116                 if scl == portScl and sda == portSda:
 
 117                     self._i2c = _I2C(portId, mode=_I2C.MASTER, baudrate=frequency)
 
 119                 # pylint: enable=unexpected-keyword-arg
 
 124                 "No Hardware I2C on (scl,sda)={}\nValid I2C ports: {}".format(
 
 128         if threading is not None:
 
 129             self._lock = threading.RLock()
 
 132         """Deinitialization"""
 
 135         except AttributeError:
 
 139         if threading is not None:
 
 143     def __exit__(self, exc_type, exc_value, traceback):
 
 144         if threading is not None:
 
 149         """Scan for attached devices"""
 
 150         return self._i2c.scan()
 
 152     def readfrom_into(self, address, buffer, *, start=0, end=None):
 
 153         """Read from a device at specified address into a buffer"""
 
 154         if start != 0 or end is not None:
 
 157             buffer = memoryview(buffer)[start:end]
 
 158         stop = True  # remove for efficiency later
 
 159         return self._i2c.readfrom_into(address, buffer, stop=stop)
 
 161     def writeto(self, address, buffer, *, start=0, end=None, stop=True):
 
 162         """Write to a device at specified address from a buffer"""
 
 163         if isinstance(buffer, str):
 
 164             buffer = bytes([ord(x) for x in buffer])
 
 165         if start != 0 or end is not None:
 
 167                 return self._i2c.writeto(address, memoryview(buffer)[start:], stop=stop)
 
 168             return self._i2c.writeto(address, memoryview(buffer)[start:end], stop=stop)
 
 169         return self._i2c.writeto(address, buffer, stop=stop)
 
 171     def writeto_then_readfrom(
 
 183         """ "Write to a device at specified address from a buffer then read
 
 184         from a device at specified address into a buffer
 
 186         return self._i2c.writeto_then_readfrom(
 
 200     Busio SPI Class for CircuitPython Compatibility. Used
 
 201     for both MicroPython and Linux.
 
 204     def __init__(self, clock, MOSI=None, MISO=None):
 
 206         if detector.board.ftdi_ft232h:
 
 207             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import SPI as _SPI
 
 208             from adafruit_blinka.microcontroller.ftdi_mpsse.ft232h.pin import (
 
 215             self._pins = (SCK, MOSI, MISO)
 
 217         if detector.board.binho_nova:
 
 218             from adafruit_blinka.microcontroller.nova.spi import SPI as _SPI
 
 219             from adafruit_blinka.microcontroller.nova.pin import SCK, MOSI, MISO
 
 221             self._spi = _SPI(clock)
 
 222             self._pins = (SCK, MOSI, MISO)
 
 224         if detector.board.greatfet_one:
 
 225             from adafruit_blinka.microcontroller.nxp_lpc4330.spi import SPI as _SPI
 
 226             from adafruit_blinka.microcontroller.nxp_lpc4330.pin import SCK, MOSI, MISO
 
 229             self._pins = (SCK, MOSI, MISO)
 
 231         if detector.board.pico_u2if:
 
 232             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_Pico as _SPI
 
 234             self._spi = _SPI(clock)  # this is really all that's needed
 
 235             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
 
 237         if detector.board.feather_u2if:
 
 238             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
 
 242             self._spi = _SPI(clock)  # this is really all that's needed
 
 243             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
 
 245         if detector.board.itsybitsy_u2if:
 
 246             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
 
 247                 SPI_ItsyBitsy as _SPI,
 
 250             self._spi = _SPI(clock)  # this is really all that's needed
 
 251             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
 
 253         if detector.board.macropad_u2if:
 
 254             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
 
 255                 SPI_MacroPad as _SPI,
 
 258             self._spi = _SPI(clock)  # this is really all that's needed
 
 259             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
 
 261         if detector.board.qtpy_u2if:
 
 262             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_QTPY as _SPI
 
 264             self._spi = _SPI(clock)  # this is really all that's needed
 
 265             self._pins = (clock, clock, clock)  # will determine MOSI/MISO from clock
 
 267         if detector.chip.id == ap_chip.RP2040:
 
 268             from adafruit_blinka.microcontroller.rp2040.spi import SPI as _SPI
 
 270             self._spi = _SPI(clock, MOSI, MISO)  # Pins configured on instantiation
 
 271             self._pins = (clock, clock, clock)  # These don't matter, they're discarded
 
 273         if detector.board.any_embedded_linux:
 
 274             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
 
 275         elif detector.board.ftdi_ft2232h:
 
 276             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import SPI as _SPI
 
 278             from adafruit_blinka.microcontroller.generic_micropython.spi import (
 
 281         from microcontroller.pin import spiPorts
 
 283         for portId, portSck, portMosi, portMiso in spiPorts:
 
 286                 and MOSI in (portMosi, None)  # Clock is required!
 
 287                 and MISO in (portMiso, None)  # But can do with just output
 
 289                 self._spi = _SPI(portId)
 
 290                 self._pins = (portSck, portMosi, portMiso)
 
 294                 "No Hardware SPI on (SCLK, MOSI, MISO)={}\nValid SPI ports:{}".format(
 
 295                     (clock, MOSI, MISO), spiPorts
 
 299     def configure(self, baudrate=100000, polarity=0, phase=0, bits=8):
 
 300         """Update the configuration"""
 
 301         if detector.board.any_nanopi and detector.chip.id == ap_chip.SUN8I:
 
 302             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
 
 303         elif detector.board.ftdi_ft232h:
 
 304             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import (
 
 307         elif detector.board.ftdi_ft2232h:
 
 308             from adafruit_blinka.microcontroller.ftdi_mpsse.mpsse.spi import (
 
 311         elif detector.board.binho_nova:
 
 312             from adafruit_blinka.microcontroller.nova.spi import SPI as _SPI
 
 313         elif detector.board.greatfet_one:
 
 314             from adafruit_blinka.microcontroller.nxp_lpc4330.spi import SPI as _SPI
 
 315         elif detector.board.any_lubancat and detector.chip.id == ap_chip.IMX6ULL:
 
 316             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
 
 317         elif detector.board.pico_u2if:
 
 318             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_Pico as _SPI
 
 319         elif detector.board.feather_u2if:
 
 320             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
 
 323         elif detector.board.itsybitsy_u2if:
 
 324             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
 
 325                 SPI_ItsyBitsy as _SPI,
 
 327         elif detector.board.macropad_u2if:
 
 328             from adafruit_blinka.microcontroller.rp2040_u2if.spi import (
 
 329                 SPI_MacroPad as _SPI,
 
 331         elif detector.board.qtpy_u2if:
 
 332             from adafruit_blinka.microcontroller.rp2040_u2if.spi import SPI_QTPY as _SPI
 
 333         elif detector.chip.id == ap_chip.RP2040:
 
 334             from adafruit_blinka.microcontroller.rp2040.spi import SPI as _SPI
 
 335         elif detector.board.any_embedded_linux:
 
 336             from adafruit_blinka.microcontroller.generic_linux.spi import SPI as _SPI
 
 338             from adafruit_blinka.microcontroller.generic_micropython.spi import (
 
 343             # TODO check if #init ignores MOSI=None rather than unsetting, to save _pinIds attribute
 
 352             raise RuntimeError("First call try_lock()")
 
 355         """Deinitialization"""
 
 361         """Return the baud rate if implemented"""
 
 363             return self._spi.frequency
 
 364         except AttributeError as error:
 
 365             raise NotImplementedError(
 
 366                 "Frequency attribute not implemented for this platform"
 
 369     def write(self, buf, start=0, end=None):
 
 370         """Write to the SPI device"""
 
 371         return self._spi.write(buf, start, end)
 
 373     def readinto(self, buf, start=0, end=None, write_value=0):
 
 374         """Read from the SPI device into a buffer"""
 
 375         return self._spi.readinto(buf, start, end, write_value=write_value)
 
 378         self, buffer_out, buffer_in, out_start=0, out_end=None, in_start=0, in_end=None
 
 380         """Write to the SPI device and read from the SPI device into a buffer"""
 
 381         return self._spi.write_readinto(
 
 382             buffer_out, buffer_in, out_start, out_end, in_start, in_end
 
 386 class UART(Lockable):
 
 388     Busio UART Class for CircuitPython Compatibility. Used
 
 389     for MicroPython and a few other non-Linux boards.
 
 393         """Parity Enumeration"""
 
 395         pass  # pylint: disable=unnecessary-pass
 
 397     Parity.ODD = Parity()
 
 398     Parity.EVEN = Parity()
 
 409         receiver_buffer_size=64,
 
 412         if detector.board.any_embedded_linux:
 
 414                 "busio.UART not supported on this platform. Please use pyserial instead."
 
 416         if detector.board.binho_nova:
 
 417             from adafruit_blinka.microcontroller.nova.uart import UART as _UART
 
 418         elif detector.board.greatfet_one:
 
 419             from adafruit_blinka.microcontroller.nxp_lpc4330.uart import UART as _UART
 
 420         elif detector.chip.id == ap_chip.RP2040:
 
 421             from adafruit_blinka.microcontroller.rp2040.uart import UART as _UART
 
 423             from machine import UART as _UART
 
 425         from microcontroller.pin import uartPorts
 
 427         self.baudrate = baudrate
 
 429         if flow is not None:  # default 0
 
 430             raise NotImplementedError(
 
 431                 "Parameter '{}' unsupported on {}".format("flow", agnostic.board_id)
 
 434         # translate parity flag for Micropython
 
 435         if parity is UART.Parity.ODD:
 
 437         elif parity is UART.Parity.EVEN:
 
 442             raise ValueError("Invalid parity")
 
 444         if detector.chip.id == ap_chip.RP2040:
 
 454             # check tx and rx have hardware support
 
 455             for portId, portTx, portRx in uartPorts:  #
 
 456                 if portTx == tx and portRx == rx:
 
 464                         read_buf_len=receiver_buffer_size,
 
 469                     "No Hardware UART on (tx,rx)={}\nValid UART ports: {}".format(
 
 475         """Deinitialization"""
 
 476         if detector.board.binho_nova:
 
 480     def read(self, nbytes=None):
 
 481         """Read from the UART"""
 
 482         return self._uart.read(nbytes)
 
 484     def readinto(self, buf, nbytes=None):
 
 485         """Read from the UART into a buffer"""
 
 486         return self._uart.readinto(buf, nbytes)
 
 489         """Read a line of characters up to a newline charater from the UART"""
 
 490         return self._uart.readline()
 
 492     def write(self, buf):
 
 493         """Write to the UART from a buffer"""
 
 494         return self._uart.write(buf)
 
 499     Stub class for OneWire, which is currently not implemented
 
 502     def __init__(self, pin):
 
 503         raise NotImplementedError("OneWire has not been implemented")
 
 507         Deinitialize the OneWire bus and release any hardware resources for reuse.
 
 509         raise NotImplementedError("OneWire has not been implemented")
 
 513         Reset the OneWire bus and read presence
 
 515         raise NotImplementedError("OneWire has not been implemented")
 
 521         raise NotImplementedError("OneWire has not been implemented")
 
 523     def write_bit(self, value):
 
 525         Write out a bit based on value.
 
 527         raise NotImplementedError("OneWire has not been implemented")