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.
|
||||
def _do_slice(self, addr):
|
||||
if not (addr.step is None or addr.step == 1):
|
||||
raise NotImplementedError(
|
||||
"only slices with step=1 (aka None) are supported"
|
||||
)
|
||||
raise NotImplementedError("only slices with step=1 (aka None) are supported")
|
||||
start = addr.start if addr.start is not None else 0
|
||||
stop = addr.stop if addr.stop is not None else self._a_bytes
|
||||
start = start if start >= 0 else self._a_bytes + start
|
||||
|
@ -82,6 +80,41 @@ class BlockDevice:
|
|||
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.
|
||||
|
||||
_RDBUFSIZE = const(32) # Size of read buffer for erasure test
|
||||
|
@ -118,9 +151,7 @@ class FlashDevice(BlockDevice):
|
|||
boff += nr
|
||||
# addr now >= self._acache: read from cache.
|
||||
sa = addr - self._acache # Offset into cache
|
||||
nr = min(
|
||||
nbytes, self._acache + self.sec_size - addr
|
||||
) # No of bytes to read from cache
|
||||
nr = min(nbytes, self._acache + self.sec_size - addr) # No of bytes to read from cache
|
||||
mvb[boff : boff + nr] = self._mvd[sa : sa + nr]
|
||||
if nbytes - nr: # Get any remaining data from chip
|
||||
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 |
|
||||
|
||||
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
|
||||
start at zero and be contiguous:
|
||||
wired to 3V3 in such a way as to give each device a unique address. In the case
|
||||
where chips are to form a single array these must start at zero and be
|
||||
contiguous:
|
||||
|
||||
| Chip no. | A2 | A1 | A0 |
|
||||
|:--------:|:---:|:---:|:---:|
|
||||
|
@ -130,26 +131,37 @@ 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
|
||||
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.
|
||||
5. `addr` override base address for first chip.
|
||||
6. `max_chips_count` override max_chips_count.
|
||||
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
|
||||
configuration
|
||||
|
||||
example:
|
||||
|
||||
array with custom chips count:
|
||||
In most cases only the first two arguments are used, with an array being
|
||||
instantiated with (for example):
|
||||
```python
|
||||
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 )
|
||||
eeprom0 = EEPROM(i2c, 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:
|
||||
```python
|
||||
eeprom0 = EEPROM( i2c, addr=0x50, max_chips_count=1 )
|
||||
eeprom1 = EEPROM( i2c, addr=0x51, max_chips_count=1 )
|
||||
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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# eep_i2c.py MicroPython test program for Microchip I2C EEPROM devices.
|
||||
|
||||
# Released under the MIT License (MIT). See LICENSE.
|
||||
# Copyright (c) 2019 Peter Hinch
|
||||
# Copyright (c) 2019-2024 Peter Hinch
|
||||
|
||||
import uos
|
||||
import time
|
||||
|
@ -23,13 +23,19 @@ def get_eep():
|
|||
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 *****
|
||||
|
@ -94,6 +100,14 @@ def test(eep=None):
|
|||
print(res)
|
||||
else:
|
||||
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 *****
|
||||
|
@ -149,3 +163,16 @@ def full_test(eep=None, block_size=128):
|
|||
else:
|
||||
print("Page {} readback failed.".format(page))
|
||||
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.
|
||||
|
||||
# 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
|
||||
from micropython import const
|
||||
from bdevice import BlockDevice
|
||||
from bdevice import EepromDevice
|
||||
|
||||
_ADDR = const(0x50) # Base address of chip
|
||||
_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
|
||||
# same size, and must have contiguous addresses.
|
||||
class EEPROM(BlockDevice):
|
||||
def __init__(self, i2c, chip_size=T24C512, verbose=True, block_size=9, addr=_ADDR, max_chips_count=_MAX_CHIPS_COUNT, page_size=7):
|
||||
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=None,
|
||||
):
|
||||
self._i2c = i2c
|
||||
if chip_size not in (T24C32, T24C64, T24C128, T24C256, T24C512):
|
||||
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
|
||||
super().__init__(block_size, nchips, chip_size)
|
||||
# Get no. of EEPROM chips
|
||||
nchips, min_chip_address = self.scan(verbose, chip_size, addr, max_chips_count)
|
||||
self._min_chip_address = min_chip_address
|
||||
self._i2c_addr = 0 # I2C address of current chip
|
||||
self._buf1 = bytearray(1)
|
||||
self._addrbuf = bytearray(2) # Memory offset into current chip
|
||||
self._page_size = 2 ** page_size
|
||||
self._page_mask = ~(self._page_size - 1)
|
||||
# superclass figures out _page_size and _page_mask
|
||||
super().__init__(block_size, nchips, chip_size, page_size, verbose)
|
||||
|
||||
# Check for a valid hardware configuration
|
||||
def scan(self, verbose, chip_size, addr, max_chips_count):
|
||||
|
@ -41,9 +52,9 @@ class EEPROM(BlockDevice):
|
|||
raise RuntimeError("EEPROM not found.")
|
||||
eeproms = sorted(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):
|
||||
raise RuntimeError('Non-contiguous chip addresses', eeproms)
|
||||
raise RuntimeError("Non-contiguous chip addresses", eeproms)
|
||||
if verbose:
|
||||
s = "{} chips detected. Total EEPROM size {}bytes."
|
||||
print(s.format(nchips, chip_size * nchips))
|
||||
|
@ -69,7 +80,7 @@ class EEPROM(BlockDevice):
|
|||
self._addrbuf[0] = (la >> 8) & 0xFF
|
||||
self._addrbuf[1] = la & 0xFF
|
||||
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)
|
||||
|
||||
# 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.readfrom_into(self._i2c_addr, mvb[start : start + npage])
|
||||
else:
|
||||
self._i2c.writevto(
|
||||
self._i2c_addr, (self._addrbuf, buf[start : start + npage])
|
||||
)
|
||||
self._i2c.writevto(self._i2c_addr, (self._addrbuf, buf[start : start + npage]))
|
||||
self._wait_rdy()
|
||||
nbytes -= npage
|
||||
start += npage
|
||||
|
|
Ładowanie…
Reference in New Issue