Skip to content

Commit 0bbd142

Browse files
author
funnygeeker
committed
update
1 parent 06cae66 commit 0bbd142

14 files changed

+958
-473
lines changed

drivers/sh1106_buf.mpy

3.14 KB
Binary file not shown.

drivers/sh1106_buf.py

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
import math
2+
import framebuf
3+
import utime as time
4+
from micropython import const
5+
6+
# a few register definitions
7+
_SET_CONTRAST = const(0x81)
8+
_SET_NORM_INV = const(0xa6)
9+
_SET_DISP = const(0xae)
10+
_SET_SCAN_DIR = const(0xc0)
11+
_SET_SEG_REMAP = const(0xa0)
12+
_LOW_COLUMN_ADDRESS = const(0x00)
13+
_HIGH_COLUMN_ADDRESS = const(0x10)
14+
_SET_PAGE_ADDRESS = const(0xB0)
15+
16+
17+
class SH1106(framebuf.FrameBuffer):
18+
def __init__(self, width, height, external_vcc, rotate=0):
19+
self.width = width
20+
self.height = height
21+
self.external_vcc = external_vcc
22+
self.flip_en = rotate == 180 or rotate == 270
23+
self.rotate90 = rotate == 90 or rotate == 270
24+
self.pages = self.height // 8
25+
self.bufsize = self.pages * self.width
26+
self.buffer = bytearray(self.bufsize)
27+
self.pages_to_update = 0
28+
29+
if self.rotate90:
30+
self.displaybuf = bytearray(self.bufsize)
31+
# HMSB is required to keep the bit order in the render buffer
32+
# compatible with byte-for-byte remapping to the display buffer,
33+
# which is in VLSB. Else we'd have to copy bit-by-bit!
34+
super().__init__(self.buffer, self.height, self.width,
35+
framebuf.MONO_HMSB)
36+
else:
37+
self.displaybuf = self.buffer
38+
super().__init__(self.buffer, self.width, self.height,
39+
framebuf.MONO_VLSB)
40+
41+
# flip() was called rotate() once, provide backwards compatibility.
42+
self.rotate = self.flip
43+
self.init_display()
44+
self.back_light(255)
45+
46+
def init_display(self):
47+
self.reset()
48+
self.fill(0)
49+
self.show()
50+
self.poweron()
51+
# rotate90 requires a call to flip() for setting up.
52+
self.flip(self.flip_en)
53+
54+
def poweroff(self):
55+
self.write_cmd(_SET_DISP | 0x00)
56+
57+
def poweron(self):
58+
self.write_cmd(_SET_DISP | 0x01)
59+
if self.delay:
60+
time.sleep_ms(self.delay)
61+
62+
def flip(self, flag=None, update=True):
63+
if flag is None:
64+
flag = not self.flip_en
65+
mir_v = flag ^ self.rotate90
66+
mir_h = flag
67+
self.write_cmd(_SET_SEG_REMAP | (0x01 if mir_v else 0x00))
68+
self.write_cmd(_SET_SCAN_DIR | (0x08 if mir_h else 0x00))
69+
self.flip_en = flag
70+
if update:
71+
self.show(True) # full update
72+
73+
def sleep(self, value):
74+
self.write_cmd(_SET_DISP | (not value))
75+
76+
def contrast(self, contrast):
77+
self.write_cmd(_SET_CONTRAST)
78+
self.write_cmd(contrast)
79+
80+
def back_light(self, value):
81+
"""
82+
背光调节
83+
84+
Args:
85+
value: 背光等级 0 ~ 255
86+
"""
87+
self.contrast(value)
88+
89+
def rotate(self, rotate):
90+
"""
91+
设置显示旋转
92+
93+
Args:
94+
rotate(int):
95+
- 0-Portrait
96+
- 1-Upper right printing left (backwards) (X Flip)
97+
- 2-Inverted Portrait
98+
- 3-Lower left printing up (backwards) (Y Flip)
99+
"""
100+
rotate %= 4
101+
mir_v = False
102+
mir_h = False
103+
if rotate == 0:
104+
mir_v = True
105+
mir_h = True
106+
elif rotate == 1:
107+
mir_h = True
108+
elif rotate == 2:
109+
pass
110+
elif rotate == 3:
111+
mir_v = True
112+
self.write_cmd(_SET_SEG_REMAP | (0x01 if mir_v else 0x00))
113+
self.write_cmd(_SET_SCAN_DIR | (0x08 if mir_h else 0x00))
114+
self.show()
115+
116+
def invert(self, invert):
117+
"""
118+
Invert mode, If true, switch to invert mode (black-on-white), else normal mode (white-on-black)
119+
"""
120+
self.write_cmd(_SET_NORM_INV | (invert & 1))
121+
122+
def show(self, full_update=False):
123+
# self.* lookups in loops take significant time (~4fps).
124+
(w, p, db, rb) = (self.width, self.pages,
125+
self.displaybuf, self.buffer)
126+
if self.rotate90:
127+
for i in range(self.bufsize):
128+
db[w * (i % p) + (i // p)] = rb[i]
129+
if full_update:
130+
pages_to_update = (1 << self.pages) - 1
131+
else:
132+
pages_to_update = self.pages_to_update
133+
# print("Updating pages: {:08b}".format(pages_to_update))
134+
for page in range(self.pages):
135+
if (pages_to_update & (1 << page)):
136+
self.write_cmd(_SET_PAGE_ADDRESS | page)
137+
self.write_cmd(_LOW_COLUMN_ADDRESS | 2)
138+
self.write_cmd(_HIGH_COLUMN_ADDRESS | 0)
139+
self.write_data(db[(w * page):(w * page + w)])
140+
self.pages_to_update = 0
141+
142+
def pixel(self, x, y, color=None):
143+
if color is None:
144+
return super().pixel(x, y)
145+
else:
146+
super().pixel(x, y, color)
147+
page = y // 8
148+
self.pages_to_update |= 1 << page
149+
150+
def text(self, text, x, y, color=1):
151+
super().text(text, x, y, color)
152+
self.register_updates(y, y + 7)
153+
154+
def line(self, x0, y0, x1, y1, color):
155+
super().line(x0, y0, x1, y1, color)
156+
self.register_updates(y0, y1)
157+
158+
def hline(self, x, y, w, color):
159+
super().hline(x, y, w, color)
160+
self.register_updates(y)
161+
162+
def vline(self, x, y, h, color):
163+
super().vline(x, y, h, color)
164+
self.register_updates(y, y + h - 1)
165+
166+
def fill(self, color):
167+
super().fill(color)
168+
self.pages_to_update = (1 << self.pages) - 1
169+
170+
def blit(self, fbuf, x, y, key=-1, palette=None):
171+
super().blit(fbuf, x, y, key, palette)
172+
self.register_updates(y, y + self.height)
173+
174+
def scroll(self, x, y):
175+
# my understanding is that scroll() does a full screen change
176+
super().scroll(x, y)
177+
self.pages_to_update = (1 << self.pages) - 1
178+
179+
def fill_rect(self, x, y, w, h, color):
180+
super().fill_rect(x, y, w, h, color)
181+
self.register_updates(y, y + h - 1)
182+
183+
def rect(self, x, y, w, h, color):
184+
super().rect(x, y, w, h, color)
185+
self.register_updates(y, y + h - 1)
186+
187+
def circle(self, x, y, radius, c, section=100):
188+
"""
189+
画圆
190+
191+
Args:
192+
c: 颜色
193+
x: 中心 x 坐标
194+
y: 中心 y 坐标
195+
radius: 半径
196+
section: 分段
197+
"""
198+
arr = []
199+
for m in range(section + 1):
200+
_x = round(radius * math.cos((2 * math.pi / section) * m - math.pi) + x)
201+
_y = round(radius * math.sin((2 * math.pi / section) * m - math.pi) + y)
202+
arr.append([_x, _y])
203+
for i in range(len(arr) - 1):
204+
self.line(*arr[i], *arr[i + 1], c)
205+
206+
def fill_circle(self, x, y, radius, c):
207+
"""
208+
画填充圆
209+
210+
Args:
211+
c: 颜色
212+
x: 中心 x 坐标
213+
y: 中心 y 坐标
214+
radius: 半径
215+
"""
216+
rsq = radius * radius
217+
for _x in range(radius):
218+
_y = int(math.sqrt(rsq - _x * _x)) # 计算 y 坐标
219+
y0 = y - _y
220+
end_y = y0 + _y * 2
221+
y0 = max(0, min(y0, self.height)) # 将 y0 限制在画布的范围内
222+
length = abs(end_y - y0) + 1
223+
self.vline(x + _x, y0, length, c) # 绘制左右两侧的垂直线
224+
self.vline(x - _x, y0, length, c)
225+
226+
def register_updates(self, y0, y1=None):
227+
# this function takes the top and optional bottom address of the changes made
228+
# and updates the pages_to_change list with any changed pages
229+
# that are not yet on the list
230+
start_page = max(0, y0 // 8)
231+
end_page = max(0, y1 // 8) if y1 is not None else start_page
232+
# rearrange start_page and end_page if coordinates were given from bottom to top
233+
if start_page > end_page:
234+
start_page, end_page = end_page, start_page
235+
for page in range(start_page, end_page + 1):
236+
self.pages_to_update |= 1 << page
237+
238+
def reset(self, res):
239+
if res is not None:
240+
res(1)
241+
time.sleep_ms(1)
242+
res(0)
243+
time.sleep_ms(20)
244+
res(1)
245+
time.sleep_ms(20)
246+
247+
248+
class SH1106_I2C(SH1106):
249+
def __init__(self, width, height, i2c, res=None, addr=0x3c,
250+
rotate=0, external_vcc=False, delay=0):
251+
self.i2c = i2c
252+
self.addr = addr
253+
self.res = res
254+
self.temp = bytearray(2)
255+
self.delay = delay
256+
if res is not None:
257+
res.init(res.OUT, value=1)
258+
super().__init__(width, height, external_vcc, rotate)
259+
260+
def write_cmd(self, cmd):
261+
self.temp[0] = 0x80 # Co=1, D/C#=0
262+
self.temp[1] = cmd
263+
self.i2c.writeto(self.addr, self.temp)
264+
265+
def write_data(self, buf):
266+
self.i2c.writeto(self.addr, b'\x40' + buf)
267+
268+
def reset(self):
269+
super().reset(self.res)
270+
271+
272+
class SH1106_SPI(SH1106):
273+
def __init__(self, width, height, spi, dc, res=None, cs=None,
274+
rotate=0, external_vcc=False, delay=0):
275+
dc.init(dc.OUT, value=0)
276+
if res is not None:
277+
res.init(res.OUT, value=0)
278+
if cs is not None:
279+
cs.init(cs.OUT, value=1)
280+
self.spi = spi
281+
self.dc = dc
282+
self.res = res
283+
self.cs = cs
284+
self.delay = delay
285+
super().__init__(width, height, external_vcc, rotate)
286+
287+
def write_cmd(self, cmd):
288+
if self.cs is not None:
289+
self.cs(1)
290+
self.dc(0)
291+
self.cs(0)
292+
self.spi.write(bytearray([cmd]))
293+
self.cs(1)
294+
else:
295+
self.dc(0)
296+
self.spi.write(bytearray([cmd]))
297+
298+
def write_data(self, buf):
299+
if self.cs is not None:
300+
self.cs(1)
301+
self.dc(1)
302+
self.cs(0)
303+
self.spi.write(buf)
304+
self.cs(1)
305+
else:
306+
self.dc(1)
307+
self.spi.write(buf)
308+
309+
def reset(self):
310+
super().reset(self.res)

drivers/ssd1306_buf.mpy

-135 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)