Skip to content

Instantly share code, notes, and snippets.

@TheRayTracer
Last active August 3, 2019 23:59
Show Gist options
  • Save TheRayTracer/f8dd686157ed6af6e656dbe94b2dc754 to your computer and use it in GitHub Desktop.
Save TheRayTracer/f8dd686157ed6af6e656dbe94b2dc754 to your computer and use it in GitHub Desktop.
The below Python source files implement an OLED display driver for the SSD1351 chipset using the SPI interface. The source code provides an interactive example – the classic Snake game that minimise screen redraw and makes use of the Curses library.
import SSD1351
import datetime
import time
import math
import random
import curses
FIELD_SIZE = 0x80
class Pallet:
def __init__(self, device, snake_body):
self._device = device
placed = False
while placed == False:
self.x = random.randrange(4, FIELD_SIZE - 4, 7)
self.y = random.randrange(18, FIELD_SIZE - 4, 7)
placed = [self.x, self.y] not in snake_body
return
def Render(self):
#for i in xrange(-3, 4, 1):
# for j in xrange(-3, 4, 1):
# self._device.DrawPixel(self.x + i, self.y + j, SSD1351.COLOR_GREEN)
self._device.DrawPixel(self.x - 2, self.y - 3, SSD1351.COLOR_GREEN)
self._device.DrawPixel(self.x + 2, self.y - 3, SSD1351.COLOR_GREEN)
self._device.DrawPixel(self.x - 1, self.y - 2, SSD1351.COLOR_GREEN)
self._device.DrawPixel(self.x + 1, self.y - 2, SSD1351.COLOR_GREEN)
self._device.DrawPixel(self.x - 2, self.y - 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 1, self.y - 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 0, self.y - 1, SSD1351.COLOR_GREEN)
self._device.DrawPixel(self.x + 1, self.y - 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 2, self.y - 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 3, self.y + 0, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 2, self.y + 0, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 1, self.y + 0, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 0, self.y + 0, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 1, self.y + 0, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 2, self.y + 0, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 3, self.y + 0, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 3, self.y + 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 2, self.y + 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 1, self.y + 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 0, self.y + 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 1, self.y + 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 2, self.y + 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 3, self.y + 1, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 2, self.y + 2, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 1, self.y + 2, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 0, self.y + 2, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 1, self.y + 2, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 2, self.y + 2, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x - 1, self.y + 3, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 0, self.y + 3, SSD1351.COLOR_RED)
self._device.DrawPixel(self.x + 1, self.y + 3, SSD1351.COLOR_RED)
return
def GetXY(self):
return self.x, self.y
class Snake:
def __init__(self, device):
self._device = device
self.body = [[67, 67], [67, 74], [67, 81]]
self.direction = [0, 1]
self.alive = True
return
def Render(self):
for i in xrange(-3, 4, 1):
for j in xrange(-3, 4, 1):
self._device.DrawPixel(self.body[0][0] + i, self.body[0][1] + j, SSD1351.COLOR_CYAN)
self._device.DrawPixel(self.body[-1][0] + i, self.body[-1][1] + j, SSD1351.COLOR_BLACK)
return
def Update(self, direction, grow):
self.body.insert(0, [self.body[0][0] + direction[0] * 7, self.body[0][1] + direction[1] * 7])
if grow == False:
self.body.pop()
return
def CollisionCheck(self):
inside_field = self.body[0][0] > 0 and self.body[0][0] < FIELD_SIZE and self.body[0][1] > 12 and self.body[0][1] < FIELD_SIZE
return (not inside_field) or self.body[0] in self.body[1:]
def GetXY(self):
return self.body[0][0], self.body[0][1]
def GetLength(self):
return len(self.body)
def GetBody(self):
return self.body
SSD1351_PIN_CS = 23
SSD1351_PIN_DC = 24
SSD1351_PIN_RST = 25
if __name__ == '__main__':
stdscr = curses.initscr()
curses.noecho()
curses.cbreak()
stdscr.keypad(1)
stdscr.nodelay(1)
random.seed(datetime.datetime.now())
device = SSD1351.SSD1351(SSD1351_PIN_DC, SSD1351_PIN_RST, SSD1351_PIN_CS)
plyr = Snake(device)
food = Pallet(device, plyr.GetBody())
try:
data_splash = SSD1351.UnpackDataFromBmp24File("splash.bmp")
device.EnableDisplay(True)
device.Blank()
time.sleep(0.1)
device.DrawFullScreenBitMap(data_splash) # Splash screen.
time.sleep(3)
device.Blank()
time.sleep(0.1)
device.DrawString(0, 1, "Score:", SSD1351.COLOR_BLUE)
device.DrawLine(0, 14, FIELD_SIZE - 1, 14, SSD1351.COLOR_WHITE)
device.DrawLine(0, FIELD_SIZE - 1, FIELD_SIZE - 1, FIELD_SIZE - 1, SSD1351.COLOR_WHITE)
device.DrawLine(0, 14, 0, FIELD_SIZE - 1, SSD1351.COLOR_WHITE)
device.DrawLine(FIELD_SIZE - 1, 14, FIELD_SIZE - 1, FIELD_SIZE - 1, SSD1351.COLOR_WHITE)
food.Render()
direction = [0, -1]
while plyr.CollisionCheck() == False:
plyr.Render()
device.DrawStringBg(38, 0, str(plyr.GetLength() * 10 - 30), SSD1351.COLOR_BLUE, SSD1351.COLOR_BLACK)
eat = (plyr.GetXY() == food.GetXY())
if eat:
food = Pallet(device, plyr.GetBody())
food.Render()
c = stdscr.getch()
if c == curses.KEY_LEFT and direction[0] == 0:
direction = [-1, 0]
elif c == curses.KEY_RIGHT and direction[0] == 0:
direction = [1, 0]
elif c == curses.KEY_UP and direction[1] == 0:
direction = [0, -1]
elif c == curses.KEY_DOWN and direction[1] == 0:
direction = [0, 1]
plyr.Update(direction, eat)
time.sleep(0.25)
device.DrawStringBg(37, 64, "Game Over", SSD1351.COLOR_WHITE, SSD1351.COLOR_BLACK)
time.sleep(3)
finally:
device.EnableDisplay(False)
device.Remove()
curses.nocbreak()
stdscr.keypad(0)
curses.echo()
curses.endwin()
import struct
import spidev
import sys
import time
import random
import RPi.GPIO as gpio
ascii = [
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x55, 0x00, 0x55, 0x00, 0x55 ],
[ 0x00, 0x00, 0x00, 0x00, 0x00 ], # sp
[ 0x00, 0x00, 0x2f, 0x00, 0x00 ], # !
[ 0x00, 0x07, 0x00, 0x07, 0x00 ], # "
[ 0x14, 0x7f, 0x14, 0x7f, 0x14 ], # #
[ 0x24, 0x2a, 0x7f, 0x2a, 0x12 ], # $
[ 0x62, 0x64, 0x08, 0x13, 0x23 ], # %
[ 0x36, 0x49, 0x55, 0x22, 0x50 ], # &
[ 0x00, 0x05, 0x03, 0x00, 0x00 ], # '
[ 0x00, 0x1c, 0x22, 0x41, 0x00 ], # (
[ 0x00, 0x41, 0x22, 0x1c, 0x00 ], # )
[ 0x14, 0x08, 0x3E, 0x08, 0x14 ], # *
[ 0x08, 0x08, 0x3E, 0x08, 0x08 ], # +
[ 0x00, 0x00, 0xA0, 0x60, 0x00 ], # ,
[ 0x08, 0x08, 0x08, 0x08, 0x08 ], # -
[ 0x00, 0x60, 0x60, 0x00, 0x00 ], # .
[ 0x20, 0x10, 0x08, 0x04, 0x02 ], # /
[ 0x3E, 0x51, 0x49, 0x45, 0x3E ], # 0
[ 0x00, 0x42, 0x7F, 0x40, 0x00 ], # 1
[ 0x42, 0x61, 0x51, 0x49, 0x46 ], # 2
[ 0x21, 0x41, 0x45, 0x4B, 0x31 ], # 3
[ 0x18, 0x14, 0x12, 0x7F, 0x10 ], # 4
[ 0x27, 0x45, 0x45, 0x45, 0x39 ], # 5
[ 0x3C, 0x4A, 0x49, 0x49, 0x30 ], # 6
[ 0x01, 0x71, 0x09, 0x05, 0x03 ], # 7
[ 0x36, 0x49, 0x49, 0x49, 0x36 ], # 8
[ 0x06, 0x49, 0x49, 0x29, 0x1E ], # 9
[ 0x00, 0x36, 0x36, 0x00, 0x00 ], # :
[ 0x00, 0x56, 0x36, 0x00, 0x00 ], # ;
[ 0x08, 0x14, 0x22, 0x41, 0x00 ], # <
[ 0x14, 0x14, 0x14, 0x14, 0x14 ], # =
[ 0x00, 0x41, 0x22, 0x14, 0x08 ], # >
[ 0x02, 0x01, 0x51, 0x09, 0x06 ], # ?
[ 0x32, 0x49, 0x59, 0x51, 0x3E ], # @
[ 0x7C, 0x12, 0x11, 0x12, 0x7C ], # A
[ 0x7F, 0x49, 0x49, 0x49, 0x36 ], # B
[ 0x3E, 0x41, 0x41, 0x41, 0x22 ], # C
[ 0x7F, 0x41, 0x41, 0x22, 0x1C ], # D
[ 0x7F, 0x49, 0x49, 0x49, 0x41 ], # E
[ 0x7F, 0x09, 0x09, 0x09, 0x01 ], # F
[ 0x3E, 0x41, 0x49, 0x49, 0x7A ], # G
[ 0x7F, 0x08, 0x08, 0x08, 0x7F ], # H
[ 0x00, 0x41, 0x7F, 0x41, 0x00 ], # I
[ 0x20, 0x40, 0x41, 0x3F, 0x01 ], # J
[ 0x7F, 0x08, 0x14, 0x22, 0x41 ], # K
[ 0x7F, 0x40, 0x40, 0x40, 0x40 ], # L
[ 0x7F, 0x02, 0x0C, 0x02, 0x7F ], # M
[ 0x7F, 0x04, 0x08, 0x10, 0x7F ], # N
[ 0x3E, 0x41, 0x41, 0x41, 0x3E ], # O
[ 0x7F, 0x09, 0x09, 0x09, 0x06 ], # P
[ 0x3E, 0x41, 0x51, 0x21, 0x5E ], # Q
[ 0x7F, 0x09, 0x19, 0x29, 0x46 ], # R
[ 0x46, 0x49, 0x49, 0x49, 0x31 ], # S
[ 0x01, 0x01, 0x7F, 0x01, 0x01 ], # T
[ 0x3F, 0x40, 0x40, 0x40, 0x3F ], # U
[ 0x1F, 0x20, 0x40, 0x20, 0x1F ], # V
[ 0x3F, 0x40, 0x38, 0x40, 0x3F ], # W
[ 0x63, 0x14, 0x08, 0x14, 0x63 ], # X
[ 0x07, 0x08, 0x70, 0x08, 0x07 ], # Y
[ 0x61, 0x51, 0x49, 0x45, 0x43 ], # Z
[ 0x00, 0x7F, 0x41, 0x41, 0x00 ], # [
[ 0x02, 0x04, 0x08, 0x10, 0x20 ], # \
[ 0x00, 0x41, 0x41, 0x7F, 0x00 ], # ]
[ 0x04, 0x02, 0x01, 0x02, 0x04 ], # ^
[ 0x40, 0x40, 0x40, 0x40, 0x40 ], # _
[ 0x00, 0x03, 0x02, 0x04, 0x00 ], # '
[ 0x20, 0x54, 0x54, 0x54, 0x78 ], # a
[ 0x7F, 0x48, 0x44, 0x44, 0x38 ], # b
[ 0x38, 0x44, 0x44, 0x44, 0x20 ], # c
[ 0x38, 0x44, 0x44, 0x48, 0x7F ], # d
[ 0x38, 0x54, 0x54, 0x54, 0x18 ], # e
[ 0x08, 0x7E, 0x09, 0x01, 0x02 ], # f
[ 0x18, 0xA4, 0xA4, 0xA4, 0x7C ], # g
[ 0x7F, 0x08, 0x04, 0x04, 0x78 ], # h
[ 0x00, 0x44, 0x7D, 0x40, 0x00 ], # i
[ 0x40, 0x80, 0x84, 0x7D, 0x00 ], # j
[ 0x7F, 0x10, 0x28, 0x44, 0x00 ], # k
[ 0x00, 0x41, 0x7F, 0x40, 0x00 ], # l
[ 0x7C, 0x04, 0x18, 0x04, 0x78 ], # m
[ 0x7C, 0x08, 0x04, 0x04, 0x78 ], # n
[ 0x38, 0x44, 0x44, 0x44, 0x38 ], # o
[ 0xFC, 0x24, 0x24, 0x24, 0x18 ], # p
[ 0x18, 0x24, 0x24, 0x18, 0xFC ], # q
[ 0x7C, 0x08, 0x04, 0x04, 0x08 ], # r
[ 0x48, 0x54, 0x54, 0x54, 0x20 ], # s
[ 0x04, 0x3F, 0x44, 0x40, 0x20 ], # t
[ 0x3C, 0x40, 0x40, 0x20, 0x7C ], # u
[ 0x1C, 0x20, 0x40, 0x20, 0x1C ], # v
[ 0x3C, 0x40, 0x30, 0x40, 0x3C ], # w
[ 0x44, 0x28, 0x10, 0x28, 0x44 ], # x
[ 0x1C, 0xA0, 0xA0, 0xA0, 0x7C ], # y
[ 0x44, 0x64, 0x54, 0x4C, 0x44 ], # z
[ 0x00, 0x08, 0x36, 0x41, 0x00 ], # {
[ 0x00, 0x00, 0x77, 0x00, 0x00 ], # |
[ 0x00, 0x41, 0x36, 0x08, 0x00 ], # }
[ 0x02, 0x01, 0x02, 0x04, 0x02 ], # ~
[ 0x55, 0x00, 0x55, 0x00, 0x55 ]
]
def Color656(r, g, b):
c = 0
c = r >> 3
c = c << 6
c = c | (g >> 2)
c = c << 5
c = c | (b >> 3)
return c
def UnpackDataFromBmp24File(name):
with open(name, "rb") as f:
signature = f.read(2)
if signature == "BM":
f.read(4) # Ignore the size of the bmp file
f.read(2) # Ignore data - reserved
f.read(2) # Ignore data - reserved
offset, = struct.unpack('<i', f.read(4)) # Read the offset to the image data.
f.seek(0x0E) # Seek to the start of the bitmap information header.
f.read(4) # Ignore the size of the header
width, height, plane, bpp = struct.unpack('<iihh', f.read(12))
raw = list()
if width == MAX_WIDTH:
if height == MAX_HEIGHT:
if plane == 1:
if bpp == 24:
f.seek(offset)
byte = f.read(1)
while byte:
raw.append(ord(byte))
byte = f.read(1)
else:
raise Exception("Image colour depth detected: " + str(bpp) + ". Image colour depth must equal 24.")
else:
raise Exception("Colour planes detected: " + str(plane) + ". Number of colour planes must equal 1.")
else:
raise Exception("Image hight detected: " + str(height) + ". Image height must equal " + str(MAX_HEIGHT) + ".")
else:
raise Exception("Image width detected: " + str(width) + ". Image width must equal " + str(MAX_WIDTH) + ".")
i = 0
data = list() # Rows.
for y in xrange(0, MAX_HEIGHT, 1):
data.append(list()) # Columns.
for x in xrange(0, MAX_WIDTH, 1):
data[y].append(list()) # Channels.
data[y][x].append(raw[i + 2]) # Swap from BGR to RGB formatted channels.
data[y][x].append(raw[i + 1])
data[y][x].append(raw[i + 0])
i = i + 3
data.reverse() # Bitmaps are stored up-side-down so let's flip the rows.
return data
COLOR_BLACK = Color656( 0, 0, 0)
COLOR_GREY = Color656(192, 192, 192)
COLOR_WHITE = Color656(255, 255, 255)
COLOR_RED = Color656(255, 0, 0)
COLOR_PINK = Color656(240, 100, 225)
COLOR_YELLOW = Color656(255, 255, 0)
COLOR_GOLDEN = Color656(255, 215, 0)
COLOR_BROWN = Color656(128, 42, 42)
COLOR_BLUE = Color656( 0, 0, 255)
COLOR_CYAN = Color656( 0, 255, 255)
COLOR_GREEN = Color656( 0, 255, 0)
COLOR_PURPLE = Color656(160, 32, 240)
MAX_WIDTH = 0x80
MAX_HEIGHT = 0x80
MAX_ROW_COL = 0x7F
LOCK_MODE_DISABLE = 0x12
LOCK_MODE_ENABLE = 0x16
LOCK_COMMANDS_ENABLE = 0xB0
LOCK_COMMANDS_DISABLE = 0xB1
SETUP_SCROLLING = 0x96
STOP_SCROLLING = 0x9E
START_SCROLLING = 0x9F
SET_COLUMN_ADDRESS = 0x15
SET_ROW_ADDRESS = 0x75
RAM_READ = 0x5D
RAM_WRITE = 0x5C
SET_REMAP = 0xA0
SET_DISPLAY_START_LINE = 0xA1
SET_DISPLAY_OFFSET = 0xA2
ENTIRE_DISPLAY_OFF = 0xA4
ENTIRE_DISPLAY_ON = 0xA5
NORMAL_DISPLAY = 0xA6
INVERSE_DISPLAY = 0xA7
SET_FUNCTION = 0xAB
DISPLAY_OFF = 0xAE # Sleep mode on
DISPLAY_ON = 0xAF # Sleep mode off
PHASE_1_2_PERIOD = 0xB1
CLOCK_DIVIDER = 0xB3
SET_PRECHARGE_2_VOLTAGE = 0xB6
SET_PRECHARGE_VOLTAGE = 0xBB
SET_V_VOLTAGE = 0xBE
LOCK_MODE = 0xFD
class SSD1351:
COMMAND = gpio.LOW
DATA = gpio.HIGH
def __init__(self, dc, rst, cs):
self.rst = rst
self.dc = dc
self.cs = cs
# Setup GPIO.
gpio.setmode(gpio.BCM)
gpio.setup(self.dc, gpio.OUT)
gpio.output(self.dc, gpio.LOW)
gpio.setup(self.rst, gpio.OUT)
gpio.output(self.rst, gpio.HIGH)
gpio.setup(self.cs, gpio.OUT)
gpio.output(self.cs, gpio.HIGH)
self.__OpenSPI() # Setup SPI.
self.__Setup() # Setup device screen.
self.Blank() # Blank the screen.
return
def __OpenSPI(self):
self.spi = spidev.SpiDev()
self.spi.open(0, 0)
self.spi.mode = 3
self.spi.max_speed_hz = 6000000
self.spi.cshigh = False
return
def __WriteCommand(self, cmd):
if isinstance(cmd, int):
gpio.output(self.dc, self.COMMAND)
self.spi.xfer([cmd])
return
def __WriteCommandData(self, cmd, data):
if isinstance(cmd, int) and (isinstance(data, list) or isinstance(data, tuple)):
gpio.output(self.dc, self.COMMAND)
self.spi.xfer([cmd])
gpio.output(self.dc, self.DATA)
self.spi.xfer(data)
return
def __WriteData(self, data):
if isinstance(data, list) or isinstance(data, tuple):
gpio.output(self.dc, self.DATA)
self.spi.xfer(data)
return
def __Setup(self):
self.spi.cshigh = True
self.spi.xfer([0])
gpio.output(self.cs, gpio.LOW)
time.sleep(0.1)
gpio.output(self.rst, gpio.LOW)
time.sleep(0.5)
gpio.output(self.rst, gpio.HIGH)
time.sleep(0.5)
self.spi.cshigh = False
self.spi.xfer([0])
self.__WriteCommandData(LOCK_MODE, [LOCK_MODE_DISABLE])
self.__WriteCommandData(LOCK_MODE, [LOCK_COMMANDS_DISABLE])
self.__WriteCommand(DISPLAY_OFF)
self.__WriteCommandData(SET_DISPLAY_START_LINE, [0x00])
self.__WriteCommandData(SET_DISPLAY_OFFSET, [0x00])
self.__WriteCommandData(PHASE_1_2_PERIOD, [0x32])
self.__WriteCommandData(SET_REMAP, [0x34]) # Reverse the color order to C -> B -> A RAM mapping to support RGB. "Rotate" the screen by chaging the memory scan lines.
self.__WriteCommandData(SETUP_SCROLLING, [0x01, 0x00, 0x80, 0x00, 0x01])
self.__WriteCommand(NORMAL_DISPLAY)
self.__WriteCommand(DISPLAY_ON)
return
def Remove(self):
self.__WriteCommand(DISPLAY_OFF)
gpio.cleanup()
self.spi.close()
return
def DrawPixel(self, x, y, c):
self.__WriteCommandData(SET_COLUMN_ADDRESS, [x, MAX_ROW_COL])
self.__WriteCommandData(SET_ROW_ADDRESS, [y, MAX_ROW_COL])
self.__WriteCommandData(RAM_WRITE, [(c >> 8) & 0xFF, c & 0xFF])
return
def DrawLine(self, x0, y0, x1, y1, c):
self.DrawLineBresenham(x0, y0, x1, y1, c)
return
# Bresenham's line algorithm.
def DrawLineBresenham(self, x0, y0, x1, y1, c):
dx = x1 - x0
if dx < 0:
dx = x0 - x1
sx = -1
if x0 < x1:
sx = 1
dy = y1 - y0
if dy < 0:
dy = y0 - y1
sy = -1
if y0 < y1:
sy = 1
err = -dy / 2
if dy < dx:
err = dx / 2
self.DrawPixel(x0, y0, c)
while x0 != x1 or y0 != y1:
e2 = err
if e2 > -dx:
err = err - dy
x0 = x0 + sx
if e2 < dy:
err = err + dx
y0 = y0 + sy
self.DrawPixel(x0, y0, c)
return
def DrawTriangle(self, x0, y0, x1, y1, x2, y2, c):
self.DrawLine(x0, y0, x1, y1, c)
self.DrawLine(x1, y1, x2, y2, c)
self.DrawLine(x0, y0, x2, y2, c)
return
def DrawRect(self, x0, y0, x1, y1, c):
self.DrawLine(x0, y0, x0, y1, c)
self.DrawLine(x0, y1, x1, y1, c)
self.DrawLine(x1, y1, x1, y0, c)
self.DrawLine(x1, y0, x0, y0, c)
return
def DrawCircle(self, x0, y0, r0, c):
x = r0
y = 0
decision_over2 = 1 - x # Decision criterion divided by 2 evaluated at x = r, y = 0.
while y <= x:
self.DrawPixel( x + x0, y + y0, c) # Octant 1.
self.DrawPixel( y + x0, x + y0, c) # Octant 2.
self.DrawPixel(-x + x0, y + y0, c) # Octant 4.
self.DrawPixel(-y + x0, x + y0, c) # Octant 3.
self.DrawPixel(-x + x0, -y + y0, c) # Octant 5.
self.DrawPixel(-y + x0, -x + y0, c) # Octant 6.
self.DrawPixel( x + x0, -y + y0, c) # Octant 8.
self.DrawPixel( y + x0, -x + y0, c) # Octant 7.
y = y + 1
if decision_over2 <= 0:
decision_over2 = decision_over2 + 2 * y + 1 # Change in decision criterion for y -> y + 1.
else:
x = x - 1
decision_over2 = decision_over2 + 2 * (y - x) + 1 # Change for y -> y + 1, x -> x - 1.
return
def DrawChar(self, x, y, ch, c):
for i in xrange(0, 5, 1):
line = ascii[ord(ch) & 0x7F][i]
for j in xrange(0, 8, 1):
if line & 0x1:
self.DrawPixel(x + i, y + j, c)
line >>= 1
return
def DrawCharBg(self, x, y, ch, c, bg):
for i in xrange(0, 5, 1):
line = ascii[ord(ch) & 0x7F][i]
self.DrawPixel(x + i, y, bg)
y = y + 1
for j in xrange(0, 8, 1):
if line & 0x1:
self.DrawPixel(x + i, y + j, c)
else:
self.DrawPixel(x + i, y + j, bg)
line >>= 1
y = y + 8
self.DrawPixel(x + i, y, bg)
y = y - 9
return
def DrawString(self, x, y, str, c):
for i in str:
if x > MAX_ROW_COL:
break
self.DrawChar(x, y, i, c)
x = x + 6
return
def DrawStringBg(self, x, y, str, c, bg):
self.DrawLine(x, y, x, y + 9, bg)
x = x + 1
for i in str:
if x > MAX_ROW_COL:
break
self.DrawCharBg(x, y, i, c, bg)
self.DrawLine(x + 5, y, x + 5, y + 9, bg)
x = x + 6
return
def DrawFullScreenBitMap(self, data):
self.__WriteCommandData(SET_COLUMN_ADDRESS, [0x00, MAX_ROW_COL])
self.__WriteCommandData(SET_ROW_ADDRESS, [0x00, MAX_ROW_COL])
self.__WriteCommand(RAM_WRITE)
for x in xrange(0, len(data), 1):
d = list() # Build up a list of pixel data to render an entire col per data write command.
for y in xrange(0, len(data[x]), 1):
c = Color656(data[x][y][0], data[x][y][1], data[x][y][2])
d.append((c >> 8) & 0xFF)
d.append(c & 0xFF)
self.__WriteData(d)
return
def Blank(self):
d = [0x00] * 4096 # This is the max number of bytes that we can write in a single call.
self.__WriteCommandData(SET_COLUMN_ADDRESS, [0x00, MAX_ROW_COL])
self.__WriteCommandData(SET_ROW_ADDRESS, [0x00, MAX_ROW_COL])
self.__WriteCommand(RAM_WRITE)
for i in xrange(0, 16, 1):
self.__WriteData(d)
return
def TestEntireDisplay(self, enable):
if enable:
self.__WriteCommand(ENTIRE_DISPLAY_ON)
else:
self.__WriteCommand(ENTIRE_DISPLAY_OFF)
return
def EnableDisplay(self, enable):
if enable:
self.__WriteCommand(DISPLAY_ON)
else:
self.__WriteCommand(DISPLAY_OFF)
return
def EnableLockMode(self, enable):
if enable:
self.__WriteCommandData(LOCK_MODE, [LOCK_MODE_ENABLE])
self.__WriteCommandData(LOCK_MODE, [LOCK_COMMANDS_ENABLE])
else:
self.__WriteCommandData(LOCK_MODE, [LOCK_MODE_DISABLE])
self.__WriteCommandData(LOCK_MODE, [LOCK_COMMANDS_DISABLE])
return
def EnableScrollMode(self, enable):
if enable:
self.__WriteCommand(START_SCROLLING)
else:
self.__WriteCommand(STOP_SCROLLING)
return
def LockTest(self):
self.Blank()
time.sleep(0.01)
self.DrawString(37, 16, "Lock Test", COLOR_WHITE)
self.EnableLockMode(True)
self.DrawString(16, 16, "Lock Test Failed", COLOR_RED)
time.sleep(5)
self.EnableLockMode(False)
return
def CharTest(self):
self.Blank()
time.sleep(0.01)
self.DrawStringBg(37, 16, "Font Test", COLOR_BLACK, COLOR_WHITE)
i = 32
for j in xrange(0, 6, 1):
for k in xrange(0, 16, 1):
self.DrawChar(k * 8 , j * 10 + 32, chr(i), Color656(k * 16, 128, j * 36))
i = i + 1
time.sleep(10)
return
def ShapeTest(self):
self.Blank()
time.sleep(0.01)
self.DrawStringBg(16, 16, "Color Shape Test", COLOR_BLACK, COLOR_WHITE)
self.DrawCircle(32, 48, 10, COLOR_RED)
self.DrawTriangle(54, 58, 64, 38, 74, 58, COLOR_GREEN)
self.DrawRect(86, 38, 106, 58, COLOR_BLUE)
self.DrawStringBg(23, 64, "Red", COLOR_BLACK, COLOR_RED)
self.DrawStringBg(48, 64, "Green", COLOR_BLACK, COLOR_GREEN)
self.DrawStringBg(84, 64, "Blue", COLOR_BLACK, COLOR_BLUE)
time.sleep(10)
return
def BitMapTest(self, data):
self.DrawFullScreenBitMap(data)
self.DrawStringBg(19, 16, "Full Image Test", COLOR_BLACK, COLOR_WHITE)
time.sleep(10)
return
def ScrollTest(self):
self.DrawStringBg(34, 16, "Scroll Test", COLOR_BLACK, COLOR_WHITE)
self.EnableScrollMode(True)
time.sleep(22)
self.EnableScrollMode(False)
return
SSD1351_PIN_CS = 23
SSD1351_PIN_DC = 24
SSD1351_PIN_RST = 25
if __name__ == '__main__':
device = SSD1351(SSD1351_PIN_DC, SSD1351_PIN_RST, SSD1351_PIN_CS)
try:
data_frog = UnpackDataFromBmp24File("frog.bmp")
device.EnableDisplay(True)
device.LockTest()
device.ShapeTest()
device.CharTest()
device.ScrollTest()
device.BitMapTest(data_frog)
device.EnableDisplay(False)
finally:
device.Remove()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment