micropython_eeprom/spiram/spiram.py

97 wiersze
3.1 KiB
Python

# spiram.py Supports 8MiB SPI RAM
# Adafruit https://www.adafruit.com/product/4677
# These chips are almost identical. Command sets are identical.
# Product ID 1st byte, LS 4 bits is density 0x8 == 2MiB 0x9 == 4MiB
# Released under the MIT License (MIT). See LICENSE.
# Copyright (c) 2020 Peter Hinch
from micropython import const
from bdevice import BlockDevice
# Command set
_WRITE = const(2)
_READ = const(3)
_RSTEN = const(0x66)
_RESET = const(0x99)
_RDID = const(0x9F)
class SPIRAM(BlockDevice):
def __init__(self, spi, cspins, size=8192, verbose=True, block_size=9):
if size != 8192:
print("SPIRAM size other than 8192KiB may not work.")
super().__init__(block_size, len(cspins), size * 1024)
self._spi = spi
self._cspins = cspins
self._ccs = None # Chip select Pin object for current chip
bufp = bytearray(6) # instruction + 3 byte address + 2 byte value
mvp = memoryview(bufp) # cost-free slicing
self._mvp = mvp
# Check hardware
for n, cs in enumerate(cspins):
mvp[:] = b"\0\0\0\0\0\0"
mvp[0] = _RDID
cs(0)
self._spi.write_readinto(mvp, mvp)
cs(1)
if mvp[4] != 0x0D or mvp[5] != 0x5D:
print("Warning: expected manufacturer ID not found.")
if verbose:
s = "Total SPIRAM size {} KiB in {} devices."
print(s.format(self._a_bytes // 1024, n + 1))
# Given an address, set current chip select and address buffer.
# Return the number of bytes that can be processed in the current chip.
def _getaddr(self, addr, nbytes):
if addr >= self._a_bytes:
raise RuntimeError("SPIRAM Address is out of range")
ca, la = divmod(addr, self._c_bytes) # ca == chip no, la == offset into chip
self._ccs = self._cspins[ca] # Current chip select
mvp = self._mvp
mvp[1] = la >> 16
mvp[2] = (la >> 8) & 0xFF
mvp[3] = la & 0xFF
pe = (addr & -self._c_bytes) + self._c_bytes # Byte 0 of next chip
return min(nbytes, pe - la)
# Interface to bdevice
def readwrite(self, addr, buf, read):
nbytes = len(buf)
mvb = memoryview(buf)
mvp = self._mvp
start = 0 # Offset into buf.
while nbytes > 0:
nchip = self._getaddr(addr, nbytes) # No of bytes that fit on current chip
cs = self._ccs
if read:
mvp[0] = _READ
cs(0)
self._spi.write(mvp[:4])
self._spi.readinto(mvb[start : start + nchip])
cs(1)
else:
mvp[0] = _WRITE
cs(0)
self._spi.write(mvp[:4])
self._spi.write(mvb[start : start + nchip])
cs(1)
nbytes -= nchip
start += nchip
addr += nchip
return buf
# Reset is unnecessary because it restores the default power-up state.
# def _reset(self, cs, bufr = bytearray(1)):
# cs(0)
# bufr[0] = _RSTEN
# self._spi.write(bufr)
# cs(1)
# cs(0)
# bufr[0] = _RESET
# self._spi.write(bufr)
# cs(1)