|
57 | 57 | _I2C_MANF_ID = const(0x0A)
|
58 | 58 | _I2C_PROD_ID = const(0x510)
|
59 | 59 |
|
| 60 | +_SPI_MANF_ID = const(0x04) |
| 61 | +_SPI_PROD_ID = const(0x302) |
| 62 | + |
60 | 63 | class FRAM:
|
61 | 64 | """
|
62 | 65 | Driver base for the FRAM Breakout.
|
@@ -102,12 +105,6 @@ def write_protected(self):
|
102 | 105 | """
|
103 | 106 | return self._wp if self._wp_pin is None else self._wp_pin.value
|
104 | 107 |
|
105 |
| - @write_protected.setter |
106 |
| - def write_protected(self, value): |
107 |
| - self._wp = value |
108 |
| - if not self._wp_pin is None: |
109 |
| - self._wp_pin.value = value |
110 |
| - |
111 | 108 | def __len__(self):
|
112 | 109 | """ The maximum size of the current FRAM chip. This is the highest
|
113 | 110 | register location that can be read or written to.
|
@@ -257,3 +254,110 @@ def _write(self, start_register, data, wraparound=False):
|
257 | 254 | buffer[1] = ((start_register + i) - self._max_size) & 0xFF
|
258 | 255 | buffer[2] = data[i]
|
259 | 256 | i2c.write(buffer)
|
| 257 | + |
| 258 | + # pylint: disable=no-member |
| 259 | + @FRAM.write_protected.setter |
| 260 | + def write_protected(self, value): |
| 261 | + if value not in (True, False): |
| 262 | + raise ValueError("Write protected value must be 'True' or 'False'.") |
| 263 | + self._wp = value |
| 264 | + if not self._wp_pin is None: |
| 265 | + self._wp_pin.value = value |
| 266 | + |
| 267 | +# the following pylint disables are related to the '_SPI_OPCODE' consts, the super |
| 268 | +# class setter '@FRAM.write_protected.setter', and pylint not being able to see |
| 269 | +# 'spi.write()' in SPIDevice. Travis run for reference: |
| 270 | +# https://travis-ci.com/sommersoft/Adafruit_CircuitPython_FRAM/builds/87112669 |
| 271 | + |
| 272 | +# pylint: disable=no-member,undefined-variable |
| 273 | +class FRAM_SPI(FRAM): |
| 274 | + """ SPI class for FRAM. |
| 275 | +
|
| 276 | + :param: ~busio.SPI spi_bus: The SPI bus the FRAM is connected to. |
| 277 | + :param: ~digitalio.DigitalInOut spi_cs: The SPI CS pin. |
| 278 | + :param: bool write_protect: Turns on/off initial write protection. |
| 279 | + Default is ``False``. |
| 280 | + :param: wp_pin: (Optional) Physical pin connected to the ``WP`` breakout pin. |
| 281 | + Must be a ``digitalio.DigitalInOut`` object. |
| 282 | + :param int baudrate: SPI baudrate to use. Default is ``1000000``. |
| 283 | + """ |
| 284 | + |
| 285 | + _SPI_OPCODE_WREN = const(0x6) # Set write enable latch |
| 286 | + _SPI_OPCODE_WRDI = const(0x4) # Reset write enable latch |
| 287 | + _SPI_OPCODE_RDSR = const(0x5) # Read status register |
| 288 | + _SPI_OPCODE_WRSR = const(0x1) # Write status register |
| 289 | + _SPI_OPCODE_READ = const(0x3) # Read memory code |
| 290 | + _SPI_OPCODE_WRITE = const(0x2) # Write memory code |
| 291 | + _SPI_OPCODE_RDID = const(0x9F) # Read device ID |
| 292 | + |
| 293 | + #pylint: disable=too-many-arguments,too-many-locals |
| 294 | + def __init__(self, spi_bus, spi_cs, write_protect=False, |
| 295 | + wp_pin=None, baudrate=100000): |
| 296 | + from adafruit_bus_device.spi_device import SPIDevice as spidev |
| 297 | + _spi = spidev(spi_bus, spi_cs, baudrate=baudrate) |
| 298 | + |
| 299 | + read_buffer = bytearray(4) |
| 300 | + with _spi as spi: |
| 301 | + spi.write(bytearray([_SPI_OPCODE_RDID])) |
| 302 | + spi.readinto(read_buffer) |
| 303 | + prod_id = (read_buffer[3] << 8) + (read_buffer[2]) |
| 304 | + if (read_buffer[0] != _SPI_MANF_ID) and (prod_id != _SPI_PROD_ID): |
| 305 | + raise OSError("FRAM SPI device not found.") |
| 306 | + |
| 307 | + self._spi = _spi |
| 308 | + super().__init__(_MAX_SIZE_SPI, write_protect, wp_pin) |
| 309 | + |
| 310 | + def _read_register(self, register, read_buffer): |
| 311 | + write_buffer = bytearray(3) |
| 312 | + write_buffer[0] = _SPI_OPCODE_READ |
| 313 | + write_buffer[1] = register >> 8 |
| 314 | + write_buffer[2] = register & 0xFF |
| 315 | + with self._spi as spi: |
| 316 | + spi.write(write_buffer) |
| 317 | + spi.readinto(read_buffer) |
| 318 | + return read_buffer |
| 319 | + |
| 320 | + def _write(self, start_register, data, wraparound=False): |
| 321 | + buffer = bytearray(3) |
| 322 | + if not isinstance(data, int): |
| 323 | + data_length = len(data) |
| 324 | + else: |
| 325 | + data_length = 1 |
| 326 | + data = [data] |
| 327 | + if (start_register + data_length) - 1 > self._max_size: |
| 328 | + if wraparound: |
| 329 | + pass |
| 330 | + else: |
| 331 | + raise ValueError("Starting register + data length extends beyond" |
| 332 | + " FRAM maximum size. Use 'wraparound=True' to" |
| 333 | + " override this warning.") |
| 334 | + with self._spi as spi: |
| 335 | + spi.write(bytearray([_SPI_OPCODE_WREN])) |
| 336 | + with self._spi as spi: |
| 337 | + buffer[0] = _SPI_OPCODE_WRITE |
| 338 | + buffer[1] = start_register >> 8 |
| 339 | + buffer[2] = start_register & 0xFF |
| 340 | + spi.write(buffer) |
| 341 | + for i in range(0, data_length): |
| 342 | + spi.write(bytearray([data[i]])) |
| 343 | + with self._spi as spi: |
| 344 | + spi.write(bytearray([_SPI_OPCODE_WRDI])) |
| 345 | + |
| 346 | + @FRAM.write_protected.setter |
| 347 | + def write_protected(self, value): |
| 348 | + # While it is possible to protect block ranges on the SPI chip, |
| 349 | + # it seems superfluous to do so. So, block protection always protects |
| 350 | + # the entire memory (BP0 and BP1). |
| 351 | + if value not in (True, False): |
| 352 | + raise ValueError("Write protected value must be 'True' or 'False'.") |
| 353 | + self._wp = value |
| 354 | + write_buffer = bytearray(2) |
| 355 | + write_buffer[0] = _SPI_OPCODE_WRSR |
| 356 | + if value: |
| 357 | + write_buffer[1] = 0x8C # set WPEN, BP0, and BP1 |
| 358 | + else: |
| 359 | + write_buffer[1] = 0x00 # clear WPEN, BP0, and BP1 |
| 360 | + with self._spi as spi: |
| 361 | + spi.write(write_buffer) |
| 362 | + if self._wp_pin is not None: |
| 363 | + self._wp_pin.value = value |
0 commit comments