-
Notifications
You must be signed in to change notification settings - Fork 2
/
lib_oled96.py
140 lines (121 loc) · 4.88 KB
/
lib_oled96.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
#!/usr/bin/env python
# Substantially derived from code by (c) 2015 Richard Hull The MIT License (MIT)
# https://github.com/rm-hull/ssd1306:
# "Permission is hereby granted, free of charge, to any person obtaining a copy
# "of this software and associated documentation files (the "Software"), to deal
# "in the Software without restriction, including without limitation the rights
# "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# "copies of the Software, and to permit persons to whom the Software is
# "furnished to do so, subject to the following conditions:
# "The above copyright notice and this permission notice shall be included in
# "all copies or substantial portions of the Software."
#
# B Lavery 2015:
# This derivative "library" module is not installed to the python system as Hull's version was:
# it simply resides alongside your own python script.
# In this version, the I2C bus object needs to be handed in as a parameter from your user code.
# This makes this one same library file work with either Raspberry Pi or Virtual GPIO system.
# Hull's (clever) auto-displaying "canvas" is replaced by a persistent draw object
# which can be incrementally changed. This canvas needs coded "display()" calls to push to the hardware.
from PIL import Image, ImageDraw
class ssd1306():
def __init__(self, bus, address=0x3C):
self.cmd_mode = 0x00
self.data_mode = 0x40
self.bus = bus
self.addr = address
self.width = 128
self.height = 64
self.pages = int(self.height / 8)
self.image = Image.new('1', (self.width, self.height))
self.canvas = ImageDraw.Draw(self.image) # this is a "draw" object for preparing display contents
self._command(
const.DISPLAYOFF,
const.SETDISPLAYCLOCKDIV, 0x80,
const.SETMULTIPLEX, 0x3F,
const.SETDISPLAYOFFSET, 0x00,
const.SETSTARTLINE,
const.CHARGEPUMP, 0x14,
const.MEMORYMODE, 0x00,
const.SEGREMAP,
const.COMSCANDEC,
const.SETCOMPINS, 0x12,
const.SETCONTRAST, 0xCF,
const.SETPRECHARGE, 0xF1,
const.SETVCOMDETECT, 0x40,
const.DISPLAYALLON_RESUME,
const.NORMALDISPLAY,
const.DISPLAYON)
def _command(self, *cmd):
"""
Sends a command or sequence of commands through to the
device - maximum allowed is 32 bytes in one go.
LIMIT ON ARDUINO: CMD BYTE + 31 = 32, SO LIMIT TO 31 bl
"""
assert(len(cmd) <= 31)
self.bus.write_i2c_block_data(self.addr, self.cmd_mode, list(cmd))
def _data(self, data):
"""
Sends a data byte or sequence of data bytes through to the
device - maximum allowed in one transaction is 32 bytes, so if
data is larger than this it is sent in chunks.
In our library, only data operation used is 128x64 long, ie whole canvas.
"""
for i in range(0, len(data), 31):
self.bus.write_i2c_block_data(self.addr, self.data_mode, list(data[i:i+31]))
def display(self):
"""
The image on the "canvas" is flushed through to the hardware display.
Takes the 1-bit image and dumps it to the SSD1306 OLED display.
"""
self._command(
const.COLUMNADDR, 0x00, self.width-1, # Column start/end address
const.PAGEADDR, 0x00, self.pages-1) # Page start/end address
pix = list(self.image.getdata())
step = self.width * 8
buf = []
for y in range(0, self.pages * step, step):
i = y + self.width-1
while i >= y:
byte = 0
for n in range(0, step, self.width):
byte |= (pix[i + n] & 0x01) << 8
byte >>= 1
buf.append(byte)
i -= 1
self._data(buf) # push out the whole lot
def cls(self):
self.canvas.rectangle((0, 0, self.width-1, self.height-1), outline=0, fill=0)
self.display()
def onoff(self, onoff):
if onoff == 0:
self._command(const.DISPLAYOFF)
else:
self._command(const.DISPLAYON)
class const:
CHARGEPUMP = 0x8D
COLUMNADDR = 0x21
COMSCANDEC = 0xC8
COMSCANINC = 0xC0
DISPLAYALLON = 0xA5
DISPLAYALLON_RESUME = 0xA4
DISPLAYOFF = 0xAE
DISPLAYON = 0xAF
EXTERNALVCC = 0x1
INVERTDISPLAY = 0xA7
MEMORYMODE = 0x20
NORMALDISPLAY = 0xA6
PAGEADDR = 0x22
SEGREMAP = 0xA0
SETCOMPINS = 0xDA
SETCONTRAST = 0x81
SETDISPLAYCLOCKDIV = 0xD5
SETDISPLAYOFFSET = 0xD3
SETHIGHCOLUMN = 0x10
SETLOWCOLUMN = 0x00
SETMULTIPLEX = 0xA8
SETPRECHARGE = 0xD9
SETSEGMENTREMAP = 0xA1
SETSTARTLINE = 0x40
SETVCOMDETECT = 0xDB
SWITCHCAPVCC = 0x2