kopia lustrzana https://github.com/peterhinch/micropython_eeprom
Add support of Microchip AT24C32 and support of single chip for `EEPROM` class
[Description] Adds support of: 1. Microchip AT24C32 (4 KiB) 2. Support of single chip setup for `EEPROM` class when single chip has address is 0x57 so entire set of chips (1 chip in our case) addresses are not started from 0x50. Example: ```python from eeprom_i2c import EEPROM, T24C32 eeprom = PROM(machine.I2C(0), T24C32) ``` Improves tests implementation by adding dependency injection of `EEPROM` object used during testing with support of old use case when object had not been provided for testing. Example (for AT24C32 connected to I2C0 of my Raspberry Pi Pico W) when we creating instance of `EEPROM` and then passing it to `full_test` and also providing proper block size for this chip: ```python import machine from eeprom_i2c import EEPROM, T24C32 from eep_i2c import full_test def get_eep(): return EEPROM(machine.I2C(0), T24C32) def main(): print("App started") print("Running tests") eep = get_eep() full_test(eep, block_size=32) print("App finished") if __name__ == "__main__": main() ``` [Motivation] Have DS3231 with soldered AT24C32 chip and want to use both RTC and EEPROM. In my case AT24C32 has 0x57 as it's address and `EEPROM` class refused to work with this setup. [Testing] Executed `full_test` from `eep_i2c` against AT24C32 (with address 0x57) connected to my Raspberry Pi Pico W. Test code: ```python import machine from eeprom_i2c import EEPROM, T24C32 from eep_i2c import full_test def get_eep(): return EEPROM(machine.I2C(0), T24C32) def main(): print("App started") print("Running tests") eep = get_eep() full_test(eep, block_size=32) print("App finished") if __name__ == "__main__": main() ```pull/13/head^2
rodzic
79ef80fa25
commit
d662501423
|
@ -127,3 +127,6 @@ dmypy.json
|
|||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# PyCharm IDE
|
||||
.idea/
|
29
README.md
29
README.md
|
@ -66,20 +66,21 @@ M95M02-DRMN6TP and M95M02-DWMN3TP/K. The latter has a wider temperature range.
|
|||
|
||||
In the table below the Interface column includes page size in bytes.
|
||||
|
||||
| Manufacturer | Part | Interface | Bytes | Technology | Docs |
|
||||
|:------------:|:---------:|:---------:|:-------:|:----------:|:-----------------------------:|
|
||||
| Various | Various | SPI 4096 | <=32MiB | Flash | [FLASH.md](./flash/FLASH.md) |
|
||||
| STM | M95M02-DR | SPI 256 | 256KiB | EEPROM | [SPI.md](./eeprom/spi/SPI.md) |
|
||||
| Microchip | 25xx1024 | SPI 256 | 128KiB | EEPROM | [SPI.md](./eeprom/spi/SPI.md) |
|
||||
| Microchip | 25xx512* | SPI 256 | 64KiB | EEPROM | [SPI.md](./eeprom/spi/SPI.md) |
|
||||
| Microchip | 24xx512 | I2C 128 | 64KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
|
||||
| Microchip | 24xx256 | I2C 128 | 32KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
|
||||
| Microchip | 24xx128 | I2C 128 | 16KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
|
||||
| Microchip | 24xx64 | I2C 128 | 8KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
|
||||
| Adafruit | 4719 | SPI n/a | 512KiB | FRAM | [FRAM_SPI.md](./fram/FRAM_SPI.md) |
|
||||
| Adafruit | 4718 | SPI n/a | 256KiB | FRAM | [FRAM_SPI.md](./fram/FRAM_SPI.md) |
|
||||
| Adafruit | 1895 | I2C n/a | 32KiB | FRAM | [FRAM.md](./fram/FRAM.md) |
|
||||
| Adafruit | 4677 | SPI n/a | 8MiB | SPIRAM | [SPIRAM.md](./spiram/SPIRAM.md) |
|
||||
| Manufacturer | Part | Interface | Bytes | Technology | Docs |
|
||||
|:------------:|:----------|:----------|:--------|:-----------|:----------------------------------|
|
||||
| Various | Various | SPI 4096 | <=32MiB | Flash | [FLASH.md](./flash/FLASH.md) |
|
||||
| STM | M95M02-DR | SPI 256 | 256KiB | EEPROM | [SPI.md](./eeprom/spi/SPI.md) |
|
||||
| Microchip | 25xx1024 | SPI 256 | 128KiB | EEPROM | [SPI.md](./eeprom/spi/SPI.md) |
|
||||
| Microchip | 25xx512* | SPI 256 | 64KiB | EEPROM | [SPI.md](./eeprom/spi/SPI.md) |
|
||||
| Microchip | 24xx512 | I2C 128 | 64KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
|
||||
| Microchip | 24xx256 | I2C 128 | 32KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
|
||||
| Microchip | 24xx128 | I2C 128 | 16KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
|
||||
| Microchip | 24xx64 | I2C 128 | 8KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
|
||||
| Microchip | 24xx32 | I2C 32 | 4KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
|
||||
| Adafruit | 4719 | SPI n/a | 512KiB | FRAM | [FRAM_SPI.md](./fram/FRAM_SPI.md) |
|
||||
| Adafruit | 4718 | SPI n/a | 256KiB | FRAM | [FRAM_SPI.md](./fram/FRAM_SPI.md) |
|
||||
| Adafruit | 1895 | I2C n/a | 32KiB | FRAM | [FRAM.md](./fram/FRAM.md) |
|
||||
| Adafruit | 4677 | SPI n/a | 8MiB | SPIRAM | [SPIRAM.md](./spiram/SPIRAM.md) |
|
||||
|
||||
Parts marked * have been tested by users (see below).
|
||||
The SPIRAM chip is equivalent to Espressif ESP-PSRAM64H.
|
||||
|
|
|
@ -124,7 +124,8 @@ is detected or if device address lines are not wired as described in
|
|||
Arguments:
|
||||
1. `i2c` Mandatory. An initialised master mode I2C bus created by `machine`.
|
||||
2. `chip_size=T24C512` The chip size in bits. The module provides constants
|
||||
`T24C64`, `T24C128`, `T24C256`, `T24C512` for the supported chip sizes.
|
||||
`T24C32`, `T24C64`, `T24C128`, `T24C256`, `T24C512` for the supported
|
||||
chip sizes.
|
||||
3. `verbose=True` If `True`, the constructor issues information on the EEPROM
|
||||
devices it has detected.
|
||||
4. `block_size=9` The block size reported to the filesystem. The size in bytes
|
||||
|
|
|
@ -8,28 +8,32 @@ import time
|
|||
from machine import I2C, Pin
|
||||
from eeprom_i2c import EEPROM, T24C512
|
||||
|
||||
|
||||
# Return an EEPROM array. Adapt for platforms other than Pyboard or chips
|
||||
# smaller than 64KiB.
|
||||
def get_eep():
|
||||
if uos.uname().machine.split(' ')[0][:4] == 'PYBD':
|
||||
Pin.board.EN_3V3.value(1)
|
||||
time.sleep(0.1) # Allow decouplers to charge
|
||||
|
||||
eep = EEPROM(I2C(2), 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
|
||||
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 >'
|
||||
|
@ -38,27 +42,29 @@ def _testblock(eep, bs):
|
|||
garbage = b'xxxxxxxxxxxxxxxxxxx'
|
||||
start = bs - len(d0)
|
||||
end = start + len(garbage)
|
||||
eep[start : end] = garbage
|
||||
res = eep[start : end]
|
||||
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
|
||||
eep[start: end] = d0
|
||||
end = start + len(garbage)
|
||||
res = eep[start : end]
|
||||
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
|
||||
eep[start: end] = d1
|
||||
start = bs - len(d0)
|
||||
end = start + len(d2)
|
||||
res = eep[start : end]
|
||||
res = eep[start: end]
|
||||
if res != d2:
|
||||
return 'Block test fail 3:' + str(list(res))
|
||||
|
||||
def test():
|
||||
eep = get_eep()
|
||||
|
||||
def test(eep = None):
|
||||
eep = eep if eep else get_eep()
|
||||
|
||||
sa = 1000
|
||||
for v in range(256):
|
||||
eep[sa + v] = v
|
||||
|
@ -92,9 +98,10 @@ def test():
|
|||
else:
|
||||
print('Test chip boundary skipped: only one chip!')
|
||||
|
||||
|
||||
# ***** TEST OF FILESYSTEM MOUNT *****
|
||||
def fstest(format=False):
|
||||
eep = get_eep()
|
||||
def fstest(eep = None, format=False):
|
||||
eep = eep if eep else get_eep()
|
||||
try:
|
||||
uos.umount('/eeprom')
|
||||
except OSError:
|
||||
|
@ -107,15 +114,17 @@ def fstest(format=False):
|
|||
uos.VfsLfs2.mkfs(eep)
|
||||
# General
|
||||
try:
|
||||
uos.mount(eep,'/eeprom')
|
||||
uos.mount(eep, '/eeprom')
|
||||
except OSError:
|
||||
raise OSError("Can't mount device: have you formatted it?")
|
||||
print('Contents of "/": {}'.format(uos.listdir('/')))
|
||||
print('Contents of "/eeprom": {}'.format(uos.listdir('/eeprom')))
|
||||
print(uos.statvfs('/eeprom'))
|
||||
|
||||
def cptest(): # Assumes pre-existing filesystem of either type
|
||||
eep = get_eep()
|
||||
|
||||
def cptest(eep = None): # Assumes pre-existing filesystem of either type
|
||||
eep = eep if eep else get_eep()
|
||||
|
||||
if 'eeprom' in uos.listdir('/'):
|
||||
print('Device already mounted.')
|
||||
else:
|
||||
|
@ -130,14 +139,16 @@ def cptest(): # Assumes pre-existing filesystem of either type
|
|||
print('Contents of "/eeprom": {}'.format(uos.listdir('/eeprom')))
|
||||
print(uos.statvfs('/eeprom'))
|
||||
|
||||
|
||||
# ***** TEST OF HARDWARE *****
|
||||
def full_test():
|
||||
eep = get_eep()
|
||||
def full_test(eep = None, block_size = 128):
|
||||
eep = eep if eep else 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:
|
||||
|
||||
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))
|
||||
else:
|
||||
print('Page {} readback failed.'.format(page))
|
||||
|
|
|
@ -8,22 +8,27 @@ from micropython import const
|
|||
from bdevice import BlockDevice
|
||||
|
||||
_ADDR = const(0x50) # Base address of chip
|
||||
_MAX_CHIPS_COUNT = 8
|
||||
|
||||
T24C512 = const(65536) # 64KiB 512Kbits
|
||||
T24C256 = const(32768) # 32KiB 256Kbits
|
||||
T24C128 = const(16384) # 16KiB 128Kbits
|
||||
T24C64 = const(8192) # 8KiB 64Kbits
|
||||
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 starting from 0x50.
|
||||
# same size, and must have contiguous addresses.
|
||||
class EEPROM(BlockDevice):
|
||||
|
||||
def __init__(self, i2c, chip_size=T24C512, verbose=True, block_size=9):
|
||||
self._i2c = i2c
|
||||
if chip_size not in (T24C64, T24C128, T24C256, T24C512):
|
||||
if chip_size not in (T24C64, T24C128, T24C256, T24C512, T24C32):
|
||||
print('Warning: possible unsupported chip. Size:', chip_size)
|
||||
nchips = self.scan(verbose, chip_size) # No. of EEPROM chips
|
||||
nchips, min_chip_address = self.scan(verbose, chip_size) # No. of EEPROM chips
|
||||
super().__init__(block_size, nchips, chip_size)
|
||||
|
||||
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
|
||||
|
@ -31,16 +36,24 @@ class EEPROM(BlockDevice):
|
|||
# Check for a valid hardware configuration
|
||||
def scan(self, verbose, chip_size):
|
||||
devices = self._i2c.scan() # All devices on I2C bus
|
||||
eeproms = [d for d in devices if _ADDR <= d < _ADDR + 8] # EEPROM chips
|
||||
eeproms = [d for d in devices if _ADDR <= d < _ADDR + _MAX_CHIPS_COUNT] # EEPROM chips
|
||||
nchips = len(eeproms)
|
||||
if nchips == 0:
|
||||
raise RuntimeError('EEPROM not found.')
|
||||
if min(eeproms) != _ADDR or (max(eeproms) - _ADDR) >= nchips:
|
||||
|
||||
eeproms = sorted(eeproms)
|
||||
if len(set(eeproms)) != len(eeproms):
|
||||
raise RuntimeError('Duplicate addresses were found', eeproms)
|
||||
|
||||
if (eeproms[-1] - eeproms[0] + 1) != len(eeproms):
|
||||
raise RuntimeError('Non-contiguous chip addresses', eeproms)
|
||||
|
||||
if verbose:
|
||||
s = '{} chips detected. Total EEPROM size {}bytes.'
|
||||
s = '{} chip(s) detected. Total EEPROM size {}bytes.'
|
||||
print(s.format(nchips, chip_size * nchips))
|
||||
return nchips
|
||||
print(f"Chips addresses are: {eeproms}")
|
||||
|
||||
return nchips, min(eeproms)
|
||||
|
||||
def _wait_rdy(self): # After a write, wait for device to become ready
|
||||
self._buf1[0] = 0
|
||||
|
@ -61,7 +74,7 @@ class EEPROM(BlockDevice):
|
|||
ca, la = divmod(addr, self._c_bytes) # ca == chip no, la == offset into chip
|
||||
self._addrbuf[0] = (la >> 8) & 0xff
|
||||
self._addrbuf[1] = la & 0xff
|
||||
self._i2c_addr = _ADDR + ca
|
||||
self._i2c_addr = self._min_chip_address + ca
|
||||
pe = (addr & ~0x7f) + 0x80 # byte 0 of next page
|
||||
return min(nbytes, pe - la)
|
||||
|
||||
|
@ -75,7 +88,7 @@ class EEPROM(BlockDevice):
|
|||
assert npage > 0
|
||||
if read:
|
||||
self._i2c.writeto(self._i2c_addr, self._addrbuf)
|
||||
self._i2c.readfrom_into(self._i2c_addr, mvb[start : start + npage])
|
||||
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._wait_rdy()
|
||||
|
@ -83,3 +96,4 @@ class EEPROM(BlockDevice):
|
|||
start += npage
|
||||
addr += npage
|
||||
return buf
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue