-
Notifications
You must be signed in to change notification settings - Fork 6
/
sh1107.py
148 lines (128 loc) · 5.47 KB
/
sh1107.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
141
142
143
144
145
146
147
148
# https://www.displayfuture.com/Display/datasheet/controller/SH1107.pdf
from micropython import const
from framebuf import FrameBuffer, MONO_VLSB, MONO_HMSB
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xA4)
SET_NORM_INV = const(0xA6)
SET_DISP = const(0xAE)
SET_DCDC_MODE = const(0xAD)
SET_MEM_MODE = const(0x20)
SET_PAGE_ADDR = const(0xB0)
SET_COL_LO_ADDR = const(0x00)
SET_COL_HI_ADDR = const(0x10)
SET_DISP_START_LINE = const(0xDC)
SET_SEG_REMAP = const(0xA0)
SET_MUX_RATIO = const(0xA8)
SET_COM_OUT_DIR = const(0xC0)
SET_DISP_OFFSET = const(0xD3)
SET_DISP_CLK_DIV = const(0xD5)
SET_PRECHARGE = const(0xD9)
SET_VCOM_DESEL = const(0xDB)
TEST_CHUNK = const(8)
class SH1107(FrameBuffer):
def __init__(self, width, height, external_vcc):
self.external_vcc = external_vcc
if width == 128 and height == 64:
self.page_mode = False
elif (width == 64 and height == 128) or (width == 128 and height == 128):
self.page_mode = True
else:
raise ValueError
self.width = width
self.height = height
self.pages = self.height // 8
self.line_bytes = self.width // 8
size = self.width * self.height // 8
self.curr_buffer = bytearray(b'\x00' * size) # self.fill(0)
self.prev_buffer = bytearray(b'\xff' * size) # force full refresh
super().__init__(self.curr_buffer, self.width, self.height, MONO_VLSB if self.page_mode else MONO_HMSB)
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_MODE | (0x00 if self.page_mode else 0x01), # 0x00 = page, 0x01 = vertical
# resolution and layout
SET_DISP_START_LINE, 0x00,
SET_SEG_REMAP | 0x00, # 0x01 rotate 180 deg
SET_COM_OUT_DIR | (0x00 if self.page_mode else 0x08), # 0x08 rotate 180 deg
SET_MUX_RATIO, 0x7f, # always this?
SET_DISP_OFFSET, 0x60 if self.width != self.height else 0x00, # offseted for 64 x 128 (Aliexpress 0.96")
# timing and driving scheme
SET_DISP_CLK_DIV, 0x50,
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
SET_VCOM_DESEL, 0x35, # 0.77 * Vcc
SET_DCDC_MODE, 0x81, # on, 0.6 * switch freq
# display
SET_CONTRAST, 0x10, # very low to avoid uneven background
SET_ENTIRE_ON | 0x00, # output follows RAM contents, not entire on
SET_NORM_INV | 0x00 # 0x00 = not inverted, 0x01 = inverted
):
self.write_cmd(cmd)
# buffers are initialized as if self.fill(0) was called
self.show()
self.poweron()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def poweron(self):
self.write_cmd(SET_DISP | 0x01)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
if self.page_mode:
self.show_page_mode()
else:
self.show_vert_mode()
self.prev_buffer[:] = self.curr_buffer
def show_page_mode(self):
for page in range(self.pages):
noffs = page * self.width
for col1, col2 in self.test_modified(noffs, self.width):
c = col1 - noffs
self.write_cmd(SET_PAGE_ADDR | page)
self.write_cmd(SET_COL_LO_ADDR | (c & 0x0f))
self.write_cmd(SET_COL_HI_ADDR | ((c & 0x70) >> 4))
self.write_data(self.curr_buffer[col1 : col2])
# print('Write offsets {} : {}, col: {}'.format(col1, col2, c))
def show_vert_mode(self):
for col in range(self.height):
noffs = col * self.line_bytes
for page1, page2 in self.test_modified(noffs, self.line_bytes):
self.write_cmd(SET_PAGE_ADDR | (page1 - noffs))
self.write_cmd(SET_COL_LO_ADDR | (col & 0x0f))
self.write_cmd(SET_COL_HI_ADDR | ((col & 0x70) >> 4))
self.write_data(self.curr_buffer[page1 : page2])
# print('Write offsets {} : {}, page: {}'.format(page1, page2, page1 - noffs))
def test_modified(self, offs, width):
ptr = offs
width += offs
while ptr < width:
# skip unmodified chunks
while ptr < width and self.curr_buffer[ptr : ptr + TEST_CHUNK] == self.prev_buffer[ptr : ptr + TEST_CHUNK]:
ptr += TEST_CHUNK
if ptr < width:
first = ptr
ptr += TEST_CHUNK
# find modified chunks
while ptr < width and self.curr_buffer[ptr : ptr + TEST_CHUNK] != self.prev_buffer[ptr : ptr + TEST_CHUNK]:
ptr += TEST_CHUNK
yield first, ptr
ptr += TEST_CHUNK
class SH1107_I2C(SH1107):
def __init__(self, width, height, i2c, addr = 0x3C, external_vcc = False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.write_list = [b'\x40', None] # Co=0, D/C#=1
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)
def write_data(self, buf):
self.write_list[1] = buf
self.i2c.writevto(self.addr, self.write_list)
# end