diff --git a/adafruit_fram.py b/adafruit_fram.py index 45c8341..fc39347 100644 --- a/adafruit_fram.py +++ b/adafruit_fram.py @@ -57,6 +57,9 @@ _I2C_MANF_ID = const(0x0A) _I2C_PROD_ID = const(0x510) +_SPI_MANF_ID = const(0x04) +_SPI_PROD_ID = const(0x302) + class FRAM: """ Driver base for the FRAM Breakout. @@ -102,12 +105,6 @@ def write_protected(self): """ return self._wp if self._wp_pin is None else self._wp_pin.value - @write_protected.setter - def write_protected(self, value): - self._wp = value - if not self._wp_pin is None: - self._wp_pin.value = value - def __len__(self): """ The maximum size of the current FRAM chip. This is the highest register location that can be read or written to. @@ -257,3 +254,110 @@ def _write(self, start_register, data, wraparound=False): buffer[1] = ((start_register + i) - self._max_size) & 0xFF buffer[2] = data[i] i2c.write(buffer) + + # pylint: disable=no-member + @FRAM.write_protected.setter + def write_protected(self, value): + if value not in (True, False): + raise ValueError("Write protected value must be 'True' or 'False'.") + self._wp = value + if not self._wp_pin is None: + self._wp_pin.value = value + +# the following pylint disables are related to the '_SPI_OPCODE' consts, the super +# class setter '@FRAM.write_protected.setter', and pylint not being able to see +# 'spi.write()' in SPIDevice. Travis run for reference: +# https://travis-ci.com/sommersoft/Adafruit_CircuitPython_FRAM/builds/87112669 + +# pylint: disable=no-member,undefined-variable +class FRAM_SPI(FRAM): + """ SPI class for FRAM. + + :param: ~busio.SPI spi_bus: The SPI bus the FRAM is connected to. + :param: ~digitalio.DigitalInOut spi_cs: The SPI CS pin. + :param: bool write_protect: Turns on/off initial write protection. + Default is ``False``. + :param: wp_pin: (Optional) Physical pin connected to the ``WP`` breakout pin. + Must be a ``digitalio.DigitalInOut`` object. + :param int baudrate: SPI baudrate to use. Default is ``1000000``. + """ + + _SPI_OPCODE_WREN = const(0x6) # Set write enable latch + _SPI_OPCODE_WRDI = const(0x4) # Reset write enable latch + _SPI_OPCODE_RDSR = const(0x5) # Read status register + _SPI_OPCODE_WRSR = const(0x1) # Write status register + _SPI_OPCODE_READ = const(0x3) # Read memory code + _SPI_OPCODE_WRITE = const(0x2) # Write memory code + _SPI_OPCODE_RDID = const(0x9F) # Read device ID + + #pylint: disable=too-many-arguments,too-many-locals + def __init__(self, spi_bus, spi_cs, write_protect=False, + wp_pin=None, baudrate=100000): + from adafruit_bus_device.spi_device import SPIDevice as spidev + _spi = spidev(spi_bus, spi_cs, baudrate=baudrate) + + read_buffer = bytearray(4) + with _spi as spi: + spi.write(bytearray([_SPI_OPCODE_RDID])) + spi.readinto(read_buffer) + prod_id = (read_buffer[3] << 8) + (read_buffer[2]) + if (read_buffer[0] != _SPI_MANF_ID) and (prod_id != _SPI_PROD_ID): + raise OSError("FRAM SPI device not found.") + + self._spi = _spi + super().__init__(_MAX_SIZE_SPI, write_protect, wp_pin) + + def _read_register(self, register, read_buffer): + write_buffer = bytearray(3) + write_buffer[0] = _SPI_OPCODE_READ + write_buffer[1] = register >> 8 + write_buffer[2] = register & 0xFF + with self._spi as spi: + spi.write(write_buffer) + spi.readinto(read_buffer) + return read_buffer + + def _write(self, start_register, data, wraparound=False): + buffer = bytearray(3) + if not isinstance(data, int): + data_length = len(data) + else: + data_length = 1 + data = [data] + if (start_register + data_length) - 1 > self._max_size: + if wraparound: + pass + else: + raise ValueError("Starting register + data length extends beyond" + " FRAM maximum size. Use 'wraparound=True' to" + " override this warning.") + with self._spi as spi: + spi.write(bytearray([_SPI_OPCODE_WREN])) + with self._spi as spi: + buffer[0] = _SPI_OPCODE_WRITE + buffer[1] = start_register >> 8 + buffer[2] = start_register & 0xFF + spi.write(buffer) + for i in range(0, data_length): + spi.write(bytearray([data[i]])) + with self._spi as spi: + spi.write(bytearray([_SPI_OPCODE_WRDI])) + + @FRAM.write_protected.setter + def write_protected(self, value): + # While it is possible to protect block ranges on the SPI chip, + # it seems superfluous to do so. So, block protection always protects + # the entire memory (BP0 and BP1). + if value not in (True, False): + raise ValueError("Write protected value must be 'True' or 'False'.") + self._wp = value + write_buffer = bytearray(2) + write_buffer[0] = _SPI_OPCODE_WRSR + if value: + write_buffer[1] = 0x8C # set WPEN, BP0, and BP1 + else: + write_buffer[1] = 0x00 # clear WPEN, BP0, and BP1 + with self._spi as spi: + spi.write(write_buffer) + if self._wp_pin is not None: + self._wp_pin.value = value diff --git a/docs/examples.rst b/docs/examples.rst index f54b285..d109edd 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -6,3 +6,7 @@ Ensure your device works with this simple test. .. literalinclude:: ../examples/fram_i2c_simpletest.py :caption: examples/fram_i2c_simpletest.py :linenos: + +.. literalinclude:: ../examples/fram_spi_simpletest.py + :caption: examples/fram_spi_simpletest.py + :linenos: diff --git a/examples/fram_i2c_simpletest.py b/examples/fram_i2c_simpletest.py index 72c040f..116d8ee 100644 --- a/examples/fram_i2c_simpletest.py +++ b/examples/fram_i2c_simpletest.py @@ -34,5 +34,5 @@ ## problems on memory-constrained platforms. #values = list(range(100)) # or bytearray or tuple -#fram[0:100] = values -#print(fram[0:100]) +#fram[0] = values +#print(fram[0:99]) diff --git a/examples/fram_spi_simpletest.py b/examples/fram_spi_simpletest.py new file mode 100644 index 0000000..2deb5f5 --- /dev/null +++ b/examples/fram_spi_simpletest.py @@ -0,0 +1,29 @@ +## Simple Example For CircuitPython/Python SPI FRAM Library + +import board +import busio +import digitalio +import adafruit_fram + +## Create a FRAM object. +spi = busio.SPI(board.SCK, board.MOSI, board.MISO) +cs = digitalio.DigitalInOut(board.D5) +fram = adafruit_fram.FRAM_SPI(spi, cs) + +## Write a single-byte value to register address '0' + +fram[0] = 1 + +## Read that byte to ensure a proper write. +## Note: 'read()' returns a bytearray + +print(fram[0]) + +## Or write a sequential value, then read the values back. +## Note: 'read()' returns a bytearray. It also allocates +## a buffer the size of 'length', which may cause +## problems on memory-constrained platforms. + +#values = list(range(100)) # or bytearray or tuple +#fram[0] = values +#print(fram[0:99])