From 2a2ceab903959121c0288fbcb4492958c4d9af5f Mon Sep 17 00:00:00 2001 From: Peter Hinch Date: Sun, 14 Jan 2024 11:28:15 +0000 Subject: [PATCH] EEPROM: Support i2c 2KiB eeproms, also MIP install. --- eeprom/i2c/I2C.md | 53 ++++++++++-- eeprom/i2c/eep_i2c.py | 35 ++++---- eeprom/i2c/eeprom_i2c.py | 9 ++- eeprom/i2c/package.json | 8 ++ eeprom/i2c/wemos_i2c_eeprom.py | 142 --------------------------------- eeprom/spi/SPI.md | 24 +++++- eeprom/spi/eep_spi.py | 3 +- eeprom/spi/package.json | 8 ++ 8 files changed, 114 insertions(+), 168 deletions(-) create mode 100644 eeprom/i2c/package.json delete mode 100644 eeprom/i2c/wemos_i2c_eeprom.py create mode 100644 eeprom/spi/package.json diff --git a/eeprom/i2c/I2C.md b/eeprom/i2c/I2C.md index 470771e..72c1b13 100644 --- a/eeprom/i2c/I2C.md +++ b/eeprom/i2c/I2C.md @@ -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. diff --git a/eeprom/i2c/eep_i2c.py b/eeprom/i2c/eep_i2c.py index 5428846..91717da 100644 --- a/eeprom/i2c/eep_i2c.py +++ b/eeprom/i2c/eep_i2c.py @@ -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() diff --git a/eeprom/i2c/eeprom_i2c.py b/eeprom/i2c/eeprom_i2c.py index 075a08f..0d8fd20 100644 --- a/eeprom/i2c/eeprom_i2c.py +++ b/eeprom/i2c/eeprom_i2c.py @@ -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 diff --git a/eeprom/i2c/package.json b/eeprom/i2c/package.json new file mode 100644 index 0000000..dbe3b40 --- /dev/null +++ b/eeprom/i2c/package.json @@ -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" +} diff --git a/eeprom/i2c/wemos_i2c_eeprom.py b/eeprom/i2c/wemos_i2c_eeprom.py deleted file mode 100644 index 9b5e9e2..0000000 --- a/eeprom/i2c/wemos_i2c_eeprom.py +++ /dev/null @@ -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"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 diff --git a/eeprom/spi/SPI.md b/eeprom/spi/SPI.md index add8d6e..6e50d4f 100644 --- a/eeprom/spi/SPI.md +++ b/eeprom/spi/SPI.md @@ -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 diff --git a/eeprom/spi/eep_spi.py b/eeprom/spi/eep_spi.py index f783b6d..3b539c3 100644 --- a/eeprom/spi/eep_spi.py +++ b/eeprom/spi/eep_spi.py @@ -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(): diff --git a/eeprom/spi/package.json b/eeprom/spi/package.json new file mode 100644 index 0000000..837c3ee --- /dev/null +++ b/eeprom/spi/package.json @@ -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" +}