2019-12-13 18:49:48 +00:00
|
|
|
# eeprom_i2c.py MicroPython driver for Microchip I2C EEPROM devices.
|
2019-12-11 09:23:17 +00:00
|
|
|
|
|
|
|
# Released under the MIT License (MIT). See LICENSE.
|
2024-01-09 14:38:53 +00:00
|
|
|
# Copyright (c) 2019-2024 Peter Hinch
|
|
|
|
|
|
|
|
# Thanks are due to Abel Deuring for help in diagnosing and fixing a page size issue.
|
2019-12-11 09:23:17 +00:00
|
|
|
|
|
|
|
import time
|
|
|
|
from micropython import const
|
2024-01-09 14:38:53 +00:00
|
|
|
from bdevice import EepromDevice
|
2019-12-11 09:23:17 +00:00
|
|
|
|
2019-12-17 17:10:43 +00:00
|
|
|
_ADDR = const(0x50) # Base address of chip
|
2022-09-24 16:42:58 +00:00
|
|
|
_MAX_CHIPS_COUNT = const(8) # Max number of chips
|
2019-12-11 09:23:17 +00:00
|
|
|
|
|
|
|
T24C512 = const(65536) # 64KiB 512Kbits
|
|
|
|
T24C256 = const(32768) # 32KiB 256Kbits
|
|
|
|
T24C128 = const(16384) # 16KiB 128Kbits
|
|
|
|
T24C64 = const(8192) # 8KiB 64Kbits
|
2022-09-24 16:42:58 +00:00
|
|
|
T24C32 = const(4096) # 4KiB 32Kbits
|
2019-12-11 09:23:17 +00:00
|
|
|
|
|
|
|
# Logical EEPROM device consists of 1-8 physical chips. Chips must all be the
|
2022-09-24 16:42:58 +00:00
|
|
|
# same size, and must have contiguous addresses.
|
2024-01-09 14:38:53 +00:00
|
|
|
class EEPROM(EepromDevice):
|
|
|
|
def __init__(
|
|
|
|
self,
|
|
|
|
i2c,
|
|
|
|
chip_size=T24C512,
|
|
|
|
verbose=True,
|
|
|
|
block_size=9,
|
|
|
|
addr=_ADDR,
|
|
|
|
max_chips_count=_MAX_CHIPS_COUNT,
|
|
|
|
page_size=None,
|
|
|
|
):
|
2019-12-11 09:23:17 +00:00
|
|
|
self._i2c = i2c
|
2022-09-24 16:42:58 +00:00
|
|
|
if chip_size not in (T24C32, T24C64, T24C128, T24C256, T24C512):
|
2022-09-22 09:04:29 +00:00
|
|
|
print("Warning: possible unsupported chip. Size:", chip_size)
|
2024-01-09 14:38:53 +00:00
|
|
|
# Get no. of EEPROM chips
|
|
|
|
nchips, min_chip_address = self.scan(verbose, chip_size, addr, max_chips_count)
|
2022-09-24 16:42:58 +00:00
|
|
|
self._min_chip_address = min_chip_address
|
2019-12-11 09:23:17 +00:00
|
|
|
self._i2c_addr = 0 # I2C address of current chip
|
|
|
|
self._buf1 = bytearray(1)
|
|
|
|
self._addrbuf = bytearray(2) # Memory offset into current chip
|
2024-01-09 14:38:53 +00:00
|
|
|
# superclass figures out _page_size and _page_mask
|
|
|
|
super().__init__(block_size, nchips, chip_size, page_size, verbose)
|
2019-12-11 09:23:17 +00:00
|
|
|
|
|
|
|
# Check for a valid hardware configuration
|
2023-10-13 13:52:26 +00:00
|
|
|
def scan(self, verbose, chip_size, addr, max_chips_count):
|
2019-12-11 09:23:17 +00:00
|
|
|
devices = self._i2c.scan() # All devices on I2C bus
|
2023-10-13 13:52:26 +00:00
|
|
|
eeproms = [d for d in devices if addr <= d < addr + max_chips_count] # EEPROM chips
|
2019-12-11 09:23:17 +00:00
|
|
|
nchips = len(eeproms)
|
|
|
|
if nchips == 0:
|
2022-09-22 09:04:29 +00:00
|
|
|
raise RuntimeError("EEPROM not found.")
|
2022-09-24 16:42:58 +00:00
|
|
|
eeproms = sorted(eeproms)
|
|
|
|
if len(set(eeproms)) != len(eeproms):
|
2024-01-09 14:38:53 +00:00
|
|
|
raise RuntimeError("Duplicate addresses were found", eeproms)
|
2022-09-24 16:42:58 +00:00
|
|
|
if (eeproms[-1] - eeproms[0] + 1) != len(eeproms):
|
2024-01-09 14:38:53 +00:00
|
|
|
raise RuntimeError("Non-contiguous chip addresses", eeproms)
|
2019-12-11 09:23:17 +00:00
|
|
|
if verbose:
|
2022-09-22 09:04:29 +00:00
|
|
|
s = "{} chips detected. Total EEPROM size {}bytes."
|
2019-12-11 09:23:17 +00:00
|
|
|
print(s.format(nchips, chip_size * nchips))
|
2022-09-24 16:42:58 +00:00
|
|
|
return nchips, min(eeproms)
|
2019-12-11 09:23:17 +00:00
|
|
|
|
|
|
|
def _wait_rdy(self): # After a write, wait for device to become ready
|
|
|
|
self._buf1[0] = 0
|
|
|
|
while True:
|
|
|
|
try:
|
|
|
|
if self._i2c.writeto(self._i2c_addr, self._buf1): # Poll ACK
|
|
|
|
break
|
|
|
|
except OSError:
|
|
|
|
pass
|
|
|
|
finally:
|
|
|
|
time.sleep_ms(1)
|
|
|
|
|
|
|
|
# Given an address, set ._i2c_addr and ._addrbuf and return the number of
|
|
|
|
# bytes that can be processed in the current page
|
|
|
|
def _getaddr(self, addr, nbytes): # Set up _addrbuf and _i2c_addr
|
|
|
|
if addr >= self._a_bytes:
|
|
|
|
raise RuntimeError("EEPROM Address is out of range")
|
|
|
|
ca, la = divmod(addr, self._c_bytes) # ca == chip no, la == offset into chip
|
2022-09-22 09:04:29 +00:00
|
|
|
self._addrbuf[0] = (la >> 8) & 0xFF
|
|
|
|
self._addrbuf[1] = la & 0xFF
|
2022-09-24 16:42:58 +00:00
|
|
|
self._i2c_addr = self._min_chip_address + ca
|
2024-01-09 14:38:53 +00:00
|
|
|
pe = (la & self._page_mask) + self._page_size # byte 0 of next page
|
2019-12-11 09:23:17 +00:00
|
|
|
return min(nbytes, pe - la)
|
|
|
|
|
|
|
|
# Read or write multiple bytes at an arbitrary address
|
|
|
|
def readwrite(self, addr, buf, read):
|
|
|
|
nbytes = len(buf)
|
|
|
|
mvb = memoryview(buf)
|
2019-12-17 17:10:43 +00:00
|
|
|
start = 0 # Offset into buf.
|
2019-12-11 09:23:17 +00:00
|
|
|
while nbytes > 0:
|
|
|
|
npage = self._getaddr(addr, nbytes) # No. of bytes in current page
|
|
|
|
assert npage > 0
|
|
|
|
if read:
|
|
|
|
self._i2c.writeto(self._i2c_addr, self._addrbuf)
|
|
|
|
self._i2c.readfrom_into(self._i2c_addr, mvb[start : start + npage])
|
|
|
|
else:
|
2024-01-09 14:38:53 +00:00
|
|
|
self._i2c.writevto(self._i2c_addr, (self._addrbuf, buf[start : start + npage]))
|
2019-12-11 09:23:17 +00:00
|
|
|
self._wait_rdy()
|
|
|
|
nbytes -= npage
|
|
|
|
start += npage
|
|
|
|
addr += npage
|
2019-12-21 09:25:32 +00:00
|
|
|
return buf
|