kopia lustrzana https://github.com/peterhinch/micropython_eeprom
EEPROM: Support i2c 2KiB eeproms, also MIP install.
rodzic
e06fd66bb3
commit
2a2ceab903
|
@ -1,7 +1,8 @@
|
||||||
# 1. A MicroPython I2C EEPROM driver
|
# 1. A MicroPython I2C EEPROM driver
|
||||||
|
|
||||||
This driver supports chips from the 64KiB 25xx512 series and related chips with
|
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
|
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
|
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.
|
7. Alternatively it can support byte-level access using Python slice syntax.
|
||||||
8. RAM allocations are reduced.
|
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)
|
##### [Main readme](../../README.md)
|
||||||
|
|
||||||
# 2. Connections
|
# 2. Connections
|
||||||
|
@ -77,11 +82,31 @@ Other platforms may vary.
|
||||||
|
|
||||||
1. `eeprom_i2c.py` Device driver.
|
1. `eeprom_i2c.py` Device driver.
|
||||||
2. `bdevice.py` (In root directory) Base class for the device driver.
|
2. `bdevice.py` (In root directory) Base class for the device driver.
|
||||||
3. `eep_i2c.py` Pyboard test programs for above.
|
3. `eep_i2c.py` Pyboard test programs for above (adapt for other hosts).
|
||||||
4. `wemos_i2c_eeprom.py` Test program using a Wemos D1 mini ESP8266 board.
|
|
||||||
|
|
||||||
Installation: copy files 1 and 2 (optionally 3 and/or 4) to the target
|
## 3.1 Installation
|
||||||
filesystem.
|
|
||||||
|
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
|
# 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)
|
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.
|
||||||
|
|
|
@ -152,22 +152,25 @@ def cptest(eep=None): # Assumes pre-existing filesystem of either type
|
||||||
|
|
||||||
|
|
||||||
# ***** TEST OF HARDWARE *****
|
# ***** 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()
|
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):
|
for sa in range(0, len(eep), block_size):
|
||||||
data = uos.urandom(block_size)
|
data = uos.urandom(block_size)
|
||||||
eep[sa : sa + block_size] = data
|
eep[sa : sa + block_size] = data
|
||||||
if 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:
|
else:
|
||||||
print("Page {} readback failed.".format(page))
|
print(f"Block {block} readback failed.")
|
||||||
page += 1
|
block += 1
|
||||||
|
print()
|
||||||
|
|
||||||
|
def help():
|
||||||
help = """Available tests:
|
st = """Available commands:
|
||||||
test() Basic fuctional test
|
help() Print this text.
|
||||||
full_test() Read-write test of EEPROM chip(s)
|
test() Basic fuctional test.
|
||||||
|
full_test() Read-write test of EEPROM chip(s).
|
||||||
fstest() Check or create a filesystem.
|
fstest() Check or create a filesystem.
|
||||||
cptest() Check a filesystem by copying source files to it.
|
cptest() Check a filesystem by copying source files to it.
|
||||||
|
|
||||||
|
@ -175,4 +178,6 @@ Utilities:
|
||||||
get_eep() Initialise and return an EEPROM instance.
|
get_eep() Initialise and return an EEPROM instance.
|
||||||
cp() Very crude file copy utility.
|
cp() Very crude file copy utility.
|
||||||
"""
|
"""
|
||||||
print(help)
|
print(st)
|
||||||
|
|
||||||
|
help()
|
||||||
|
|
|
@ -40,6 +40,7 @@ class EEPROM(EepromDevice):
|
||||||
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._onebyte = chip_size <= 256 # Single byte address
|
||||||
# superclass figures out _page_size and _page_mask
|
# superclass figures out _page_size and _page_mask
|
||||||
super().__init__(block_size, nchips, chip_size, page_size, verbose)
|
super().__init__(block_size, nchips, chip_size, page_size, verbose)
|
||||||
|
|
||||||
|
@ -90,12 +91,14 @@ class EEPROM(EepromDevice):
|
||||||
start = 0 # Offset into buf.
|
start = 0 # Offset into buf.
|
||||||
while nbytes > 0:
|
while nbytes > 0:
|
||||||
npage = self._getaddr(addr, nbytes) # No. of bytes in current page
|
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:
|
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])
|
self._i2c.readfrom_into(self._i2c_addr, mvb[start : start + npage])
|
||||||
else:
|
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()
|
self._wait_rdy()
|
||||||
nbytes -= npage
|
nbytes -= npage
|
||||||
start += npage
|
start += npage
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
|
@ -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
|
|
|
@ -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.
|
2. `bdevice.py` (In root directory) Base class for the device driver.
|
||||||
3. `eep_spi.py` Test programs for above.
|
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
|
# 4. The device driver
|
||||||
|
|
||||||
|
|
|
@ -165,11 +165,12 @@ def full_test(stm=False):
|
||||||
eep[sa : sa + 256] = data
|
eep[sa : sa + 256] = data
|
||||||
got = eep[sa : sa + 256]
|
got = eep[sa : sa + 256]
|
||||||
if got == data:
|
if got == data:
|
||||||
print(f"Block {block} passed")
|
print(f"Block {block} passed\r", end="")
|
||||||
else:
|
else:
|
||||||
print(f"Block {block} readback failed.")
|
print(f"Block {block} readback failed.")
|
||||||
break
|
break
|
||||||
block += 1
|
block += 1
|
||||||
|
print()
|
||||||
|
|
||||||
|
|
||||||
def help():
|
def help():
|
||||||
|
|
|
@ -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"
|
||||||
|
}
|
Ładowanie…
Reference in New Issue