/ src / bitbangio.py
bitbangio.py
  1  """
  2  `bitbangio` - Bitbanged bus protocols
  3  ==============================================================
  4  
  5  See `CircuitPython:bitbangio` in CircuitPython for more details.
  6  
  7  * Author(s): cefn
  8  """
  9  
 10  import adafruit_platformdetect.constants.boards as ap_board
 11  from adafruit_blinka import Lockable, agnostic
 12  
 13  # pylint: disable=import-outside-toplevel,too-many-arguments
 14  
 15  
 16  class I2C(Lockable):
 17      """Bitbang/Software I2C implementation"""
 18  
 19      def __init__(self, scl, sda, frequency=400000):
 20          # TODO: This one is a bit questionable:
 21          if agnostic.board_id == ap_board.PYBOARD:
 22              raise NotImplementedError("No software I2C on {}".format(agnostic.board_id))
 23          if agnostic.detector.board.any_embedded_linux:
 24              # TODO: Attempt to load this library automatically
 25              raise NotImplementedError(
 26                  "For bitbangio on Linux, please use Adafruit_CircuitPython_BitbangIO"
 27              )
 28          self.init(scl, sda, frequency)
 29  
 30      def init(self, scl, sda, frequency):
 31          """Initialization"""
 32          from machine import Pin
 33          from machine import I2C as _I2C
 34  
 35          self.deinit()
 36          id = (  # pylint: disable=redefined-builtin
 37              -1
 38          )  # force bitbanging implementation - in future
 39          # introspect platform if SDA/SCL matches hardware I2C
 40          self._i2c = _I2C(id, Pin(scl.id), Pin(sda.id), freq=frequency)
 41  
 42      def deinit(self):
 43          """Deinitialization"""
 44          try:
 45              del self._i2c
 46          except AttributeError:
 47              pass
 48  
 49      def __enter__(self):
 50          return self
 51  
 52      def __exit__(self, exc_type, exc_value, traceback):
 53          self.deinit()
 54  
 55      def scan(self):
 56          """Scan for attached devices"""
 57          return self._i2c.scan()
 58  
 59      def readfrom_into(self, address, buffer, start=0, end=None):
 60          """Read from a device at specified address into a buffer"""
 61          if start != 0 or end is not None:
 62              if end is None:
 63                  end = len(buffer)
 64              buffer = memoryview(buffer)[start:end]
 65          stop = True  # remove for efficiency later
 66          return self._i2c.readfrom_into(address, buffer, stop)
 67  
 68      def writeto(self, address, buffer, start=0, end=None, stop=True):
 69          """Write to a device at specified address from a buffer"""
 70          if start != 0 or end is not None:
 71              if end is None:
 72                  return self._i2c.writeto(address, memoryview(buffer)[start:], stop)
 73              return self._i2c.writeto(address, memoryview(buffer)[start:end], stop)
 74          return self._i2c.writeto(address, buffer, stop)
 75  
 76  
 77  # TODO untested, as actually busio.SPI was on
 78  # tasklist https://github.com/adafruit/Adafruit_Micropython_Blinka/issues/2 :(
 79  class SPI(Lockable):
 80      """Bitbang/Software SPI implementation"""
 81  
 82      def __init__(self, clock, MOSI=None, MISO=None):
 83          if agnostic.detector.board.any_embedded_linux:
 84              # TODO: Attempt to load this library automatically
 85              raise NotImplementedError(
 86                  "For bitbangio on Linux, please use Adafruit_CircuitPython_BitbangIO"
 87              )
 88          from machine import SPI as _SPI
 89  
 90          self._spi = _SPI(-1)
 91          self._pins = (clock, MOSI, MISO)
 92  
 93      def configure(self, baudrate=100000, polarity=0, phase=0, bits=8):
 94          """Update the configuration"""
 95          from machine import Pin
 96          from machine import SPI as _SPI
 97  
 98          if self._locked:
 99              # TODO verify if _spi obj 'caches' sck, mosi, miso to
100              # avoid storing in _attributeIds (duplicated in busio)
101              # i.e. #init ignores MOSI=None rather than unsetting
102              self._spi.init(
103                  baudrate=baudrate,
104                  polarity=polarity,
105                  phase=phase,
106                  bits=bits,
107                  firstbit=_SPI.MSB,
108                  sck=Pin(self._pins[0].id),
109                  mosi=Pin(self._pins[1].id),
110                  miso=Pin(self._pins[2].id),
111              )
112          else:
113              raise RuntimeError("First call try_lock()")
114  
115      def write(self, buf):
116          """Write to the SPI device"""
117          return self._spi.write(buf)
118  
119      def readinto(self, buf):
120          """Read from the SPI device into a buffer"""
121          return self.readinto(buf)
122  
123      def write_readinto(self, buffer_out, buffer_in):
124          """Write to the SPI device and read from the SPI device into a buffer"""
125          return self.write_readinto(buffer_out, buffer_in)