# uPy - Esp32 / RPi Pico SPI driver for fram MB85RS64
# Copyright by Anthony Vogelaar, Perth Scotland
# rev 0.10  2025-01-14  initial release

# Implemented:
# MB85RS64(spi, cs)
#    MB85RS64.getStatus(self)
#    MB85RS64.writeProtect(protect=True)
#    MB85RS64.write (addr=0x0000, data=b'\x00')
#    MB85RS64.read  (addr=0x0000, n=1)			-->  bytes
#    MB85RS64.swrite(addr=0x0000, s='')			-->  n
#    MB85RS64.sread (addr=0x0000)				-->  s

# ================== Fram Constants ===================

WREN	= 0x06						# MB85R64 set   write enable latch
WRDI	= 0x04						# MB85R64 reset write enable latch
RDSR	= 0x05						# MB85R64 read  status register
WRSR	= 0x01						# MB85R64 write status register, protected by /WP = gnd
READ	= 0x03						# MB85R64 read  date
WRITE	= 0x02						# MB85R64 write data

# ==================== class Fram =====================

class MB85RS64:
    
    def __init__(self, spi, cs):
        self.spi  = spi
        self.cs	  = cs
        self.buf1 = bytearray(1)
        self.size = 8192				# number of available bytes	
        
    
    # return fram status byte
    def getStatus(self):
        self.cs(0)
        data_rx = self.spi.read(2, RDSR)
        self.cs(1)
        return(data_rx[1])
        

    # set fram write enable latch to protect
    def writeProtect(self, protect=True):
        self.cs(0)
        self.buf1[0] = WRDI if protect else WREN
        self.spi.write(self.buf1)
        self.cs(1)


    # write bytes / bytearray to addr
    def write(self, addr=0x0000, data=b'\x00'):
        if not isinstance(data, (bytes, bytearray)): return
        data_tx = bytes([WRITE, addr >> 8, addr & 0xFF]) + data
        self.writeProtect(False)
        self.cs(0)
        self.spi.write(data_tx)
        self.cs(1)
        self.writeProtect(True)
        
        
    # read n bytearray from addr
    def read(self, addr=0x0000, n=1):
        data_tx = bytes([READ, addr >> 8, addr & 0xFF]) + bytes(n)
        data_rx = bytearray(3+n)
        self.cs(0)
        self.spi.write_readinto(data_tx, data_rx)
        self.cs(1)
        return data_rx[3:]
    

    # write utf-8 string (len <= 255) to addr
    def swrite(self, addr=0x0000, s=''):
        if not s: return
        data 	 = bytes(s, 'utf-8')
        lng = len(data)
        if lng > 255: return				# error, string too long
        data_tx = bytearray([lng])
        data_tx.extend(data)
        self.write(addr, data_tx)
        return lng+1


    # read utf-8 string from addr
    def sread(self, addr=0x0000):
        lng = self.read(addr)[0]
        if not lng: return					# exit if no string available
        str_b = self.read(addr+1, lng)
        return str_b.decode()				# utf-8 is default

# =========================== Test ============================

if __name__ == '__main__':

    from	machine		import	Pin, SPI
    
    # ============== Hardware definitions (Pico) ==============

    pin_led         = 25				# on-board green led
    pin_spi_sck		= 18
    pin_spi_mosi	= 19
    pin_spi_miso	= 16
    pin_spi_cs		= 17

    # ================= Hardware setup  =======================

    led		= Pin(pin_led, Pin.OUT, value=1)
    spi	    = SPI(0, 10_000_000, sck=pin_spi_sck, mosi=pin_spi_mosi,
                  miso=pin_spi_miso)
    spi_cs  = Pin(pin_spi_cs, Pin.OUT, value=1)

    # ======================== main ===========================
    
    def test():
        fram = MB85RS64(spi=spi, cs=spi_cs)
        fram.write(0x0000, bytes([1, 2, 3, 4, 5]))
        print(fram.read(0x0000, 5))
        fram.swrite(0x0100, '12345')
        s = fram.sread(0x0100)
        print(type(s), s)


    test()

# ====================== End ==========================
