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
Bohdan Danishevsky 2022-09-20 23:49:52 +01:00
rodzic 79ef80fa25
commit d662501423
5 zmienionych plików z 74 dodań i 44 usunięć

3
.gitignore vendored
Wyświetl plik

@ -127,3 +127,6 @@ dmypy.json
# Pyre type checker # Pyre type checker
.pyre/ .pyre/
# PyCharm IDE
.idea/

Wyświetl plik

@ -67,7 +67,7 @@ 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. In the table below the Interface column includes page size in bytes.
| Manufacturer | Part | Interface | Bytes | Technology | Docs | | Manufacturer | Part | Interface | Bytes | Technology | Docs |
|:------------:|:---------:|:---------:|:-------:|:----------:|:-----------------------------:| |:------------:|:----------|:----------|:--------|:-----------|:----------------------------------|
| Various | Various | SPI 4096 | <=32MiB | Flash | [FLASH.md](./flash/FLASH.md) | | Various | Various | SPI 4096 | <=32MiB | Flash | [FLASH.md](./flash/FLASH.md) |
| STM | M95M02-DR | SPI 256 | 256KiB | EEPROM | [SPI.md](./eeprom/spi/SPI.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 | 25xx1024 | SPI 256 | 128KiB | EEPROM | [SPI.md](./eeprom/spi/SPI.md) |
@ -76,6 +76,7 @@ In the table below the Interface column includes page size in bytes.
| Microchip | 24xx256 | I2C 128 | 32KiB | 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 | 24xx128 | I2C 128 | 16KiB | EEPROM | [I2C.md](./eeprom/i2c/I2C.md) |
| Microchip | 24xx64 | I2C 128 | 8KiB | 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 | 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 | 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 | 1895 | I2C n/a | 32KiB | FRAM | [FRAM.md](./fram/FRAM.md) |

Wyświetl plik

@ -124,7 +124,8 @@ is detected or if device address lines are not wired as described in
Arguments: Arguments:
1. `i2c` Mandatory. An initialised master mode I2C bus created by `machine`. 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 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 3. `verbose=True` If `True`, the constructor issues information on the EEPROM
devices it has detected. devices it has detected.
4. `block_size=9` The block size reported to the filesystem. The size in bytes 4. `block_size=9` The block size reported to the filesystem. The size in bytes

Wyświetl plik

@ -8,16 +8,19 @@ import time
from machine import I2C, Pin from machine import I2C, Pin
from eeprom_i2c import EEPROM, T24C512 from eeprom_i2c import EEPROM, T24C512
# Return an EEPROM array. Adapt for platforms other than Pyboard or chips # Return an EEPROM array. Adapt for platforms other than Pyboard or chips
# smaller than 64KiB. # smaller than 64KiB.
def get_eep(): def get_eep():
if uos.uname().machine.split(' ')[0][:4] == 'PYBD': if uos.uname().machine.split(' ')[0][:4] == 'PYBD':
Pin.board.EN_3V3.value(1) Pin.board.EN_3V3.value(1)
time.sleep(0.1) # Allow decouplers to charge time.sleep(0.1) # Allow decouplers to charge
eep = EEPROM(I2C(2), T24C512) eep = EEPROM(I2C(2), T24C512)
print('Instantiated EEPROM') print('Instantiated EEPROM')
return eep return eep
# Dumb file copy utility to help with managing EEPROM contents at the REPL. # Dumb file copy utility to help with managing EEPROM contents at the REPL.
def cp(source, dest): def cp(source, dest):
if dest.endswith('/'): # minimal way to allow if dest.endswith('/'): # minimal way to allow
@ -30,6 +33,7 @@ def cp(source, dest):
if len(buf) < 100: if len(buf) < 100:
break break
# ***** TEST OF DRIVER ***** # ***** TEST OF DRIVER *****
def _testblock(eep, bs): def _testblock(eep, bs):
d0 = b'this >' d0 = b'this >'
@ -57,8 +61,10 @@ def _testblock(eep, bs):
if res != d2: if res != d2:
return 'Block test fail 3:' + str(list(res)) 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 sa = 1000
for v in range(256): for v in range(256):
eep[sa + v] = v eep[sa + v] = v
@ -92,9 +98,10 @@ def test():
else: else:
print('Test chip boundary skipped: only one chip!') print('Test chip boundary skipped: only one chip!')
# ***** TEST OF FILESYSTEM MOUNT ***** # ***** TEST OF FILESYSTEM MOUNT *****
def fstest(format=False): def fstest(eep = None, format=False):
eep = get_eep() eep = eep if eep else get_eep()
try: try:
uos.umount('/eeprom') uos.umount('/eeprom')
except OSError: except OSError:
@ -114,8 +121,10 @@ def fstest(format=False):
print('Contents of "/eeprom": {}'.format(uos.listdir('/eeprom'))) print('Contents of "/eeprom": {}'.format(uos.listdir('/eeprom')))
print(uos.statvfs('/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('/'): if 'eeprom' in uos.listdir('/'):
print('Device already mounted.') print('Device already mounted.')
else: else:
@ -130,14 +139,16 @@ def cptest(): # Assumes pre-existing filesystem of either type
print('Contents of "/eeprom": {}'.format(uos.listdir('/eeprom'))) print('Contents of "/eeprom": {}'.format(uos.listdir('/eeprom')))
print(uos.statvfs('/eeprom')) print(uos.statvfs('/eeprom'))
# ***** TEST OF HARDWARE ***** # ***** TEST OF HARDWARE *****
def full_test(): def full_test(eep = None, block_size = 128):
eep = get_eep() eep = eep if eep else get_eep()
page = 0 page = 0
for sa in range(0, len(eep), 128):
data = uos.urandom(128) for sa in range(0, len(eep), block_size):
eep[sa:sa + 128] = data data = uos.urandom(block_size)
if eep[sa:sa + 128] == data: eep[sa:sa + block_size] = data
if eep[sa:sa + block_size] == data:
print('Page {} passed'.format(page)) print('Page {} passed'.format(page))
else: else:
print('Page {} readback failed.'.format(page)) print('Page {} readback failed.'.format(page))

Wyświetl plik

@ -8,22 +8,27 @@ from micropython import const
from bdevice import BlockDevice from bdevice import BlockDevice
_ADDR = const(0x50) # Base address of chip _ADDR = const(0x50) # Base address of chip
_MAX_CHIPS_COUNT = 8
T24C512 = const(65536) # 64KiB 512Kbits T24C512 = const(65536) # 64KiB 512Kbits
T24C256 = const(32768) # 32KiB 256Kbits T24C256 = const(32768) # 32KiB 256Kbits
T24C128 = const(16384) # 16KiB 128Kbits T24C128 = const(16384) # 16KiB 128Kbits
T24C64 = const(8192) # 8KiB 64Kbits T24C64 = const(8192) # 8KiB 64Kbits
T24C32 = const(4096) # 4KiB 32Kbits
# Logical EEPROM device consists of 1-8 physical chips. Chips must all be the # 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): class EEPROM(BlockDevice):
def __init__(self, i2c, chip_size=T24C512, verbose=True, block_size=9): def __init__(self, i2c, chip_size=T24C512, verbose=True, block_size=9):
self._i2c = i2c 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) 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) super().__init__(block_size, nchips, chip_size)
self._min_chip_address = min_chip_address
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
@ -31,16 +36,24 @@ class EEPROM(BlockDevice):
# Check for a valid hardware configuration # Check for a valid hardware configuration
def scan(self, verbose, chip_size): def scan(self, verbose, chip_size):
devices = self._i2c.scan() # All devices on I2C bus 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) nchips = len(eeproms)
if nchips == 0: if nchips == 0:
raise RuntimeError('EEPROM not found.') 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) raise RuntimeError('Non-contiguous chip addresses', eeproms)
if verbose: if verbose:
s = '{} chips detected. Total EEPROM size {}bytes.' s = '{} chip(s) detected. Total EEPROM size {}bytes.'
print(s.format(nchips, chip_size * nchips)) 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 def _wait_rdy(self): # After a write, wait for device to become ready
self._buf1[0] = 0 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 ca, la = divmod(addr, self._c_bytes) # ca == chip no, la == offset into chip
self._addrbuf[0] = (la >> 8) & 0xff self._addrbuf[0] = (la >> 8) & 0xff
self._addrbuf[1] = la & 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 pe = (addr & ~0x7f) + 0x80 # byte 0 of next page
return min(nbytes, pe - la) return min(nbytes, pe - la)
@ -83,3 +96,4 @@ class EEPROM(BlockDevice):
start += npage start += npage
addr += npage addr += npage
return buf return buf