kopia lustrzana https://github.com/peterhinch/micropython_eeprom
Fix bugs in I2C EEPROM. Auto detect page size.
rodzic
4a8c00bf98
commit
ab0adab7d8
43
bdevice.py
43
bdevice.py
|
@ -34,9 +34,7 @@ class BlockDevice:
|
||||||
# Handle special cases of a slice. Always return a pair of positive indices.
|
# Handle special cases of a slice. Always return a pair of positive indices.
|
||||||
def _do_slice(self, addr):
|
def _do_slice(self, addr):
|
||||||
if not (addr.step is None or addr.step == 1):
|
if not (addr.step is None or addr.step == 1):
|
||||||
raise NotImplementedError(
|
raise NotImplementedError("only slices with step=1 (aka None) are supported")
|
||||||
"only slices with step=1 (aka None) are supported"
|
|
||||||
)
|
|
||||||
start = addr.start if addr.start is not None else 0
|
start = addr.start if addr.start is not None else 0
|
||||||
stop = addr.stop if addr.stop is not None else self._a_bytes
|
stop = addr.stop if addr.stop is not None else self._a_bytes
|
||||||
start = start if start >= 0 else self._a_bytes + start
|
start = start if start >= 0 else self._a_bytes + start
|
||||||
|
@ -82,6 +80,41 @@ class BlockDevice:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
class EepromDevice(BlockDevice):
|
||||||
|
def __init__(self, nbits, nchips, chip_size, page_size, verbose):
|
||||||
|
super().__init__(nbits, nchips, chip_size)
|
||||||
|
# Handle page size arg
|
||||||
|
if page_size not in (None, 16, 32, 64, 128, 256):
|
||||||
|
raise ValueError(f"Invalid page size: {page_size}")
|
||||||
|
self._set_pagesize(page_size) # Set page size
|
||||||
|
verbose and print("Page size:", self._page_size)
|
||||||
|
|
||||||
|
def _psize(self, ps): # Set page size and page mask
|
||||||
|
self._page_size = ps
|
||||||
|
self._page_mask = ~(ps - 1)
|
||||||
|
|
||||||
|
def get_page_size(self): # For test script
|
||||||
|
return self._page_size
|
||||||
|
|
||||||
|
def _set_pagesize(self, page_size):
|
||||||
|
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:
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
# Hardware agnostic base class for flash memory.
|
# Hardware agnostic base class for flash memory.
|
||||||
|
|
||||||
_RDBUFSIZE = const(32) # Size of read buffer for erasure test
|
_RDBUFSIZE = const(32) # Size of read buffer for erasure test
|
||||||
|
@ -118,9 +151,7 @@ class FlashDevice(BlockDevice):
|
||||||
boff += nr
|
boff += nr
|
||||||
# addr now >= self._acache: read from cache.
|
# addr now >= self._acache: read from cache.
|
||||||
sa = addr - self._acache # Offset into cache
|
sa = addr - self._acache # Offset into cache
|
||||||
nr = min(
|
nr = min(nbytes, self._acache + self.sec_size - addr) # No of bytes to read from cache
|
||||||
nbytes, self._acache + self.sec_size - addr
|
|
||||||
) # No of bytes to read from cache
|
|
||||||
mvb[boff : boff + nr] = self._mvd[sa : sa + nr]
|
mvb[boff : boff + nr] = self._mvd[sa : sa + nr]
|
||||||
if nbytes - nr: # Get any remaining data from chip
|
if nbytes - nr: # Get any remaining data from chip
|
||||||
self.rdchip(addr + nr, mvb[boff + nr :])
|
self.rdchip(addr + nr, mvb[boff + nr :])
|
||||||
|
|
|
@ -43,8 +43,9 @@ EEPROM Pin numbers assume a PDIP package (8 pin plastic dual-in-line).
|
||||||
| 8 Vcc | 3V3 | 3V3 |
|
| 8 Vcc | 3V3 | 3V3 |
|
||||||
|
|
||||||
For multiple chips the address lines A0, A1 and A2 of each chip need to be
|
For multiple chips the address lines A0, A1 and A2 of each chip need to be
|
||||||
wired to 3V3 in such a way as to give each device a unique address. These must
|
wired to 3V3 in such a way as to give each device a unique address. In the case
|
||||||
start at zero and be contiguous:
|
where chips are to form a single array these must start at zero and be
|
||||||
|
contiguous:
|
||||||
|
|
||||||
| Chip no. | A2 | A1 | A0 |
|
| Chip no. | A2 | A1 | A0 |
|
||||||
|:--------:|:---:|:---:|:---:|
|
|:--------:|:---:|:---:|:---:|
|
||||||
|
@ -130,26 +131,37 @@ Arguments:
|
||||||
devices it has detected.
|
devices it has detected.
|
||||||
4. `block_size=9` The block size reported to the filesystem. The size in bytes
|
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.
|
is `2**block_size` so is 512 bytes by default.
|
||||||
5. `addr` override base address for first chip
|
5. `addr` override base address for first chip.
|
||||||
6. `max_chips_count` override max_chips_count
|
6. `max_chips_count` override max_chips_count.
|
||||||
7. `page_size=7` The binary logarithm of the page size of the EEPROMs, i.e., the page size in bytes is `2 ** page_size`. The page size may vary between chips from different manufacturers even for the same storage size. Note that specifying a too large value will most likely lead to data corruption in write operations. Look up the correct value for your setup in the chip's datasheet.
|
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.
|
||||||
|
|
||||||
With `addr` and `max_chips_count` override, it's possible to make multiple
|
In most cases only the first two arguments are used, with an array being
|
||||||
configuration
|
instantiated with (for example):
|
||||||
|
```python
|
||||||
example:
|
from machine import I2C
|
||||||
|
from eeprom_i2c import EEPROM, T24C512
|
||||||
array with custom chips count:
|
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
|
```python
|
||||||
eeprom0 = EEPROM( i2c, max_chips_count=2 )
|
eeprom0 = EEPROM(i2c, max_chips_count = 2)
|
||||||
eeprom1 = EEPROM( i2c, addr=0x52, max_chips_count=2 )
|
eeprom1 = EEPROM(i2c, addr = 0x52, max_chips_count = 2)
|
||||||
```
|
```
|
||||||
1st array using address 0x50 and 0x51 and 2nd using array address 0x52 and 0x53.
|
1st array uses address 0x50 and 0x51 and 2nd uses address 0x52 and 0x53.
|
||||||
|
|
||||||
individual chip usage:
|
individual chip usage:
|
||||||
```python
|
```python
|
||||||
eeprom0 = EEPROM( i2c, addr=0x50, max_chips_count=1 )
|
eeprom0 = EEPROM(i2c, addr = 0x50, max_chips_count = 1)
|
||||||
eeprom1 = EEPROM( i2c, addr=0x51, max_chips_count=1 )
|
eeprom1 = EEPROM(i2c, addr = 0x51, max_chips_count = 1)
|
||||||
```
|
```
|
||||||
|
|
||||||
### 4.1.2 Methods providing byte level access
|
### 4.1.2 Methods providing byte level access
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# eep_i2c.py MicroPython test program for Microchip I2C EEPROM devices.
|
# eep_i2c.py MicroPython test program for Microchip I2C EEPROM devices.
|
||||||
|
|
||||||
# Released under the MIT License (MIT). See LICENSE.
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
# Copyright (c) 2019 Peter Hinch
|
# Copyright (c) 2019-2024 Peter Hinch
|
||||||
|
|
||||||
import uos
|
import uos
|
||||||
import time
|
import time
|
||||||
|
@ -23,13 +23,19 @@ def get_eep():
|
||||||
def cp(source, dest):
|
def cp(source, dest):
|
||||||
if dest.endswith("/"): # minimal way to allow
|
if dest.endswith("/"): # minimal way to allow
|
||||||
dest = "".join((dest, source.split("/")[-1])) # cp /sd/file /eeprom/
|
dest = "".join((dest, source.split("/")[-1])) # cp /sd/file /eeprom/
|
||||||
with open(source, "rb") as infile: # Caller should handle any OSError
|
try:
|
||||||
with open(dest, "wb") as outfile: # e.g file not found
|
with open(source, "rb") as infile: # Caller should handle any OSError
|
||||||
while True:
|
with open(dest, "wb") as outfile: # e.g file not found
|
||||||
buf = infile.read(100)
|
while True:
|
||||||
outfile.write(buf)
|
buf = infile.read(100)
|
||||||
if len(buf) < 100:
|
outfile.write(buf)
|
||||||
break
|
if len(buf) < 100:
|
||||||
|
break
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == 28:
|
||||||
|
print("Insufficient space for copy.")
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
# ***** TEST OF DRIVER *****
|
# ***** TEST OF DRIVER *****
|
||||||
|
@ -94,6 +100,14 @@ def test(eep=None):
|
||||||
print(res)
|
print(res)
|
||||||
else:
|
else:
|
||||||
print("Test chip boundary skipped: only one chip!")
|
print("Test chip boundary skipped: only one chip!")
|
||||||
|
pe = eep.get_page_size() + 1 # 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 *****
|
# ***** TEST OF FILESYSTEM MOUNT *****
|
||||||
|
@ -149,3 +163,16 @@ def full_test(eep=None, block_size=128):
|
||||||
else:
|
else:
|
||||||
print("Page {} readback failed.".format(page))
|
print("Page {} readback failed.".format(page))
|
||||||
page += 1
|
page += 1
|
||||||
|
|
||||||
|
|
||||||
|
help = """Available tests:
|
||||||
|
test() Basic fuctional test
|
||||||
|
full_test() Read-write test of EEPROM chip(s)
|
||||||
|
fstest() Check or create a filesystem.
|
||||||
|
cptest() Check a filesystem by copying source files to it.
|
||||||
|
|
||||||
|
Utilities:
|
||||||
|
get_eep() Initialise and return an EEPROM instance.
|
||||||
|
cp() Very crude file copy utility.
|
||||||
|
"""
|
||||||
|
print(help)
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
# eeprom_i2c.py MicroPython driver for Microchip I2C EEPROM devices.
|
# eeprom_i2c.py MicroPython driver for Microchip I2C EEPROM devices.
|
||||||
|
|
||||||
# Released under the MIT License (MIT). See LICENSE.
|
# Released under the MIT License (MIT). See LICENSE.
|
||||||
# Copyright (c) 2019 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
|
import time
|
||||||
from micropython import const
|
from micropython import const
|
||||||
from bdevice import BlockDevice
|
from bdevice import EepromDevice
|
||||||
|
|
||||||
_ADDR = const(0x50) # Base address of chip
|
_ADDR = const(0x50) # Base address of chip
|
||||||
_MAX_CHIPS_COUNT = const(8) # Max number of chips
|
_MAX_CHIPS_COUNT = const(8) # Max number of chips
|
||||||
|
@ -18,19 +20,28 @@ T24C32 = const(4096) # 4KiB 32Kbits
|
||||||
|
|
||||||
# Logical EEPROM device consists of 1-8 physical chips. Chips must all be the
|
# Logical EEPROM device consists of 1-8 physical chips. Chips must all be the
|
||||||
# same size, and must have contiguous addresses.
|
# same size, and must have contiguous addresses.
|
||||||
class EEPROM(BlockDevice):
|
class EEPROM(EepromDevice):
|
||||||
def __init__(self, i2c, chip_size=T24C512, verbose=True, block_size=9, addr=_ADDR, max_chips_count=_MAX_CHIPS_COUNT, page_size=7):
|
def __init__(
|
||||||
|
self,
|
||||||
|
i2c,
|
||||||
|
chip_size=T24C512,
|
||||||
|
verbose=True,
|
||||||
|
block_size=9,
|
||||||
|
addr=_ADDR,
|
||||||
|
max_chips_count=_MAX_CHIPS_COUNT,
|
||||||
|
page_size=None,
|
||||||
|
):
|
||||||
self._i2c = i2c
|
self._i2c = i2c
|
||||||
if chip_size not in (T24C32, T24C64, T24C128, T24C256, T24C512):
|
if chip_size not in (T24C32, T24C64, T24C128, T24C256, T24C512):
|
||||||
print("Warning: possible unsupported chip. Size:", chip_size)
|
print("Warning: possible unsupported chip. Size:", chip_size)
|
||||||
nchips, min_chip_address = self.scan(verbose, chip_size, addr, max_chips_count) # No. of EEPROM chips
|
# Get no. of EEPROM chips
|
||||||
super().__init__(block_size, nchips, chip_size)
|
nchips, min_chip_address = self.scan(verbose, chip_size, addr, max_chips_count)
|
||||||
self._min_chip_address = min_chip_address
|
self._min_chip_address = min_chip_address
|
||||||
self._i2c_addr = 0 # I2C address of current chip
|
self._i2c_addr = 0 # I2C address of current chip
|
||||||
self._buf1 = bytearray(1)
|
self._buf1 = bytearray(1)
|
||||||
self._addrbuf = bytearray(2) # Memory offset into current chip
|
self._addrbuf = bytearray(2) # Memory offset into current chip
|
||||||
self._page_size = 2 ** page_size
|
# superclass figures out _page_size and _page_mask
|
||||||
self._page_mask = ~(self._page_size - 1)
|
super().__init__(block_size, nchips, chip_size, page_size, verbose)
|
||||||
|
|
||||||
# Check for a valid hardware configuration
|
# Check for a valid hardware configuration
|
||||||
def scan(self, verbose, chip_size, addr, max_chips_count):
|
def scan(self, verbose, chip_size, addr, max_chips_count):
|
||||||
|
@ -41,9 +52,9 @@ class EEPROM(BlockDevice):
|
||||||
raise RuntimeError("EEPROM not found.")
|
raise RuntimeError("EEPROM not found.")
|
||||||
eeproms = sorted(eeproms)
|
eeproms = sorted(eeproms)
|
||||||
if len(set(eeproms)) != len(eeproms):
|
if len(set(eeproms)) != len(eeproms):
|
||||||
raise RuntimeError('Duplicate addresses were found', eeproms)
|
raise RuntimeError("Duplicate addresses were found", eeproms)
|
||||||
if (eeproms[-1] - eeproms[0] + 1) != len(eeproms):
|
if (eeproms[-1] - eeproms[0] + 1) != len(eeproms):
|
||||||
raise RuntimeError('Non-contiguous chip addresses', eeproms)
|
raise RuntimeError("Non-contiguous chip addresses", eeproms)
|
||||||
if verbose:
|
if verbose:
|
||||||
s = "{} chips detected. Total EEPROM size {}bytes."
|
s = "{} chips detected. Total EEPROM size {}bytes."
|
||||||
print(s.format(nchips, chip_size * nchips))
|
print(s.format(nchips, chip_size * nchips))
|
||||||
|
@ -69,7 +80,7 @@ class EEPROM(BlockDevice):
|
||||||
self._addrbuf[0] = (la >> 8) & 0xFF
|
self._addrbuf[0] = (la >> 8) & 0xFF
|
||||||
self._addrbuf[1] = la & 0xFF
|
self._addrbuf[1] = la & 0xFF
|
||||||
self._i2c_addr = self._min_chip_address + ca
|
self._i2c_addr = self._min_chip_address + ca
|
||||||
pe = (addr & self._page_mask) + self._page_size # byte 0 of next page
|
pe = (la & self._page_mask) + self._page_size # byte 0 of next page
|
||||||
return min(nbytes, pe - la)
|
return min(nbytes, pe - la)
|
||||||
|
|
||||||
# Read or write multiple bytes at an arbitrary address
|
# Read or write multiple bytes at an arbitrary address
|
||||||
|
@ -84,9 +95,7 @@ class EEPROM(BlockDevice):
|
||||||
self._i2c.writeto(self._i2c_addr, self._addrbuf)
|
self._i2c.writeto(self._i2c_addr, self._addrbuf)
|
||||||
self._i2c.readfrom_into(self._i2c_addr, mvb[start : start + npage])
|
self._i2c.readfrom_into(self._i2c_addr, mvb[start : start + npage])
|
||||||
else:
|
else:
|
||||||
self._i2c.writevto(
|
self._i2c.writevto(self._i2c_addr, (self._addrbuf, buf[start : start + npage]))
|
||||||
self._i2c_addr, (self._addrbuf, buf[start : start + npage])
|
|
||||||
)
|
|
||||||
self._wait_rdy()
|
self._wait_rdy()
|
||||||
nbytes -= npage
|
nbytes -= npage
|
||||||
start += npage
|
start += npage
|
||||||
|
|
Ładowanie…
Reference in New Issue