Skip to content

Commit 62ee18a

Browse files
author
funnygeeker
committed
Add ink screen driver experimentally
1 parent 75baf8b commit 62ee18a

File tree

2 files changed

+283
-0
lines changed

2 files changed

+283
-0
lines changed

driver/epaper_buf.mpy

3.03 KB
Binary file not shown.

driver/epaper_buf.py

Lines changed: 283 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
# 适用于 E-Paper 的 Framebuffer 驱动
2+
# Github: https://github.com/funnygeeker/micropython-easydisplay
3+
# Author: funnygeeker
4+
# Licence: MIT
5+
# Date: 2024/2/17
6+
#
7+
# 参考资料:
8+
# https://github.com/mcauser/micropython-waveshare-epaper
9+
# https://github.com/AntonVanke/MicroPython-uFont/blob/master/driver/e1in54.py
10+
import math
11+
from struct import pack
12+
from time import sleep_ms
13+
from machine import Pin, PWM
14+
from micropython import const
15+
from framebuf import FrameBuffer, MONO_HLSB
16+
17+
# Display resolution
18+
EPD_WIDTH = const(200)
19+
EPD_HEIGHT = const(200)
20+
21+
# Display commands
22+
DRIVER_OUTPUT_CONTROL = const(0x01)
23+
BOOSTER_SOFT_START_CONTROL = const(0x0C)
24+
#GATE_SCAN_START_POSITION = const(0x0F)
25+
DEEP_SLEEP_MODE = const(0x10)
26+
DATA_ENTRY_MODE_SETTING = const(0x11)
27+
#SW_RESET = const(0x12)
28+
#TEMPERATURE_SENSOR_CONTROL = const(0x1A)
29+
MASTER_ACTIVATION = const(0x20)
30+
#DISPLAY_UPDATE_CONTROL_1 = const(0x21)
31+
DISPLAY_UPDATE_CONTROL_2 = const(0x22)
32+
WRITE_RAM = const(0x24)
33+
WRITE_VCOM_REGISTER = const(0x2C)
34+
WRITE_LUT_REGISTER = const(0x32)
35+
SET_DUMMY_LINE_PERIOD = const(0x3A)
36+
SET_GATE_TIME = const(0x3B) # not in datasheet
37+
#BORDER_WAVEFORM_CONTROL = const(0x3C)
38+
SET_RAM_X_ADDRESS_START_END_POSITION = const(0x44)
39+
SET_RAM_Y_ADDRESS_START_END_POSITION = const(0x45)
40+
SET_RAM_X_ADDRESS_COUNTER = const(0x4E)
41+
SET_RAM_Y_ADDRESS_COUNTER = const(0x4F)
42+
TERMINATE_FRAME_READ_WRITE = const(0xFF) # aka NOOP
43+
44+
BUSY = const(1) # 1=busy, 0=idle
45+
46+
class EPD(FrameBuffer):
47+
LUT_FULL_UPDATE = bytearray(b'\x02\x02\x01\x11\x12\x12\x22\x22\x66\x69\x69\x59\x58\x99\x99\x88\x00\x00\x00\x00\xF8\xB4\x13\x51\x35\x51\x51\x19\x01\x00')
48+
LUT_PARTIAL_UPDATE = bytearray(b'\x10\x18\x18\x08\x18\x18\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x13\x14\x44\x12\x00\x00\x00\x00\x00\x00')
49+
50+
def __init__(self, width: int, height: int, spi, res: int, dc: int, busy:int,
51+
cs: int = None, bl: int = None):
52+
"""
53+
初始化屏幕驱动
54+
55+
Args:
56+
width: 宽度
57+
height: 高度
58+
spi: SPI 实例
59+
res: RESET 引脚
60+
dc: Data / Command 引脚
61+
busy:
62+
cs: 片选引脚
63+
bl: 背光引脚
64+
"""
65+
self.width = width
66+
self.height = height
67+
self.spi = spi
68+
self.res = Pin(res, Pin.OUT, Pin.PULL_DOWN, value=0)
69+
self.dc = Pin(dc, Pin.OUT, Pin.PULL_DOWN, value=0)
70+
self.busy = Pin(busy, Pin.IN)
71+
if cs is None:
72+
self.cs = int
73+
else:
74+
self.cs = Pin(cs, Pin.OUT, Pin.PULL_DOWN, value=1)
75+
if bl is not None:
76+
self.bl = PWM(Pin(bl, Pin.OUT))
77+
self.back_light(255)
78+
else:
79+
self.bl = None
80+
self.pages = self.height // 8
81+
self.buffer = bytearray(self.width * self.pages)
82+
super().__init__(self.buffer, self.width, self.height, MONO_HLSB)
83+
self.init()
84+
85+
def init(self):
86+
self.hard_reset()
87+
self._write(DRIVER_OUTPUT_CONTROL)
88+
self.write_data(bytearray([(EPD_HEIGHT - 1) & 0xFF]))
89+
self.write_data(bytearray([((EPD_HEIGHT - 1) >> 8) & 0xFF]))
90+
self.write_data(bytearray([0x00])) # GD = 0 SM = 0 TB = 0
91+
self._write(BOOSTER_SOFT_START_CONTROL, b'\xD7\xD6\x9D')
92+
self._write(WRITE_VCOM_REGISTER, b'\xA8') # VCOM 7C
93+
self._write(SET_DUMMY_LINE_PERIOD, b'\x1A') # 4 dummy lines per gate
94+
self._write(SET_GATE_TIME, b'\x08') # 2us per line
95+
self._write(DATA_ENTRY_MODE_SETTING, b'\x03') # X increment Y increment
96+
self.set_lut(self.LUT_FULL_UPDATE)
97+
98+
def _write(self, command=None, data=None):
99+
"""SPI write to the device: commands and data."""
100+
self.cs(0)
101+
if command is not None:
102+
self.dc(0)
103+
self.spi.write(bytes([command]))
104+
if data is not None:
105+
self.dc(1)
106+
self.spi.write(data)
107+
self.cs(1)
108+
109+
def write_cmd(self, cmd):
110+
"""
111+
写命令
112+
113+
Args:
114+
cmd: 命令内容
115+
"""
116+
self.cs(0)
117+
self.dc(0)
118+
self.spi.write(bytes([cmd]))
119+
self.cs(1)
120+
121+
def write_data(self, data):
122+
"""
123+
写数据
124+
125+
Args:
126+
data: 数据内容
127+
"""
128+
self.cs(0)
129+
self.dc(1)
130+
self.spi.write(data)
131+
self.cs(1)
132+
133+
def wait_until_idle(self):
134+
while self.busy.value() == BUSY:
135+
sleep_ms(50)
136+
137+
def hard_reset(self):
138+
"""
139+
Hard reset display.
140+
"""
141+
self.res(0)
142+
sleep_ms(100)
143+
self.res(1)
144+
sleep_ms(100)
145+
146+
def soft_reset(self):
147+
# Function not realized
148+
pass
149+
150+
def set_lut(self, lut):
151+
self._write(WRITE_LUT_REGISTER, lut)
152+
153+
def set_refresh(self, full_update=True):
154+
"""
155+
Set the refresh mode
156+
157+
Args:
158+
full_update: Full screen refresh
159+
"""
160+
self.set_lut(self.LUT_FULL_UPDATE) if full_update else self.set_lut(self.LUT_PARTIAL_UPDATE)
161+
162+
# put an image in the frame memory
163+
def set_frame_memory(self, image, x, y, w, h):
164+
# x point must be the multiple of 8 or the last 3 bits will be ignored
165+
x = x & 0xF8
166+
w = w & 0xF8
167+
168+
if x + w >= self.width:
169+
x_end = self.width - 1
170+
else:
171+
x_end = x + w - 1
172+
173+
if y + h >= self.height:
174+
y_end = self.height - 1
175+
else:
176+
y_end = y + h - 1
177+
178+
self.set_window(x, y, x_end, y_end)
179+
self.set_memory_pointer(x, y)
180+
self._write(WRITE_RAM, image)
181+
182+
# replace the frame memory with the specified color
183+
def clear_frame_memory(self, color):
184+
self.set_window(0, 0, self.width - 1, self.height - 1)
185+
self.set_memory_pointer(0, 0)
186+
self._write(WRITE_RAM)
187+
# send the color data
188+
for i in range(0, self.width // 8 * self.height):
189+
self.write_data(bytearray([color]))
190+
191+
# draw the current frame memory and switch to the next memory area
192+
def display_frame(self):
193+
self._write(DISPLAY_UPDATE_CONTROL_2, b'\xC4')
194+
self._write(MASTER_ACTIVATION)
195+
self._write(TERMINATE_FRAME_READ_WRITE)
196+
self.wait_until_idle()
197+
198+
# specify the memory area for data R/W
199+
def set_memory_area(self, x_start, y_start, x_end, y_end):
200+
self._write(SET_RAM_X_ADDRESS_START_END_POSITION)
201+
# x point must be the multiple of 8 or the last 3 bits will be ignored
202+
self.write_data(bytearray([(x_start >> 3) & 0xFF]))
203+
self.write_data(bytearray([(x_end >> 3) & 0xFF]))
204+
self._write(SET_RAM_Y_ADDRESS_START_END_POSITION, pack("<HH", y_start, y_end))
205+
206+
# specify the start point for data R/W
207+
def set_memory_pointer(self, x, y):
208+
self._write(SET_RAM_X_ADDRESS_COUNTER)
209+
# x point must be the multiple of 8 or the last 3 bits will be ignored
210+
self.write_data(bytearray([(x >> 3) & 0xFF]))
211+
self._write(SET_RAM_Y_ADDRESS_COUNTER, pack("<H", y))
212+
self.wait_until_idle()
213+
214+
def clear(self):
215+
self.fill(0)
216+
217+
218+
def show(self):
219+
self.set_frame_memory(self.buffer, 0, 0, 200, 200)
220+
self.display_frame()
221+
222+
# to wake call reset() or init()
223+
def poweroff(self):
224+
"""Enable display sleep mode."""
225+
self._write(DEEP_SLEEP_MODE, b'\x01') # enter deep sleep A0=1, A0=0 power on
226+
self.wait_until_idle()
227+
228+
def poweron(self):
229+
"""Disable display sleep mode."""
230+
self.hard_reset()
231+
self.wait_until_idle()
232+
233+
def back_light(self, value):
234+
"""
235+
背光调节
236+
237+
Args:
238+
value: 背光等级 0 ~ 255
239+
"""
240+
self.bl.freq(1000)
241+
if value >= 0xff:
242+
value = 0xff
243+
data = value * 0xffff >> 8
244+
self.bl.duty_u16(data)
245+
246+
def circle(self, x, y, radius, c, section=100):
247+
"""
248+
画圆
249+
250+
Args:
251+
c: 颜色
252+
x: 中心 x 坐标
253+
y: 中心 y 坐标
254+
radius: 半径
255+
section: 分段
256+
"""
257+
arr = []
258+
for m in range(section + 1):
259+
_x = round(radius * math.cos((2 * math.pi / section) * m - math.pi) + x)
260+
_y = round(radius * math.sin((2 * math.pi / section) * m - math.pi) + y)
261+
arr.append([_x, _y])
262+
for i in range(len(arr) - 1):
263+
self.line(*arr[i], *arr[i + 1], c)
264+
265+
def fill_circle(self, x, y, radius, c):
266+
"""
267+
画填充圆
268+
269+
Args:
270+
c: 颜色
271+
x: 中心 x 坐标
272+
y: 中心 y 坐标
273+
radius: 半径
274+
"""
275+
rsq = radius * radius
276+
for _x in range(radius):
277+
_y = int(math.sqrt(rsq - _x * _x)) # 计算 y 坐标
278+
y0 = y - _y
279+
end_y = y0 + _y * 2
280+
y0 = max(0, min(y0, self.height)) # 将 y0 限制在画布的范围内
281+
length = abs(end_y - y0) + 1
282+
self.vline(x + _x, y0, length, c) # 绘制左右两侧的垂直线
283+
self.vline(x - _x, y0, length, c)

0 commit comments

Comments
 (0)