Skip to content

Instantly share code, notes, and snippets.

@ciniml
Last active October 21, 2024 16:21
Show Gist options
  • Save ciniml/e39503ad36624fe2a5f37112e5ff3f70 to your computer and use it in GitHub Desktop.
Save ciniml/e39503ad36624fe2a5f37112e5ff3f70 to your computer and use it in GitHub Desktop.
GOWIN SSPI configuration script for M5Stack
import os
import machine
import time
class GowinConfig(object):
def __init__(self, pin_cs:machine.Pin, pin_mode0:machine.Pin, pin_reconfig_n:machine.Pin, pin_ready:machine.Pin, spi:machine.SPI):
self.__pin_cs = pin_cs
self.__pin_mode0 = pin_mode0
self.__pin_reconfig_n = pin_reconfig_n
self.__pin_ready = pin_ready
self.__sspi = spi
def begin_reconfig(self):
self.__pin_cs.on()
self.__pin_reconfig_n.on()
self.__pin_mode0.on()
time.sleep_ms(100)
self.__pin_reconfig_n.off()
time.sleep_ms(100)
self.__pin_reconfig_n.on()
time.sleep_ms(100)
self.__sspi.write(b'\xff')
def is_ready(self) -> bool:
return self.__pin_ready.value() != 0
def wait_ready(self) -> None:
while not self.is_ready:
pass
def read_id(self):
self.__pin_cs.off()
self.__sspi.write(b'\x11\x00\x00\x00')
id = self.__sspi.read(4)
self.__pin_cs.on()
self.__sspi.write(b'\xff')
return id
def config_enable(self):
self.__pin_cs.off()
self.__sspi.write(b'\x15xff')
self.__pin_cs.on()
self.__sspi.write(b'\xff')
def config_disable(self):
self.__pin_cs.off()
self.__sspi.write(b'\x3axff')
self.__pin_cs.on()
self.__sspi.write(b'\xff')
def configure(self, path:str, buffer:Optional[bytearray]=None):
buf = memoryview(buffer if buffer is not None else bytearray(8192))
with open(path, 'rb') as f:
remaining = f.seek(0, 2)
f.seek(0, 0)
self.__pin_cs.off()
self.__sspi.write(b'\x3b')
while remaining > 0:
print(remaining)
bytes_to_read = len(buf) if remaining > len(buf) else remaining
f.readinto(buf[0:bytes_to_read])
self.__sspi.write(buf[0:bytes_to_read])
remaining -= bytes_to_read
self.__pin_cs.on()
self.__sspi.write(b'\xff')
import os
import machine
import time
import gowin
i2c = machine.I2C(0, sda=machine.Pin(21), scl=machine.Pin(22))
class IOExpander(object):
"I/O Expander on I2C bus"
REG_INPUT = const(0)
REG_OUTPUT = const(1)
REG_INVERSION = const(2)
REG_CONFIG = const(3)
def __init__(self, i2c: machine.I2C, address: int, output:int=0x00, inversion:int=0xf0, direction:int=0xff):
"""
Initialize I/O expander driver
i2c: machine.I2C object on which the I/O expander is.
address: I2C slave address of the I/O expander.
"""
self.__i2c = i2c
self.__address = address
self.__regs = memoryview(bytearray([0x00, output, inversion, direction]))
self.__apply()
def __apply(self) -> None:
for i in (3,2,1):
self.__i2c.writeto_mem(self.__address, i, self.__regs[i:i+1])
def configure(self, direction:int, inversion:int, output:int) -> None:
self.__regs[IOExpander.REG_OUTPUT] = output
self.__regs[IOExpander.REG_INVERSION] = inversion
self.__regs[IOExpander.REG_CONFIG] = direction
self.__apply()
def input(self) -> int:
self.__i2c.readfrom_mem_into(self.__address, IOExpander.REG_INPUT, self.__regs[IOExpander.REG_INPUT:IOExpander.REG_INPUT+1])
return self.__regs[IOExpander.REG_INPUT]
def last_input(self) -> int:
return self.__regs[IOExpander.REG_INPUT]
def output(self, value:Optional[int,None]=None, mask:int=0xff) -> int:
if value is not None:
self.__set_masked(IOExpander.REG_OUTPUT, value, mask)
return self.__regs[IOExpander.REG_OUTPUT]
def direction(self, value:Optional[int,None]=None, mask:int=0xff) -> int:
if value is not None:
self.__set_masked(IOExpander.REG_CONFIG, value, mask)
return self.__regs[IOExpander.REG_CONFIG]
def __set_masked(self, reg:int, value:int, mask:int) -> None:
self.__regs[reg] = (self.__regs[reg] & ~mask) | value
self.__i2c.writeto_mem(self.__address, reg, self.__regs[reg:reg+1])
def pin(self, pin:int) -> IOExpanderPin:
return IOExpanderPin(self, pin)
class IOExpanderPin(object):
def __init__(self, ioe:IOExpander, pin:int, is_open_drain:bool=False):
self.__ioe = ioe
self.__mask = 1<<pin
self.__is_open_drain = is_open_drain
def value(self, x:Optional[bool,None]=None) -> bool:
if x is not None:
value = self.__mask if x else 0
self.__ioe.output(value=value, mask=self.__mask)
if not self.__is_open_drain:
if (self.__ioe.direction() & self.__mask) == 0:
return (self.__ioe.output() & self.__mask) != 0
else:
return (self.__ioe.input() & self.__mask) != 0
else:
return (self.__ioe.direction() & self.__mask) == 0
def enable_output(self) -> None:
self.__ioe.direction(value=0, mask=self.__mask)
def disable_output(self) -> None:
self.__ioe.direction(value=self.__mask, mask=self.__mask)
def on(self) -> None:
if self.__is_open_drain:
self.disable_output()
else:
self.value(True)
def off(self) -> None:
if self.__is_open_drain:
self.enable_output()
else:
self.value(False)
ioe = IOExpander(i2c, 24, output=0x00, inversion=0x00, direction=0x7f)
pin_mode0 = IOExpanderPin(ioe, 7)
pin_reconfig_n = IOExpanderPin(ioe, 6, is_open_drain=True)
pin_ready = IOExpanderPin(ioe, 5)
#pin_reconfig_n = machine.Pin(5, machine.Pin.OPEN_DRAIN)
#pin_ready = machine.Pin(36, machine.Pin.IN)
pin_cs = machine.Pin(13, machine.Pin.OUT)
pin_sclk = machine.Pin(12, machine.Pin.OUT)
pin_miso = machine.Pin(34, machine.Pin.IN)
pin_mosi = machine.Pin(15, machine.Pin.OUT)
pin_sd_cs = machine.Pin(4, machine.Pin.OUT)
pin_sd_sck = machine.Pin(18, machine.Pin.OUT)
pin_sd_miso = machine.Pin(19, machine.Pin.IN)
pin_sd_mosi = machine.Pin(23, machine.Pin.OUT)
sd = machine.SDCard(slot=2, sck=pin_sd_sck, miso=pin_sd_miso, mosi=pin_sd_mosi, cs=pin_sd_cs)
sd.info()
os.mount(sd, '/sd')
pin_reconfig_n.on()
pin_cs.on()
sspi = machine.SPI(baudrate=1000000, polarity=0, phase=0, sck=pin_sclk, mosi=pin_mosi, miso=pin_miso)
sspi.init()
sspi.write(b'\xff')
gowin = gowin.GowinConfig(pin_cs, pin_mode0, pin_reconfig_n, pin_ready, sspi)
def configure_file(path:str = '/sd/blinky.bin'):
gowin.begin_reconfig()
gowin.wait_ready()
time.sleep_ms(100)
print('Reading device ID...')
while(True):
id = gowin.read_id()
if id == b'\x01\x00\x38\x1b':
break
else:
print('invalid ID', id)
print('Device detected')
print('Begin config')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment