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
|
||||
|
||||
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.
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
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
|
||||
|
||||
|
|
|
@ -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():
|
||||
|
|
|
@ -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