From e06fd66bb35536d1bfc8728bf1b8fc55a2a5aa28 Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Fri, 12 Jan 2024 08:39:07 +0000 Subject: [PATCH] spi: Bug in presence detect. --- bdevice.py | 31 +++++---- eeprom/i2c/I2C.md | 81 +++++++++++++++--------- eeprom/spi/SPI.md | 70 +++++++++++++++------ eeprom/spi/eep_spi.py | 65 ++++++++++++------- eeprom/spi/eeprom_spi.py | 131 ++++++++++++++++----------------------- 5 files changed, 210 insertions(+), 168 deletions(-) diff --git a/bdevice.py b/bdevice.py index 6539ea6..158fa4d 100644 --- a/bdevice.py +++ b/bdevice.py @@ -4,7 +4,7 @@ # Documentation in BASE_CLASSES.md # Released under the MIT License (MIT). See LICENSE. -# Copyright (c) 2019 Peter Hinch +# Copyright (c) 2019-2024 Peter Hinch from micropython import const @@ -80,6 +80,7 @@ class BlockDevice: return 0 +# Hardware agnostic base class for EEPROM arrays class EepromDevice(BlockDevice): def __init__(self, nbits, nchips, chip_size, page_size, verbose): super().__init__(nbits, nchips, chip_size) @@ -96,26 +97,22 @@ class EepromDevice(BlockDevice): def get_page_size(self): # For test script return self._page_size + # Measuring page size should not be done in production code. See docs. def _set_pagesize(self, page_size): - if page_size is None: # Measure it + if page_size is None: # Measure it. self._psize(16) # Conservative old = self[:129] # Save old contents (nonvolatile!) + self._psize(256) # Ambitious + r = (16, 32, 64, 128) # Legal page sizes + 256 + for x in r: + self[x] = 255 # Write single bytes, don't invoke page write + self[0:129] = b"\0" * 129 # Zero 129 bytes attempting to use 256 byte pages try: - self._psize(256) # Ambitious - r = (16, 32, 64, 128) # Legal page sizes + 256 - for x in r: - self[x] = 255 # Write single bytes, don't invoke page write - self[0:129] = b"\0" * 129 # Zero 129 bytes attempting to use 256 byte pages - try: - ps = next(z for z in r if self[z]) - except StopIteration: - ps = 256 - self._psize(ps) - self[:129] = old - except: # If anything goes wrong, restore old data and raise - for n, v in enumerate(old): - self[n] = v - raise + ps = next(z for z in r if self[z]) + except StopIteration: + ps = 256 + self._psize(ps) + self[:129] = old else: # Validated page_size was supplied self._psize(page_size) diff --git a/eeprom/i2c/I2C.md b/eeprom/i2c/I2C.md index c3cab09..470771e 100644 --- a/eeprom/i2c/I2C.md +++ b/eeprom/i2c/I2C.md @@ -131,17 +131,13 @@ Arguments: devices it has detected. 4. `block_size=9` The block size reported to the filesystem. The size in bytes is `2**block_size` so is 512 bytes by default. - 5. `addr` override base address for first chip. - 6. `max_chips_count` override max_chips_count. + 5. `addr` Override base address for first chip. See + [4.1.6 Special configurations](./I2C.md#416-special-configurations). + 6. `max_chips_count` Override max_chips_count - see above reference. 7. `page_size=None` EEPROM chips have a page buffer. By default the driver determines the size of this automatically. It is possible to override this by - passing an integer being the page size in bytes: 16, 32, 64, 128 or 256. The - page size may vary between chips from different manufacturers even for the - same storage size. Note that specifying too large a value will most likely lead - to data corruption in write operations and will cause the test script's basic - test to fail. The correct value for a device may be found in in the chip - datasheet. It is also reported if `verbose` is set. Auto-detecting page size - carries a risk of data loss if power fails while auto-detect is in progress. + passing an integer being the page size in bytes: 16, 32, 64, 128 or 256. See + [4.1.5 Page size](./I2C.md#414-page-size) for issues surrounding this. In most cases only the first two arguments are used, with an array being instantiated with (for example): @@ -150,26 +146,13 @@ from machine import I2C from eeprom_i2c import EEPROM, T24C512 eep = EEPROM(I2C(2), T24C512) ``` -It is possible to configure multiple chips as multiple arrays. This is done by -means of the `addr` and `max_chips_count` args. Examples: - ```python - eeprom0 = EEPROM(i2c, max_chips_count = 2) - eeprom1 = EEPROM(i2c, addr = 0x52, max_chips_count = 2) - ``` - 1st array uses address 0x50 and 0x51 and 2nd uses address 0x52 and 0x53. - - individual chip usage: - ```python - eeprom0 = EEPROM(i2c, addr = 0x50, max_chips_count = 1) - eeprom1 = EEPROM(i2c, addr = 0x51, max_chips_count = 1) - ``` ### 4.1.2 Methods providing byte level access It is possible to read and write individual bytes or arrays of arbitrary size. Larger arrays are faster, especially when writing: the driver uses the chip's -hardware page access where possible. Writing a page (128 bytes) takes the same -time (~5ms) as writing a single byte. +hardware page access where possible. Writing a page takes the same time (~5ms) +as writing a single byte. #### 4.1.2.1 `__getitem__` and `__setitem__` @@ -223,6 +206,10 @@ Other than for debugging there is no need to call `scan()`: the constructor will throw a `RuntimeError` if it fails to communicate with and correctly identify the chip. +#### get_page_size + +Return the page size in bytes. + ### 4.1.4 Methods providing the block protocol These are provided by the base class. For the protocol definition see @@ -236,6 +223,37 @@ their use in application code is not recommended. `writeblocks()` `ioctl()` +### 4.1.5 Page size + +EEPROM chips have a RAM buffer enabling fast writing of data blocks. Writing a +page takes the same time (~5ms) as writing a single byte. The page size may vary +between chips from different manufacturers even for the same storage size. +Specifying too large a value will most likely lead to data corruption in write +operations and will cause the test script's basic test to fail. Too small a +value will impact write performance. The correct value for a device may be found +in in the chip datasheet. It is also reported if `verbose` is set and when +running the test scripts. + +Auto-detecting page size carries a risk of data loss if power fails while +auto-detect is in progress. In production code the value should be specified +explicitly. + +### 4.1.6 Special configurations + +It is possible to configure multiple chips as multiple arrays. This is done by +means of the `addr` and `max_chips_count` args. Examples: + ```python + eeprom0 = EEPROM(i2c, max_chips_count = 2) + eeprom1 = EEPROM(i2c, addr = 0x52, max_chips_count = 2) + ``` + 1st array uses address 0x50 and 0x51 and 2nd uses address 0x52 and 0x53. + + Individual chip usage: + ```python + eeprom0 = EEPROM(i2c, addr = 0x50, max_chips_count = 1) + eeprom1 = EEPROM(i2c, addr = 0x51, max_chips_count = 1) + ``` + ## 4.2 Byte addressing usage example A sample application: saving a configuration dict (which might be large and @@ -268,20 +286,21 @@ possible to use JSON/pickle to store objects in a filesystem. # 5. Test program eep_i2c.py -This assumes a Pyboard 1.x or Pyboard D with EEPROM(s) wired as above. It -provides the following. +This assumes a Pyboard 1.x or Pyboard D with EEPROM(s) wired as above. On other +hardware, adapt `get_eep` at the start of the script. It provides the following. ## 5.1 test() This performs a basic test of single and multi-byte access to chip 0. The test -reports how many chips can be accessed. Existing array data will be lost. This -primarily tests the driver: as a hardware test it is not exhaustive. +reports how many chips can be accessed. The current page size is printed and its +validity is tested. Existing array data will be lost. This primarily tests the +driver: as a hardware test it is not exhaustive. ## 5.2 full_test() -This is a hardware test. Tests the entire array. Fills each 128 byte page with -random data, reads it back, and checks the outcome. Existing array data will be -lost. +This is a hardware test. Tests the entire array. Fills the array with random +data in blocks of 256 byes. After each block is written, it is read back and the +contents compared to the data written. Existing array data will be lost. ## 5.3 fstest(format=False) diff --git a/eeprom/spi/SPI.md b/eeprom/spi/SPI.md index a8eea4c..add8d6e 100644 --- a/eeprom/spi/SPI.md +++ b/eeprom/spi/SPI.md @@ -18,9 +18,12 @@ The driver has the following attributes: 7. Alternatively it can support byte-level access using Python slice syntax. 8. RAM allocations are minimised. Buffer sizes are tiny. -##### [Main readme](../../README.md) +## 1.1 Notes -## 1.1 This document +As of Jan 2024 this driver has been updated to fix a bug where the device page +size was less than 256. A further aim was to make the driver more generic, with +a better chance of working with other SPI EEPROM chips. The constructor has +additional optional args to support this. Code samples assume one or more Microchip devices. If using the STM chip the SPI baudrate should be 5MHz and the chip size must be specified to the `EEPROM` @@ -29,6 +32,8 @@ constructor, e.g.: eep = EEPROM(SPI(2, baudrate=5_000_000), cspins, 256) ``` +##### [Main readme](../../README.md) + # 2. Connections Any SPI interface may be used. The table below assumes a Pyboard running SPI(2) @@ -91,7 +96,7 @@ import os from machine import SPI, Pin from eeprom_spi import EEPROM cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1)) -eep = EEPROM(SPI(2, baudrate=20_000_000), cspins) +eep = EEPROM(SPI(2, baudrate=20_000_000), cspins, 128) # 128KiB chips # Format the filesystem os.VfsLfs2.mkfs(eep) # Omit this to mount an existing filesystem os.mount(eep,'/eeprom') @@ -103,14 +108,15 @@ Note that, at the outset, you need to decide whether to use the array as a mounted filesystem or as a byte array. The filesystem is relatively small but has high integrity owing to the hardware longevity. Typical use-cases involve files which are frequently updated. These include files used for storing Python -objects serialised using Pickle/ujson or files holding a btree database. +objects serialised using Pickle/json or files holding a btree database. The SPI bus must be instantiated using the `machine` module. ## 4.1 The EEPROM class An `EEPROM` instance represents a logical EEPROM: this may consist of multiple -physical devices on a common SPI bus. +physical devices on a common SPI bus. Alternatively multiple EEPROM instances +may share the bus, differentiated by their CS pins. ### 4.1.1 Constructor @@ -119,14 +125,21 @@ each chip select line an EEPROM array is instantiated. A `RuntimeError` will be raised if a device is not detected on a CS line. Arguments: - 1. `spi` Mandatory. An initialised SPI bus created by `machine`. + 1. `spi` An initialised SPI bus created by `machine`. 2. `cspins` A list or tuple of `Pin` instances. Each `Pin` must be initialised as an output (`Pin.OUT`) and with `value=1` and be created by `machine`. - 3. `size=128` Chip size in KiB. Set to 256 for the STM chip. - 4. `verbose=True` If `True`, the constructor issues information on the EEPROM - devices it has detected. + 3. `size` Chip size in KiB. Set to 256 for the STM chip, 128 for the Microchip. + 4. `verbose=True` If `True`, the constructor performs a presence check for an + EEPROM on each chip select pin and reports devices it has detected. See + [4.1.5 Auto detection](./SPI.md#415-auto-detection) for observations on + production code. 5. `block_size=9` The block size reported to the filesystem. The size in bytes is `2**block_size` so is 512 bytes by default. + 6. `page_size=None` EEPROM devices have a RAM buffer enabling fast writes. The + driver determines this automatically by default. It is possible to override + this by passing an integer being the page size in bytes: 16, 32, 64, 128 or 256. + See [4.1.5 Auto detection](./SPI.md#415-auto-detection) for reasons why this + is advised in production code. SPI baudrate: The 25LC1024 supports baudrates of upto 20MHz. If this value is specified the platform will produce the highest available frequency not @@ -152,7 +165,7 @@ of single byte access: from machine import SPI, Pin from eeprom_spi import EEPROM cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1)) -eep = EEPROM(SPI(2, baudrate=20_000_000), cspins) +eep = EEPROM(SPI(2, baudrate=20_000_000), cspins, 128) eep[2000] = 42 print(eep[2000]) # Return an integer ``` @@ -162,7 +175,7 @@ writing, the size of the slice must match the length of the buffer: from machine import SPI, Pin from eeprom_spi import EEPROM cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1)) -eep = EEPROM(SPI(2, baudrate=20_000_000), cspins) +eep = EEPROM(SPI(2, baudrate=20_000_000), cspins, 128) eep[2000:2002] = bytearray((42, 43)) print(eep[2000:2002]) # Returns a bytearray ``` @@ -187,7 +200,7 @@ advantage when reading of using a pre-allocated buffer. Arguments: #### The len operator The size of the EEPROM array in bytes may be retrieved by issuing `len(eep)` -where `eep` is the `EEPROM` instance. +where `eep` is an `EEPROM` instance. #### scan @@ -201,7 +214,11 @@ identify the chip. #### erase -Erases the entire array. Available only on the Microchip device. +Zero the entire array. Can take several seconds. + +#### get_page_size + +Return the page size in bytes. ### 4.1.4 Methods providing the block protocol @@ -216,6 +233,19 @@ their use in application code is not recommended. `writeblocks()` `ioctl()` +### 4.1.5 Auto detection + +The driver constructor uses auto-detection in two circumstances: +* If `verbose` is specified, it checks each chip select for chip presence. +* If `page_size` is set to `None` the value is determined by measurement. + +In both cases data is written to the chips, then restored from RAM. If a power +outage were to occur while either process was in progress, corruption could +occur. It is therefore recommended that, in production code, `verbose` is +`False` and `page_size` is set to an integer. The page size may be determined +from the chip datasheet. It is also printed on instantiation if `verbose` is +set: running any of the test scripts will do this. + ## 4.2 Byte addressing usage example A sample application: saving a configuration dict (which might be large and @@ -225,7 +255,7 @@ import ujson from machine import SPI, Pin from eeprom_spi import EEPROM cspins = (Pin(Pin.board.Y5, Pin.OUT, value=1), Pin(Pin.board.Y4, Pin.OUT, value=1)) -eep = EEPROM(SPI(2, baudrate=20_000_000), cspins) +eep = EEPROM(SPI(2, baudrate=20_000_000), cspins, 128) d = {1:'one', 2:'two'} # Some kind of large object wdata = ujson.dumps(d).encode('utf8') sl = '{:10d}'.format(len(wdata)).encode('utf8') @@ -252,18 +282,20 @@ possible to use JSON/pickle to store objects in a filesystem. This assumes a Pyboard 1.x or Pyboard D with two EEPROMs wired to SPI(2) as above with chip selects connected to pins `Y4` and `Y5`. It provides the following. In all cases the stm arg should be `True` if using the STM chips. +On other hardware, adapt `cspins` and `get_eep` at the start of the script. ## 5.1 test(stm=False) This performs a basic test of single and multi-byte access to chip 0. The test -reports how many chips can be accessed. Existing array data will be lost. This -primarily tests the driver: as a hardware test it is not exhaustive. +reports how many chips can be accessed. The current page size is printed and its +validity is tested. Existing array data will be lost. This primarily tests the +driver: as a hardware test it is not exhaustive. ## 5.2 full_test(stm=False) -This is a hardware test. Tests the entire array. Fills each 256 byte page with -random data, reads it back, and checks the outcome. Existing array data will be -lost. +This is a hardware test. Tests the entire array. Fills the array with random +data in blocks of 256 byes. After each block is written, it is read back and the +contents compared to the data written. Existing array data will be lost. ## 5.3 fstest(format=False, stm=False) diff --git a/eeprom/spi/eep_spi.py b/eeprom/spi/eep_spi.py index 2d22d81..f783b6d 100644 --- a/eeprom/spi/eep_spi.py +++ b/eeprom/spi/eep_spi.py @@ -19,7 +19,7 @@ def get_eep(stm): if stm: eep = EEPROM(SPI(2, baudrate=5_000_000), cspins, 256) else: - eep = EEPROM(SPI(2, baudrate=20_000_000), cspins) + eep = EEPROM(SPI(2, baudrate=20_000_000), cspins, 128) print("Instantiated EEPROM") return eep @@ -28,13 +28,19 @@ def get_eep(stm): def cp(source, dest): if dest.endswith("/"): # minimal way to allow dest = "".join((dest, source.split("/")[-1])) # cp /sd/file /eeprom/ - with open(source, "rb") as infile: # Caller should handle any OSError - with open(dest, "wb") as outfile: # e.g file not found - while True: - buf = infile.read(100) - outfile.write(buf) - if len(buf) < 100: - break + try: + with open(source, "rb") as infile: # Caller should handle any OSError + with open(dest, "wb") as outfile: # e.g file not found + while True: + buf = infile.read(100) + outfile.write(buf) + if len(buf) < 100: + break + except OSError as e: + if e.errno == 28: + print("Insufficient space for copy.") + else: + raise # ***** TEST OF DRIVER ***** @@ -72,9 +78,7 @@ def test(stm=False): eep[sa + v] = v for v in range(256): if eep[sa + v] != v: - print( - "Fail at address {} data {} should be {}".format(sa + v, eep[sa + v], v) - ) + print("Fail at address {} data {} should be {}".format(sa + v, eep[sa + v], v)) break else: print("Test of byte addressing passed") @@ -101,6 +105,14 @@ def test(stm=False): print(res) else: print("Test chip boundary skipped: only one chip!") + pe = eep.get_page_size() # One byte past page + eep[pe] = 0xFF + eep[:257] = b"\0" * 257 + print("Test page size: ", end="") + if eep[pe]: + print("FAIL") + else: + print("passed") # ***** TEST OF FILESYSTEM MOUNT ***** @@ -137,8 +149,9 @@ def cptest(stm=False): # Assumes pre-existing filesystem of either type print("Fail mounting device. Have you formatted it?") return print("Mounted device.") - cp("eep_spi.py", "/eeprom/") - cp("eeprom_spi.py", "/eeprom/") + cp(__file__, "/eeprom/") + # We may have the source file or a precompiled binary (*.mpy) + cp(__file__.replace("eep", "eeprom"), "/eeprom/") print('Contents of "/eeprom": {}'.format(uos.listdir("/eeprom"))) print(uos.statvfs("/eeprom")) @@ -146,23 +159,29 @@ def cptest(stm=False): # Assumes pre-existing filesystem of either type # ***** TEST OF HARDWARE ***** def full_test(stm=False): eep = get_eep(stm) - page = 0 + block = 0 for sa in range(0, len(eep), 256): data = uos.urandom(256) eep[sa : sa + 256] = data got = eep[sa : sa + 256] if got == data: - print("Page {} passed".format(page)) + print(f"Block {block} passed") else: - print("Page {} readback failed.".format(page)) + print(f"Block {block} readback failed.") break - page += 1 + block += 1 -test_str = """Available tests (see SPI.md): -test(stm=False) Basic hardware test. -full_test(stm=False) Thorough hardware test. -fstest(format=False, stm=False) Filesystem test (see doc). -cptest(stm=False) Copy files to filesystem (see doc). +def help(): + test_str = """Available commands (see SPI.md): + help() Print this text. + test(stm=False) Basic hardware test. + full_test(stm=False) Thorough hardware test. + fstest(format=False, stm=False) Filesystem test (see doc). + cptest(stm=False) Copy files to filesystem (see doc). +stm: True is 256K chip, 5MHz bus. False is 128K chip, 20MHz bus. """ -print(test_str) + print(test_str) + + +help() diff --git a/eeprom/spi/eeprom_spi.py b/eeprom/spi/eeprom_spi.py index ca59d67..8f41716 100644 --- a/eeprom/spi/eeprom_spi.py +++ b/eeprom/spi/eeprom_spi.py @@ -2,114 +2,89 @@ # tested devices). # Released under the MIT License (MIT). See LICENSE. -# Copyright (c) 2019-2022 Peter Hinch +# Copyright (c) 2019-2024 Peter Hinch + +# Thanks are due to Abel Deuring for help in diagnosing and fixing a page size issue. import time +from os import urandom from micropython import const -from bdevice import BlockDevice +from bdevice import EepromDevice # Supported instruction set - common to both chips: _READ = const(3) _WRITE = const(2) _WREN = const(6) # Write enable _RDSR = const(5) # Read status register -# Microchip only: -_RDID = const(0xAB) # Read chip ID -_CE = const(0xC7) # Chip erase -# STM only: -_RDID_STM = const(0x83) # Read ID page -_WRID_STM = const(0x82) -_STM_ID = const(0x30) # Arbitrary ID for STM chip -# Not implemented: Write disable and Write status register -# _WRDI = const(4) -# _WRSR = const(1) # Logical EEPROM device comprising one or more physical chips sharing an SPI bus. -class EEPROM(BlockDevice): - def __init__(self, spi, cspins, size=128, verbose=True, block_size=9): - # args: virtual block size in bits, no. of chips, bytes in each chip +# args: SPI bus, tuple of CS Pin instances, chip size in KiB +# verbose: Test for chip presence and report +# block_size: Sector size for filesystems. See docs. +# erok: True if chip supports erase. +# page_size: None is auto detect. See docs. +class EEPROM(EepromDevice): + def __init__(self, spi, cspins, size, verbose=True, block_size=9, page_size=None): if size not in (64, 128, 256): - print("Warning: possible unsupported chip. Size:", size) - super().__init__(block_size, len(cspins), size * 1024) - self._stm = size == 256 + print(f"Warning: possible unsupported chip. Size: {size}KiB") self._spi = spi self._cspins = cspins self._ccs = None # Chip select Pin object for current chip + self._size = size * 1024 # Chip size in bytes self._bufp = bytearray(5) # instruction + 3 byte address + 1 byte value self._mvp = memoryview(self._bufp) # cost-free slicing - self.scan(verbose) + if verbose: # Test for presence of devices + self.scan() + # superclass figures out _page_size and _page_mask + super().__init__(block_size, len(cspins), self._size, page_size, verbose) + if verbose: + print(f"Total EEPROM size {self._a_bytes:,} bytes.") - # Read ID block ID[0] - def _stm_rdid(self, n): - cs = self._cspins[n] + # Low level device presence detect. Reads a location, then writes to it. If + # a write value is passed, uses that, otherwise writes the one's complement + # of the value read. + def _devtest(self, cs, la, v=None): + buf = bytearray(1) mvp = self._mvp - mvp[:] = b"\0\0\0\0\0" - mvp[0] = _RDID_STM + mvp[:] = b"\0" * 5 + # mvp[1] = la >> 16 + # mvp[2] = (la >> 8) & 0xFF + # mvp[3] = la & 0xFF + mvp[0] = _READ cs(0) - self._spi.write_readinto(mvp, mvp) + self._spi.write(mvp[:4]) + res = self._spi.read(1) cs(1) - return mvp[4] - - # Write a fixed value to ID[0] - def _stm_wrid(self, n): - cs = self._ccs - mvp = self._mvp mvp[0] = _WREN cs(0) - self._spi.write(mvp[:1]) # Enable write + self._spi.write(mvp[:1]) cs(1) - mvp[:] = b"\0\0\0\0\0" - mvp[0] = _WRID_STM - mvp[4] = _STM_ID + mvp[0] = _WRITE cs(0) - self._spi.write(mvp) - cs(1) - self._wait_rdy() + self._spi.write(mvp[:4]) + buf[0] = res[0] ^ 0xFF if v is None else v + self._spi.write(buf) + cs(1) # Trigger write start + self._ccs = cs + self._wait_rdy() # Wait until done (6ms max) + return res[0] - # Check for valid hardware on each CS pin: use ID block - def _stm_scan(self): + def scan(self): + # Generate a random address to minimise wear + la = int.from_bytes(urandom(3), "little") % self._size for n, cs in enumerate(self._cspins): - self._ccs = cs - if self._stm_rdid(n) != _STM_ID: - self._stm_wrid(n) - if self._stm_rdid(n) != _STM_ID: - raise RuntimeError("M95M02 chip not found at cs[{}].".format(n)) + old = self._devtest(cs, la) + new = self._devtest(cs, la, old) + if old != new ^ 0xFF: + raise RuntimeError(f"Chip not found at cs[{n}]") + print(f"{n + 1} chips detected.") return n - # Scan for Microchip devices: read manf ID - def _mc_scan(self): - mvp = self._mvp - for n, cs in enumerate(self._cspins): - mvp[:] = b"\0\0\0\0\0" - mvp[0] = _RDID - cs(0) - self._spi.write_readinto(mvp, mvp) - cs(1) - if mvp[4] != 0x29: - raise RuntimeError("25xx1024 chip not found at cs[{}].".format(n)) - return n - - # Check for a valid hardware configuration - def scan(self, verbose): - n = self._stm_scan() if self._stm else self._mc_scan() - if verbose: - s = "{} chips detected. Total EEPROM size {}bytes." - print(s.format(n + 1, self._a_bytes)) - def erase(self): - if self._stm: - raise RuntimeError("Erase not available on STM chip") mvp = self._mvp - for cs in self._cspins: # For each chip - mvp[0] = _WREN - cs(0) - self._spi.write(mvp[:1]) # Enable write - cs(1) - mvp[0] = _CE - cs(0) - self._spi.write(mvp[:1]) # Start erase - cs(1) - self._wait_rdy() # Wait for erase to complete + block = b"\0" * 256 + for n in range(0, self._a_bytes, 256): + self[n : n + 256] = block def _wait_rdy(self): # After a write, wait for device to become ready mvp = self._mvp @@ -134,7 +109,7 @@ class EEPROM(BlockDevice): mvp[1] = la >> 16 mvp[2] = (la >> 8) & 0xFF mvp[3] = la & 0xFF - pe = (addr & ~0xFF) + 0x100 # byte 0 of next page + pe = (la & self._page_mask) + self._page_size # byte 0 of next page return min(nbytes, pe - la) # Read or write multiple bytes at an arbitrary address