forked from jeffmer/micropython-ili9341
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ili934xnew.py
289 lines (260 loc) · 9.93 KB
/
ili934xnew.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# This is an adapted version of the ILI934X driver as below.
# It works with multiple fonts and also works with the esp32 H/W SPI implementation
# Also includes a word wrap print function
# Proportional fonts are generated by Peter Hinch's Font-to-py
# MIT License; Copyright (c) 2017 Jeffrey N. Magee
# This file is part of MicroPython ILI934X driver
# Copyright (c) 2016 - 2017 Radomir Dopieralski, Mika Tuupola
#
# Licensed under the MIT license:
# http://www.opensource.org/licenses/mit-license.php
#
# Project home:
# https://github.com/tuupola/micropython-ili934x
import time
import ustruct
import glcdfont
import framebuf
from micropython import const
_RDDSDR = const(0x0f) # Read Display Self-Diagnostic Result
_SLPOUT = const(0x11) # Sleep Out
_GAMSET = const(0x26) # Gamma Set
_DISPOFF = const(0x28) # Display Off
_DISPON = const(0x29) # Display On
_CASET = const(0x2a) # Column Address Set
_PASET = const(0x2b) # Page Address Set
_RAMWR = const(0x2c) # Memory Write
_RAMRD = const(0x2e) # Memory Read
_MADCTL = const(0x36) # Memory Access Control
_VSCRSADD = const(0x37) # Vertical Scrolling Start Address
_PIXSET = const(0x3a) # Pixel Format Set
_PWCTRLA = const(0xcb) # Power Control A
_PWCRTLB = const(0xcf) # Power Control B
_DTCTRLA = const(0xe8) # Driver Timing Control A
_DTCTRLB = const(0xea) # Driver Timing Control B
_PWRONCTRL = const(0xed) # Power on Sequence Control
_PRCTRL = const(0xf7) # Pump Ratio Control
_PWCTRL1 = const(0xc0) # Power Control 1
_PWCTRL2 = const(0xc1) # Power Control 2
_VMCTRL1 = const(0xc5) # VCOM Control 1
_VMCTRL2 = const(0xc7) # VCOM Control 2
_FRMCTR1 = const(0xb1) # Frame Rate Control 1
_DISCTRL = const(0xb6) # Display Function Control
_ENA3G = const(0xf2) # Enable 3G
_PGAMCTRL = const(0xe0) # Positive Gamma Control
_NGAMCTRL = const(0xe1) # Negative Gamma Control
_CHUNK = const(1024) #maximum number of pixels per spi write
def color565(r, g, b):
return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3
class ILI9341:
width = 320
height = 240
def __init__(self, spi, cs, dc, rst):
self.spi = spi
self.cs = cs
self.dc = dc
self.rst = rst
self.cs.init(self.cs.OUT, value=1)
self.dc.init(self.dc.OUT, value=0)
self.rst.init(self.rst.OUT, value=0)
self.reset()
self.init()
self._scroll = 0
self._buf = bytearray(_CHUNK * 2)
self._colormap = bytearray(b'\x00\x00\xFF\xFF') #default white foregraound, black background
self._x = 0
self._y = 0
self._font = glcdfont
self.scrolling = False
def set_color(self,fg,bg):
self._colormap[0] = bg>>8
self._colormap[1] = bg & 255
self._colormap[2] = fg>>8
self._colormap[3] = fg & 255
def set_pos(self,x,y):
self._x = x
self._y = y
def reset_scroll(self):
self.scrolling = False
self._scroll = 0
self.scroll(0)
def set_font(self, font):
self._font = font
def init(self):
for command, data in (
(_RDDSDR, b"\x03\x80\x02"),
(_PWCRTLB, b"\x00\xc1\x30"),
(_PWRONCTRL, b"\x64\x03\x12\x81"),
(_DTCTRLA, b"\x85\x00\x78"),
(_PWCTRLA, b"\x39\x2c\x00\x34\x02"),
(_PRCTRL, b"\x20"),
(_DTCTRLB, b"\x00\x00"),
(_PWCTRL1, b"\x23"),
(_PWCTRL2, b"\x10"),
(_VMCTRL1, b"\x3e\x28"),
(_VMCTRL2, b"\x86"),
#(_MADCTL, b"\x48"),
(_MADCTL, b"\x08"),
(_PIXSET, b"\x55"),
(_FRMCTR1, b"\x00\x18"),
(_DISCTRL, b"\x08\x82\x27"),
(_ENA3G, b"\x00"),
(_GAMSET, b"\x01"),
(_PGAMCTRL, b"\x0f\x31\x2b\x0c\x0e\x08\x4e\xf1\x37\x07\x10\x03\x0e\x09\x00"),
(_NGAMCTRL, b"\x00\x0e\x14\x03\x11\x07\x31\xc1\x48\x08\x0f\x0c\x31\x36\x0f")):
self._write(command, data)
self._write(_SLPOUT)
time.sleep_ms(120)
self._write(_DISPON)
def reset(self):
self.rst(0)
time.sleep_ms(50)
self.rst(1)
time.sleep_ms(50)
def _write(self, command, data=None):
self.dc(0)
self.cs(0)
self.spi.write(bytearray([command]))
self.cs(1)
if data is not None:
self._data(data)
def _data(self, data):
self.dc(1)
self.cs(0)
self.spi.write(data)
self.cs(1)
def _writeblock(self, x0, y0, x1, y1, data=None):
self._write(_CASET, ustruct.pack(">HH", x0, x1))
self._write(_PASET, ustruct.pack(">HH", y0, y1))
self._write(_RAMWR, data)
def _readblock(self, x0, y0, x1, y1):
self._write(_CASET, ustruct.pack(">HH", x0, x1))
self._write(_PASET, ustruct.pack(">HH", y0, y1))
if data is None:
return self._read(_RAMRD, (x1 - x0 + 1) * (y1 - y0 + 1) * 3)
def _read(self, command, count):
self.dc(0)
self.cs(0)
self.spi.write(bytearray([command]))
data = self.spi.read(count)
self.cs(1)
return data
def pixel(self, x, y, color=None):
if color is None:
r, b, g = self._readblock(x, y, x, y)
return color565(r, g, b)
if not 0 <= x < self.width or not 0 <= y < self.height:
return
self._writeblock(x, y, x, y, ustruct.pack(">H", color))
def fill_rectangle(self, x, y, w, h, color=None):
x = min(self.width - 1, max(0, x))
y = min(self.height - 1, max(0, y))
w = min(self.width - x, max(1, w))
h = min(self.height - y, max(1, h))
if color:
color = ustruct.pack(">H", color)
else:
color = self._colormap[0:2] #background
for i in range(_CHUNK):
self._buf[2*i]=color[0]; self._buf[2*i+1]=color[1]
chunks, rest = divmod(w * h, _CHUNK)
self._writeblock(x, y, x + w - 1, y + h - 1, None)
if chunks:
for count in range(chunks):
self._data(self._buf)
if rest != 0:
mv = memoryview(self._buf)
self._data(mv[:rest*2])
def erase(self):
self.fill_rectangle(0, 0, self.width, self.height)
def blit(self, bitbuff, x, y, w, h):
x = min(self.width - 1, max(0, x))
y = min(self.height - 1, max(0, y))
w = min(self.width - x, max(1, w))
h = min(self.height - y, max(1, h))
chunks, rest = divmod(w * h, _CHUNK)
self._writeblock(x, y, x + w - 1, y + h - 1, None)
written = 0
for iy in range(h):
for ix in range(w):
index = ix+iy*w - written
if index >=_CHUNK:
self._data(self._buf)
written += _CHUNK
index -= _CHUNK
c = bitbuff.pixel(ix,iy)
self._buf[index*2] = self._colormap[c*2]
self._buf[index*2+1] = self._colormap[c*2+1]
rest = w*h - written
if rest != 0:
mv = memoryview(self._buf)
self._data(mv[:rest*2])
def chars(self, str, x, y):
str_w = self._font.get_width(str)
div, rem = divmod(self._font.height(),8)
nbytes = div+1 if rem else div
buf = bytearray(str_w * nbytes)
pos = 0
for ch in str:
glyph, char_w = self._font.get_ch(ch)
for row in range(nbytes):
index = row*str_w + pos
for i in range(char_w):
buf[index+i] = glyph[nbytes*i+row]
pos += char_w
fb = framebuf.FrameBuffer(buf,str_w, self._font.height(), framebuf.MONO_VLSB)
self.blit(fb,x,y,str_w,self._font.height())
return x+str_w
def scroll(self, dy):
self._scroll = (self._scroll + dy) % self.height
self._write(_VSCRSADD, ustruct.pack(">H", self._scroll))
def next_line(self, cury, char_h):
global scrolling
if not self.scrolling:
res = cury + char_h
self.scrolling = (res >= self.height)
if self.scrolling:
self.scroll(char_h)
res = (self.height - char_h + self._scroll)%self.height
self.fill_rectangle(0, res, self.width, self._font.height())
return res
def write(self, text): #does character wrap, compatible with stream output
curx = self._x; cury = self._y
char_h = self._font.height()
width = 0
written = 0
for pos, ch in enumerate(text):
if ch == '\n':
if pos>0:
self.chars(text[written:pos],curx,cury)
curx = 0; written = pos+1; width = 0
cury = self.next_line(cury,char_h)
else:
char_w = self._font.get_width(ch)
if curx + width + char_w >= self.width:
self.chars(text[written:pos], curx,cury)
curx = 0 ; written = pos; width = char_h
cury = self.next_line(cury,char_h)
else:
width += char_w
if written<len(text):
curx = self.chars(text[written:], curx,cury)
self._x = curx; self._y = cury
def print(self, text): #does word wrap, leaves self._x unchanged
cury = self._y; curx = self._x
char_h = self._font.height()
char_w = self._font.max_width()
lines = text.split('\n')
for line in lines:
words = line.split(' ')
for word in words:
if curx + self._font.get_width(word) >= self.width:
curx = self._x; cury = self.next_line(cury,char_h)
while self._font.get_width(word) > self.width:
self.chars(word[:self.width//char_w],curx,cury)
word = word[self.width//char_w:]
cury = self.next_line(cury,char_h)
if len(word)>0:
curx = self.chars(word+' ', curx,cury)
curx = self._x; cury = self.next_line(cury,char_h)
self._y = cury