Last active
August 3, 2019 23:59
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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