-
-
Save Michshelle/d41fbd3f608ddc1ad7bbe1c3b6282dfe to your computer and use it in GitHub Desktop.
The below Python source files control an OLED display (size 96 x 64, 65K colours) using a SSD1331 chipset and the SPI interface. The source code initialises the chipset and includes hardware accelerated functions for drawing primitive shapes and a non-hardware accelerated full ASCII set. Examples include a basic Space Invaders game, and a clock.
This file contains hidden or 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 SSD1331 | |
import datetime | |
import time | |
import math | |
SSD1331_PIN_CS = 23 | |
SSD1331_PIN_DC = 24 | |
SSD1331_PIN_RST = 25 | |
if __name__ == '__main__': | |
device = SSD1331.SSD1331(SSD1331_PIN_DC, SSD1331_PIN_RST, SSD1331_PIN_CS) | |
try: | |
device.EnableDisplay(True) | |
device.Clear() | |
today_last_time = "Unknown" | |
while True: | |
my_now = datetime.datetime.now() | |
today_date = my_now.strftime("%Y-%B-%d %A") | |
today_time = my_now.strftime("%H:%M") | |
if today_time != today_last_time: | |
device.Clear() | |
time.sleep(0.01) | |
hours_angle = 270 + (30 * (my_now.hour + (my_now.minute / 60.0))) | |
hours_dx = int(math.cos(math.radians(hours_angle)) * 12) | |
hours_dy = int(math.sin(math.radians(hours_angle)) * 12) | |
minutes_angle = 270 + (6 * my_now.minute) | |
minutes_dx = int(math.cos(math.radians(minutes_angle)) * 18) | |
minutes_dy = int(math.sin(math.radians(minutes_angle)) * 18) | |
device.DrawCircle(30, 32, 20, SSD1331.COLOR_WHITE) | |
device.DrawLine(30, 32, 30 + hours_dx, 32 + hours_dy, SSD1331.COLOR_WHITE) | |
device.DrawLine(30, 32, 30 + minutes_dx, 32 + minutes_dy, SSD1331.COLOR_WHITE) | |
device.DrawString(60, 28, today_time, SSD1331.COLOR_WHITE) | |
today_last_time = today_time | |
time.sleep(0.5) | |
finally: | |
device.EnableDisplay(False) | |
device.Remove() |
This file contains hidden or 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 SSD1331 | |
import datetime | |
import time | |
import math | |
import random | |
arrow = [0x04, 0x02, 0x01, 0x02, 0x04] | |
alien1 = [0x4C, 0x1A, 0xB6, 0x5F, 0x5F, 0xB6, 0x1A, 0x4C] | |
alien2 = [0x18, 0xFD, 0xA6, 0x3C, 0x3C, 0xA6, 0xFD, 0x18] | |
alien3 = [0xFC, 0x98, 0x35, 0x7E, 0x7E, 0x35, 0x98, 0xFC] | |
ARMY_SIZE_ROWS = 2 | |
ARMY_SIZE_COLS = 6 | |
class Bullet: | |
def __init__(self, device, x, y): | |
self._device = device | |
self.x = x | |
self.y = y | |
self.Alive = False | |
def Render(self, on): | |
if self.Alive: | |
if on: | |
c = SSD1331.COLOR_WHITE | |
else: | |
c = SSD1331.COLOR_BLACK | |
self._device.DrawPixel(self.x, self.y + 0, c) | |
self._device.DrawPixel(self.x, self.y + 1, c) | |
self._device.DrawPixel(self.x, self.y + 2, c) | |
return | |
def Reset(self, x, y): | |
self.x = x | |
self.y = y | |
self.Alive = True | |
return | |
def Update(self, direction): | |
if self.Alive: | |
self.y = self.y + (direction * 4) | |
if self.y < 10: | |
self.Alive = False | |
return | |
class Player: | |
def __init__(self, device): | |
self._device = device | |
self.x = 48 | |
self.y = 54 | |
self.Bullets = [] | |
for i in xrange(0, 4, 1): | |
self.Bullets.append(Bullet(device, 0, 0)) | |
return | |
def Render(self, on): | |
if on: | |
c = SSD1331.COLOR_WHITE | |
else: | |
c = SSD1331.COLOR_BLACK | |
for i in xrange(0, len(arrow), 1): | |
line = arrow[i] | |
for j in xrange(0, 3, 1): | |
if line & 0x1: | |
self._device.DrawPixel(self.x - 2 + i, self.y + j, c) | |
line >>= 1 | |
for i in xrange(0, len(self.Bullets), 1): | |
self.Bullets[i].Render(on) | |
return | |
def Update(self, direction): | |
t = self.x + (direction * 2) | |
if t > 4 and t < 92: | |
self.x = t | |
for i in xrange(0, len(self.Bullets), 1): | |
self.Bullets[i].Update(-1) | |
return | |
def Shoot(self): | |
for i in xrange(0, len(self.Bullets), 1): | |
if self.Bullets[i].Alive == False: | |
self.Bullets[i].Reset(self.x, self.y) | |
break | |
return | |
class Invader: | |
def __init__(self, device, minx, maxx, x, y): | |
self._device = device | |
self.x = x | |
self.y = y | |
self._direction = 1 | |
self.Alive = True | |
self.Score = 10 | |
self._minx = minx | |
self._maxx = maxx | |
return | |
def Render(self, on): | |
if self.Alive: | |
if on: | |
c = SSD1331.COLOR_GREEN | |
else: | |
c = SSD1331.COLOR_BLACK | |
for i in xrange(0, len(alien2), 1): | |
line = alien2[i] | |
for j in xrange(0, 8, 1): | |
if line & 0x1: | |
self._device.DrawPixel(self.x - 4 + i, self.y - 4 + j, c) | |
line >>= 1 | |
return | |
def Update(self): | |
invaded = False | |
if self.Alive: | |
t = self.x + self._direction | |
if t > self._minx and t < self._maxx: | |
self.x = self.x + self._direction | |
else: | |
self._direction = self._direction * -1 | |
self.y = self.y + 2 | |
if self.y > 44: | |
invaded = True | |
return invaded | |
class Army: | |
def __init__(self, device): | |
self._device = device | |
self.Invaded = False | |
self.Invaders = [] | |
for i in xrange(0, ARMY_SIZE_ROWS, 1): | |
row = [] | |
for j in xrange(0, ARMY_SIZE_COLS, 1): | |
row.append(Invader(device, 4 + (j * 12) , 30 + (j * 12), 4 + (j * 12), 14 + (i * 12))) | |
self.Invaders.append(row) | |
return | |
def Render(self, on): | |
for i in xrange(0, len(self.Invaders), 1): | |
for j in xrange(0, len(self.Invaders[i]), 1): | |
self.Invaders[i][j].Render(on) | |
return | |
def Update(self, bullets): | |
for i in xrange(0, len(self.Invaders), 1): | |
for j in xrange(0, len(self.Invaders[i]), 1): | |
if self.Invaders[i][j].Update(): | |
self.Invaded = True | |
for i in xrange(0, len(self.Invaders), 1): | |
for j in xrange(0, len(self.Invaders[i]), 1): | |
if self.Invaders[i][j].Alive: | |
for k in xrange(0, len(bullets), 1): | |
if bullets[k].Alive: | |
t = (self.Invaders[i][j].x - bullets[k].x) * (self.Invaders[i][j].x - bullets[k].x) + (self.Invaders[i][j].y - bullets[k].y) * (self.Invaders[i][j].y - bullets[k].y) | |
# if point is in circle | |
if t < 25: # 5 * 5 = r * r | |
self.Invaders[i][j].Alive = False | |
bullets[k].Alive = False | |
return | |
def Size(self): | |
size = 0 | |
for i in xrange(0, len(self.Invaders), 1): | |
for j in xrange(0, len(self.Invaders[i]), 1): | |
if self.Invaders[i][j].Alive: | |
size = size + 1 | |
return size | |
def Score(self): | |
score = 0 | |
for i in xrange(0, len(self.Invaders), 1): | |
for j in xrange(0, len(self.Invaders[i]), 1): | |
if self.Invaders[i][j].Alive == False: | |
score = score + self.Invaders[i][j].Score | |
return score | |
def AiLogicShoot(army, plyr): | |
for i in xrange(0, len(army.Invaders), 1): | |
for j in xrange(0, len(army.Invaders[i]), 1): | |
if army.Invaders[i][j].Alive != False: | |
if plyr.x > (army.Invaders[i][j].x - 2) and plyr.x < (army.Invaders[i][j].x + 2): | |
if random.random() < 0.75: | |
plyr.Shoot() | |
return | |
return | |
def AiLogicMove(army, plyr, rows): | |
for i in xrange(0, len(rows), 1): | |
for j in xrange(0, len(rows[i]), 1): | |
if army.Invaders[i][rows[i][j]].Alive != False: | |
if plyr.x < army.Invaders[i][rows[i][j]].x: | |
plyr.Update(1) | |
return | |
elif plyr.x > army.Invaders[i][rows[i][j]].x: | |
plyr.Update(-1) | |
return | |
return | |
SSD1331_PIN_CS = 23 | |
SSD1331_PIN_DC = 24 | |
SSD1331_PIN_RST = 25 | |
if __name__ == '__main__': | |
device = SSD1331.SSD1331(SSD1331_PIN_DC, SSD1331_PIN_RST, SSD1331_PIN_CS) | |
plyr = Player(device) | |
army = Army(device) | |
rows = [] | |
rows.append(random.sample(range(6), 6)) | |
rows.append(random.sample(range(6), 6)) | |
try: | |
raw_splash = SSD1331.GetRawPixelDataFromBmp24File("splash.bmp") | |
data_splash = SSD1331.UnpackRawPixelBmp24Data(raw_splash) | |
device.EnableDisplay(True) | |
device.Clear() | |
time.sleep(0.1) | |
device.DrawFullScreenBitMap(data_splash) # Splash screen. | |
time.sleep(3) | |
device.Clear() | |
time.sleep(0.1) | |
device.DrawLine(0, 61, 95, 61, SSD1331.COLOR_WHITE) | |
device.DrawLine(0, 63, 95, 63, SSD1331.COLOR_WHITE) | |
time.sleep(0.1) | |
while army.Invaded == False and army.Size() > 0: | |
plyr.Render(False) | |
army.Render(False) | |
AiLogicShoot(army, plyr) | |
AiLogicMove(army, plyr, rows) | |
army.Update(plyr.Bullets) | |
army.Render(True) | |
plyr.Render(True) | |
device.DrawStringBg(8, 0, "Score:" + str(army.Score()), SSD1331.COLOR_BLUE, SSD1331.COLOR_BLACK) | |
time.sleep(0.8) | |
if army.Size() == 0: | |
device.DrawStringBg(27, 28, "Victory", SSD1331.COLOR_BLUE, SSD1331.COLOR_BLACK) | |
else: | |
device.DrawStringBg(30, 28, "Defeat", SSD1331.COLOR_RED, SSD1331.COLOR_BLACK) | |
time.sleep(10) | |
finally: | |
device.EnableDisplay(False) | |
device.Remove() |
This file contains hidden or 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 GetRawPixelDataFromBmp24File(name): | |
with open(name, "rb") as f: | |
signature = f.read(2) | |
if signature == "BM": | |
f.read(4) # Ignore data | |
f.read(2) # Ignore data | |
f.read(2) # Ignore data | |
offset, = struct.unpack('<i', f.read(4)) | |
f.seek(offset) | |
data = list() | |
byte = f.read(1) | |
while byte: | |
data.append(ord(byte)) | |
byte = f.read(1) | |
return data | |
def UnpackRawPixelBmp24Data(raw): | |
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 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 = 0x60 | |
MAX_HEIGHT = 0x40 | |
FILL_RECT_DISABLE = 0x00 | |
FILL_RECT_ENABLE = 0x01 | |
H_SCROLL_DISABLE = 0x00 | |
H_SCROLL_ENABLE = 0x01 | |
V_SCROLL_DISABLE = 0x00 | |
V_SCROLL_ENABLE = 0x01 | |
LOCK_MODE_DISABLE = 0x12 | |
LOCK_MODE_ENABLE = 0x16 | |
SET_COLUMN_ADDRESS = 0x15 | |
SET_ROW_ADDRESS = 0x75 | |
DRAW_LINE = 0x21 | |
DRAW_RECT = 0x22 | |
CLEAR_WINDOW = 0x25 | |
FILL_RECT = 0x26 | |
CONTINUOUS_SCROLLING_SETUP = 0x27 | |
DEACTIVE_SCROLLING = 0x2E | |
ACTIVE_SCROLLING = 0x2F | |
SET_CONTRAST_A = 0x81 | |
SET_CONTRAST_B = 0x82 | |
SET_CONTRAST_C = 0x83 | |
MASTER_CURRENT_CONTROL = 0x87 | |
SET_PRECHARGE_SPEED_A = 0x8A | |
SET_PRECHARGE_SPEED_B = 0x8B | |
SET_PRECHARGE_SPEED_C = 0x8C | |
SET_REMAP = 0xA0 | |
SET_DISPLAY_START_LINE = 0xA1 | |
SET_DISPLAY_OFFSET = 0xA2 | |
NORMAL_DISPLAY = 0xA4 | |
ENTIRE_DISPLAY_ON = 0xA5 | |
ENTIRE_DISPLAY_OFF = 0xA6 | |
INVERSE_DISPLAY = 0xA7 | |
SET_MULTIPLEX_RATIO = 0xA8 | |
DISPLAY_ON_DIM = 0xAC | |
SET_MASTER_CONFIGURE = 0xAD | |
DISPLAY_OFF = 0xAE | |
DISPLAY_ON = 0xAF | |
POWER_SAVE_MODE = 0xB0 | |
PHASE_1_2_PERIOD = 0xB1 | |
CLOCK_DIVIDER = 0xB3 | |
SET_PRECHARGE_VOLTAGE = 0xBB | |
SET_V_VOLTAGE = 0xBE | |
LOCK_MODE = 0xFD | |
class SSD1331: | |
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.Clear() # 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, data): | |
gpio.output(self.dc, self.COMMAND) | |
if isinstance(data, list) or isinstance(data, tuple): | |
self.spi.xfer(data) | |
return | |
def __WriteData(self, data): | |
gpio.output(self.dc, self.DATA) | |
if isinstance(data, list) or isinstance(data, tuple): | |
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.__WriteCommand([DISPLAY_OFF]) | |
self.__WriteCommand([SET_REMAP, 0x72]) | |
self.__WriteCommand([SET_DISPLAY_START_LINE, 0x00]) | |
self.__WriteCommand([SET_DISPLAY_OFFSET, 0x00]) | |
self.__WriteCommand([NORMAL_DISPLAY]) | |
self.__WriteCommand([SET_MULTIPLEX_RATIO, 0x3F]) | |
self.__WriteCommand([SET_MASTER_CONFIGURE, 0x8E]) | |
self.__WriteCommand([POWER_SAVE_MODE, 0x0B]) # Disabled. | |
self.__WriteCommand([PHASE_1_2_PERIOD, 0x74]) # Default value. | |
self.__WriteCommand([CLOCK_DIVIDER, 0xD0]) # Default value. | |
self.__WriteCommand([SET_PRECHARGE_SPEED_A, 0x80]) | |
self.__WriteCommand([SET_PRECHARGE_SPEED_B, 0x80]) | |
self.__WriteCommand([SET_PRECHARGE_SPEED_C, 0x80]) | |
self.__WriteCommand([SET_PRECHARGE_VOLTAGE, 0x3E]) # Default value. | |
self.__WriteCommand([SET_V_VOLTAGE, 0x3E]) # Default value. | |
self.__WriteCommand([MASTER_CURRENT_CONTROL, 0x0F]) | |
self.__WriteCommand([SET_CONTRAST_A, 0xFF]) | |
self.__WriteCommand([SET_CONTRAST_B, 0xFF]) | |
self.__WriteCommand([SET_CONTRAST_C, 0xFF]) | |
self.__WriteCommand([DISPLAY_ON]) | |
return | |
def Remove(self): | |
gpio.cleanup() | |
self.spi.close() | |
return | |
def DrawPixel(self, x, y, c): | |
self.__WriteCommand([SET_COLUMN_ADDRESS, x, 0x5F, SET_ROW_ADDRESS, y, 0x3F]) | |
self.__WriteData([(c >> 8) & 0xFF, c & 0xFF]) | |
return | |
def DrawLine(self, x0, y0, x1, y1, c): | |
self.__WriteCommand([DRAW_LINE, x0 & 0xFF, y0 & 0xFF, x1 & 0xFF, y1 & 0xFF]) | |
self.__WriteCommand([(c >> 11) << 1, (c >> 5) & 0x3F, (c << 1) & 0x3F]) | |
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, bg = 0): | |
self.__WriteCommand([DRAW_RECT, x0 & 0xFF, y0 & 0xFF, x1 & 0xFF, y1 & 0xFF]) | |
self.__WriteCommand([(c >> 11) << 1, (c >> 5) & 0x3F, (c << 1) & 0x3F]) | |
self.__WriteCommand([(bg >> 11) << 1, (bg >> 5) & 0x3F, (bg << 1) & 0x3F]) | |
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 > 0x5F: | |
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 > 0x5F: | |
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.__WriteCommand([SET_COLUMN_ADDRESS, 0, 0x5F, SET_ROW_ADDRESS, 0, 0x3F]) # Set the address to 0, 0. | |
for y in xrange(0, len(data), 1): | |
d = list() # Build up a list of pixel data to render an entire row per data write command. | |
for x in xrange(0, len(data[y]), 1): | |
c = Color656(data[y][x][0], data[y][x][1], data[y][x][2]) | |
d.append((c >> 8) & 0xFF) | |
d.append(c & 0xFF) | |
self.__WriteData(d) | |
return | |
def Blank(self): | |
self.EnableFillMode(True) | |
self.DrawRect(0x00, 0x00, 0x5F, 0x3F, COLOR_BLACK) | |
self.EnableFillMode(False) | |
return | |
def Clear(self): | |
self.__WriteCommand([CLEAR_WINDOW, 0x00, 0x00, 0x5F, 0x3F]) | |
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 EnableFillMode(self, enable): | |
if enable: | |
self.__WriteCommand([FILL_RECT, FILL_RECT_ENABLE]) | |
else: | |
self.__WriteCommand([FILL_RECT, FILL_RECT_DISABLE]) | |
return | |
def EnableLockMode(self, enable): | |
if enable: | |
self.__WriteCommand([LOCK_MODE, LOCK_MODE_ENABLE]) | |
else: | |
self.__WriteCommand([LOCK_MODE, LOCK_MODE_DISABLE]) | |
return | |
def SetScrollMode(self, horizontal, vertical): | |
self.EnableScrollMode(False) | |
self.__WriteCommand([CONTINUOUS_SCROLLING_SETUP, horizontal, 0x00, 0x3F, vertical, 0x00]) | |
return | |
def EnableScrollMode(self, enable): | |
if enable: | |
self.__WriteCommand([ACTIVE_SCROLLING]) | |
else: | |
self.__WriteCommand([DEACTIVE_SCROLLING]) | |
return | |
def RectTest(self): | |
self.Clear() | |
time.sleep(0.01) | |
self.EnableFillMode(True) | |
for z in xrange(0, 10, 1): | |
self.Clear() | |
time.sleep(0.01) | |
for x in xrange(0, 6, 1): | |
for y in xrange(0, 4, 1): | |
r0 = random.randint(0, 255) | |
g0 = random.randint(0, 255) | |
b0 = random.randint(0, 255) | |
r1 = random.randint(0, 255) | |
g1 = random.randint(0, 255) | |
b1 = random.randint(0, 255) | |
self.DrawRect(1 + x * 16, 1 + y * 16, (x * 16) + 14, (y * 16) + 14, Color656(r0, g0, b0), Color656(r1, g1, b1)) | |
time.sleep(1) | |
self.EnableFillMode(False) | |
for z in xrange(0, 10, 1): | |
self.Clear() | |
time.sleep(0.01) | |
for x in xrange(0, 6, 1): | |
for y in xrange(0, 4, 1): | |
r = random.randint(0, 255) | |
g = random.randint(0, 255) | |
b = random.randint(0, 255) | |
self.DrawRect(1 + x * 16, 1 + y * 16, (x * 16) + 14, (y * 16) + 14, Color656(r, g, b)) | |
time.sleep(1) | |
self.EnableFillMode(True) | |
for z in xrange(0, 10, 1): | |
self.Clear() | |
time.sleep(0.01) | |
for x in xrange(0, 6, 1): | |
for y in xrange(0, 4, 1): | |
r = random.randint(0, 255) | |
g = random.randint(0, 255) | |
b = random.randint(0, 255) | |
self.DrawRect(1 + x * 16, 1 + y * 16, (x * 16) + 14, (y * 16) + 14, Color656(r, g, b), Color656(r, g, b)) | |
time.sleep(1) | |
return | |
def LineTest(self): | |
self.Clear() | |
time.sleep(0.01) | |
for y in xrange(0, 32, 1): | |
r = random.randint(0, 255) | |
g = random.randint(0, 255) | |
b = random.randint(0, 255) | |
self.DrawLine(0x00, y * 2, 0x5F, y * 2, Color656(r, g, b)) | |
time.sleep(10) | |
return | |
def LockTest(self): | |
self.Clear() | |
time.sleep(0.01) | |
self.DrawString(21, 16, "Lock Test", COLOR_WHITE) | |
self.EnableLockMode(True) | |
self.DrawString(0, 16, "Lock Test Failed", COLOR_RED) | |
time.sleep(10) | |
self.EnableLockMode(False) | |
return | |
def CharTest(self): | |
self.Clear() | |
time.sleep(0.01) | |
i = 32 | |
for j in xrange(0, 6, 1): | |
for k in xrange(0, 16, 1): | |
self.DrawChar(k * 6, j * 8 + 4, chr(i), Color656(k * 16, 128, j * 36)) | |
i = i + 1 | |
time.sleep(10) | |
return | |
def ShapeTest(self): | |
self.Clear() | |
time.sleep(0.01) | |
self.DrawStringBg(18, 4, "Shape Test", COLOR_BLACK, COLOR_WHITE) | |
self.DrawCircle(16, 32, 10, COLOR_RED) | |
self.DrawTriangle(38, 42, 58, 42, 48, 22, COLOR_GREEN) | |
self.DrawRect(70, 22, 90, 42, COLOR_BLUE) | |
time.sleep(10) | |
return | |
def BitMapTest(self, data): | |
self.DrawFullScreenBitMap(data) | |
time.sleep(10) | |
return | |
def ScrollTest(self): | |
self.SetScrollMode(H_SCROLL_ENABLE, V_SCROLL_DISABLE) | |
self.EnableScrollMode(True) | |
time.sleep(10) | |
self.EnableScrollMode(False) | |
return | |
SSD1331_PIN_CS = 23 | |
SSD1331_PIN_DC = 24 | |
SSD1331_PIN_RST = 25 | |
if __name__ == '__main__': | |
raw_balloon = GetRawPixelDataFromBmp24File("balloon.bmp") | |
data_balloon = UnpackRawPixelBmp24Data(raw_balloon) | |
device = SSD1331(SSD1331_PIN_DC, SSD1331_PIN_RST, SSD1331_PIN_CS) | |
try: | |
device.EnableDisplay(True) | |
device.LockTest() | |
device.LineTest() | |
device.RectTest() | |
device.ScrollTest() | |
device.ShapeTest() | |
device.CharTest() | |
device.BitMapTest(data_balloon) | |
device.EnableDisplay(False) | |
finally: | |
device.Remove() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment