EEPROM: Support i2c 2KiB eeproms, also MIP install.

pull/24/head
Peter Hinch 2024-01-14 11:28:15 +00:00
rodzic e06fd66bb3
commit 2a2ceab903
8 zmienionych plików z 114 dodań i 168 usunięć

Wyświetl plik

@ -1,7 +1,8 @@
# 1. A MicroPython I2C EEPROM driver
This driver supports chips from the 64KiB 25xx512 series and related chips with
smaller capacities.
smaller capacities, now including chips as small as 2KiB with single byte
addressing.
From one to eight chips may be used to construct a nonvolatile memory module
with sizes upto 512KiB. The driver allows the memory either to be mounted in
@ -20,6 +21,10 @@ the subsequent improvements to MicroPython to achieve these advantages:
7. Alternatively it can support byte-level access using Python slice syntax.
8. RAM allocations are reduced.
Chips of 2KiB and below store the upper three address bits in the chip address.
Thus a 2KiB chip looks like 8 chips of 256 bytes each. See
[6. Small chips case study](./I2C.md#6-small-chips-case-study).
##### [Main readme](../../README.md)
# 2. Connections
@ -77,11 +82,31 @@ Other platforms may vary.
1. `eeprom_i2c.py` Device driver.
2. `bdevice.py` (In root directory) Base class for the device driver.
3. `eep_i2c.py` Pyboard test programs for above.
4. `wemos_i2c_eeprom.py` Test program using a Wemos D1 mini ESP8266 board.
3. `eep_i2c.py` Pyboard test programs for above (adapt for other hosts).
Installation: copy files 1 and 2 (optionally 3 and/or 4) to the target
filesystem.
## 3.1 Installation
This installs the above files in the `lib` directory.
On networked hardware this may be done with `mip` which is included in recent
firmware. On non-networked hardware this is done using the official
[mpremote utility](http://docs.micropython.org/en/latest/reference/mpremote.html)
which should be installed on the PC as described in this doc.
#### Any hardware
On the PC issue:
```bash
$ mpremote mip install "github:peterhinch/micropython_eeprom/eeprom/i2c"
```
#### Networked hardware
At the device REPL issue:
```python
>>> import mip
>>> mip.install("github:peterhinch/micropython_eeprom/eeprom/i2c")
```
# 4. The device driver
@ -329,4 +354,20 @@ cp('/flash/main.py','/eeprom/')
```
See `upysh` in [micropython-lib](https://github.com/micropython/micropython-lib.git)
for other filesystem tools for use at the REPL.
for filesystem tools for use at the REPL.
# 6. Small chips case study
A generic 2KiB EEPROM was tested. Performing an I2C scan revealed that it
occupied 8 I2C addresses starting at 80 (0x50). Note it would be impossible to
configure such chips in a multi-chip array as all eight addresses are used: the
chip can be regarded as an array of eight 256 byte virtual chips. The driver was
therefore initialised as follows:
```python
i2c = SoftI2C(scl=Pin(9, Pin.OPEN_DRAIN, value=1), sda=Pin(8, Pin.OPEN_DRAIN, value=1))
eep = EEPROM(i2c, 256, addr=0x50)
```
A hard I2C interface would also work. At risk of stating the obvious it is not
possible to build a filesystem on a chip of this size. Tests `eep_i2c.test` and
`eep_i2c.full_test` should be run and will work if the driver is correctly
configured.

Wyświetl plik

@ -152,27 +152,32 @@ def cptest(eep=None): # Assumes pre-existing filesystem of either type
# ***** TEST OF HARDWARE *****
def full_test(eep=None, block_size=128):
def full_test(eep=None, block_size=256):
eep = eep if eep else get_eep()
page = 0
print(f"Testing with {block_size}byte blocks of random data...")
block = 0
for sa in range(0, len(eep), block_size):
data = uos.urandom(block_size)
eep[sa : sa + block_size] = data
if eep[sa : sa + block_size] == data:
print("Page {} passed".format(page))
print(f"Block {block} passed\r", end="")
else:
print("Page {} readback failed.".format(page))
page += 1
print(f"Block {block} readback failed.")
block += 1
print()
def help():
st = """Available commands:
help() Print this text.
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.
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(st)
Utilities:
get_eep() Initialise and return an EEPROM instance.
cp() Very crude file copy utility.
"""
print(help)
help()

Wyświetl plik

@ -40,6 +40,7 @@ class EEPROM(EepromDevice):
self._i2c_addr = 0 # I2C address of current chip
self._buf1 = bytearray(1)
self._addrbuf = bytearray(2) # Memory offset into current chip
self._onebyte = chip_size <= 256 # Single byte address
# superclass figures out _page_size and _page_mask
super().__init__(block_size, nchips, chip_size, page_size, verbose)
@ -90,12 +91,14 @@ class EEPROM(EepromDevice):
start = 0 # Offset into buf.
while nbytes > 0:
npage = self._getaddr(addr, nbytes) # No. of bytes in current page
assert npage > 0
# assert npage > 0
# Offset address into chip: one or two bytes
vaddr = self._addrbuf[1:] if self._onebyte else self._addrbuf
if read:
self._i2c.writeto(self._i2c_addr, self._addrbuf)
self._i2c.writeto(self._i2c_addr, vaddr)
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, (vaddr, buf[start : start + npage]))
self._wait_rdy()
nbytes -= npage
start += npage

Wyświetl plik

@ -0,0 +1,8 @@
{
"urls": [
["bdevice.py", "github:peterhinch/micropython_eeprom/bdevice.py"],
["eep_i2c.py", "github:peterhinch/micropython_eeprom/eeprom/i2c/eep_i2c.py"],
["eeprom_i2c.py", "github:peterhinch/micropython_eeprom/eeprom/i2c/eeprom_i2c.py"]
],
"version": "0.1"
}

Wyświetl plik

@ -1,142 +0,0 @@
# wemos_fi2c_eeprom.py Test I2C EEPROM chips with ESP8266 host
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch
import uos
from machine import I2C, Pin
from eeprom_i2c import EEPROM, T24C512
i2c = I2C(-1, scl=Pin(13, Pin.OPEN_DRAIN), sda=Pin(12, Pin.OPEN_DRAIN))
# Return an EEPROM array. Adapt for platforms other than Pyboard or chips
# smaller than 64KiB.
def get_eep():
eep = EEPROM(i2c, T24C512)
print("Instantiated EEPROM")
return eep
# Dumb file copy utility to help with managing EEPROM contents at the REPL.
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
# ***** TEST OF DRIVER *****
def _testblock(eep, bs):
d0 = b"this >"
d1 = b"<is the boundary"
d2 = d0 + d1
garbage = b"xxxxxxxxxxxxxxxxxxx"
start = bs - len(d0)
end = start + len(garbage)
eep[start:end] = garbage
res = eep[start:end]
if res != garbage:
return "Block test fail 1:" + str(list(res))
end = start + len(d0)
eep[start:end] = d0
end = start + len(garbage)
res = eep[start:end]
if res != b"this >xxxxxxxxxxxxx":
return "Block test fail 2:" + str(list(res))
start = bs
end = bs + len(d1)
eep[start:end] = d1
start = bs - len(d0)
end = start + len(d2)
res = eep[start:end]
if res != d2:
return "Block test fail 3:" + str(list(res))
def test():
eep = get_eep()
sa = 1000
for v in range(256):
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)
)
break
else:
print("Test of byte addressing passed")
data = uos.urandom(30)
sa = 2000
eep[sa : sa + 30] = data
if eep[sa : sa + 30] == data:
print("Test of slice readback passed")
block = 256
res = _testblock(eep, block)
if res is None:
print("Test block boundary {} passed".format(block))
else:
print("Test block boundary {} fail".format(block))
print(res)
block = eep._c_bytes
if eep._a_bytes > block:
res = _testblock(eep, block)
if res is None:
print("Test chip boundary {} passed".format(block))
else:
print("Test chip boundary {} fail".format(block))
print(res)
else:
print("Test chip boundary skipped: only one chip!")
# ***** TEST OF FILESYSTEM MOUNT *****
def fstest(format=False):
eep = get_eep()
# ***** CODE FOR LITTLEFS *****
if format:
uos.VfsLfs2.mkfs(eep)
try:
uos.mount(eep, "/eeprom")
except OSError: # Already mounted
pass
print('Contents of "/": {}'.format(uos.listdir("/")))
print('Contents of "/eeprom": {}'.format(uos.listdir("/eeprom")))
print(uos.statvfs("/eeprom"))
def cptest():
eep = get_eep()
if "eeprom" in uos.listdir("/"):
print("Device already mounted.")
else:
try:
uos.mount(eep, "/eeprom")
except OSError:
print("Fail mounting device. Have you formatted it?")
return
print("Mounted device.")
cp("eep_i2c.py", "/eeprom/")
cp("eeprom_i2c.py", "/eeprom/")
print('Contents of "/eeprom": {}'.format(uos.listdir("/eeprom")))
print(uos.statvfs("/eeprom"))
# ***** TEST OF HARDWARE *****
def full_test():
eep = get_eep()
page = 0
for sa in range(0, len(eep), 128):
data = uos.urandom(128)
eep[sa : sa + 128] = data
if eep[sa : sa + 128] == data:
print("Page {} passed".format(page))
else:
print("Page {} readback failed.".format(page))
page += 1

Wyświetl plik

@ -82,7 +82,29 @@ The SPI bus is fast: wiring should be short and direct.
2. `bdevice.py` (In root directory) Base class for the device driver.
3. `eep_spi.py` Test programs for above.
Installation: copy files 1 and 2 (optionally 3) to the target filesystem.
## 3.1 Installation
This installs the first three files in the `lib` directory.
On networked hardware this may be done with `mip` which is included in recent
firmware. On non-networked hardware this is done using the official
[mpremote utility](http://docs.micropython.org/en/latest/reference/mpremote.html)
which should be installed on the PC as described in this doc.
#### Any hardware
On the PC issue:
```bash
$ mpremote mip install "github:peterhinch/micropython_eeprom/eeprom/spi"
```
#### Networked hardware
At the device REPL issue:
```python
>>> import mip
>>> mip.install("github:peterhinch/micropython_eeprom/eeprom/spi")
```
# 4. The device driver

Wyświetl plik

@ -165,11 +165,12 @@ def full_test(stm=False):
eep[sa : sa + 256] = data
got = eep[sa : sa + 256]
if got == data:
print(f"Block {block} passed")
print(f"Block {block} passed\r", end="")
else:
print(f"Block {block} readback failed.")
break
block += 1
print()
def help():

Wyświetl plik

@ -0,0 +1,8 @@
{
"urls": [
["bdevice.py", "github:peterhinch/micropython_eeprom/bdevice.py"],
["eep_spi.py", "github:peterhinch/micropython_eeprom/eeprom/spi/eep_spi.py"],
["eeprom_spi.py", "github:peterhinch/micropython_eeprom/eeprom/spi/eeprom_spi.py"]
],
"version": "0.1"
}