Tinypico Play Shield¶
Published on 2020-12-18 in Stage, a Tile and Sprite Engine.
After watching Unexpected Maker’s stream on which he builds his Play Shield for Tinypico, I decided to try and see if the Stage library would run on it.
I asked him about which display he uses and how it is connected, and made this “driver” for it:
import ustruct
import utime
class Display(object): # ST7739
_BUF = bytearray(4)
width = 240
height = 240
def __init__(self, spi, dc, cs=None, rst=None):
self.spi = spi
self.dc = dc
self.cs = cs or (lambda x: x)
self.rst = rst or (lambda x: x)
self.reset()
def reset(self):
self.rst(0)
utime.sleep_ms(50)
self.rst(1)
utime.sleep_ms(50)
self.cs(0)
for command, data in (
# (b'\x01', None), # reset
(b'\x11', None), # wake
(b'\x3a', b'\x55'), # format
(b'\x36', b'\xc8'), # mad
(b'\x21', None), # invert
(b'\x13', None), # no partial
(b'\x29', None), # on
):
self.write(command, data)
utime.sleep_ms(150)
self.cs(1)
utime.sleep_ms(50)
def write(self, command=None, data=None):
if command is not None:
self.dc(0)
self.spi.write(command)
if data:
self.dc(1)
self.spi.write(data)
def block(self, x0, y0, x1, y1):
y0 += 80
y1 += 80
ustruct.pack_into('>HH', self._BUF, 0, x0, x1)
self.write(b'\x2a', self._BUF)
ustruct.pack_into('>HH', self._BUF, 0, y0, y1)
self.write(b'\x2b', self._BUF)
self.write(b'\x2c')
self.dc(1)
def clear(self, color=0x00):
self.cs(0)
self.block(0, 0, self.width, self.height)
chunks, rest = divmod(self.width * self.height, 512)
pixel = ustruct.pack('>H', color)
if chunks:
data = pixel * 512
for count in range(chunks):
self.spi.write(data)
if rest:
self.spi.write(pixel * rest)
self.cs(1)
def __enter__(self):
self.cs(0)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.cs(1)
One surprising thing that took me a while to figure out is that to get correct colors, you have to put the display in inverted color mode — a bit weird, but I guess it’s a question of how the actual LCD is connected to the chip inside. A quick test confirms that it works:
Next I needed to handle the buttons. The Play Shield uses MPR121 chip to handle them, so I just added this button-handling class:
class Buttons: # mpr121
def __init__(self, i2c, address=0x5a):
self._i2c = i2c
self._address = address
for register, value in (
(0x80, b'\x63'), # reset
(0x53, b'\x00'), # stop mode, reset config
(0x2b, b'\x01\x01\x0e\x00\x01\x05\x01\x00\x00\x00\x00'),
(0x5b, b'\x00\x10\x20'), # debounce, config1, config2
(0x53, b'\x8f'), # exit stop mode
):
self._i2c.writeto_mem(self._address, register, value)
def _get_pressed(self):
return int.from_bytes(
self._i2c.readfrom_mem(self._address, 0x00, 2), 'big')
Not having an MPR121 chip at hand, I couldn’t test it, but once I sent the compiled binaries and some example code to Unexpected Maker to test on the actual shield, it ran correctly, as he shows on Twitter: https://twitter.com/unexpectedmaker/status/1339756136224890880
Well, OK, that demo doesn’t actually use the buttons, but it initializes the chip, and that seems to have worked.
I can do further refining once I get my hands on the actual shield.
The full code and compiling instructions are at https://github.com/python-ugame/micropython- stage/tree/master/tinypicost7789